/* Foglio di stile del cruscotto Blasat Analytics.
 *
 * Era il blocco <style> in testa a index.html: estratto qui l'11/09/2026,
 * INVARIATO riga per riga, perché la scheda "Progetti" (progetti.html) è una
 * pagina a sé e deve avere esattamente la stessa grammatica visiva senza
 * ricopiarla. Un foglio solo per due pagine significa anche che un ritocco ai
 * token del brand si fa in un punto.
 *
 * ⚠️ L'estrazione è stata verificata a schermo: catture di index.html a 390 e
 * 1440 px prima e dopo, confrontate pixel per pixel (identiche). Se si
 * riorganizza questo file, si rifà quel confronto — le regole qui dentro
 * portano una decina di trappole già pagate (l'header sticky, la rotella del
 * mouse, il rimbalzo di iOS) i cui commenti spiegano perché NON si toccano.
 *
 * Chi lo usa: index.html (cruscotto), progetti.html (registro dei progetti).
 * In coda, dopo le regole del cruscotto, il blocco `.hq-*` della scheda
 * Progetti: usa gli stessi token e non ridefinisce niente di sopra.
 */
  :root {
    /* Palette identica a blasat.com / feedback.blasat.com — stessi valori OKLCH del brand.
       Nessun colore nuovo: tutte le tinte sotto stanno sulla hue 154-168 del brand,
       più i due soli stati di allarme già in uso (warn ambra, down rosso). */
    --forest: oklch(32.4% 0.057 167.9);
    --forest-mid: oklch(42.2% 0.073 165.7);
    --forest-soft: oklch(57% 0.14 167.9);
    --sage-faint: oklch(95.7% 0.006 153.8);
    --cream: oklch(95.4% 0.011 76.6);
    --cream-border: oklch(91.1% 0.016 82.8);
    --ink: oklch(23.3% 0.023 165.9);
    --ink-mid: oklch(38.3% 0.018 155);
    /* Alzato da L 56.8% a L 50%: i label piccoli su fondo cream erano al limite
       del contrasto leggibile (soprattutto su iPhone in esterno). */
    --ink-faint: oklch(50% 0.019 154.2);
    --accent: oklch(32.4% 0.057 167.9);
    --accent-up: oklch(50% 0.13 167.9);
    --status-warn: oklch(0.68 0.13 75);
    --status-down: oklch(0.57 0.16 27);
    --on-forest: oklch(78% 0.035 167.9);
    /* ── Movimento (07/10/2026): una curva e quattro durate, gli stessi valori
       di Cally (lo standard delle app personali). Prima ogni transizione aveva
       la sua: .15s, .18s, .25s, 250ms, 0.5s... Su iPhone WebKit tiene a 60 fps
       quello che anima in JavaScript, mentre transform e opacity in CSS o in
       Web Animations girano sul compositore e arrivano a 120 Hz: per questo
       tutto il movimento qui passa da questi token e anima SOLO transform e
       opacity (mai height, width, top).
         --t-foglio  fogli e pannelli che ENTRANO
         --t-esce    uscite (un foglio che scende)
         --t-breve   stati: dissolvenze, colori, un menu che compare
         --t-tocco   la pressione di un tasto
       Con «Riduci movimento» (blocco in fondo al foglio) foglio e uscite si
       accorciano e restano solo dissolvenze.
       ⚠️ I quattro valori devono restare TUTTI diversi: app/foglio.js e
       app/movimento.js li rileggono da qui per i timer di smontaggio. */
    --ease-ios: cubic-bezier(.32, .72, 0, 1);
    --t-foglio: .45s;
    --t-esce: .3s;
    --t-breve: .2s;
    --t-tocco: .1s;
    /* Colori di identità delle proprietà. Croma basso: una famiglia sola,
       non quattro accenti in gara col verde del brand. */
    --p1: oklch(45% 0.10 168);
    --p2: oklch(48% 0.10 232);
    --p3: oklch(52% 0.11 92);
    --p4: oklch(50% 0.11 32);
    --p5: oklch(48% 0.10 310);
    /* ── Aree sicure del telefono ────────────────────────────────────────
       Con viewport-fit=cover la pagina si estende SOTTO il notch e sotto la
       barra di casa: questi quattro valori sono il riempimento che tiene i
       contenuti al di qua. Passano da variabili invece che da env() diretto
       per un motivo pratico: env() in un browser da banco di prova vale
       sempre 0px e non c'è modo di imporgli un altro valore, quindi il notch
       non sarebbe COLLAUDABILE. Con le variabili si può simulare un iPhone
       scrivendo --sat: 59px sul documento e guardare dove finisce l'header.
       ⚠️ In produzione non cambia niente: il valore è lo stesso env() di prima. */
    --sat: env(safe-area-inset-top, 0px);
    --sab: env(safe-area-inset-bottom, 0px);
    --sal: env(safe-area-inset-left, 0px);
    --sar: env(safe-area-inset-right, 0px);
    /* 20px: i riquadri sono "widget" (Salute, Screen Time), non schede da
       modulo — angoli più morbidi degli altri controlli (10px). */
    --radius-card: 20px;
    --widget-shadow: 0 1px 2px oklch(32.4% 0.057 167.9 / 0.05), 0 14px 32px -20px oklch(32.4% 0.057 167.9 / 0.32);
    --radius-sm: 10px;
    --font-display: 'Bricolage Grotesque', ui-sans-serif, system-ui, sans-serif;
    --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-family: var(--font-sans);
  }
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
  /* In modalità PWA standalone su iOS, trascinando oltre il bordo si vedeva
     lampeggiare lo sfondo di sistema (bianco/trasparente) invece del crema
     dell'app: overflow-x:hidden da solo non basta a fermare il "rubber band"
     elastico nativo di WebKit. overscroll-behavior:none lo disattiva su
     ENTRAMBI gli assi — prima era solo -x, e trascinando in giù dall'alto
     compariva una fascia chiara sopra l'header verde (stesso bug, asse
     verticale; qui non c'è pull-to-refresh da preservare: dashboard di sola
     lettura, iOS standalone non lo offre comunque). background su html è una
     rete di sicurezza sulle versioni iOS che non supportano ancora
     overscroll-behavior. */
  html {
    overflow-x: hidden;
    overscroll-behavior: none;
    background: var(--cream);
  }
  body {
    background: var(--cream); color: var(--ink); min-height: 100vh;
    -webkit-font-smoothing: antialiased;
    /* ⚠️ Qui c'era `overflow-x: hidden`. Tolto il 2026-09-02, e questa e' la
       coda del bug della rotella del 31/08 — stessa causa, sintomo diverso.
       `overflow-x: hidden` faceva di body un CONTENITORE DI SCORRIMENTO (la
       specifica forza l'asse verticale da `visible` ad `auto` da sola: in
       console body risultava `overflow-y: auto` senza che nessuno l'avesse
       scritto). Ma il vero scroller della pagina e' html. Conseguenza
       misurata: l'header, che dichiara `position: sticky; top: 0` dalla riga
       226, si posizionava rispetto allo scrollport di BODY — che non scorre
       mai — e quindi non si attaccava a niente. A scrollY 1500 il suo
       getBoundingClientRect().top era -1500: scorreva via come un elemento
       qualunque. Sul telefono, dalla seconda schermata in poi, il menu e il
       nome della struttura sparivano.
       Il ricambio e' `overflow-x: clip` su main qui sotto: `clip` NON crea uno
       scroll container, quindi lo sticky sopravvive e la sbavatura
       orizzontale resta impedita (piu' `overflow-x: hidden` su html, che
       resta e da solo basta). Misurato prima e dopo, 393px e 1440px: altezza
       documento identica al pixel, scrollWidth == clientWidth in entrambi.
       ⚠️ NON rimettere `overflow-x: hidden` qui, e NON metterci
       `overscroll-behavior` (vedi il blocco qui sotto): l'una riporta l'header
       morto, l'altra la rotella morta. */
    /* NIENTE overscroll-behavior QUI. Rimosso il 2026-08-31 dopo una
       segnalazione di Aziz: su desktop la rotella del mouse e la tastiera non
       scorrevano piu' la pagina, mentre trascinare la barra funzionava.
       Il meccanismo, confermato per bisezione sulla sua macchina:
         1. `overflow-x: hidden`, che allora stava qui, rendeva body un CONTENITORE DI
            SCORRIMENTO — la specifica CSS forzava l'asse verticale da `visible`
            ad `auto` da sola (in console `body` risultava `overflow-y: auto`
            senza che nessuno l'avesse scritto);
         2. body pero' e' alto quanto il suo contenuto, quindi non ha nulla da
            scorrere: lo scorrimento andrebbe PASSATO a html, che e' il vero
            scroller della pagina;
         3. `overscroll-behavior: none` e' esattamente la regola che VIETA quel
            passaggio. Risultato: il gesto muore su body e la pagina non si
            muove. La barra continuava a funzionare perche' agisce diretta su
            html, saltando la catena.
       La protezione dal rimbalzo elastico di iOS NON si perde: resta su `html`,
       che e' l'elemento che governa l'overscroll del viewport (la sua
       dichiarazione si propaga al viewport, come fa `overflow`). Qui era
       ridondante e dannosa.
       ⚠️ NON rimettere `overscroll-behavior` su body: il bug della rotella
       si ripresenta identico.
       ⚠️ Aggiornamento del 2026-09-02: `overflow-x: hidden` NON e' piu' qui —
       vedi il commento in cima a questo blocco. La sbavatura orizzontale
       resta impedita da `overflow-x: hidden` su html piu' `overflow-x: clip`
       su main; e togliendolo l'header sticky, che con body scroll container
       non si attaccava a niente, e' tornato vivo. Misurato: scrollWidth ==
       clientWidth a 320, 393, 430, 1440 e 1920.
       ⚠️ La finestra scorrevole del feed (.feed-scroll) aveva
       `overscroll-behavior: contain`. Era lecito in teoria (è un div interno,
       non il body) ma in pratica rifaceva lo stesso danno: il feed occupa
       mezzo schermo e la rotella ci restava incastrata. Tolto il 2026-09-01,
       vedi il commento su .feed-scroll. */
    text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
  }
  /* Rete di sicurezza per il rimbalzo VERTICALE in alto (iOS senza supporto a
     overscroll-behavior): durante il bounce l'intero documento — fixed inclusi —
     viene trascinato giù, quindi questo blocco verde, parcheggiato appena sopra
     il viewport, scende insieme alla pagina e riempie esattamente la fascia
     scoperta sopra l'header. In basso non serve: lì si scopre il crema di html,
     che combacia col fondo pagina. Mai visibile in condizioni normali. */
  body::before {
    content: ""; position: fixed; z-index: -1; pointer-events: none;
    top: -100vh; left: 0; right: 0; height: 100vh;
    background: var(--forest);
  }
  /* textarea compresa: senza, il browser le lascia in monospace di default e il
     modulo delle risposte ufficiali sembra un terminale. */
  button, select, input, textarea { font-family: inherit; }

  /* .tb-btn/.mini-btn dichiarano display:inline-flex e battevano l'attributo
     hidden dello UA: "Ripristina" restava visibile fuori dal riordino. */
  [hidden] { display: none !important; }

  /* Ring di focus visibile solo da tastiera, mai al tocco. */
  :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
  header :focus-visible { outline-color: var(--on-forest); }
  #login-screen :focus-visible, .pop :focus-visible { outline-color: var(--accent); }

  /* «Riduci movimento»: fino al 07/10 una regola globale portava OGNI animazione
     e transizione a 0,01 ms, dissolvenze comprese — l'opposto di quello che
     chiede la preferenza di sistema, che vuole meno SPOSTAMENTO, non meno
     gentilezza. Ora restano le dissolvenze brevi e spariscono spostamenti e
     scale: la regola vive in fondo al foglio (blocco «Movimento»), accanto
     alle animazioni che deve riscrivere. */

  /* ── Boot: splash finché /api/me non risponde ────────── */
  /* La dashboard parte NASCOSTA, così non c'è il lampo di dashboard vuota
     (o di dashboard altrui) prima del login. */
  #splash {
    position: fixed; inset: 0; z-index: 200;
    background: var(--forest);
    display: none; align-items: center; justify-content: center;
    padding: 2rem;
  }
  body.booting #splash { display: flex; }
  body.booting header, body.booting main, body.booting footer { display: none !important; }
  #splash .mark { width: 76px; height: auto; opacity: 0; animation: splashIn 0.5s ease 0.12s forwards; }
  @keyframes splashIn { to { opacity: 1; } }

  /* ── Login overlay ───────────────────────────────────── */
  #login-screen {
    display: none;
    position: fixed; inset: 0; z-index: 100;
    background: var(--forest);
    align-items: center; justify-content: center;
    padding: 1.5rem;
    padding-top: max(1.5rem, var(--sat));
    padding-bottom: max(1.5rem, var(--sab));
    overflow-y: auto;
  }
  #login-screen.visible { display: flex; }

  .login-card {
    background: white; border-radius: 20px;
    padding: 2.5rem 2rem;
    width: 100%; max-width: 380px;
    box-shadow: 0 24px 80px oklch(32.4% 0.057 167.9 / 0.35);
  }
  .login-logo { display: block; height: 30px; width: auto; margin-bottom: 0.5rem; }
  .login-intro { font-size: 0.8125rem; color: var(--ink-faint); margin-bottom: 1.75rem; line-height: 1.45; }
  .login-credit { font-size: 0.72rem; color: var(--ink-faint); text-align: center; margin-top: 1.25rem; }

  .mode-toggle {
    display: grid; grid-template-columns: 1fr 1fr;
    background: var(--cream); border-radius: 12px; padding: 3px;
    margin-bottom: 1.5rem;
  }
  .mode-btn {
    font-size: 0.875rem; font-weight: 600;
    min-height: 44px; padding: 0.5rem;
    border: none; border-radius: 9px;
    background: transparent; color: var(--ink-faint);
    cursor: pointer; transition: background 0.15s, color 0.15s;
  }
  .mode-btn.active { background: white; color: var(--forest); box-shadow: 0 1px 4px oklch(32.4% 0.057 167.9 / 0.14); }

  .login-fields { display: flex; flex-direction: column; gap: 0.875rem; margin-bottom: 1.25rem; }
  .login-field { display: flex; flex-direction: column; gap: 0.375rem; }
  .login-field label { font-size: 0.8rem; font-weight: 600; color: var(--ink-mid); }
  .login-field input {
    font-size: 1rem; min-height: 44px;
    padding: 0.625rem 0.875rem;
    border: 1.5px solid var(--cream-border); border-radius: var(--radius-sm);
    background: white; color: var(--ink);
    outline: none; transition: border-color 0.15s;
  }
  .login-field input:focus { border-color: var(--accent); }
  .login-hint { font-size: 0.75rem; color: var(--ink-faint); }

  .login-btn {
    font-size: 0.9375rem; font-weight: 600;
    min-height: 46px; padding: 0.75rem; width: 100%;
    background: var(--forest); color: white;
    border: none; border-radius: var(--radius-sm);
    cursor: pointer; transition: opacity 0.15s;
  }
  .login-btn:hover { opacity: 0.88; }
  .login-btn:disabled { opacity: 0.5; cursor: default; }
  .login-error { font-size: 0.8125rem; color: var(--status-down); text-align: center; margin-top: 0.75rem; min-height: 1.2em; }

  /* ── Topbar ──────────────────────────────────────────── */
  /* Il max(0.625rem, var(--sat)) qui sotto e' un residuo utile, non un errore:
     dal 21/09/2026 la status bar e' "black" e non piu' "black-translucent"
     (vedi il commento in index.html), quindi la webview e' insettata sotto il
     notch e --sat vale 0px -- e vince il fallback. Lasciato perche' copre
     ancora il caso in cui si tornasse al translucent, e perche' su Android e
     nei browser in scheda il display-cutout puo' ancora dare un inset. */
  header {
    background: var(--forest); color: var(--cream);
    position: sticky; top: 0; z-index: 30;
    padding: 0.625rem 1.5rem;
    padding-top: max(0.625rem, var(--sat));
    padding-left: max(1.5rem, var(--sal));
    padding-right: max(1.5rem, var(--sar));
  }
  .topbar {
    max-width: 1680px; margin: 0 auto;
    min-height: 44px;
    display: flex; align-items: center; gap: 1rem;
  }
  .brand { display: flex; align-items: center; gap: 0.75rem; min-width: 0; flex-shrink: 0; }
  .brand .mark { height: 26px; width: auto; display: block; flex-shrink: 0; }
  .brand-word { font-family: var(--font-display); font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.01em; color: var(--cream); white-space: nowrap; }

  /* Nome proprietà nella barra: solo mobile, dove il selettore non c'è. */
  .topbar-prop {
    display: none; min-width: 0; flex: 1;
    font-size: 0.875rem; font-weight: 600; color: var(--cream);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  #picker-desktop { display: flex; align-items: center; margin-left: 0.5rem; min-width: 0; }
  #picker-desktop:empty { display: none; }

  select#proj {
    font-size: 1rem; font-weight: 500;
    min-height: 44px; max-width: 260px;
    padding: 0.375rem 2rem 0.375rem 0.75rem;
    border-radius: var(--radius-sm); border: 1px solid oklch(45% 0.06 167.9);
    background: var(--forest-mid) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23a8d3c2' stroke-width='2.5'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") no-repeat right 0.625rem center;
    color: var(--cream); cursor: pointer; appearance: none;
    text-overflow: ellipsis; width: 100%;
  }
  select#proj:disabled { opacity: 0.5; cursor: default; }

  .tb-btn {
    font-size: 0.8125rem; font-weight: 600;
    min-height: 44px; padding: 0.5rem 0.9rem;
    border-radius: var(--radius-sm); border: 1px solid oklch(45% 0.06 167.9);
    background: transparent; color: var(--on-forest);
    cursor: pointer; white-space: nowrap;
    transition: background 0.15s, color 0.15s;
    display: inline-flex; align-items: center; gap: 0.4rem;
  }
  .tb-btn:hover { background: var(--forest-mid); color: var(--cream); }
  .tb-btn--primary { background: var(--cream); border-color: var(--cream); color: var(--forest); }
  .tb-btn--primary:hover { background: white; color: var(--forest); }
  .tb-btn[aria-pressed="true"] { background: var(--cream); border-color: var(--cream); color: var(--forest); }

  /* ⚠️ Dal 29/09 il ☰ vive SOLO sotto i 1024px: sopra, tutto quello che
     conteneva (Aggiorna, Riordina, Report, Installa, Esci) sta nella barra
     laterale (vedi ".sidebar" più sotto), e l'header intero sparisce. Prima
     spariva solo il ☰ sopra i 900px, e le voci non erano raggiungibili da
     nessuna parte: qui non serve più quella toppa. */
  .menu-btn {
    display: inline-flex;
    width: 44px; height: 44px; flex-shrink: 0;
    align-items: center; justify-content: center;
    border-radius: var(--radius-sm); border: 1px solid oklch(45% 0.06 167.9);
    background: transparent; color: var(--on-forest); cursor: pointer;
  }
  .menu-btn svg { width: 20px; height: 20px; }

  /* ── Popover / menu ──────────────────────────────────── */
  .pop-wrap { position: relative; }
  .pop {
    position: absolute; top: calc(100% + 0.5rem); right: 0; z-index: 40;
    min-width: 220px;
    background: white; color: var(--ink);
    border: 1px solid var(--cream-border); border-radius: 14px;
    box-shadow: 0 16px 44px oklch(32.4% 0.057 167.9 / 0.18);
    padding: 0.375rem; overflow: hidden;
  }
  .pop[hidden] { display: none; }
  /* Alcune voci del menu sono <a> (es. "Installa l'app" in richieste.html):
     stessa faccia dei fratelli <button>, senza la sottolineatura del link. */
  a.pop-item { text-decoration: none; }
  .pop-item {
    display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
    width: 100%; min-height: 44px; padding: 0.5rem 0.75rem;
    font-size: 0.875rem; font-weight: 500; text-align: left;
    background: transparent; border: none; border-radius: 9px;
    color: var(--ink); cursor: pointer;
  }
  .pop-item:hover { background: var(--sage-faint); }
  .pop-item--danger { color: var(--status-down); }
  .pop-note { font-size: 0.7rem; color: var(--ink-faint); }
  .pop-sep { height: 1px; background: var(--cream-border); margin: 0.375rem 0.25rem; }
  .pop-label { font-size: 0.68rem; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase; color: var(--ink-faint); padding: 0.5rem 0.75rem 0.25rem; }

  /* ── Main ────────────────────────────────────────────── */
  main {
    max-width: 1680px; margin: 0 auto;
    padding: 1.5rem 1.5rem 2rem;
    padding-left: max(1.5rem, var(--sal));
    padding-right: max(1.5rem, var(--sar));
  }
  /* `clip` e non `hidden`: hidden qui rifarebbe di main uno scroll container
     e riporterebbe l'header sticky a non attaccarsi (vedi il commento su
     body). clip taglia e basta, senza creare uno scrollport. Safari 16+,
     Chrome 90+. E' la cintura contro la sbavatura orizzontale; le bretelle
     sono `overflow-x: hidden` su html. */
  main { overflow-x: clip; }
  .view[hidden] { display: none; }
  .page-head { margin-bottom: 1.25rem; }
  .page-eyebrow { font-size: 0.7rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint); }
  .page-title { font-family: var(--font-display); font-size: 1.5rem; font-weight: 700; letter-spacing: -0.025em; color: var(--forest); line-height: 1.15; margin-top: 0.2rem; }
  .page-sub { font-size: 0.8125rem; color: var(--ink-faint); line-height: 1.45; margin-top: 0.3rem; max-width: 68ch; }

  /* ── Griglia a due binari + riordino ─────────────────── */
  /* Il feed è alto, i riquadri di contorno gli si impilano accanto invece di
     lasciare una colonna bianca lunga 1500px. */
  .rails { display: grid; grid-template-columns: minmax(0, 1.62fr) minmax(0, 1fr); gap: 1.25rem; align-items: start; }
  .rails.rails--una { grid-template-columns: minmax(0, 1fr); }
  .rail { display: flex; flex-direction: column; gap: 1.25rem; min-width: 0; }
  .blk { min-width: 0; container-type: inline-size; }
  .stack { display: flex; flex-direction: column; gap: 1.25rem; }

  /* Il feed è una finestra su un flusso, non un rotolo: scorre dentro di sé.
     La testata (titolo + filtri) resta ferma sopra. */
  /* ⚠️ NIENTE `overscroll-behavior: contain` QUI. Tolto il 2026-09-01 in
     collaudo: a 1440x900 questa finestra misura 809x594 — la meta' sinistra
     dello schermo sotto l'intestazione — e con `contain` la rotella del mouse
     ci moriva dentro. Due colpi di rotella portavano il feed a fondo corsa e
     dal terzo in poi la pagina non si muoveva piu': misurati 21 punti morti su
     60 campionati, e otto colpi consecutivi con scrollY fermo a 0. E' lo
     stesso sintomo segnalato da Aziz il 31/08 (la barra scorre, la rotella
     no), per una causa diversa. Con `auto` il feed scorre finche' ha righe e
     poi passa il gesto alla pagina, che e' quello che il dito fa da sempre.
     Su telefono la regola non c'entra: sotto i 900px .feed-scroll diventa
     `overflow: visible` (vedi la media query in fondo) e non scorre affatto. */
  /* ⚠️ 2026-09-02, seconda passata. Passare da `contain` ad `auto` aveva
     addolcito il sintomo, non tolto la causa: con `auto` il feed cede il gesto
     alla pagina solo DOPO essere arrivato a fondo corsa, e con 43 domande
     dentro significa parecchi colpi di rotella prima che la pagina si muova.
     Misurato a 1440 e a 1920 col puntatore al centro dello schermo — cioe'
     dove sta il mouse: `scrollTop` fermo a 0 sopra il feed, 900 in ogni altro
     punto della pagina. La tastiera scorreva, la rotella no: lo stesso sintomo
     segnalato da Aziz il 31/08, per la terza causa diversa.
     La cura e' il principio che questo file dichiarava gia' per il telefono e
     applicava solo li' — "niente finestra scorrevole dentro una pagina che
     gia' scorre". Ora vale a ogni larghezza: nessun contenitore interno, si
     mostrano le prime righe e il resto si apre a richiesta. La pagina resta
     corta perche' le righe sono poche, non perche' sono chiuse in una scatola.
     NON rimettere `max-height` + `overflow-y` qui: e' il terzo giro. */
  .feed-scroll { margin-right: -0.4rem; padding-right: 0.4rem; }
  /* Massimo CINQUE righe visibili, a ogni larghezza (era otto sopra i
     1024px: unificato il 29/09/2026, "struttura Apple" — vedi il "See All"
     dei widget iOS/macOS). Il resto è dietro il link "Tutte (N)" in testa al
     riquadro (era un bottone in fondo). */
  .feed .fitem:nth-child(n+6) { display: none; }
  .feed.all .fitem:nth-child(n+6) { display: block; }

  /* stato "in trascinamento" */
  .blk.dragging { z-index: 5; }
  .blk.dragging > .card { box-shadow: 0 22px 60px oklch(32.4% 0.057 167.9 / 0.28); border-color: var(--forest-soft); transform: rotate(-0.35deg); }
  .drop-slot {
    border: 2px dashed var(--forest-soft); border-radius: var(--radius-card);
    background: oklch(96.5% 0.022 167.9); min-height: 110px;
    display: flex; align-items: center; justify-content: center;
    font-size: 0.8125rem; font-weight: 600; color: var(--forest-soft); letter-spacing: 0.03em;
  }
  .drop-slot::after { content: "Rilascia qui"; }

  /* maniglia + frecce: sempre 44px, target conforme 2.5.8 */
  .handle, .icon-btn {
    width: 44px; height: 44px; display: none; align-items: center; justify-content: center;
    border: 1px solid var(--cream-border); background: white; border-radius: 10px;
    color: var(--ink-faint); cursor: pointer;
  }
  .handle { cursor: grab; touch-action: none; }
  .handle:active { cursor: grabbing; }
  .handle svg, .icon-btn svg { width: 18px; height: 18px; }
  .icon-btn:hover, .handle:hover { background: var(--sage-faint); color: var(--forest); }
  .icon-btn:disabled { opacity: 0.35; cursor: default; }
  body.reorder .handle, body.reorder .icon-btn { display: inline-flex; }
  body.reorder .blk > .card { border-style: dashed; border-color: var(--forest-soft); }
  body.reorder .blk-body { opacity: 0.42; pointer-events: none; }
  .reorder-hint {
    font-size: 0.78rem; color: var(--ink-mid); background: oklch(96.5% 0.022 167.9);
    border: 1px solid oklch(88% 0.04 167.9); border-radius: 12px;
    padding: 0.65rem 0.9rem; margin-bottom: 1.25rem; line-height: 1.5;
  }

  /* ── ⓘ spiegazione dietro icona ─────────────────────── */
  /* Si apre AL TOCCO (su telefono non esiste hover, e i tooltip title=""
     non compaiono mai) come foglio dal basso (app/foglio.js): si chiude con
     «Fine», col tocco sul velo, trascinandolo giù o con Esc. */
  .info {
    width: 22px; height: 22px; flex-shrink: 0; border-radius: 50%;
    border: 1px solid var(--cream-border); background: var(--sage-faint); color: var(--ink-faint);
    font-size: 0.6875rem; font-weight: 700; line-height: 1; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    position: relative; vertical-align: 0.05em;
  }
  /* Area di tocco reale 44px senza gonfiare il disegno (WCAG 2.5.8). */
  .info::before { content: ""; position: absolute; inset: -11px; }
  .info:hover { background: oklch(93% 0.03 167.9); color: var(--forest); border-color: oklch(85% 0.045 167.9); }
  .card--accent .info { background: var(--forest-mid); border-color: oklch(45% 0.06 167.9); color: oklch(88% 0.03 167.9); }
  /* ── Card ────────────────────────────────────────────── */
  /* Il riquadro è un "widget" (Salute, Screen Time), non una scheda da modulo:
     ombra morbida appena percettibile invece del bordo, angoli più larghi. Il
     bordo resta trasparente (non tolto): su fondo cream un'ombra sola, senza
     nessun filo, sui riquadri piccoli in fila si vedeva "galleggiare" male a
     schermate intere — misurato in collaudo il 29/09/2026. */
  .card {
    background: white; border: 1px solid transparent; border-radius: var(--radius-card);
    padding: 1.25rem 1.375rem; box-shadow: var(--widget-shadow);
  }
  .card-head { display: flex; align-items: flex-start; gap: 0.6rem; margin-bottom: 0.9rem; }
  /* 17px: la taglia dei titoli di widget (Salute, Home), non le 15px di un
     controllo di modulo. */
  .card-title { font-size: 1.0625rem; font-weight: 600; color: var(--ink); letter-spacing: -0.01em; display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; }
  /* ⚠️ Non più Bricolage/forest qui: fino al 29/09/2026 i riquadri "ask"
     (i titoli-domanda della vista cliente, innesto da "Il Racconto") erano
     in Bricolage verde, fino a un corpo percepito ~22px sul telefono, mentre
     l'admin aveva lo stesso .card-title in font di sistema nero — due lingue
     tipografiche per lo stesso ruolo (titolo di widget). Uniformato: il
     titolo del riquadro è SEMPRE font di sistema 17px, ovunque. Bricolage
     resta solo per il titolo grande di pagina (.page-title) e il logo. */
  .card-title.ask { }
  .card-count { font-size: 0.75rem; font-weight: 600; color: var(--ink-faint); }
  .card-sub { font-size: 0.75rem; color: var(--ink-faint); margin-top: 0.2rem; line-height: 1.45; }
  .head-tools { margin-left: auto; display: flex; align-items: center; gap: 0.55rem; flex-shrink: 0; }
  /* Il link "Mostra tutto"/"Nuova" in testa al riquadro: testo, non bottone. */
  .widget-link {
    background: none; border: none; padding: 0; margin: 0; font: inherit;
    font-size: 0.8125rem; font-weight: 600; color: var(--forest); cursor: pointer;
    white-space: nowrap;
  }
  .widget-link:hover { text-decoration: underline; }
  /* L'area di tocco della ⓘ sborda di 11px per lato (vedi .info::before). Con
     un gap di 4px copriva gli ultimi 7px del bottone accanto e, essendo
     posizionata, vinceva: cliccare il bordo destro di "Scala comune" apriva
     la spiegazione invece di cambiare scala. Verificato con elementFromPoint. */
  .head-tools .info { margin-left: 0.5rem; }
  .card--accent { background: var(--forest); border-color: var(--forest); }
  .card--accent .card-title, .card--accent .num-val { color: var(--cream); }
  /* Su fondo verde l'assaggio in --forest era verde su verde: invisibile. */
  .card--accent .card-peek { color: var(--on-forest); }
  .card--accent .m-toggle { color: var(--on-forest); }
  .card--accent .card-sub, .card--accent .num-label, .card--accent .num-sub, .card--accent .num-val small { color: var(--on-forest); }

  /* ── Numeri nudi ─────────────────────────────────────── */
  .nums { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 1rem; }
  .num { min-width: 0; }
  .num-label { font-size: 0.75rem; font-weight: 600; color: var(--ink-faint); display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }
  .num-val { font-family: var(--font-display); font-size: 2.25rem; font-weight: 700; line-height: 1; letter-spacing: -0.035em; color: var(--ink); margin-top: 0.35rem; font-variant-numeric: tabular-nums; }
  .num-val small { font-size: 0.9rem; font-weight: 600; letter-spacing: 0; color: var(--ink-faint); }
  /* ⚠️ Non Bricolage qui: ".num-val" sopra dichiara font-display per i numeri
     grandi, ma i valori "sm" sono spesso PAROLE ("Non ancora misurata",
     "Nessuno scambio", «Oggi») — in Bricolage 700 arrivavano a un corpo
     percepito ~22px su due righe. Font di sistema, semibold, un filo più
     piccolo: si leggono come un titolo di widget, non come un titolo di
     pagina (29/09/2026). */
  .num-val.sm { font-family: var(--font-sans); font-size: 1.125rem; font-weight: 600; letter-spacing: normal; line-height: 1.3; }
  .num-sub { font-size: 0.8125rem; color: var(--ink-faint); margin-top: 0.35rem; line-height: 1.4; }
  .trend { font-weight: 700; white-space: nowrap; }
  .trend.up { color: var(--accent-up); } .trend.down { color: var(--status-down); } .trend.flat { color: var(--ink-faint); }
  /* Sul costo la direzione "buona" è invertita: salire è una spesa che cresce. */
  .trend.cost-up { color: var(--status-warn); } .trend.cost-down { color: var(--accent-up); }

  /* ── "Oggi" (admin): fila di widget piccoli, non un riquadro pieno ─────
     Il .card che shell() disegna intorno diventa trasparente: quello che si
     vede sono i tre .num, ciascuno un widget vero (fondo bianco, ombra),
     e il titolo "Oggi" torna un'etichetta di gruppo sopra la fila. */
  .blk[data-k="oggi"] > .card { background: transparent; border: none; box-shadow: none; padding: 0; }
  /* In riordino il bordo tratteggiato torna (regola più specifica su
     body.reorder): senza un po' di respiro l'etichetta "Oggi" lo tocca. */
  body.reorder .blk[data-k="oggi"] > .card { padding: 0.6rem 0.6rem 0.85rem; }
  .blk[data-k="oggi"] .card-head { margin-bottom: 0.6rem; }
  .blk[data-k="oggi"] .card-title {
    font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--ink-faint); font-family: var(--font-sans);
  }
  /* SEMPRE tre per riga — mai 2+1 con l'orfano — a ogni larghezza: la regola
     generale ".nums" (auto-fit, o 2 colonne sotto i 1024px) non vale qui.
     Sotto i 480px numeri ed etichette si stringono invece di andare a capo. */
  /* Selettore doppio (".nums.nums--widgets") apposta: deve battere, per
     specificità e non per !important, la regola ".nums" che sotto i 767px
     forza 2 colonne per la fila "Oggi", che qui deve restarne 3. */
  .nums.nums--widgets { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.6rem; }
  .nums--widgets > .num {
    background: white; border-radius: var(--radius-card); box-shadow: var(--widget-shadow);
    padding: 0.85rem 0.7rem; position: relative; min-width: 0;
  }
  .nums--widgets .num-val { font-size: 1.625rem; }
  .nums--widgets .num-label { font-size: 0.7rem; }
  .nums--widgets .num-sub { font-size: 0.68rem; margin-top: 0.25rem; }
  @media (max-width: 479px) {
    .nums--widgets .num-val { font-size: 1.375rem; }
    .nums--widgets > .num { padding: 0.7rem 0.55rem; }
  }
  /* «Da approvare» e' un BOTTONE (apre «Risposte ufficiali»): senza lo stile di
     un bottone, con la faccia del riquadro, e l'area di tocco su tutto il
     riquadro (il ::before sborda fino ai suoi bordi: il riquadro e' il bersaglio,
     non solo le parole). */
  .nums--widgets .num-hit {
    display: block; position: relative; width: 100%; padding: 0; margin: 0;
    background: none; border: 0; font: inherit; color: inherit; text-align: left; cursor: pointer;
  }
  .nums--widgets .num-hit > span { display: block; }
  .nums--widgets .num-hit::before { content: ""; position: absolute; inset: -0.85rem -0.7rem; }
  @media (max-width: 479px) { .nums--widgets .num-hit::before { inset: -0.7rem -0.55rem; } }
  /* L'ⓘ sull'angolo: non è dentro il link, è un controllo a sé (vedi il
     commento nel BLOCKS.oggi che lo genera). */
  .nums--widgets .num--linked .info { position: absolute; top: 0.7rem; right: 0.7rem; }
  .num--warn .num-val { color: oklch(45% 0.1 62); }

  .tile { background: var(--sage-faint); border-radius: 12px; padding: 0.85rem 0.95rem; min-width: 0; }
  .tile.hero { background: var(--forest); color: var(--cream); }
  .tile.hero .num-label, .tile.hero .num-sub, .tile.hero .num-val small { color: var(--on-forest); }
  .tile.hero .num-val { color: var(--cream); }
  .tile.hero .info { background: var(--forest-mid); border-color: oklch(45% 0.06 167.9); color: oklch(88% 0.03 167.9); }

  /* Misurato sui PIXEL (non sui token: Chrome serializza oklch come lab()):
     sul fondo forest i trend cadevano a 2,27:1 (up), 2,52:1 (down) e 2,06:1
     (flat). Il numero grande si leggeva e la sua variazione no — cioè proprio
     il pezzo che dice se le cose vanno meglio o peggio. Qui passano alla
     famiglia chiara: 7,3-8,2:1. */
  .tile.hero .trend.up, .card--accent .trend.up,
  .tile.hero .trend.cost-down, .card--accent .trend.cost-down { color: oklch(85% 0.15 165); }
  .tile.hero .trend.down, .card--accent .trend.down { color: oklch(82% 0.11 25); }
  .tile.hero .trend.cost-up, .card--accent .trend.cost-up { color: oklch(85% 0.12 85); }
  .tile.hero .trend.flat, .card--accent .trend.flat { color: var(--on-forest); }

  /* Riga permanente: sta SOTTO il numero, non dietro la ⓘ. Serve a impedire
     di leggere il numero al contrario, e chi legge al contrario non apre la ⓘ. */
  .caveat { font-size: 0.72rem; color: var(--ink-mid); line-height: 1.45; margin-top: 0.4rem; border-left: 2px solid var(--cream-border); padding-left: 0.55rem; }

  /* stato, non numero: ciò che è sempre 0 diventa un badge */
  .statepill {
    display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.78rem; font-weight: 600;
    padding: 0.4rem 0.7rem; border-radius: 999px; background: oklch(94% 0.04 167.9); color: var(--forest);
    border: 1px solid oklch(86% 0.05 167.9);
  }
  .statepill.warn { background: oklch(96% 0.05 75); color: oklch(45% 0.1 62); border-color: oklch(88% 0.06 75); }
  .statepill.muted { background: var(--sage-faint); color: var(--ink-faint); border-color: var(--cream-border); }
  .statepill.muted .pd { background: oklch(75% 0.01 154); }
  .statepill .pd { width: 8px; height: 8px; border-radius: 50%; background: var(--forest-soft); }
  .statepill.warn .pd { background: var(--status-warn); }

  /* distribuzione a conteggi (mai percentuali su pochi casi) */
  .dist { display: flex; gap: 3px; margin-top: 0.6rem; flex-wrap: wrap; }
  .dist .seg2 { flex: 1; min-width: 76px; border-radius: 6px; background: var(--sage-faint); padding: 0.4rem 0.3rem; text-align: center; border: 1px solid transparent; }
  .dist .seg2.on { background: oklch(93% 0.045 167.9); border-color: oklch(85% 0.05 167.9); }
  .dist .seg2 b { display: block; font-family: var(--font-display); font-size: 1rem; font-weight: 700; color: var(--ink); line-height: 1; }
  .dist .seg2.on b { color: var(--forest); }
  .dist .seg2 span { display: block; font-size: 0.6rem; color: var(--ink-faint); margin-top: 0.2rem; }

  .srow { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0; border-bottom: 1px solid var(--cream-border); font-size: 0.8125rem; color: var(--ink-mid); }
  .srow:last-child { border-bottom: none; }
  .srow .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .srow .vv { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
  .pdot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; display: inline-block; }

  /* ── Lollipop ────────────────────────────────────────── */
  .lolli { display: flex; flex-direction: column; gap: 0.1rem; }
  .lrow { display: grid; grid-template-columns: minmax(84px, 38%) minmax(0, 1fr) auto; align-items: center; gap: 0.75rem; padding: 0.42rem 0; }
  /* Niente ellissi: le etichette lunghe vanno a capo. Troncate, "Menu —
     ristorante La T…" e "Menu — ristorante La L…" diventavano la stessa riga —
     e i due ristoranti (La Terrazza / La Loggia) sono proprio la parte
     tagliata. Due righe costano qualche pixel, una classifica illeggibile
     costa la classifica. */
  .lname { font-size: 0.8125rem; color: var(--ink-mid); line-height: 1.25; overflow-wrap: anywhere; }
  .ltrack { position: relative; height: 14px; }
  .ltrack::before { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 1px; background: var(--cream-border); }
  .lstem { position: absolute; left: 0; top: 50%; height: 2px; transform: translateY(-50%); background: var(--forest-soft); border-radius: 2px; }
  .ldot { position: absolute; top: 50%; width: 11px; height: 11px; margin-left: -5.5px; transform: translateY(-50%); border-radius: 50%; background: var(--forest); }
  .lval { font-size: 0.8125rem; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
  .lval small { font-weight: 500; color: var(--ink-faint); }

  /* ── Grafici ─────────────────────────────────────────── */
  .chart-box svg { display: block; width: 100%; height: auto; }
  /* La linea si disegna una volta, la prima volta che il riquadro compare
     (JS mette .ln-entra sull'svg solo allora, e mai con «Riduci movimento»).
     pathLength=1 sulla linea: dasharray/offset a 1 la nascondono, a 0 la
     mostrano. L'area si scopre con clip-path da sinistra e il punto finale
     compare quando la linea arriva. Mai height: si anima solo cio' che il
     compositore sa fare senza ricalcolare il layout. */
  .ln-entra .ln-linea { stroke-dasharray: 1; stroke-dashoffset: 1; animation: ln-disegna var(--t-foglio) var(--ease-ios) both; }
  .ln-entra .ln-area { animation: ln-scopri var(--t-foglio) var(--ease-ios) both; }
  .ln-entra .ln-fine { animation: ln-compare var(--t-foglio) var(--ease-ios) both; }
  @keyframes ln-disegna { to { stroke-dashoffset: 0; } }
  @keyframes ln-scopri { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
  @keyframes ln-compare { 0%, 55% { opacity: 0; } 100% { opacity: 1; } }
  @media (prefers-reduced-motion: reduce) {
    .ln-entra .ln-linea, .ln-entra .ln-area, .ln-entra .ln-fine { animation: none; stroke-dasharray: none; stroke-dashoffset: 0; }
  }

  /* ── Scorrere sul grafico (04-grafici.js) ───────────────
     pan-y: il dito in verticale resta della pagina (o del foglio dell'hotel);
     l'orizzontale e' del grafico. Niente selezione ne' menu di pressione lunga,
     o tenere premuto per scorrere selezionerebbe il testo accanto.
     Il cursore (linea, pallino, etichetta) e' gia' nell'svg: qui si decide solo
     quando si vede. Compare subito, sparisce in --t-breve; il punto dell'ultimo
     valore si attenua. Sono transizioni di sola opacita'. */
  .grafico-scorri { touch-action: pan-y; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
  .chart-box.grafico-scorri { cursor: crosshair; }
  .grafico-scorri svg { -webkit-tap-highlight-color: transparent; }
  .ln-cursore { opacity: 0; pointer-events: none; transition: opacity var(--t-breve) var(--ease-ios); }
  svg.scrub .ln-cursore { opacity: 1; transition-duration: 0s; }
  .ln-fine-v { transition: opacity var(--t-breve) var(--ease-ios); }
  svg.scrub .ln-fine-v { opacity: 0.25; }
  .ln-et { font-variant-numeric: tabular-nums; }

  /* Il numero grande di «Le app»: due strati nella stessa cella. Quello del
     giorno scelto compare di colpo e se ne va in dissolvenza. */
  .qnum { display: grid; justify-items: end; align-items: start; }
  .qnum-base, .qnum-scrub { grid-area: 1 / 1; text-align: right; transition: opacity var(--t-breve) var(--ease-ios); }
  .qnum-scrub { opacity: 0; }
  .qrow.scorrendo .qnum-base { opacity: 0; transition-duration: 0s; }
  .qrow.scorrendo .qnum-scrub { opacity: 1; transition-duration: 0s; }
  @media (prefers-reduced-motion: reduce) {
    .ln-cursore, .ln-fine-v, .qnum-base, .qnum-scrub { transition: none; }
  }

  /* ── Riga proprietà espandibile (innesto dal "Quadro") ─ */
  .qrow {
    /* Sempre impilata (nome e numero, poi la linea, poi la frase di stato):
       fino al 07/10/2026 sul computer stava tutto su una riga, ma il riquadro
       e' mezza colonna e alla linea restavano cento pixel — le lettere dei
       giorni si accavallavano.
       Righe dello STESSO gruppo, come le Impostazioni di iOS: niente bordo,
       ombra o raggio per riga (erano una carta dentro la carta del riquadro).
       Le separa un filetto che parte dal nome e finisce al bordo; l'ultima non
       lo ha. */
    position: relative;
    display: grid; grid-template-columns: minmax(0, 1fr) 120px;
    grid-template-areas: "name num" "chart chart" "state state";
    gap: 0.5rem 0.75rem; align-items: center; padding: 0.8rem 0.4rem; margin: 0 -0.4rem; border-radius: 0;
    width: 100%; text-align: left; background: transparent;
    border: none; cursor: pointer; color: inherit; transition: background 0.15s;
  }
  .qrow::after {
    content: ""; position: absolute; bottom: 0; right: 0.4rem; height: 1px;
    left: calc(0.4rem + 9px + 0.5rem);  /* padding + .pdot + gap di .qname: sotto il nome */
    background: var(--cream-border);
  }
  .qrow:last-child::after { display: none; }
  .qrow:hover, .qrow[aria-expanded="true"] { background: var(--sage-faint); }
  .qname { display: flex; align-items: center; gap: 0.5rem; min-width: 0; grid-area: name; }
  .qname > div { min-width: 0; }
  .qname b { font-size: 0.9rem; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }
  .qname small { display: block; font-size: 0.68rem; color: var(--ink-faint); margin-top: 0.12rem; }
  .qchart { min-width: 0; grid-area: chart; }
  .qchart svg { display: block; width: 100%; height: auto; }
  .qnum { text-align: right; grid-area: num; }
  .qval { font-family: var(--font-display); font-size: 1.35rem; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1; }
  .qlab { font-size: 0.66rem; color: var(--ink-faint); display: block; margin-top: 0.2rem; }
  .qstate { grid-area: state; text-align: left; font-size: 0.8125rem; color: var(--ink-faint); line-height: 1.45; }
  .qd-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-top: 0.5rem; }
  .qd-box { background: var(--sage-faint); border-radius: 12px; padding: 0.75rem 0.85rem; min-width: 0; }
  .qd-box h5 { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 0.5rem; }
  .qd-full { grid-column: 1 / -1; }

  /* ── Clic dalle mail (riquadro admin, Piano di Carico demo) ────────────
     Sempre a due righe (nome sopra, numeri sotto): il riquadro sta in mezza
     colonna sul computer, e in riga sola il nome si spezzava a tre lettere. */
  .mrow { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.45rem; align-items: center; padding: 0.7rem 0; border-bottom: 1px solid var(--cream-border); }
  .mrow:first-child { padding-top: 0.2rem; }
  .mrow-id { min-width: 0; }
  .mrow-id b { display: block; font-size: 0.9rem; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
  .mrow-id small { display: block; font-size: 0.72rem; color: var(--ink-faint); margin-top: 0.15rem; line-height: 1.4; }
  .mrow-nums { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
  .mn { text-align: center; min-width: 2.4rem; }
  .mn b { display: block; font-family: var(--font-display); font-size: 1.05rem; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1.1; }
  .mn small { display: block; font-size: 0.66rem; color: var(--ink-faint); margin-top: 0.15rem; }
  .mini-rank { display: flex; flex-direction: column; gap: 0.35rem; }
  .mini-rank .mr { display: flex; justify-content: space-between; gap: 0.6rem; font-size: 0.76rem; color: var(--ink-mid); }
  .mini-rank .mr b { color: var(--ink); font-variant-numeric: tabular-nums; }
  .mini-rank .mt { position: relative; height: 4px; border-radius: 3px; background: var(--cream-border); margin-top: 0.2rem; }
  .mini-rank .mt i { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 3px; }

  /* ── Feed concierge ──────────────────────────────────── */
  .feed-filters { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.5rem; }
  .fchip {
    font-size: 0.75rem; font-weight: 600; min-height: 36px; padding: 0.3rem 0.7rem;
    border-radius: 999px; border: 1px solid var(--cream-border); background: white; color: var(--ink-mid); cursor: pointer;
    display: inline-flex; align-items: center; gap: 0.35rem;
  }
  .fchip[aria-pressed="true"] { background: var(--forest); border-color: var(--forest); color: var(--cream); }
  .feed { display: flex; flex-direction: column; }
  .fitem { border-bottom: 1px solid var(--cream-border); }
  .fitem:last-child { border-bottom: none; }
  /* Riga in stile elenco di Mail/Messaggi: il tempo sta a destra della
     domanda, in grigio, non in una colonna a sinistra (cambiato il 29/09/2026
     — prima "23 ore fa" viveva in una colonna fissa da 70px a sinistra). */
  .frow {
    display: block;
    width: 100%; text-align: left; padding: 0.75rem 0.5rem; margin: 0 -0.5rem;
    background: transparent; border: none; border-radius: 10px; color: inherit; cursor: pointer;
    transition: background 0.15s;
  }
  .frow:hover { background: var(--sage-faint); }
  .fmain { min-width: 0; }
  .fq-row { display: flex; align-items: baseline; justify-content: space-between; gap: 0.6rem; }
  .fq { font-size: 0.9375rem; font-weight: 600; color: var(--ink); line-height: 1.35; overflow-wrap: anywhere; }
  .fwhen { flex-shrink: 0; font-size: 0.75rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
  /* Una riga sola: la risposta intera è a un tocco, che è già il gesto per
     correggerla — non serve una seconda riga per farlo capire. */
  .fa {
    font-size: 0.8125rem; color: var(--ink-mid); line-height: 1.45; margin-top: 0.3rem;
    display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;
  }
  .fmeta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; margin-top: 0.45rem; }
  .fdet { padding: 0 0 1rem; display: flex; flex-direction: column; gap: 0.75rem; }
  .fdet[hidden] { display: none; }
  .chatq-n { font-size: 0.7rem; font-weight: 700; color: var(--forest); background: var(--sage-faint); border-radius: 5px; padding: 0.1rem 0.35rem; margin-left: 0.4rem; white-space: nowrap; }

  /* Maiuscola solo l'iniziale ("Risposta vostra", "Attiva"), non maiuscoletto:
     le parole arrivano già così da JS (vedi projectShort/statoProp/FIX_STATUS),
     qui prima c'era un text-transform che le urlava (tolto il 29/09/2026). */
  .badge { font-size: 0.6875rem; font-weight: 700; padding: 0.15rem 0.45rem; border-radius: 5px; background: var(--sage-faint); color: var(--ink-faint); border: 1px solid var(--cream-border); white-space: nowrap; }
  .badge.on { background: oklch(93% 0.03 167.9); color: var(--forest); border-color: oklch(85% 0.045 167.9); }
  .badge.warn { background: oklch(96% 0.04 75); color: oklch(46% 0.1 75); border-color: oklch(88% 0.06 75); }
  .badge.off { background: white; color: var(--ink-faint); border-color: var(--cream-border); }

  /* Chip di proprietà. Deroga consapevole al "solo hue di brand": qui il colore
     ha una funzione, riconoscere l'hotel senza leggere. Croma tenuto basso. */
  .chip { font-size: 0.6875rem; font-weight: 700; padding: 0.15rem 0.45rem; border-radius: 5px; border: 1px solid; white-space: nowrap; }
  .chip-1 { background: oklch(94% 0.032 167.9); color: oklch(36% 0.06 167.9); border-color: oklch(86% 0.045 167.9); }
  .chip-2 { background: oklch(94% 0.032 232); color: oklch(38% 0.06 232); border-color: oklch(86% 0.045 232); }
  .chip-3 { background: oklch(94% 0.036 96); color: oklch(38% 0.06 96); border-color: oklch(86% 0.05 96); }
  .chip-4 { background: oklch(94% 0.03 32); color: oklch(40% 0.06 32); border-color: oklch(87% 0.042 32); }
  .chip-5 { background: oklch(94% 0.028 310); color: oklch(40% 0.055 310); border-color: oklch(87% 0.04 310); }

  /* Blocco risposta: quella dell'AI è neutra, quella ufficiale è in tinta brand
     — a colpo d'occhio si deve capire quale delle due vince. */
  .answer-block { background: var(--sage-faint); border-radius: 12px; padding: 0.75rem 0.875rem; }
  .answer-block--official { background: oklch(96.5% 0.022 167.9); border: 1px solid oklch(88% 0.04 167.9); }
  .answer-label { font-size: 0.68rem; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 0.3rem; }
  .answer-block--official .answer-label { color: var(--forest); }
  .answer-text { font-size: 0.8125rem; color: var(--ink-mid); line-height: 1.55; white-space: pre-line; overflow-wrap: anywhere; }
  .answer-note { font-size: 0.75rem; color: var(--ink-faint); line-height: 1.5; }
  .answer-note .badge { margin-right: 0.35rem; vertical-align: 0.05em; }
  .answer-block .fix-meta { margin-top: 0.45rem; }

  /* ── Risposte ufficiali ──────────────────────────────── */
  /* position:relative + padding-right: il "⋯" (vedi .fix-more-btn più sotto)
     è ancorato in alto a destra, sulla riga del titolo — non una riga sua
     sotto i metadati, che allungava ogni voce di ~30px (29/09/2026). */
  .fix-row { position: relative; display: flex; flex-direction: column; gap: 0.4rem; padding: 0.8125rem 2.25rem 0.8125rem 0; border-bottom: 1px solid var(--cream-border); }
  .fix-row:last-child { border-bottom: none; }
  .fix-q { font-size: 0.875rem; font-weight: 600; color: var(--ink); line-height: 1.4; overflow-wrap: anywhere; }
  .fix-a { font-size: 0.8125rem; color: var(--ink-mid); line-height: 1.5; white-space: pre-line; overflow-wrap: anywhere; }
  .fix-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; font-size: 0.72rem; color: var(--ink-faint); }
  .fix-actions { display: flex; flex-wrap: wrap; gap: 0.375rem; margin-top: 0.15rem; }
  .fix-row--muted .fix-q, .fix-row--muted .fix-a { color: var(--ink-faint); }
  /* Modifica/Disattiva/Elimina dietro un "⋯": prima erano sempre in vista,
     tre per riga per sette righe (29/09/2026). <details> nativo: niente stato
     da gestire a mano, resta aperto da solo quando c'è una conferma in corso.
     Il contenuto aperto resta dov'era (in fondo, dopo i metadati): solo il
     bottone che lo apre (il <summary>) è ancorato sulla riga del titolo. */
  .fix-more-btn {
    position: absolute; top: 0.7rem; right: 0;
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: 8px; list-style: none; cursor: pointer;
    color: var(--ink-faint); font-size: 1.1rem; font-weight: 700; line-height: 1;
  }
  .fix-more-btn::-webkit-details-marker { display: none; }
  .fix-more-btn:hover { background: var(--sage-faint); color: var(--forest); }
  .fix-more[open] .fix-actions { margin-top: 0.5rem; }
  .fix-group { margin-bottom: 0.5rem; }
  .fix-group-h { display: flex; align-items: center; gap: 0.45rem; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-faint); padding: 0.6rem 0 0.2rem; }

  .mini-btn {
    font-size: 0.75rem; font-weight: 600;
    min-height: 38px; padding: 0.4rem 0.75rem;
    border: 1px solid var(--cream-border); border-radius: 9px;
    background: white; color: var(--ink-mid);
    cursor: pointer; white-space: nowrap;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
  }
  .mini-btn:hover { background: var(--sage-faint); color: var(--ink); }
  .mini-btn--primary { background: var(--forest); border-color: var(--forest); color: var(--cream); }
  .mini-btn--primary:hover { background: var(--forest-mid); color: white; }
  .mini-btn--danger { color: var(--status-down); }
  .mini-btn--danger:hover { background: oklch(96% 0.03 27); color: var(--status-down); }
  .mini-btn:disabled { opacity: 0.5; cursor: default; }

  /* Conferme in linea: niente confirm() del browser, che su iPhone in modalità
     app arriva come un alert di sistema fuori contesto. */
  .fix-confirm { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; background: var(--sage-faint); border-radius: 10px; padding: 0.5rem 0.625rem; }
  .fix-confirm-text { font-size: 0.78rem; color: var(--ink-mid); }
  .fix-confirm input { flex: 1; min-width: 150px; }

  .fix-form { display: flex; flex-direction: column; gap: 0.75rem; background: oklch(98.5% 0.01 167.9); border: 1px solid oklch(88% 0.04 167.9); border-radius: 12px; padding: 0.9375rem; }
  /* Il modulo di correzione porta scritto SOPRA a quale proprietà appartiene:
     è l'errore del 31/08 reso impossibile da commettere alla cieca. */
  .fix-scope {
    display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap;
    font-size: 0.78rem; color: var(--ink-mid); font-weight: 600;
    background: oklch(94% 0.032 167.9); border: 1px solid oklch(86% 0.045 167.9);
    border-radius: 9px; padding: 0.5rem 0.65rem;
  }
  .fix-scope strong { color: var(--forest); }
  .fix-field { display: flex; flex-direction: column; gap: 0.3rem; }
  .fix-field label { font-size: 0.72rem; font-weight: 600; color: var(--ink-mid); }
  /* 1rem pieni: sotto i 16px iOS zooma da solo al focus e sballa il layout. */
  .fix-field textarea, .fix-field input, .fix-confirm input {
    font-size: 1rem; line-height: 1.45; width: 100%;
    padding: 0.5rem 0.75rem;
    border: 1.5px solid var(--cream-border); border-radius: var(--radius-sm);
    background: white; color: var(--ink);
    outline: none; transition: border-color 0.15s;
  }
  .fix-field textarea { resize: vertical; min-height: 66px; }
  .fix-field input, .fix-confirm input { min-height: 44px; }
  .fix-field textarea:focus, .fix-field input:focus, .fix-confirm input:focus { border-color: var(--accent); }
  .fix-count { font-size: 0.68rem; color: var(--ink-faint); text-align: right; font-variant-numeric: tabular-nums; }
  .fix-count.over { color: var(--status-down); font-weight: 700; }
  .fix-err { font-size: 0.78rem; color: var(--status-down); line-height: 1.45; }
  .fix-hint { font-size: 0.72rem; color: var(--ink-faint); line-height: 1.45; }

  /* Le tre KPI in cima alla vista cliente. Questi valori sono esattamente
     quelli che stavano nello style inline: sopra i 900px non cambia un pixel. */
  .kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1.25rem; }
  /* Widget piccoli (kpi-grid del cliente, nums--widgets di "Oggi"): etichetta
     e ⓘ SEMPRE sulla stessa riga. Prima, in poco spazio, l'etichetta andava a
     capo e la ⓘ restava sola in mezzo fra etichetta e numero (29/09/2026). Il
     testo si tronca con "…" invece di andare a capo; la ⓘ non si restringe
     mai. Sotto i 480px, se proprio non basta, la ⓘ di questi widget sparisce
     (l'informazione resta comunque raggiungibile dagli altri riquadri). */
  .kpi-grid .num-label, .nums--widgets .num-label { flex-wrap: nowrap; }
  .kpi-grid .num-label-txt, .nums--widgets .num-label-txt {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1 1 auto;
  }
  .kpi-grid .num-label .info, .nums--widgets .num-label .info { flex-shrink: 0; }
  @media (max-width: 479px) {
    .kpi-grid .num-label > .info, .nums--widgets .num-label > .info { display: none; }
  }

  /* ── Preset (cliente) — controllo segmentato stile iOS ──
     Un solo pezzo con due metà (come "Riepilogo/Tutto" di Salute), non due
     pillole separate. ⚠️ --sage-faint (L 95.7%) e --cream (L 95.4%) sono
     quasi lo stesso chiaro: il binario ci spariva sopra, e restava visibile
     solo dove "Tutto" (trasparente) mostrava il pieno del tondo — sembravano
     due pillole slegate invece di un binario unico. Qui un grigio più netto
     (L 89%) più un filo di bordo, cosi' il binario si vede intero sotto ENTRAMBE
     le metà, e quella attiva (bianca, con ombra) sembra sollevata sopra. */
  /* ── I due controlli segmentati della vista cliente ──────
     "Cosa mostrare" (Riepilogo/Tutto) e "Periodo" (24 ore·72 ore·7 giorni·
     30 giorni, nuovo il 30/09/2026): sulla stessa riga sul computer (periodo
     a destra), impilati sul telefono col periodo sopra — vedi la media query
     più sotto. */
  .cli-controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-start; gap: 0.75rem; margin-bottom: 1.25rem; }
  .preset-bar {
    display: inline-flex; flex-wrap: wrap; align-items: stretch; gap: 0;
    background: oklch(89% 0.014 160); border: 1px solid var(--cream-border);
    border-radius: 12px; padding: 3px;
    margin-bottom: 1.25rem; max-width: 100%;
  }
  .cli-controls .preset-bar, .cli-controls .periodo-bar { margin-bottom: 0; }
  .preset-lab { font-size: 0.7rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint); margin-right: 0.2rem; }
  .pbtn {
    font-size: 0.8125rem; font-weight: 600; min-height: 40px; padding: 0.45rem 1.1rem;
    border-radius: 9px; border: none; background: transparent; color: var(--ink-mid); cursor: pointer;
  }
  .pbtn[aria-pressed="true"] { background: white; color: var(--forest); box-shadow: 0 1px 3px oklch(32.4% 0.057 167.9 / 0.22); }
  /* ── «Date» e confronto fra due periodi (10/10/2026) ──────────────────
     Il pannello sotto i bottoni del periodo: due campi data NATIVI per periodo
     (su iPhone si apre la rotella del sistema), il secondo periodo solo se lo si
     chiede. Righe a filetto come il resto, nessuna carta dentro una carta.
     A 390 px i due campi stanno affiancati, mezzo e mezzo: `min-width: 0` perche'
     il campo data di Safari ha una larghezza sua e senza spingerebbe fuori. */
  .date-panel {
    background: white; border: 1px solid var(--cream-border); border-radius: 14px;
    padding: 0.85rem 0.95rem 0.7rem; margin: -0.35rem 0 1.25rem; max-width: 34rem;
  }
  .date-panel[hidden] { display: none; }
  .date-riga + .date-riga { margin-top: 0.8rem; padding-top: 0.8rem; border-top: 1px solid var(--cream-border); }
  .date-nome { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8125rem; font-weight: 700; color: var(--ink); margin-bottom: 0.45rem; }
  .date-campi { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0.6rem; }
  .date-campo { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
  .date-campo > span { font-size: 0.72rem; font-weight: 600; color: var(--ink-faint); }
  .date-campo input {
    width: 100%; min-width: 0; max-width: 100%; min-height: 44px; font: inherit; font-size: 1rem; color: var(--ink);
    background: white; border: 1.5px solid var(--cream-border); border-radius: var(--radius-sm); padding: 0.4rem 0.6rem;
  }
  .date-campo input:focus { border-color: var(--accent); outline: none; }
  .date-err { font-size: 0.8125rem; color: var(--status-down); line-height: 1.45; margin-top: 0.6rem; }
  .date-err[hidden] { display: none; }
  .date-azioni { display: flex; align-items: center; gap: 0.6rem; margin-top: 0.75rem; min-height: 32px; }
  .date-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; margin-right: 0.35rem; vertical-align: 0.05em; }
  .date-dot--a { background: var(--forest); }
  .date-dot--b { background: var(--p2); }
  .date-nome .date-dot { margin-right: 0; }
  /* I due numeri affiancati di una scheda di confronto. */
  .cmp-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0.75rem; margin-top: 0.4rem; }
  .cmp-col { min-width: 0; }
  .cmp-tag { font-size: 0.72rem; font-weight: 600; color: var(--ink-faint); }
  .cmp-col .num-val { margin-top: 0.2rem; }
  .cmp-legenda { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; margin-top: 0.6rem; font-size: 0.78rem; color: var(--ink-mid); }
  /* Sezioni, azioni, momenti: A e B in colonne, righe a filetto. */
  .cmp-gruppo + .cmp-gruppo { margin-top: 1.1rem; }
  .cmp-titolo { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 0.15rem; }
  .cmp-row {
    display: grid; grid-template-columns: minmax(0, 1fr) 3.1rem 3.1rem 4.3rem; align-items: baseline; gap: 0.35rem;
    padding: 0.5rem 0; border-bottom: 1px solid var(--cream-border); font-size: 0.8125rem; color: var(--ink-mid);
  }
  .cmp-gruppo--senza-var .cmp-row { grid-template-columns: minmax(0, 1fr) 3.4rem 3.4rem; }
  .cmp-row:last-child { border-bottom: none; }
  .cmp-nome { min-width: 0; overflow-wrap: anywhere; }
  .cmp-n { text-align: right; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
  .cmp-var { text-align: right; font-size: 0.75rem; white-space: nowrap; }
  .cmp-head { font-size: 0.72rem; color: var(--ink-faint); padding: 0.25rem 0; }
  .cmp-head .cmp-n { font-weight: 600; color: var(--ink-faint); }
  .cmp-head .date-dot { margin-right: 0.25rem; }
  .cmp-vuoto, .cmp-altre { font-size: 0.78rem; color: var(--ink-faint); padding: 0.4rem 0; }
  @media (max-width: 479px) {
    .date-panel { margin-top: 0; max-width: none; }
    .cmp-cols .num-val { font-size: 1.9rem; }
  }

  .btn {
    font-size: 0.8125rem; font-weight: 600; min-height: 40px; padding: 0.5rem 0.9rem;
    border-radius: var(--radius-sm); border: 1px solid var(--cream-border); background: white; color: var(--forest); cursor: pointer;
  }
  .btn:hover { background: var(--sage-faint); }

  .empty { font-size: 0.82rem; color: var(--ink-faint); padding: 1rem 0.2rem; line-height: 1.55; background: var(--sage-faint); border-radius: 12px; text-align: left; padding-left: 0.85rem; padding-right: 0.85rem; }
  .empty b { color: var(--ink-mid); }

  /* ── Skeleton ────────────────────────────────────────── */
  .skel { color: transparent !important; position: relative; }
  .skel::after {
    content: ""; position: absolute; inset: 0.1em 0 0.2em;
    border-radius: 6px; background: var(--sage-faint);
    animation: pulse 1.4s ease-in-out infinite;
  }
  @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }

  /* ── Footer ──────────────────────────────────────────── */
  footer {
    max-width: 1680px; margin: 0 auto;
    display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
    font-size: 0.75rem; color: var(--ink-faint);
    padding: 0.5rem 1.5rem 2.5rem;
    padding-bottom: max(2.5rem, calc(var(--sab) + 1.5rem));
    line-height: 1.6;
  }

  .live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

  /* ── PWA: senza rete ─────────────────────────────────────
     Non è un ripiego grafico: è la schermata che si vede quando l'app viene
     aperta dall'icona e il server non risponde. Prima al suo posto compariva
     il modulo di accesso con scritto "Connessione non disponibile", cioè si
     chiedeva la password a chi era già dentro e non aveva nessun problema di
     credenziali. */
  #offline-screen {
    position: fixed; inset: 0; z-index: 120;
    display: none; flex-direction: column; align-items: center; justify-content: center;
    gap: 0.75rem; text-align: center;
    padding: max(1.5rem, var(--sat)) 1.5rem max(1.5rem, var(--sab));
    background: var(--cream);
  }
  #offline-screen.visible { display: flex; }
  #offline-screen .mark { height: 30px; width: auto; margin-bottom: 0.5rem; }
  #offline-screen h1 { font-family: var(--font-display); font-size: 1.25rem; font-weight: 700; color: var(--forest); letter-spacing: -0.02em; }
  #offline-screen p { font-size: 0.875rem; color: var(--ink-mid); line-height: 1.55; max-width: 30rem; }
  #offline-screen .off-why { font-size: 0.8125rem; color: var(--ink-faint); max-width: 30rem; }
  #offline-screen button {
    margin-top: 0.5rem; min-height: 44px; padding: 0 1.25rem;
    border: none; border-radius: var(--radius-sm); cursor: pointer;
    background: var(--forest); color: var(--cream);
    font-size: 0.9375rem; font-weight: 600;
  }
  #offline-screen button:disabled { opacity: 0.55; cursor: default; }
  #offline-screen .off-credit { margin-top: 1.5rem; font-size: 0.75rem; color: var(--ink-faint); }

  /* Striscia di rete caduta a sessione già aperta: i numeri a schermo sono
     quelli caricati poco fa, restano in memoria e sono onesti — si dichiara
     solo che da quel momento non si aggiornano più. */
  /* ⚠️ Dal 07/10/2026 NON e' piu' la prima riga del corpo: stava nel flusso e a
     ogni comparsa spingeva giu' tutta la pagina (e il suo ritiro la faceva
     risalire). Adesso e' una striscia sopra la pagina, nel contenitore
     #avvisi in basso, e ENTRA con transform e opacita' (avviso-su): non sposta
     niente. */
  #avvisi {
    position: fixed; z-index: 90;
    left: max(0.75rem, var(--sal)); right: max(0.75rem, var(--sar));
    bottom: calc(0.75rem + var(--sab));
    display: flex; flex-direction: column; align-items: center; gap: 0.5rem;
    pointer-events: none;
  }
  #avvisi > * { pointer-events: auto; width: 100%; max-width: 34rem; }
  @keyframes avviso-su { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
  #net-bar {
    display: none; align-items: center; gap: 0.5rem; flex-wrap: wrap;
    padding: 0.625rem 0.875rem; border-radius: 14px;
    background: var(--status-warn); color: oklch(20% 0.03 75);
    box-shadow: 0 12px 36px oklch(32.4% 0.057 167.9 / 0.28);
    font-size: 0.8125rem; font-weight: 500; line-height: 1.4;
  }
  #net-bar.visible { display: flex; animation: avviso-su var(--t-esce) var(--ease-ios); }
  #net-bar button {
    margin-left: auto; min-height: 32px; padding: 0 0.75rem; cursor: pointer;
    border: 1px solid oklch(20% 0.03 75 / 0.35); border-radius: 8px;
    background: transparent; color: inherit; font-size: 0.8125rem; font-weight: 600;
  }

  /* ── PWA: nuova versione ─────────────────────────────────
     In fondo e NON dentro l'header: l'header è sticky, e una barra fixed dentro
     un antenato posizionato su iOS installato perde l'ancoraggio al viewport
     (trappola già pagata su altri progetti). Qui è figlia di body e basta. */
  #update-bar {
    display: none; align-items: center; gap: 0.75rem; flex-wrap: wrap;
    padding: 0.75rem 0.875rem;
    border-radius: 14px;
    background: var(--forest); color: var(--cream);
    box-shadow: 0 12px 36px oklch(32.4% 0.057 167.9 / 0.35);
    font-size: 0.8125rem; line-height: 1.45;
  }
  #update-bar.visible { display: flex; animation: avviso-su var(--t-esce) var(--ease-ios); }
  #update-bar .ub-text { flex: 1; min-width: 11rem; }
  /* I due pulsanti restano una coppia: senza il contenitore, a 393px "Più tardi"
     andava a capo da solo su una riga tutta sua. */
  #update-bar .ub-actions { display: flex; align-items: center; gap: 0.5rem; margin-left: auto; }
  #update-bar .ub-warn { display: block; color: var(--status-warn); font-weight: 600; margin-top: 0.2rem; }
  #update-bar button {
    min-height: 40px; padding: 0 0.875rem; cursor: pointer;
    border-radius: 9px; font-size: 0.8125rem; font-weight: 600; font-family: inherit;
  }
  #update-bar .ub-go { border: none; background: var(--cream); color: var(--forest); }
  #update-bar .ub-later { border: 1px solid oklch(45% 0.06 167.9); background: transparent; color: var(--on-forest); }

  /* ── PWA: invito a installare ────────────────────────────
     Il velo che stacca la carta dalla pagina. La carta la disegna la libreria
     dentro il suo shadow root; qui c'è solo quello che sta fuori. */
  #invito-scrim {
    position: fixed; inset: 0; z-index: 2147483000;
    background: rgba(12, 18, 26, 0.30);
    opacity: 0; pointer-events: none;
    transition: opacity 250ms ease-out;
  }
  @media (prefers-reduced-motion: reduce) { #invito-scrim { transition: none; } }
  .footer-install {
    background: none; border: none; padding: 0; cursor: pointer;
    font: inherit; color: var(--ink-mid); text-decoration: underline;
    text-underline-offset: 2px; min-height: 32px;
  }

  /* ── Container queries: il riquadro si adatta a SÉ ───── */
  @container (max-width: 420px) {
    .num-val { font-size: 1.75rem; }
    .lrow { grid-template-columns: minmax(74px, 46%) minmax(0, 1fr) auto; gap: 0.5rem; }
  }

  /* ── Telefono e tablet (< 1024px, il guscio "iOS") ─────
     Un solo breakpoint per tutto il guscio: sopra i 1024px c'è la barra
     laterale del computer, sotto la barra in alto compatta + la barra
     inferiore. Prima erano DUE soglie scucite fra loro (720px per le schede,
     900px per il resto): unificate il 29/09/2026 col ridisegno Apple.
     Il conteggio delle colonne dei riquadri (1 sotto i 768px, 2 fra 768 e
     1023) è una soglia A PARTE, più sotto: qui dentro sta tutto quello che è
     "comportamento da telefono" a prescindere da quante colonne ci sono. */
  @media (max-width: 1023px) {
    .rail { gap: 1rem; }
    .stack { gap: 1rem; }
    /* Niente finestra scorrevole dentro una pagina che già scorre: il feed
       mostra le prime cinque e apre il resto a richiesta (vedi il link "Tutte
       (N)" — stessa soglia a ogni larghezza, regola qui sopra). */
    .feed-scroll { max-height: none; overflow: visible; mask-image: none; -webkit-mask-image: none; }

    /* ── Chip filtro: una riga sola, scorrevole ────────────
       Cinque chip sono il numero giusto per i chip inline (3-6); il difetto
       era l'andare a capo, non il numero. Una riga scorrevole costa 48px
       invece di 94. Niente foglio inferiore: per cinque filtri di cui uno
       usato quasi sempre, aggiungerebbe un tocco per arrivare a "Tutte".
       ⚠️ NIENTE overscroll-behavior qui. E' uno scorrevole orizzontale dentro
       una pagina che scorre in verticale — la famiglia di guai gia' pagata due
       volte (31/08 sul body, 01/09 dentro .feed-scroll). La catena in
       orizzontale e' gia' fermata da `overscroll-behavior: none` su html. */
    .feed-filters {
      flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
      scroll-snap-type: x proximity;
      scrollbar-width: none; -ms-overflow-style: none;
      margin-right: -1.05rem; padding-right: 1.05rem;
    }
    .feed-filters::-webkit-scrollbar { display: none; }
    .fchip { flex: 0 0 auto; scroll-snap-align: start; }
    /* La sfumatura dichiara che c'e' dell'altro a destra, e sparisce quando
       non c'e' piu' niente: un chip fuori schermo, senza, e' invisibile. */
    .feed-filters.altro-dx {
      mask-image: linear-gradient(to right, #000 calc(100% - 34px), transparent);
      -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 34px), transparent);
    }

    /* ── Intestazione di pagina ────────────────────────────
       "PLANCIA" sopra "Cosa sta succedendo adesso" non aggiunge niente che il
       titolo non dica gia'. Nella vista cliente l'occhiello resta: la' porta
       lo stato ("La vostra app · in pausa"), che e' un'informazione. */
    #view-admin .page-eyebrow { display: none; }
    .page-head { margin-bottom: 0.85rem; }

    /* ── Vista cliente: le KPI 2×2 e la barra VISTA su una riga ──
       Tre card impilate a tutta larghezza erano ~330px per tre numeri. La
       lezione era gia' stata imparata a fine agosto (il guscio del 31/08 aveva
       la stessa regola per quattro card) e si e' persa nel rifacimento.
       L'ultima, se resta spaiata, prende tutta la riga: mezza card vuota
       accanto sembra un riquadro che non ha caricato. */
    /* "VISTA" in maiuscoletto mangiava lo spazio che mandava "Riordina" da
       solo su una seconda riga: i tre bottoni si spiegano da soli. I 44px del
       bersaglio da dito NON si toccano — anzi qui salgono da 40. */
    .preset-lab { display: none; }
    /* A tutta larghezza, metà e metà — non due pillole della misura del testo. */
    .preset-bar { display: flex; width: 100%; margin-bottom: 0; }
    /* Impilati, il periodo sopra: è la prima domanda ("di che finestra sto
       guardando i numeri"), "Riepilogo/Tutto" viene dopo. */
    .cli-controls { flex-direction: column; align-items: stretch; margin-bottom: 0.9rem; gap: 0.6rem; }
    .cli-controls .periodo-bar { order: -1; }
    /* Quattro voci in poco più di 350px: un filo più strette delle due di
       "Riepilogo/Tutto", altrimenti "30 giorni" va a capo dentro il segmento. */
    .periodo-bar .pbtn { font-size: 0.7rem; padding: 0.4rem 0.3rem; }
    .pbtn { flex: 1 1 0; font-size: 0.78rem; min-height: 44px; }
    main { padding: 1.125rem 1rem 2rem; }
    header { padding: 0.625rem 1rem; padding-top: max(0.625rem, var(--sat)); }
    footer { padding: 0.5rem 1rem 2.5rem; padding-bottom: max(2.5rem, calc(var(--sab) + 1.5rem)); }
    .card { padding: 1.05rem 1.05rem; }
    .page-title { font-size: 1.3125rem; }
    /* Su 393px il trascinamento non esiste: solo frecce. È anche il percorso
       da tastiera e l'alternativa richiesta da WCAG 2.5.7 — e sotto i 1024px
       il trascinamento è comunque spento in JS (vedi il pointerdown in
       10-disposizione.js), quindi la maniglia sparisce alla stessa soglia. */
    body.reorder .handle { display: none; }
    /* A 393px la barra andava a tre righe e spingeva giù mezza schermata:
       il nome del prodotto lascia il posto ai comandi. */
    .brand-word { display: none; }
    .topbar { gap: 0.5rem; }
    .nums { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .num-val { font-size: 1.75rem; }
    /* Bersagli da dito: 44px minimi. A 36–38px la terna Modifica / Disattiva /
       Elimina metteva la voce distruttiva a 8px dalle altre; su desktop le
       misure restano compatte perché lì si mira col mouse. */
    .fchip, .mini-btn, .footer-install { min-height: 44px; }
    .fix-actions { gap: 0.6rem; }

    /* ── Riquadri richiudibili (SOLO telefono) ─────────────
       Ripescato dal guscio del 31/08 (index.html.bak-20260831-165147, righe
       612-632) e rimesso con una differenza: là il bersaglio era `.card-head`
       intera, ma dentro quella testa vivono già le frecce di riordino e la ⓘ —
       controlli dentro un controllo, HTML invalido e illeggibile a uno screen
       reader. Qui il bersaglio è un bottone dedicato con aria-expanded +
       aria-controls, accanto agli altri e non attorno a loro.

       ⚠️ Queste classi non compaiono MAI nel DOM sopra i 900px: shell() le
       scrive solo se isPhone(). La plancia a 1440 e 1920 resta identica al
       pixel — misurato prima e dopo, vedi il rapporto. */
    .m-collapse > .card-head { margin-bottom: 0; }
    .m-collapse.m-open > .card-head { margin-bottom: 0.9rem; }
    .m-collapse .card-head > div:first-child { min-width: 0; }
    /* `position: relative` NON e' decorativo: senza, l'area di tocco della ⓘ
       (.info::before, che sborda di 11px per lato ed e' posizionata) copriva i
       primi 6px del chevron — misurato con elementFromPoint — e toccare li
       apriva la spiegazione invece del riquadro. E' lo stesso guaio gia'
       documentato qui sopra per "Scala comune"; il chevron e' arrivato dopo e
       non era stato protetto. Posizionando anche il chevron, che nel DOM viene
       DOPO la ⓘ, a parita' di z-index vince lui. */
    .m-toggle {
      flex-shrink: 0; width: 40px; min-height: 44px; margin-right: -0.35rem;
      display: inline-flex; align-items: center; justify-content: center;
      background: transparent; border: none; color: var(--ink-mid); cursor: pointer;
      position: relative;
    }
    /* Tutta la testa chiusa apre il riquadro, non il solo chevron da 40px.
       Toccare il titolo di una sezione chiusa e' il gesto che fanno tutti
       (NN/G, "Accordion Icons"): prima non succedeva niente — verificato con
       elementFromPoint, sul titolo rispondeva "nessun bottone". Non si puo'
       avvolgere la testa in un <button> — dentro ci vivono gia' la ⓘ e le
       frecce, e sarebbero controlli dentro un controllo — quindi il bottone
       resta quello che c'e' e allunga la sola AREA DI TOCCO su tutta la riga.
       ⚠️ Vale solo da CHIUSO: da aperto la testa porta testo che si legge e si
       seleziona, e un velo li' sopra darebbe fastidio. Per chiudere resta il
       chevron, che da aperto e' li' e si vede. */
    .m-collapse:not(.m-open) > .card-head { position: relative; }
    /* ⚠️ `position: static` qui NON e' una svista: serve a far ancorare il velo
       alla TESTA e non al chevron. Con il chevron relative, `inset: 0` si
       risolveva sul chevron stesso e il velo copriva i suoi 40px — cioe'
       niente. Static solo da chiuso; da aperto il chevron torna relative e
       continua a vincere sull'area di tocco della ⓘ. */
    .m-collapse:not(.m-open) .m-toggle { position: static; }
    .m-collapse:not(.m-open) .m-toggle::before { content: ""; position: absolute; inset: 0; }
    /* ...e la ⓘ resta sopra il velo, altrimenti la spiegazione non si apre
       piu': il chevron viene dopo nel DOM e coprirebbe tutto. */
    .m-collapse:not(.m-open) .head-tools .info { z-index: 1; }
    .m-toggle svg { width: 18px; height: 18px; transition: transform 0.18s ease; }
    .m-collapse.m-open .m-toggle svg { transform: rotate(180deg); }
    /* !important: dentro "Oggi, su tutto" e nelle KPI il corpo porta un
       display inline che altrimenti vincerebbe sulla chiusura (stessa ragione
       documentata nel guscio vecchio). */
    .m-collapse:not(.m-open) > :not(.card-head) { display: none !important; }
    /* Chiuso, un bottone nella testa comanderebbe un corpo che non si vede. */
    .m-collapse:not(.m-open) .head-tools .mini-btn { display: none; }
    /* Stessa cosa per i link nella testa («Tutte (103)», «Tutti», «Nuova»): il
       velo che apre il riquadro li copriva, e il tocco apriva con cinque righe
       invece di fare quello che il link prometteva. */
    .m-collapse:not(.m-open) .head-tools .widget-link { display: none; }
    /* L'assaggio: una riga sola col numero che risponde alla domanda della
       card. Senza, la testa chiusa non dice cosa c'è dentro e non si apre. */
    .card-peek {
      display: none; margin-top: 0.3rem;
      font-size: 0.8125rem; font-weight: 600; color: var(--forest); line-height: 1.35;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .m-collapse:not(.m-open) .card-peek { display: block; }
    .m-collapse:not(.m-open) .card-peek:empty { display: none; }
  }

  /* ── Telefono stretto (< 768px): una colonna sola ──────
     Fra 768 e 1023 (tablet) i riquadri restano su due colonne — stesso
     comportamento "da telefono" qui sopra, ma binari/scacchiera come sul
     computer (vedi isNarrowPhone() in 03-menu.js e i due rami in
     11-disegno.js). Sotto i 768px una colonna, e "sposta nell'altra colonna"
     smette di voler dire qualcosa. */
  @media (max-width: 767px) {
    /* Un binario solo. I riquadri dei due binari si intercalano nell'ordine
       della lista, invece di svuotare prima la colonna sinistra e poi la destra.
       NB: niente display:contents sui binari. Provato, e a 393px azzerava la
       larghezza dei riquadri (container-type + display:contents in Chromium):
       misurato .blk a 0px e le domande incolonnate una lettera per riga. */
    .rails { display: block; }
    /* Una colonna sola: "sposta nell'altra colonna" non vuol dire niente. */
    body.reorder .icon-btn[data-d="x"] { display: none; }
    /* Tre card impilate a tutta larghezza erano ~330px per tre numeri; due per
       riga (la terza, sola, prende tutta la riga) le rimette alla forma di
       widget piccoli. */
    .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.7rem; }
    .kpi-grid > .card { padding: 0.85rem 0.9rem; }
    .kpi-grid .num-val { font-size: 1.5rem; }
    .kpi-grid .num-val.sm { font-size: 0.9375rem; }
    .kpi-grid .num-sub { line-height: 1.35; }
    .kpi-grid > *:last-child:nth-child(odd) { grid-column: 1 / -1; }
  }

  /* A 320px la testa lascia all'assaggio ~174px, e la riga si tagliava in
     mezzo al numero: "Grand Hotel in testa, 204 s…" (ne servivano 197),
     "Apertura — concierge in t…" (243). Il numero E' l'assaggio: senza, la
     riga non fa aprire niente ed e' proprio il difetto che l'assaggio doveva
     togliere. Sotto i 360px si concede una seconda riga — costa ~18px su un
     riquadro chiuso. Da 361px in su resta la riga unica gia' misurata, e
     sopra i 900px questa classe non esiste nel DOM. */
  @media (max-width: 360px) {
    .m-collapse:not(.m-open) .card-peek {
      white-space: normal;
      display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
    }
  }

/* ══════════════════════════════════════════════════════════════════════════
   Attrezzi ereditati dalla scheda "Progetti" (progetti.html, rimossa).
   Qui restano SOLO le quattro classi che richieste.html usa ancora — barra di
   ricerca/filtri e riga di riepilogo. Tutto il resto (.hq-card, .hq-gruppo,
   .hq-sem, .hq-fantasma…) era morto e tolto il 29/09/2026, col ridisegno
   Apple: non c'era più nessuna pagina che lo disegnasse.
   ══════════════════════════════════════════════════════════════════════════ */
.hq-bar { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-bottom: 0.7rem; }
/* 1rem pieno (16 px): sotto, iOS ingrandisce la pagina quando si tocca il campo
   e resta ingrandita (07/10/2026: erano 13 px). Lo zoom con le dita NON si blocca:
   si toglie la causa, non la possibilita' di ingrandire. */
.hq-cerca, .hq-sel {
  font: inherit; font-size: 1rem; color: var(--ink);
  background: white; border: 1px solid var(--cream-border);
  padding: 0 0.9rem; min-height: 44px;
}
/* Il campo di ricerca è arrotondato stile iOS; le tendine restano rettangolari
   (var(--radius-sm)), come ogni altro controllo a scelta della pagina. */
.hq-cerca { flex: 1 1 210px; min-width: 0; border-radius: 999px; }
.hq-sel { flex: 0 1 auto; max-width: 100%; border-radius: var(--radius-sm); }
.hq-cerca:focus, .hq-sel:focus { border-color: var(--accent); outline: none; }
.hq-riepilogo { font-size: 0.8125rem; color: var(--ink-mid); line-height: 1.55; margin-bottom: 1.25rem; }
.hq-riepilogo b { color: var(--ink); font-variant-numeric: tabular-nums; }
.hq-riepilogo .hq-giu { color: var(--status-down); }

/* ══════════════════════════════════════════════════════════════════════════
   La scheda "Richieste" (richieste.html) — tappa 2 del piano "Blasat HQ".
   Aggiunta in coda l'11/09/2026: NIENTE qui sopra è stato toccato.

   Riusa quello che c'è già — .card, .badge, .mini-btn, .empty, .hq-bar e i
   suoi filtri — e aggiunge solo quello che non esisteva: la barra colorata di
   stato sulla carta, il messaggio che si accorcia, il pannello del dettaglio.
   Due viste molto diverse (admin e cliente) dallo STESSO foglio: al cliente
   cambia cosa c'è dentro la carta, non che aspetto ha il pannello.
   ══════════════════════════════════════════════════════════════════════════ */

/* Niente gap: lo spazio fra i gruppi lo dà .ric-gruppo (margin-bottom), e il
   caso cliente ha un solo figlio (.ric-gruppo-lista). Un gap qui si
   sommerebbe a quel margine e raddoppierebbe lo spazio fra "Da fare" e
   "Chiuse". */
.ric-lista { display: flex; flex-direction: column; }

/* Elenco raggruppato iOS (inset grouped list): un riquadro bianco per gruppo
   — qui .ric-gruppo-lista, lo stesso linguaggio dei widget (ombra morbida,
   angoli larghi) — con le richieste come righe flush dentro, separate da una
   linea sottile. Una colonna sola anche sul desktop: il contenuto è un TESTO
   che si legge, non una scheda che si scorre con l'occhio.
   ⚠️ Fino al 29/09/2026 ogni richiesta era una .card a sé col bordo sinistro
   colorato per stato. Il colore adesso sta SOLO nella pastiglia (.badge a
   destra): un bordo e una pastiglia che dicono la stessa cosa erano un
   doppione, e "in coda"/"rifiutata" — due grigi vicini — si confondevano più
   facilmente in un bordo di 3px che in una pastiglia con la parola scritta. */
.ric-gruppo-lista {
  background: white; border-radius: var(--radius-card); box-shadow: var(--widget-shadow);
  overflow: hidden;
}
.ric-card {
  background: transparent; border: none; border-radius: 0; box-shadow: none;
  padding: 1rem 1.1rem; border-bottom: 1px solid var(--cream-border);
}
.ric-gruppo-lista > .ric-card:last-child { border-bottom: none; }

.ric-top { display: flex; align-items: flex-start; gap: 0.6rem; justify-content: space-between; }
.ric-app { font-size: 0.9375rem; font-weight: 600; color: var(--ink); letter-spacing: -0.01em; line-height: 1.3; }
.ric-meta { font-size: 0.72rem; color: var(--ink-faint); margin-top: 0.15rem; line-height: 1.45; }
.ric-top .badge { flex-shrink: 0; text-transform: none; letter-spacing: 0; font-weight: 600; font-size: 0.66rem; }
/* Manca in ui.css un rosso per le pastiglie: .badge.warn è giallo, .off è
   neutro. Serve per "Non riuscita", che è l'unico stato che chiede una mossa. */
.badge.no { background: oklch(96% 0.03 27); color: var(--status-down); border-color: oklch(89% 0.05 27); }
/* E un'ardesia per "In coda": con lo sfondo di serie era identica a
   "Rifiutata" (bianca), cioe' due stati opposti con la stessa faccia. */
.badge.coda { background: oklch(92% 0.009 165); color: var(--ink-mid); border-color: oklch(85% 0.012 165); }

/* Il messaggio: due righe nell'elenco, intero nel dettaglio. `-webkit-` senza
   alternative perché line-clamp standard non è ancora ovunque; il ripiego
   naturale (nessun taglio) resta leggibile, solo più lungo. */
.ric-msg {
  font-size: 0.875rem; color: var(--ink); line-height: 1.55; margin-top: 0.55rem;
  white-space: pre-line; overflow-wrap: anywhere;
}
.ric-msg--corto {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.ric-risposta {
  font-size: 0.8125rem; color: var(--ink-mid); line-height: 1.5; margin-top: 0.5rem;
  border-left: 2px solid var(--forest-soft); padding-left: 0.6rem; white-space: pre-line;
}
.ric-risposta b { color: var(--ink); font-weight: 600; }

.ric-azioni { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.7rem; align-items: center; }
.ric-azioni .mini-btn { text-decoration: none; }

/* Il dettaglio si apre DENTRO la carta invece che in una finestra: su un
   telefono una modale sopra un elenco lungo fa perdere il posto in cui si era.
   Resta chiuso finché non lo si chiede — e finché non lo si chiede, la sua
   richiesta al server non parte. */
.ric-dett { margin-top: 0.8rem; border-top: 1px solid var(--cream-border); padding-top: 0.8rem; }
.ric-dett-riga { font-size: 0.75rem; color: var(--ink-faint); line-height: 1.6; overflow-wrap: anywhere; }
.ric-dett-riga b { color: var(--ink-mid); font-weight: 600; }
.ric-dett-tit {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--ink-faint); margin: 0.7rem 0 0.35rem;
}
.ric-foto { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.ric-foto a { display: block; border: 1px solid var(--cream-border); border-radius: 8px; overflow: hidden; background: var(--sage-faint); }
.ric-foto img { display: block; width: 108px; height: 108px; object-fit: cover; }
.ric-repo {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem;
  padding: 0.4rem 0; border-top: 1px dashed var(--cream-border);
}
.ric-repo:first-of-type { border-top: 0; }
.ric-repo-nome { font-size: 0.75rem; font-weight: 600; color: var(--ink-mid); margin-right: 0.15rem; }
/* I .mini-btn sono <a>: senza questa riga arrivano sottolineati e in mezzo a
   una riga di testo sembrano link nudi invece che bottoni. */
.ric-repo .mini-btn, .ric-foto a { text-decoration: none; }

@media (max-width: 1023px) {
  .ric-card { padding: 0.9rem 0.95rem; }
  .ric-foto img { width: 92px; height: 92px; }
  /* Quattro filtri a coppie, stessa regola dei progetti: senza min-width:0 un
     <select> si prende la larghezza della sua voce più lunga. */
  .ric-bar .hq-sel { flex: 1 1 calc(50% - 0.25rem); min-width: 0; }
  .ric-bar .hq-cerca { flex: 1 1 100%; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Il modulo "Nuova richiesta" (richieste.html, solo cliente) — tappa 3.
   Aggiunto in coda il 11/09/2026: NIENTE qui sopra è stato toccato.

   Il modulo è una .card come le altre, non un riquadro con un aspetto suo: è
   la stessa pagina, e un pannello d'invio che sembra un'app diversa fa
   sospettare di aver lasciato il sito. Larghezza limitata a ~62ch anche sul
   desktop — una riga di testo lunga mezzo schermo non si scrive e non si
   rilegge, e qui dentro si scrive.
   ══════════════════════════════════════════════════════════════════════════ */

.ric-nuova-sub { font-size: 0.8125rem; color: var(--ink-faint); line-height: 1.5; margin-top: 0.25rem; margin-bottom: 1rem; }

.ric-campo { margin-bottom: 0.95rem; }
.ric-campo > label {
  display: block; font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 0.35rem;
}
.ric-campo input[type="text"],
.ric-campo select,
.ric-campo textarea {
  width: 100%; font: inherit; font-size: 0.9375rem; color: var(--ink);
  background: white; border: 1.5px solid var(--cream-border);
  border-radius: var(--radius-sm); padding: 0.55rem 0.7rem;
  /* Senza, Safari su iPhone ingrandisce la pagina quando si tocca un campo:
     il rimedio è la dimensione del testo (≥16px), non disabilitare lo zoom. */
  font-size: 16px; line-height: 1.45;
}
/* Bersaglio da dito: il nome e l'app misuravano 43 e 39 px. */
.ric-campo input[type="text"], .ric-campo select { min-height: 44px; }
.ric-campo textarea { resize: vertical; min-height: 7.5rem; }
.ric-campo input[type="text"]:focus,
.ric-campo select:focus,
.ric-campo textarea:focus { border-color: var(--accent); outline: none; }
.ric-campo input[type="text"]:disabled,
.ric-campo select:disabled,
.ric-campo textarea:disabled { background: var(--sage-faint); color: var(--ink-faint); }

.ric-aiuto { font-size: 0.72rem; color: var(--ink-faint); line-height: 1.45; margin-top: 0.3rem; }
.ric-aiuto--vicino { color: oklch(46% 0.1 75); }

/* La zona degli allegati è un <button>, non un <div> con un click sopra: così
   ci si arriva col tasto di tabulazione e l'annuncio dello screen reader è
   quello giusto. Il trascinamento è in più, non è l'unico modo. */
.ric-drop {
  width: 100%; font: inherit; font-size: 0.8125rem; color: var(--ink-mid);
  background: var(--sage-faint); border: 2px dashed var(--cream-border);
  border-radius: var(--radius-sm); padding: 1.1rem 0.7rem; cursor: pointer;
  line-height: 1.45; text-align: center; transition: border-color 0.15s, background 0.15s;
}
.ric-drop:hover, .ric-drop--sopra { border-color: var(--forest-soft); background: white; }
.ric-drop:disabled { cursor: default; opacity: 0.5; }

.ric-file { list-style: none; margin-top: 0.5rem; display: flex; flex-direction: column; gap: 0.3rem; }
.ric-file li {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  background: var(--sage-faint); border-radius: 8px; padding: 0.4rem 0.6rem;
  font-size: 0.78rem; color: var(--ink-mid);
}
.ric-file .ric-file-nome { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ric-file .ric-file-peso { color: var(--ink-faint); }

/* Il campo esca. NON display:none: un robot che legge il CSS salta quello che
   è nascosto così, e l'esca non pescherebbe niente. Fuori dallo schermo,
   fuori dal percorso di tabulazione, dichiarato nascosto agli assistivi. */
.ric-esca {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; opacity: 0; pointer-events: none;
}
.ric-esca input { font-size: 16px; }

.ric-errore {
  font-size: 0.8125rem; line-height: 1.5; color: var(--status-down);
  background: oklch(97% 0.02 27); border: 1px solid oklch(90% 0.04 27);
  border-radius: 10px; padding: 0.6rem 0.75rem; margin-bottom: 0.8rem;
}
.ric-fatto {
  font-size: 0.8125rem; line-height: 1.55; color: var(--forest);
  background: oklch(96% 0.025 167.9); border: 1px solid oklch(88% 0.04 167.9);
  border-radius: 10px; padding: 0.7rem 0.85rem; margin-bottom: 1.1rem; max-width: 64ch;
}
.ric-fatto b { font-weight: 700; }


@media (max-width: 1023px) {
  .ric-fatto { max-width: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Le due azioni dell'admin (richieste.html) — tappa 4.
   Aggiunto in coda il 11/09/2026: NIENTE qui sopra è stato toccato.

   Il pannello "cosa succede se approvi" NON è un riquadro d'allarme: è una
   scheda tecnica che si legge prima di decidere. Il rosso e il giallo stanno
   solo sugli esiti veri (un conflitto, un ramo che manca), mai sul pannello
   intero — un fondo colorato sotto ogni richiesta insegna a ignorarlo, e
   quando poi serve davvero non lo guarda più nessuno.

   La conferma invece SÌ: è l'unico posto della scheda con un bordo suo,
   perché è l'unico posto da cui non si torna indietro.
   ══════════════════════════════════════════════════════════════════════════ */

.ric-azioni--admin { margin-top: 0.75rem; gap: 0.4rem; }
.ric-azioni--admin .mini-btn { padding: 0.5rem 0.9rem; font-size: 0.8125rem; }

.ric-insieme {
  margin-top: 0.45rem; padding: 0.5rem 0.65rem;
  background: var(--sage-faint); border-radius: 8px;
}
.ric-insieme b { color: var(--ink-mid); }

/* "Sto pubblicando": il testo cambia, e con lui il colore — così si vede al
   volo che la pagina sta facendo qualcosa e non è rimasta ferma. */
.ric-attesa { color: var(--ink-mid); font-weight: 600; }
.ric-guasto { color: var(--status-down); }
.ric-fatta { color: var(--forest); font-weight: 600; }

.ric-conferma {
  margin-top: 0.75rem; padding: 0.8rem 0.85rem;
  background: oklch(97.5% 0.012 76.6);
  border: 1.5px solid var(--cream-border);
  border-left: 3px solid var(--forest-soft);
  border-radius: var(--radius-sm);
}
.ric-conferma-tit { font-size: 0.8125rem; color: var(--ink); line-height: 1.5; }
.ric-conferma-tit b { font-weight: 700; }
.ric-conferma-elenco {
  list-style: none; margin: 0.5rem 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 0.25rem;
  font-size: 0.78rem; color: var(--ink-mid); line-height: 1.5;
}
.ric-conferma-elenco li { overflow-wrap: anywhere; }
.ric-conferma-elenco b { color: var(--ink); font-weight: 600; }
/* La frase che dice che non si torna indietro. In corpo piccolo ma non
   grigio: è la riga che qualcuno rileggerà fra sei mesi chiedendosi se aveva
   capito cosa stava premendo. */
.ric-conferma-nota { margin-top: 0.5rem; font-size: 0.75rem; color: var(--status-down); line-height: 1.5; }
.ric-conferma-lab {
  display: block; margin-top: 0.6rem; margin-bottom: 0.3rem;
  font-size: 0.72rem; color: var(--ink-faint); line-height: 1.45;
}
.ric-conferma-nota-campo {
  width: 100%; font: inherit; font-size: 16px; line-height: 1.45; color: var(--ink);
  background: white; border: 1.5px solid var(--cream-border);
  border-radius: var(--radius-sm); padding: 0.5rem 0.65rem; resize: vertical;
}
.ric-conferma-nota-campo:focus { border-color: var(--accent); outline: none; }

@media (max-width: 1023px) {
  /* Come per l'invio: un bottone che pubblica in produzione non sta in un
     angolo di riga a larghezza di telefono. */
  .ric-azioni--admin .mini-btn { flex: 1 1 100%; justify-content: center; }
}

/* Le righe del pannello "cosa succede": due piani invece di uno. A 390 px il
   nome dell'app, il conteggio, il repository e due bottoni su una riga sola
   andavano a capo in un punto diverso per ogni app, e l'elenco non si leggeva
   più come una colonna. */
.ric-repo--azione { flex-direction: column; align-items: stretch; gap: 0.35rem; }
.ric-repo-riga { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; }
.ric-repo--azione .ric-dett-riga { overflow-wrap: anywhere; }

/* ══════════════════════════════════════════════════════════════════════════
   Il guscio condiviso: navigazione primaria (app/nav.js) — 11/09/2026,
   ridisegnato struttura Apple il 29/09/2026.

   Le voci sono scritte UNA volta (app/nav.js) e lette da due elenchi diversi
   a seconda di dove sei:
   - da 1024px in su, la barra laterale del computer (.sidebar > .nav-tabs),
     verticale, icona + parola, come Finder/Impostazioni;
   - sotto i 1024px, la barra inferiore fissa da app installata (.nav-bottom),
     dove il pollice arriva e dove le PWA la cercano; l'header in alto resta
     solo un titolo compatto + il ☰ del secondario.
   Sotto i 1024px la barra laterale non esiste (display:none): non convivono
   mai due navigazioni primarie sullo stesso schermo.
   ══════════════════════════════════════════════════════════════════════════ */

/* Fino all'accesso app/nav.js non ha ancora scritto niente: un contenitore
   vuoto non deve lasciare ne' un vuoto nella barra ne' una striscia verde in
   fondo alla schermata di accesso. */
.nav-tabs:empty, .nav-bottom:empty { display: none; }

/* Fuori dalla barra laterale (cioè sotto i 1024px, dove .nav-tabs non si
   vede) questa regola non conta niente: la vera forma verticale sta nel
   blocco ".sidebar" qui sotto. */
.nav-tabs { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.nav-tab {
  display: flex; align-items: center; gap: 0.7rem;
  min-height: 44px; padding: 0.5rem 0.75rem;
  font-size: 0.875rem; font-weight: 600; white-space: nowrap;
  color: var(--on-forest); text-decoration: none;
  border-radius: var(--radius-sm);
  transition: background 0.15s, color 0.15s;
}
.nav-tab svg { width: 19px; height: 19px; flex-shrink: 0; }
.nav-tab-word { flex: 1 1 auto; min-width: 0; }
.nav-tab .nav-conto { margin-left: auto; }
.nav-tab:hover { background: var(--forest-mid); color: var(--cream); }
/* La voce attiva e' una pastiglia piena, non un colore appena diverso: crema
   su verde scuro, gli stessi due valori del bottone primario della barra. */
.nav-tab--qui, .nav-tab--qui:hover { background: var(--cream); color: var(--forest); }

.nav-bottom { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   La barra laterale (computer, ≥1024px) — sostituisce la vecchia barra in
   alto orizzontale: marchio, navigazione, selettore delle app del cliente,
   e in fondo Report/Riordina/Installa/Esci (prima nel ☰, che sul computer
   sparisce del tutto: vedi la regola su <header> qui sotto).
   ══════════════════════════════════════════════════════════════════════════ */
.sidebar { display: none; }
.app-shell { display: flex; align-items: stretch; min-height: 100vh; }
/* ⚠️ NIENTE `display: flex` qui. header/main/footer sono già impilati
   dall'ordine dei blocchi — un flex-column con `main{margin:auto}` dentro
   spegne lo stretch (i margini auto sull'asse trasversale battono
   align-items:stretch) e main si restringe al contenuto invece di riempire
   fino a max-width: misurato il 29/09/2026, main a 355px invece di 1180. */
.main-col { flex: 1 1 auto; min-width: 0; }

@media (min-width: 1024px) {
  /* Tutto quello che l'header portava vive ora nella barra laterale. */
  header { display: none; }

  .sidebar {
    display: flex; flex-direction: column;
    width: 240px; flex-shrink: 0;
    /* "Fissa": resta ferma mentre il contenuto scorre. sticky e non fixed,
       così non serve compensare a mano lo spazio che occuperebbe. */
    position: sticky; top: 0; align-self: flex-start;
    height: 100vh; overflow-y: auto;
    background: var(--forest); color: var(--on-forest);
    padding: 1.5rem 1rem;
    padding-top: max(1.5rem, var(--sat));
    padding-bottom: max(1.5rem, var(--sab));
    padding-left: max(1rem, var(--sal));
  }
  .sidebar-brand { display: flex; align-items: center; gap: 0.7rem; padding: 0 0.6rem; margin-bottom: 1.75rem; flex-shrink: 0; }
  .sidebar-brand .mark { height: 26px; width: auto; display: block; flex-shrink: 0; }
  .sidebar-brand .brand-word { display: block; font-family: var(--font-display); font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.01em; color: var(--cream); white-space: nowrap; }

  .sidebar-app { margin-top: 1.5rem; padding: 0 0.6rem; }
  /* Niente selettore (una sola app, o richieste.html che non ne ha uno): niente
     etichetta "App" sopra il vuoto. */
  .sidebar-app:has(#picker-sidebar:empty) { display: none; }
  .sidebar-label { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--on-forest); opacity: 0.8; margin-bottom: 0.4rem; }
  #picker-sidebar select#proj { width: 100%; max-width: none; }

  .sidebar-spacer { flex: 1 1 auto; }

  .sidebar-foot { display: flex; flex-direction: column; gap: 0.15rem; padding-top: 1rem; border-top: 1px solid oklch(45% 0.06 167.9); }
  /* Alcune voci sono <a> (Installa l'app in richieste.html): niente
     sottolineatura, stessa faccia dei fratelli <button>. */
  .side-link {
    display: flex; align-items: center; gap: 0.7rem; width: 100%;
    min-height: 40px; padding: 0.45rem 0.75rem;
    border: none; border-radius: 9px; background: transparent;
    color: var(--on-forest); font-size: 0.8125rem; font-weight: 600;
    text-align: left; cursor: pointer; text-decoration: none;
  }
  .side-link svg { width: 16px; height: 16px; flex-shrink: 0; }
  .side-link:hover { background: var(--forest-mid); color: var(--cream); }
  .side-link--danger:hover { color: oklch(85% 0.1 25); }
  /* Il menu "Report mensile" nella barra laterale: non un popover assoluto
     (verrebbe tagliato dall'overflow-y:auto della barra), un pannello che
     nasce nel flusso, sotto il bottone. */
  .sidebar-foot .pop-wrap { position: static; }
  .sidebar-foot .pop {
    position: static; width: 100%; margin-top: 0.25rem;
    box-shadow: none; border: 1px solid oklch(45% 0.06 167.9); background: var(--forest-mid);
  }
  .sidebar-foot .pop-item { color: var(--cream); }
  .sidebar-foot .pop-item:hover { background: oklch(38% 0.07 167.9); }
  .sidebar-credit { margin-top: 0.75rem; padding: 0 0.6rem; font-size: 0.72rem; color: var(--on-forest); opacity: 0.8; line-height: 1.5; }
  .sidebar-credit strong { color: var(--cream); }

  /* Colonna dei contenuti: crema, margini generosi, ~1400px di colonna
     (era 1180: sullo screenshot di Aziz a 1892px, con la barra laterale, il
     grafico stava nella colonna stretta della scacchiera larga solo ~360px —
     30/09/2026). Il padding cresce con clamp(): dolce, non a scatti a un
     breakpoint. */
  main, footer { max-width: 1400px; margin-left: auto; margin-right: auto; }
  main { padding: 2.5rem clamp(2.5rem, 5vw, 5rem) 3rem; }
  footer { padding: 0.75rem clamp(2.5rem, 5vw, 5rem) 2.5rem; }
}

@media (max-width: 1023px) {
  .nav-tabs { display: none; }
  .nav-bottom {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 35;
    background: var(--forest);
    border-top: 1px solid oklch(45% 0.06 167.9);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .nav-btm {
    position: relative; flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 0.15rem; min-height: 56px; padding: 0.45rem 0.25rem;
    font-size: 0.6875rem; font-weight: 600; line-height: 1.2; text-align: center;
    color: var(--on-forest); text-decoration: none;
  }
  .nav-btm svg { width: 22px; height: 22px; }
  /* Il pallino sta sull'angolo dell'icona (come i contatori di Mail/App
     Store), non dopo la parola: prima finiva sotto e allungava la barra
     (misurato 29/09/2026, vedi fase-a/admin-tel-_richieste_html.png). */
  .nav-ico { position: relative; display: inline-flex; }
  .nav-ico .nav-conto {
    position: absolute; top: -6px; right: -9px; margin-left: 0;
    min-width: 1.05rem; height: 1.05rem; font-size: 0.625rem;
    border: 2px solid var(--forest);
  }
  .nav-btm--qui { color: var(--cream); }
  /* Il colore da solo non basta a dire dove sei: sopra la voce attiva c'e'
     anche un trattino, e nel markup c'e' aria-current="page". */
  .nav-btm--qui::before {
    content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
    width: 34px; height: 3px; border-radius: 0 0 3px 3px; background: var(--cream);
  }
  /* Lo spazio che la barra occupa lo restituisce il corpo, altrimenti l'ultima
     riga del piede finisce sotto. Sta su body e NON su main: cosi' vale anche
     per il footer, che di main non e' figlio.
     ⚠️ 57 e non 56: 56 sono il bersaglio da dito, piu' il filo di bordo sopra.
     Misurato col piede a fondo pagina, i 56 lasciavano l'ultima riga sotto la
     barra per 1,1px. */
  body { padding-bottom: calc(57px + env(safe-area-inset-bottom, 0px)); }
  /* Le strisce di stato (#avvisi) si alzano di conseguenza: erano ancorate al
     fondo dello schermo, cioe' esattamente dove ora c'e' la barra. */
  #avvisi { bottom: calc(0.75rem + var(--sab) + 57px); }

  /* In mezzo alla barra in alto: il titolo della pagina, o il selettore delle
     app per il cliente che ne ha piu' di una. */
  .topbar-prop { display: block; }
  #picker-desktop { flex: 1 1 auto; min-width: 0; margin-left: 0; }
  #picker-desktop select#proj { max-width: none; font-size: 1rem; padding-right: 1.75rem; }
}

/* ══════════════════════════════════════════════════════════════════════════
   La scheda "Richieste", secondo giro (fasi B e C) — 11/09/2026.
   Aggiunto in coda: NIENTE qui sopra è stato toccato, tranne tre righe
   dichiarate nei loro commenti (i colori di stato delle carte, la pastiglia
   "In coda", il riquadro tinto tolto).

   Cosa cambia, in una riga: l'azione principale sale accanto al titolo, i
   contatori diventano i filtri del cliente, la carta intera è il bottone, e
   l'admin trova per primo quello che aspetta lui.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Testata: titolo, una frase, l'azione ─────────────────────────────── */
.ric-testata { display: flex; align-items: flex-start; gap: 1rem; justify-content: space-between; flex-wrap: wrap; }
.ric-testata-testo { min-width: 0; flex: 1 1 22rem; }
.ric-testata-azione { flex: 0 0 auto; }
.ric-btn-nuova { min-height: 44px; padding: 0.6rem 1.1rem; font-size: 0.8125rem; }

@media (max-width: 1023px) {
  /* Sul telefono l'azione va sotto il testo e prende tutta la riga: è la cosa
     per cui si apre questa pagina, e in un angolo non lo sembrava. */
  .ric-testata { flex-direction: column; gap: 0.85rem; }
  /* ⚠️ In colonna `flex-basis` misura l'ALTEZZA: i 22rem che sopra i 720px
     tengono il titolo su una riga, qui diventavano 350px di vuoto fra la
     frase e il bottone. Costato una cattura. */
  .ric-testata-testo { flex: 0 0 auto; width: 100%; }
  .ric-testata-azione { width: 100%; }
  .ric-btn-nuova { width: 100%; justify-content: center; }
}

/* ── Cliente: i contatori sono i filtri ──────────────────────────────── */
.ric-stati { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 1.1rem; }
.ric-pill {
  display: inline-flex; align-items: center; gap: 0.3rem;
  min-height: 44px; padding: 0.4rem 0.8rem;
  font-size: 0.78rem; font-weight: 600; white-space: nowrap;
  color: var(--ink-mid); background: white;
  border: 1px solid var(--cream-border); border-radius: 999px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.ric-pill b { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink); }
.ric-pill:hover { background: var(--sage-faint); }
/* Attiva = pastiglia piena, come la scheda attiva della barra: stessi due
   valori, crema su verde. Il colore da solo non basta, e infatti c'è anche
   aria-pressed. */
.ric-pill--qui, .ric-pill--qui:hover { background: var(--forest); border-color: var(--forest); color: var(--cream); }
.ric-pill--qui b { color: var(--cream); }

/* ── Admin: "Da fare" prima di tutto ─────────────────────────────────── */
.ric-dafare {
  border: 1px solid oklch(88% 0.06 75); background: oklch(97% 0.03 75);
  border-radius: 12px; padding: 0.8rem 0.95rem; margin-bottom: 1.1rem; max-width: 68ch;
}
/* Niente da fare non è un allarme spento: è una buona notizia, e si vede. */
.ric-dafare--libero { border-color: oklch(88% 0.045 167.9); background: oklch(97% 0.02 167.9); }
.ric-dafare-tit { font-size: 0.9375rem; font-weight: 700; color: var(--ink); line-height: 1.3; }
.ric-dafare--libero .ric-dafare-tit { color: var(--forest); }
.ric-dafare-sotto { font-size: 0.8125rem; color: var(--ink-mid); line-height: 1.5; margin-top: 0.2rem; }
.ric-dafare-sotto b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }

/* ── Admin: i filtri, e dove stanno sul telefono ─────────────────────── */
/* Sopra i 720px `display: contents` scioglie il contenitore: le tendine
   tornano nella stessa riga flex di prima, identiche, e la classe che le
   chiude sul telefono qui non ha effetto (è voluto). */
.ric-tendine { display: contents; }
.ric-filtri-btn { display: none; }

@media (max-width: 1023px) {
  .ric-filtri-btn { display: inline-flex; align-items: center; min-height: 44px; }
  /* Con un filtro acceso il bottone lo dice: dietro un pannello chiuso, un
     filtro dimenticato fa sembrare che l'elenco abbia perso delle righe. */
  .ric-filtri-btn--attivi { border-color: var(--forest-soft); color: var(--forest); font-weight: 700; }
  .ric-tendine { display: flex; flex-wrap: wrap; gap: 0.5rem; flex: 1 1 100%; }
  .ric-tendine--chiuse { display: none; }
  .ric-tendine .hq-sel { flex: 1 1 calc(50% - 0.25rem); min-width: 0; }
  .ric-tendine .mini-btn { flex: 1 1 100%; justify-content: center; }
  .ric-bar .hq-cerca { flex: 1 1 auto; }
}

/* ── Admin: i due gruppi ─────────────────────────────────────────────── */
.ric-gruppo { margin-bottom: 1.4rem; }
.ric-gruppo-h {
  display: flex; align-items: center; gap: 0.4rem;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--ink-faint); padding: 0.2rem 0 0.6rem;
}
.ric-gruppo-h b { font-variant-numeric: tabular-nums; color: var(--ink-mid); }
/* Il gruppo delle chiuse si apre e si chiude: il triangolino di serie cambia
   da un browser all'altro, questo no. */
.ric-chiuse > summary { cursor: pointer; min-height: 44px; list-style: none; }
.ric-chiuse > summary::-webkit-details-marker { display: none; }
.ric-chiuse > summary::before { content: "\25B8"; font-size: 0.8rem; }
.ric-chiuse[open] > summary::before { content: "\25BE"; }

/* ── La carta intera è il bottone ────────────────────────────────────── */
.ric-tutta {
  display: flex; align-items: flex-start; gap: 0.7rem; width: 100%;
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
}
.ric-corpo { flex: 1 1 auto; min-width: 0; }
.ric-id { min-width: 0; }
/* Dentro il bottone il riassunto è fatto di soli <span> (un <h3> o un <p> lì
   dentro non sarebbe HTML valido): il comportamento a blocco glielo rimette
   questa riga. */
.ric-tutta .ric-app, .ric-tutta .ric-meta, .ric-tutta .ric-msg, .ric-tutta .ric-risposta { display: block; }
.ric-quando { white-space: nowrap; }
/* Tre righe e non due: il messaggio è quello che fa riconoscere la richiesta,
   e a due righe le segnalazioni lunghe si somigliavano tutte. */
.ric-msg--corto { -webkit-line-clamp: 3; }
/* La riga qui sopra che rimette `display: block` ai <span> dentro il bottone
   ha specificità 0,2,0 e vinceva sul `display: -webkit-box` di .ric-msg--corto
   (0,1,0): senza il box il line-clamp è inerte e ogni carta chiusa mostrava il
   messaggio intero (visto in produzione l'11/09). Stessa specificità, dopo. */
.ric-tutta .ric-msg--corto { display: -webkit-box; }
/* Dentro l'app installata "Installa l'app" non ha senso. */
@media (display-mode: standalone) { .pop-item--installa { display: none; } }
.ric-chevron {
  width: 20px; height: 20px; flex: 0 0 auto; margin-top: 0.15rem;
  color: var(--ink-faint); transition: transform 0.15s;
}
.ric-tutta[aria-expanded="true"] .ric-chevron { transform: rotate(90deg); }
.ric-tutta:hover .ric-chevron { color: var(--ink-mid); }
.ric-tutta:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 10px; }

/* La richiesta appena inviata: contorno per qualche secondo, perché in cima
   all'elenco ce n'è già un'altra che le somiglia. */
.ric-card--fresca { box-shadow: 0 0 0 2px var(--forest-soft); }

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

/* ── Cliente senza richieste ─────────────────────────────────────────── */
.ric-vuoto {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 0.35rem; padding: 2rem 1rem;
  background: var(--sage-faint); border-radius: var(--radius-card);
  /* Su schermo largo un riquadro da 1400px con tre righe in mezzo sembra una
     pagina che non ha finito di caricare. */
  max-width: 40rem;
}
.ric-vuoto-dis { width: 120px; height: 84px; color: oklch(82% 0.02 165); margin-bottom: 0.4rem; }
.ric-vuoto-tit { font-size: 0.9375rem; font-weight: 700; color: var(--ink); }
.ric-vuoto-sotto { font-size: 0.8125rem; color: var(--ink-mid); line-height: 1.5; max-width: 34ch; margin-bottom: 0.6rem; }
.ric-vuoto .mini-btn { min-height: 44px; padding: 0.6rem 1.1rem; font-size: 0.8125rem; }

/* ── Dettaglio admin: etichetta a sinistra, valore a destra ──────────── */
.ric-griglia {
  display: grid; grid-template-columns: minmax(6.5rem, auto) minmax(0, 1fr);
  gap: 0.3rem 0.7rem; align-items: baseline;
  padding: 0.2rem 0; margin-bottom: 0.3rem;
}
.ric-et { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; color: var(--ink-faint); }
.ric-va { font-size: 0.78rem; color: var(--ink-mid); line-height: 1.5; overflow-wrap: anywhere; }
.ric-va .mini-btn { text-decoration: none; }

@media (max-width: 480px) {
  /* A 390px due colonne lasciavano al valore quattro caratteri per riga. */
  .ric-griglia { grid-template-columns: minmax(0, 1fr); gap: 0.1rem; }
  .ric-va { margin-bottom: 0.35rem; }
}

/* Il numero sulle voci della barra (solo admin, dal 29/09): richieste da fare
   su "Richieste", risposte da approvare su "Concierge". Lo scrive app/nav.js. */
.nav-conto {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.15rem; height: 1.15rem; padding: 0 0.3rem; margin-left: 0.4rem;
  border-radius: 999px; background: var(--status-warn); color: var(--ink);
  font-size: 0.68rem; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums;
  vertical-align: 0.1em;
}
.nav-conto[hidden] { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   Movimento, pressione e bersagli (07/10/2026) — standard delle app personali
   ══════════════════════════════════════════════════════════════════════════
   Le regole sono qui, in un posto solo, e non sparse nelle quaranta dei singoli
   bottoni: chi aggiunge un controllo lo aggiunge a UNA lista.

   ⚠️ SOLO `transform` e `opacity`. WebKit su iPhone tiene a 60 fps tutto quello
   che anima in JavaScript o che ridisegna il layout (height, width, top, left):
   transform e opacity, in CSS o in Web Animations, girano sul compositore e
   arrivano a 120 Hz. Per le cose che «si aprono» niente altezze animate: si
   fanno entrare in dissolvenza (vedi .m-entra più sotto).                      */

/* Il lampo grigio che iOS disegna sotto il dito su un link o un bottone: la
   pressione la dichiariamo noi (:active qui sotto). */
* { -webkit-tap-highlight-color: transparent; }

/* Controlli che si premono: scala 0,97 in 0,1 s, e colori in 0,2 s. */
.mini-btn, .tb-btn, .pbtn, .btn, .fchip, .ric-pill, .mode-btn, .login-btn, .menu-btn,
.icon-btn, .handle, .widget-link, .info, .m-toggle, .pop-item, .side-link, .nav-tab,
.nav-btm, .footer-install, .fix-more-btn, .ric-drop, .ric-chiuse > summary,
#net-bar button, #update-bar button, #offline-screen button {
  transition-property: background-color, color, border-color, opacity, transform, box-shadow;
  transition-duration: var(--t-breve), var(--t-breve), var(--t-breve), var(--t-breve), var(--t-tocco), var(--t-breve);
  transition-timing-function: ease, ease, ease, ease, var(--ease-ios), ease;
}
.mini-btn:active, .tb-btn:active, .pbtn:active, .btn:active, .fchip:active, .ric-pill:active,
.mode-btn:active, .login-btn:active, .menu-btn:active, .icon-btn:active, .handle:active,
.widget-link:active, .info:active, .m-toggle:active, .pop-item:active, .side-link:active,
.nav-tab:active, .nav-btm:active, .footer-install:active, .fix-more-btn:active, .ric-drop:active,
#net-bar button:active, #update-bar button:active, #offline-screen button:active {
  transform: scale(0.97);
}
/* Le righe lunghe (domande, app, richieste) non si rimpiccioliscono: come nelle
   liste di iOS, sotto il dito si accende lo sfondo. */
.frow:active, .qrow:active { background: var(--sage-faint); }
.ric-tutta { transition: opacity var(--t-tocco) var(--ease-ios); }
.ric-tutta:active { opacity: 0.6; }

/* ── Bersagli da dito ≥ 44 px ────────────────────────────────────────────────
   Dove il disegno non può crescere (un link di testo in testa a un riquadro, il
   «⋯» sulla riga del titolo) l'area di tocco sborda con un ::before trasparente:
   la misura è quella del dito, non quella dell'inchiostro. */
.widget-link { position: relative; }
.widget-link::before { content: ""; position: absolute; inset: -15px -4px; }
.fix-more-btn::before { content: ""; position: absolute; inset: -6px; }
.m-toggle { width: 44px; margin-left: -2px; margin-right: -0.6rem; }
#net-bar button { min-height: 44px; }
#update-bar button { min-height: 44px; }

/* ── «Riduci movimento» ─────────────────────────────────────────────────────
   Restano solo dissolvenze brevi: niente spostamenti, niente scala, niente
   cicli decorativi. I fogli (app/foglio.js) leggono la stessa preferenza e
   scelgono la variante a sola opacità. */
@media (prefers-reduced-motion: reduce) {
  :root { --t-foglio: .24s; --t-esce: .2s; }
  .mini-btn:active, .tb-btn:active, .pbtn:active, .btn:active, .fchip:active, .ric-pill:active,
  .mode-btn:active, .login-btn:active, .menu-btn:active, .icon-btn:active, .handle:active,
  .widget-link:active, .info:active, .m-toggle:active, .pop-item:active, .side-link:active,
  .nav-tab:active, .nav-btm:active, .footer-install:active, .fix-more-btn:active, .ric-drop:active,
  #net-bar button:active, #update-bar button:active, #offline-screen button:active { transform: none; }
  .m-toggle svg, .ric-chevron { transition: none; }
  .skel::after { animation: none; opacity: .7; }
  #splash .mark { animation-duration: .2s; animation-delay: 0s; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Il foglio dal basso (app/foglio.js) — 07/10/2026
   ══════════════════════════════════════════════════════════════════════════
   Una scheda sopra la pagina velata: maniglia, barra col titolo al centro
   («Annulla» a sinistra e l'azione a destra per i moduli, «Fine» a destra per
   quello che si legge soltanto), poi il contenuto che scorre da solo.
   Entrata e uscita sono Web Animations di sola transform/opacity: qui non
   c'è nessuna `transition` e nessuna `animation` sul foglio, e NON ci deve
   essere — due animazioni sulla stessa proprietà litigano. */
#fogli { position: relative; z-index: 130; }
.fg {
  position: fixed; left: 0; right: 0; top: 0; bottom: 0;
  display: flex; flex-direction: column; justify-content: flex-end; align-items: center;
  /* `clip` e non `hidden`: il foglio parte fuori schermo (translateY 100%) e un
     contenitore `hidden` sarebbe scorribile da programma — iOS lo fa scorrere
     per mostrare un campo con il fuoco, e il foglio resterebbe spostato. */
  overflow: clip; outline: none;
}
/* Tastiera aperta (app/tastiera.js): il guscio segue il visual viewport, e lo
   spazio dell'area sicura in basso non serve più, la tastiera la copre. */
html[data-tastiera] { --sab: 0px; }
html[data-tastiera] .fg { bottom: auto; top: var(--vvt, 0px); height: var(--vvh, 100%); }
.fg-velo { position: absolute; inset: 0; background: oklch(23.3% 0.023 165.9 / 0.38); touch-action: none; }
.fg-foglio {
  position: relative; width: 100%; max-width: 640px;
  /* Resta una fascia di pagina velata sopra: è un foglio, non una pagina. */
  max-height: calc(100% - max(44px, var(--sat) + 12px));
  display: flex; flex-direction: column;
  background: var(--cream); color: var(--ink);
  border-radius: 20px 20px 0 0;
  box-shadow: 0 -12px 44px oklch(32.4% 0.057 167.9 / 0.28);
  padding-left: var(--sal); padding-right: var(--sar);
}
html[data-tastiera] .fg-foglio { max-height: calc(100% - 8px); }
.fg--ampio .fg-foglio { max-width: 880px; }
.fg-testa { flex: none; touch-action: none; -webkit-user-select: none; user-select: none; }
.fg-maniglia {
  display: block; position: relative; width: 120px; height: 22px; margin: 0 auto; padding: 0;
  border: 0; background: none; cursor: pointer;
}
.fg-maniglia::before { content: ""; position: absolute; inset: -11px 0; }
.fg-maniglia span { display: block; width: 36px; height: 5px; border-radius: 3px; background: oklch(80% 0.012 160); margin: 9px auto 0; }
.fg-barra { display: grid; grid-template-columns: 1fr minmax(0, auto) 1fr; align-items: center; gap: 0.5rem; padding: 0 0.5rem; min-height: 52px; }
.fg-lato { display: flex; min-width: 0; }
.fg-lato--sx { justify-self: start; }
.fg-lato--dx { justify-self: end; }
.fg-tit {
  margin: 0; font-family: var(--font-sans); font-size: 1.0625rem; font-weight: 600; letter-spacing: -0.01em;
  color: var(--ink); text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.fg-btn {
  min-height: 44px; min-width: 44px; padding: 0 0.75rem; border: 0; background: none; border-radius: 10px;
  font: inherit; font-size: 1.0625rem; color: var(--forest); cursor: pointer; white-space: nowrap;
}
.fg-btn--forte { font-weight: 600; }
.fg-btn:disabled { opacity: 0.4; cursor: default; }
.fg-corpo {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  padding: 0.25rem 1rem calc(1.25rem + var(--sab));
}
/* Le spiegazioni ⓘ (erano un popover): testo che si legge. */
.fg-testo { font-size: 0.9375rem; line-height: 1.6; color: var(--ink-mid); }
.fg-testo p + p { margin-top: 0.65rem; }
.fg-testo b, .fg-testo strong { color: var(--ink); }

@media (min-width: 1024px) {
  /* Sul computer il foglio sale al centro, non attaccato al bordo di uno schermo da 1900 px. */
  .fg { justify-content: center; }
  .fg-foglio { border-radius: 20px; max-height: min(86%, 760px); margin-bottom: 0; }
  .fg-corpo { padding-bottom: 1.5rem; }
}

/* ── Scheletri (07/10/2026) ──────────────────────────────────────────────────
   Finché i dati non sono arrivati la pagina disegna la SUA FORMA — barre grigie
   che pulsano, alte come i numeri veri — e non valori a zero: uno zero finto è
   un'informazione sbagliata, e il suo cambio all'arrivo dei dati sposta tutto.
   Quando i dati arrivano il contenuto entra in dissolvenza (--t-breve). */
.skel-barra { display: block; border-radius: 6px; background: var(--sage-faint); animation: pulse 1.4s ease-in-out infinite; }
.ric-pill--skel { pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .skel-barra { animation: none; opacity: 0.7; } }
/* La barra al posto di un numero: alta come il numero (font-size e line-height
   sono quelli del .num-val che la contiene, per questo l'altezza e' in em). */
.skel-num { width: 2.4ch; height: 0.82em; margin: 0.09em 0; }
.skel-riga { height: 0.8rem; margin-top: 0.55rem; }
.num-val.sm .skel-riga { margin-top: 0.2rem; }
.blk-skel { padding: 0.2rem 0 0.4rem; }
.blk-skel .skel-riga { margin-top: 0.85rem; }
/* I dati appena arrivati entrano in dissolvenza (--t-breve), solo opacita':
   solo quello che prima era uno scheletro, non titoli ed etichette — che erano
   gia' veri e non devono lampeggiare. */
@keyframes dati-entrano { from { opacity: 0; } to { opacity: 1; } }
.dati-entrano .blk-body > *, .dati-entrano .num-val, .dati-entrano .num-sub, .dati-entrano .card-peek,
.dati-entrano .card-count, .dati-entrano .page-sub, .dati-entrano .kpi-grid .num-val {
  animation: dati-entrano var(--t-breve) ease-out;
}

/* ── Una scheda per volta (07/10/2026) ───────────────────────────────────────
   Barra laterale e menu sono uno solo per le due schede: quello che vale per la
   sola plancia (riordinare i riquadri, il report mensile, la scelta dell'app)
   non si mostra in Richieste. `data-scheda` lo scrive app/nav.js sull'<html>. */
html[data-scheda="richieste"] #btn-reorder,
html[data-scheda="richieste"] #btn-reset,
html[data-scheda="richieste"] #report-wrap,
html[data-scheda="richieste"] #sidebar-app-wrap,
html[data-scheda="richieste"] #menu-riordina,
html[data-scheda="richieste"] #report-label-m,
html[data-scheda="richieste"] #report-menu-m,
html[data-scheda="richieste"] #report-sep-m,
html[data-scheda="richieste"] #picker-desktop { display: none; }

/* Altezza degli scheletri dei riquadri: quella che il riquadro ha con i dati
   veri, misurata il 07/10/2026 sul banco di prova (telefono 402 px e computer
   1440 px). Un riquadro vuoto più basso del vero spingerebbe verso il basso
   tutto quello che sta sotto, quando i dati arrivano. Sono ordini di grandezza,
   non promesse: con più o meno righe il vero può differire, ma lo scarto non e'
   piu' di una riga o due invece di centinaia di pixel. */
.blk-skel[data-sk="feed"] { min-height: 35.9rem; }
.blk-skel[data-sk="c-feed"] { min-height: 15rem; }  /* il feed segue il periodo: 2-3 righe a 7 giorni */
.blk-skel[data-sk="c-curva"] { min-height: 19.7rem; }
.blk-skel[data-sk="c-sezioni"], .blk-skel[data-sk="c-azioni"], .blk-skel[data-sk="c-quando"] { min-height: 14rem; }
.blk-skel[data-sk="c-ufficiali"] { min-height: 16rem; }
.blk-skel[data-sk="proprieta"] { min-height: 75.4rem; }  /* sei righe impilate con la linea (07/10/2026) */
.blk-skel[data-sk="azioni"] { min-height: 16.4rem; }
.blk-skel[data-sk="mail"] { min-height: 61.6rem; }
.blk-skel[data-sk="salute"] { min-height: 26.2rem; }
.blk-skel[data-sk="ufficiali"] { min-height: 40rem; }
@media (min-width: 1024px) {
  .blk-skel[data-sk="feed"] { min-height: 34.2rem; }
}

/* ── Risposte ufficiali: approvare a due tocchi (07/10/2026) ─────────────────
   Le proposte in attesa stanno in cima, hanno Approva e Rifiuta sulla riga (non
   dietro il «⋯») e un'intestazione loro. Chi arriva dal riquadro «Da approvare»
   le trova gia' in vista. */
.fix-group-h--attesa { color: oklch(45% 0.1 62); padding-top: 0.2rem; }
.fix-row--attesa { background: oklch(97.5% 0.025 75); margin: 0 -0.55rem; padding-left: 0.55rem; padding-right: 2.8rem; border-radius: 12px; border-bottom-color: transparent; }
.fix-row--attesa .fix-more-btn { right: 0.4rem; }
.fix-decidi { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.3rem; }
.fix-decidi .mini-btn { flex: 1 1 8rem; justify-content: center; min-height: 44px; }
.fix-decidi .fix-confirm { flex: 1 1 100%; }
/* Le due scelte occupano tutta la riga: il padding a destra della riga serve al
   «⋯» che sta sopra, non a loro. */
.fix-row--attesa .fix-decidi { margin-right: -2.25rem; }
/* Chi arriva con «scorrimento verso» un riquadro non lo trova sotto la barra in alto. */
.blk { scroll-margin-top: 5rem; }
@media (min-width: 1024px) { .blk { scroll-margin-top: 1.5rem; } }

/* ── Il modulo dentro un foglio ──────────────────────────────────────────────
   Il foglio e' gia' la scheda: il modulo non ha bisogno di un secondo riquadro
   col bordo e lo sfondo suo. I campi stanno sul fondo del foglio. */
.fg-corpo .fix-form { background: transparent; border: 0; padding: 0; gap: 0.9rem; }
.fg-corpo .ric-nuova-sub { margin-top: 0; }
.fg--modulo .fg-corpo { padding-top: 0.5rem; }

/* ── Selettore «Oggi / 7 giorni» (07/10/2026) ────────────────────────────────
   Un controllo segmentato stile iOS, in piccolo, nella testa del riquadro:
   stesso linguaggio di «24 ore · 72 ore · 7 giorni · 30 giorni» del cliente. La
   parte visibile e' alta 30 px; l'area di tocco arriva a 44 col ::before. */
.seg { display: inline-flex; padding: 2px; border-radius: 10px; background: oklch(89% 0.014 160); border: 1px solid var(--cream-border); }
.seg-b {
  position: relative; font: inherit; font-size: 0.75rem; font-weight: 600; min-height: 28px; padding: 0 0.7rem;
  border: 0; border-radius: 8px; background: transparent; color: var(--ink-mid); cursor: pointer; white-space: nowrap;
  transition-property: background-color, color, box-shadow, transform;
  transition-duration: var(--t-breve), var(--t-breve), var(--t-breve), var(--t-tocco);
  transition-timing-function: ease, ease, ease, var(--ease-ios);
}
.seg-b::before { content: ""; position: absolute; inset: -8px -2px; }
.seg-b:active { transform: scale(0.97); }
.seg-b[aria-pressed="true"] { background: white; color: var(--forest); box-shadow: 0 1px 3px oklch(32.4% 0.057 167.9 / 0.22); }
.blk[data-k="oggi"] .head-tools { align-self: center; }
@media (prefers-reduced-motion: reduce) { .seg-b:active { transform: none; } }

/* ── Il link «Mostra tutto» in fondo alla vista cliente ──────────────────── */
.cli-fondo { display: flex; justify-content: center; padding: 1.1rem 0 0.2rem; }
.cli-tutto { font-size: 0.875rem; }
.cli-tutto::before { inset: -14px -24px; }

/* ── Il foglio di un hotel (admin) ───────────────────────────────────────── */
.fg--hotel .fg-corpo { padding-top: 0.5rem; }
.hotel-periodo { display: flex; width: 100%; margin-bottom: 1rem; }
.hotel-periodo .pbtn { flex: 1 1 0; padding-left: 0.3rem; padding-right: 0.3rem; font-size: 0.78rem; min-height: 44px; }
.hotel-stack { gap: 1rem; }
.hotel-extra { margin-top: 1.25rem; }
.hotel-extra-tit { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 0.4rem; }
.hotel-extra .fix-actions { margin-top: 0.2rem; }
.fg--hotel .kpi-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
@media (max-width: 767px) { .fg--hotel .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.7rem; } }

/* Il riquadro che dice a quale struttura appartiene la correzione: chip, frase e ⓘ
   restano sulla stessa riga (nel foglio la ⓘ andava a capo da sola). */
.fix-scope { flex-wrap: nowrap; align-items: flex-start; }
.fix-scope > span:nth-child(2) { flex: 1 1 auto; min-width: 0; }
.fix-scope .info { margin-left: auto; }

/* ── Entrate (07/10/2026): solo transform e opacity ──────────────────────────
   Il menu ☰ e quelli del report entrano da app/movimento.js (Web Animations). Il
   «⋯» sulle risposte ufficiali e' un <details> nativo e non si anima da JS: il
   suo contenuto entra con una piccola salita. Il contenuto dei riquadri a
   fisarmonica, quando si apre, entra in dissolvenza di --t-breve: la freccia
   resta com'e'. */
.pop { transform-origin: top right; }
@keyframes entra-su { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.fix-more[open] > :not(summary) { animation: entra-su var(--t-breve) var(--ease-ios); }
.m-entra > * { animation: dati-entrano var(--t-breve) ease-out; }
@media (prefers-reduced-motion: reduce) {
  @keyframes avviso-su { from { opacity: 0; } to { opacity: 1; } }
  @keyframes entra-su { from { opacity: 0; } to { opacity: 1; } }
}

/* ── Contatti del ristorante (12/10/2026): «Com'e' andata?» e «Iscritti» ───── */
.op-lista { list-style: none; margin: 0; padding: 0; }
.op-riga { padding: 0.7rem 0; border-bottom: 1px solid var(--cream-border); }
.op-riga:last-child { border-bottom: none; }
.op-top { display: flex; align-items: baseline; gap: 0.5rem; }
.op-stelle { color: var(--forest); font-size: 0.95rem; letter-spacing: 0.08em; line-height: 1; }
.op-stelle i { font-style: normal; color: var(--cream-border); }
.op-voto { font-size: 0.75rem; font-weight: 600; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.op-testo { margin-top: 0.3rem; font-size: 0.9375rem; line-height: 1.45; color: var(--ink); overflow-wrap: anywhere; white-space: pre-wrap; }
.op-senza { color: var(--ink-faint); font-size: 0.8125rem; white-space: normal; }
.op-contatto { margin-top: 0.3rem; font-size: 0.8125rem; color: var(--ink-mid); overflow-wrap: anywhere; }
.op-meta { margin-top: 0.3rem; font-size: 0.75rem; color: var(--ink-faint); }
.op-tutte { display: block; margin: 0.6rem auto 0; font-size: 0.875rem; }
.isc-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.9rem; }
@media (max-width: 480px) { .isc-grid { grid-template-columns: minmax(0, 1fr); gap: 0.7rem; } }
.isc-grid .num-val { font-size: 1.75rem; }
.isc-nota { margin-top: 0.8rem; }
.isc-csv { display: inline-flex; align-items: center; margin-top: 0.9rem; text-decoration: none; }

/* ══════════════════════════════════════════════════════════════════════════
   Il Riepilogo dell'admin (22/10/2026) — app/plancia/19-riepilogo.js
   Sezioni una sotto l'altra, senza accordion. Gruppi di righe bianchi come le
   liste di iOS; le card dei numeri sono bottoni (aprono un foglio).
   ══════════════════════════════════════════════════════════════════════════ */
.rip { display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; }
.rip-cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; min-width: 0; }
.rip-col { display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; }
@media (min-width: 768px) {
  .rip-cols { grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); gap: 1.5rem; }
}
.rip-tit {
  font-family: var(--font-sans); font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-faint); margin: 0.9rem 0.2rem 0;
}
.rip > .rip-tit:first-child { margin-top: 0; }
.rip-tit-riga { display: flex; align-items: baseline; justify-content: space-between; margin-top: 0.9rem; }
.rip-col:last-child > .rip-tit-riga:first-child { margin-top: 0.9rem; }
.rip-tit-riga .rip-tit { margin-top: 0; }
@media (min-width: 768px) { .rip-col:last-child > .rip-tit-riga:first-child { margin-top: 0; } }
.rip-tit-riga .widget-link { display: inline-flex; align-items: center; position: relative; }
.rip-tit-riga .widget-link::before { content: ""; position: absolute; inset: -14px -10px; }
.rip-freccia { width: 16px; height: 16px; flex-shrink: 0; color: var(--ink-faint); }
.rip-freccia--pic { width: 14px; height: 14px; color: currentColor; }

.rip-gruppo { background: white; border-radius: var(--radius-card); box-shadow: var(--widget-shadow); overflow: hidden; }
.rip-gruppo--sk { padding: 0.9rem 1rem; display: flex; flex-direction: column; gap: 0.7rem; }
.rip-riga, .rip-app, .rip-dom {
  display: flex; align-items: center; gap: 0.7rem; width: 100%; text-align: left; font: inherit; color: inherit;
  background: transparent; border: 0; cursor: pointer; padding: 0.8rem 1rem; min-height: 48px; position: relative;
}
.rip-riga + .rip-riga::before, .rip-app + .rip-app::before, .rip-dom + .rip-dom::before {
  content: ""; position: absolute; top: 0; left: 1rem; right: 0; height: 1px; background: var(--cream-border);
}
.rip-riga:hover, .rip-app:hover, .rip-dom:hover { background: var(--sage-faint); }
.rip-riga:active, .rip-app:active, .rip-dom:active, .rip-card:active { background: var(--sage-faint); }
.rip-testo { flex: 1 1 auto; min-width: 0; font-size: 0.9375rem; font-weight: 500; color: var(--ink); line-height: 1.35; }
.rip-testo b { display: block; font-weight: 600; }
.rip-testo small { display: block; font-size: 0.75rem; color: var(--ink-faint); margin-top: 0.1rem; font-weight: 400; }
.rip-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
.rip-dot--alta { background: var(--status-down); }
.rip-dot--media { background: var(--status-warn); }
.rip-ok { display: flex; align-items: center; gap: 0.55rem; padding: 0.9rem 1rem; font-size: 0.9375rem; font-weight: 600; color: var(--accent-up); }
.rip-ok svg { width: 20px; height: 20px; }
.rip-vuoto { padding: 0.9rem 1rem; font-size: 0.875rem; color: var(--ink-faint); }

.rip-periodo { display: flex; width: 100%; margin: 1rem 0 0.1rem; }
.rip-periodo .pbtn { flex: 1 1 0; min-height: 44px; font-size: 0.8125rem; }
.rip-kpi { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem; margin-top: 0.4rem; }
.rip-card {
  position: relative; display: flex; flex-direction: column; align-items: flex-start; text-align: left;
  background: white; border: 0; border-radius: var(--radius-card); box-shadow: var(--widget-shadow);
  padding: 0.8rem 0.85rem 0.7rem; font: inherit; color: inherit; cursor: pointer; min-width: 0; overflow: hidden;
}
.rip-card--sk { cursor: default; }
.rip-card-et { font-size: 0.72rem; font-weight: 600; color: var(--ink-faint); }
.rip-card-val { font-family: var(--font-display); font-size: 1.625rem; font-weight: 700; line-height: 1; letter-spacing: -0.035em; color: var(--ink); margin-top: 0.35rem; font-variant-numeric: tabular-nums; }
.rip-card-sub { font-size: 0.72rem; color: var(--ink-faint); margin-top: 0.3rem; line-height: 1.35; min-height: 2.7em; }
.rip-card .spark { width: 100%; height: 26px; margin-top: 0.35rem; }

.spark { display: block; color: var(--forest); overflow: visible; }
.spark-linea { fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
.spark-area { fill: currentColor; opacity: 0.1; stroke: none; }
.spark-punto { display: none; }

.rip-app { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 0.8fr) auto 16px; gap: 0.6rem; }
.rip-app-nome { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.rip-app-et { font-size: 0.875rem; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rip-app .spark { width: 100%; height: 26px; }
.rip-app-num { text-align: right; min-width: 3.4rem; }
.rip-app-num b { display: block; font-family: var(--font-display); font-size: 1.1rem; font-weight: 700; color: var(--ink); line-height: 1; font-variant-numeric: tabular-nums; }
.rip-app-num small { display: block; font-size: 0.66rem; color: var(--ink-faint); margin-top: 0.2rem; white-space: nowrap; }
.rip-app-num small.rip-ferma { color: var(--status-down); font-weight: 600; }
.rip-dom { flex-direction: column; align-items: flex-start; gap: 0.15rem; }
.rip-dom-q { font-size: 0.9375rem; font-weight: 600; color: var(--ink); line-height: 1.3; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.rip-dom-meta { font-size: 0.75rem; color: var(--ink-faint); }
.rip-dom-err { color: var(--status-down); font-weight: 600; }
.fg-azioni { display: flex; justify-content: flex-end; margin: -0.2rem 0 0.7rem; }
.fg-azioni:empty { display: none; }
.fg--adm .fg-corpo { padding-top: 0.5rem; }
@media (max-width: 767px) { .adm-rip { padding-bottom: 0.5rem; } }

/* ══════════════════════════════════════════════════════════════════════════
   La scheda Domande dell'admin (22/10/2026) — app/plancia/20-domande.js
   ══════════════════════════════════════════════════════════════════════════ */
.dom-cerca { display: flex; margin: 0 0 0.7rem; }
.dom-cerca .hq-cerca { flex: 1 1 auto; }
.dom-modo { margin: 0 0 0.8rem; }
.dom-appr { margin-bottom: 0.7rem; }
.dom-chip { margin-bottom: 0.45rem; }
#dom-corpo .rip-tit:first-child { margin-top: 0.4rem; }
.dom-riga {
  display: flex; flex-direction: column; align-items: stretch; gap: 0.2rem; width: 100%; text-align: left;
  font: inherit; color: inherit; background: transparent; border: 0; cursor: pointer;
  padding: 0.75rem 1rem; position: relative;
}
.dom-riga + .dom-riga::before { content: ""; position: absolute; top: 0; left: 1rem; right: 0; height: 1px; background: var(--cream-border); }
.dom-riga:hover, .dom-riga:active { background: var(--sage-faint); }
.dom-riga-testa { display: flex; align-items: baseline; justify-content: space-between; gap: 0.7rem; }
.dom-q { font-size: 0.9375rem; font-weight: 600; color: var(--ink); line-height: 1.3; min-width: 0; }
.dom-a { font-size: 0.8125rem; color: var(--ink-mid); line-height: 1.4; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dom-meta { font-size: 0.72rem; color: var(--ink-faint); }
.dom-uff { padding: 1rem; }
.dom-nuova { margin: 0 0.2rem 0.6rem; }
.dom-fg-q { font-size: 1.1875rem; font-weight: 700; line-height: 1.3; color: var(--ink); letter-spacing: -0.01em; }
.dom-fg-meta { font-size: 0.8125rem; color: var(--ink-faint); margin: 0.45rem 0 1rem; display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.fg--domanda .fix-actions { margin-top: 1rem; }
@media (min-width: 768px) { #view-dom .dom-cerca, #view-dom .dom-modo { max-width: 640px; } #dom-corpo { max-width: 760px; } }

/* ══════════════════════════════════════════════════════════════════════════
   «Il quadro» (T1, dietro ?nuova=1) — app/plancia/22-quadro-dati.js, 23-quadro.js, 24-app.js
   ──────────────────────────────────────────────────────────────────────────
   Tutte le regole sono `.q-*` e quelle che toccano il resto sono sotto
   `body.q-on`: con la bandierina spenta niente di questo blocco ha effetto.
   Palette: solo forest + crema; per i cali un terracotta scuro (#9a3b26, sul
   bianco ~7:1) e SEMPRE con freccia e segno, non solo il colore. Il verde dei
   rialzi e' --forest-mid (non --forest-soft: sul bianco sta sotto 4.5:1).
   ⚠️ La barra del periodo e' sticky SOTTO l'header (sul telefono e' alto 44px +
   i due riempimenti): se l'header cambia altezza, si rivede --q-top.
   ══════════════════════════════════════════════════════════════════════════ */
body.q-on #view-admin > :not(.q-radice),
body.q-on #view-client > :not(.q-radice) { display: none; }

.q-radice {
  --q-su: var(--forest-mid);
  --q-giu: #9a3b26;
  --q-paper: #faf7f1;
  --q-sand: #ece5d8;
  --q-linea: var(--cream-border);
  --q-top: calc(44px + 0.625rem + max(0.625rem, var(--sat)));
  font-variant-numeric: tabular-nums;
  min-width: 0;
}
.q-radice:not(.q-r-pc) { max-width: 720px; }
@media (min-width: 1024px) { .q-radice { --q-top: 0px; } }
.q-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.q-muted { color: var(--ink-faint); }

/* Titolo e riga del periodo */
.q-testa-r { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.q-tit { font-family: var(--font-display); font-weight: 600; font-size: 2.1rem; line-height: 1.12; letter-spacing: -0.022em; color: var(--ink); }
.q-tit:focus { outline: none; }
.q-testa-r--app .q-tit { font-size: 1.85rem; }
.q-data { font-size: 0.78rem; color: var(--ink-faint); white-space: nowrap; }
.q-sub { margin-top: 0.25rem; font-size: 0.78rem; line-height: 1.4; color: var(--ink-faint); }

/* La barra del periodo: resta in vista */
.q-barra {
  position: sticky; top: var(--q-top); z-index: 20;
  background: var(--cream); padding: 0.5rem 1rem 0.7rem; margin: 0.6rem -1rem 0;
  border-bottom: 1px solid var(--q-linea);
}
@media (min-width: 1024px) { .q-barra { margin: 0.6rem calc(clamp(2.5rem, 5vw, 5rem) * -1) 0; padding-left: clamp(2.5rem, 5vw, 5rem); padding-right: clamp(2.5rem, 5vw, 5rem); display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1.25rem; } .q-seg--periodo { width: 360px; } }
.q-seg { display: flex; gap: 2px; padding: 3px; border-radius: 999px; background: var(--q-sand); }
.q-seg--col { margin-top: 0.5rem; background: white; border: 1px solid var(--q-linea); }
.q-seg-b {
  flex: 1 1 0; min-height: 44px; padding: 0 0.3rem; border: 0; border-radius: 999px; cursor: pointer;
  background: transparent; color: var(--ink-mid); font: 500 0.875rem/1 var(--font-sans);
}
.q-seg--col .q-seg-b { min-height: 40px; font-size: 0.78rem; }
.q-seg-b[aria-pressed="true"] { background: white; color: var(--ink); font-weight: 600; box-shadow: 0 1px 3px oklch(32.4% 0.057 167.9 / 0.22); }
.q-seg--col .q-seg-b[aria-pressed="true"] { background: var(--forest); color: white; box-shadow: none; }
.q-seg-b:focus-visible, .q-riga:focus-visible, .q-trow:focus-visible, .q-indietro:focus-visible, .q-prec:focus-visible, .q-succ:focus-visible { outline: 2px solid var(--forest); outline-offset: 2px; }
@media (min-width: 1024px) { .q-solo-tel { display: none; } }
.q-date { display: flex; gap: 0.6rem; margin-top: 0.5rem; }
.q-date-c { flex: 1 1 0; display: flex; flex-direction: column; gap: 0.15rem; font-size: 0.72rem; color: var(--ink-faint); }
.q-date-c input { min-height: 44px; padding: 0 0.6rem; border: 1.5px solid var(--q-linea); border-radius: var(--radius-sm); background: white; color: var(--ink); font: inherit; font-size: 0.9rem; }
@media (min-width: 1024px) { .q-date { margin-top: 0; } .q-date-c { flex-direction: row; align-items: center; gap: 0.5rem; } }
.q-avviso { margin: 0.7rem 0 0; padding: 0.6rem 0.8rem; border-radius: var(--radius-sm); background: white; border: 1px solid var(--q-giu); color: var(--q-giu); font-size: 0.8125rem; }
.q-corpo { padding-top: 0.2rem; }
.q-corpo-i { transition: opacity var(--t-breve) ease; }
.q-vecchio { opacity: 0.45; }
.q-fine { margin: 1rem 0 0; text-align: center; font-size: 0.72rem; color: var(--ink-faint); }

/* Pezzi comuni */
.q-card { margin-top: 1rem; padding: 1rem; background: white; border: 1px solid var(--q-linea); border-radius: 1rem; min-width: 0; }
.q-card--nota { background: var(--q-paper); }
.q-card-testa { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.8rem; }
.q-h2 { font-family: var(--font-display); font-weight: 600; font-size: 1.25rem; line-height: 1.2; letter-spacing: -0.022em; color: var(--ink); }
.q-h2--m { font-size: 1.05rem; }
.q-h2--p { font-size: 1rem; }
.q-sub2 { margin-top: 0.15rem; font-size: 0.78rem; line-height: 1.35; color: var(--ink-faint); }
.q-card-testa .q-sub2 { margin-top: 0; }
.q-vuoto { margin-top: 0.7rem; font-size: 0.875rem; color: var(--ink-faint); }
.q-nota { margin-top: 0.6rem; font-size: 0.75rem; line-height: 1.45; color: var(--ink-faint); }
.q-nota--sotto { margin: 0.6rem 0.2rem 0; }
.q-nota--tratt { padding-top: 0.6rem; border-top: 1px dashed var(--cream-border); }
.q-nota--cop { margin: 0.8rem 0 0; padding: 0.55rem 0.8rem; border-radius: var(--radius-sm); background: var(--q-paper); border: 1px solid var(--q-linea); }
.q-var { font-size: 0.78rem; font-weight: 700; white-space: nowrap; }
.q-var--su { color: var(--q-su); }
.q-var--giu { color: var(--q-giu); }
.q-var--pari, .q-var--nd { color: var(--ink-faint); font-weight: 600; }
.q-leg { display: flex; flex-wrap: wrap; gap: 0.3rem 0.9rem; margin: 0.7rem 0 0.2rem; font-size: 0.75rem; line-height: 1.3; color: var(--ink-mid); }
.q-leg-v { display: inline-flex; align-items: center; gap: 0.35rem; }
.q-leg-v b { color: var(--ink); }
.q-sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
.q-sw--q { width: 14px; height: 14px; border-radius: 4px; border: 1px solid var(--q-linea); }

/* I colori: una famiglia sola (forest e sabbia). Il testo sopra e' scelto per il contrasto. */
.q-c0 { background: #0d3d2e; color: #fff; }
.q-c1 { background: #9dbaab; color: var(--ink); }
.q-c2 { background: #e0d5bf; color: var(--ink); }
.q-c3 { background: #5a8f78; color: var(--ink); }
.q-c4 { background: #c4b497; color: var(--ink); }
.q-c5 { background: #7a6a4f; color: #fff; }
.q-s-direct { background: #0d3d2e; }
.q-s-qr { background: #5a8f78; }
.q-s-nfc { background: #c4b497; }
.q-s-social { background: #7a6a4f; }

/* Barra impilata */
.q-imp { display: flex; height: 18px; border-radius: 6px; overflow: hidden; background: var(--q-paper); }
.q-imp--alta { height: 16px; border-radius: 5px; }
.q-imp-s { display: flex; align-items: center; justify-content: center; min-width: 3px; font-size: 0.72rem; font-weight: 700; border-right: 2px solid white; }
.q-imp-s:last-child { border-right: 0; }

/* ── TELEFONO ── */
.q-intest { display: flex; justify-content: space-between; align-items: flex-end; padding: 0.9rem 0.2rem 0.4rem; font-size: 0.75rem; color: var(--ink-faint); }
.q-gruppo { background: white; border: 1px solid var(--q-linea); border-radius: 1rem; overflow: hidden; }
.q-riga { display: flex; align-items: center; gap: 0.4rem; min-height: 54px; padding: 0.35rem 0.7rem 0.35rem 0.85rem; text-decoration: none; color: inherit; }
.q-riga + .q-riga, .q-riga + .q-tot { border-top: 1px solid var(--q-linea); }
.q-riga:active { background: var(--sage-faint); }
.q-riga-sx { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.q-nome { font-size: 0.84rem; font-weight: 600; line-height: 1.25; color: var(--ink); overflow-wrap: anywhere; }
.q-riga-sotto { display: flex; align-items: center; gap: 0.5rem; }
.q-aper { font-size: 0.78rem; color: var(--ink-mid); white-space: nowrap; }
.q-aper b { color: var(--ink); font-weight: 700; }
.q-spark { display: block; flex-shrink: 0; color: var(--forest); overflow: visible; }
.q-sp-linea { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linejoin: round; stroke-linecap: round; }
.q-sp-punto { fill: currentColor; }
.q-sp-base { stroke: var(--q-linea); stroke-width: 1; }
.q-sp-oggi-linea { stroke: currentColor; stroke-width: 1.5; stroke-dasharray: 2 2; stroke-linecap: round; }
.q-sp-oggi { fill: white; stroke: currentColor; stroke-width: 1.4; }
.q-terza { flex: 0 0 140px; display: flex; align-items: center; min-width: 0; }
@media (max-width: 359px) { .q-terza { flex-basis: 118px; } }
.q-vai { width: 14px; height: 14px; flex-shrink: 0; color: var(--ink-faint); }
.q-pill { display: flex; align-items: center; justify-content: space-between; gap: 0.4rem; width: 100%; min-height: 32px; padding: 0 0.7rem; border-radius: 999px; background: var(--sage-faint); color: var(--ink); font-size: 0.75rem; }
.q-pill-t { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.q-pill b { font-size: 0.875rem; font-weight: 700; }
.q-pill--nd { justify-content: center; background: transparent; border: 1px dashed var(--cream-border); color: var(--ink-faint); font-weight: 500; }
.q-prov { display: flex; flex-direction: column; gap: 4px; width: 100%; min-width: 0; }
.q-prov-t { font-size: 0.72rem; color: var(--ink-mid); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.q-prov .q-imp { height: 10px; border-radius: 3px; }
.q-and { display: flex; flex-direction: column; gap: 5px; width: 100%; }
.q-and-riga { display: flex; align-items: center; gap: 5px; font-size: 0.7rem; color: var(--ink-mid); }
.q-and-et { flex: 0 0 28px; }
.q-and-barra { flex: 1 1 auto; height: 7px; border-radius: 3px; background: var(--q-paper); overflow: hidden; }
.q-and-barra i { display: block; height: 100%; }
.q-and-prima { background: #c4b497; }
.q-and-ora { background: var(--forest); }
.q-and-riga b { flex: 0 0 24px; text-align: right; color: var(--ink); }
.q-tot { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; flex-wrap: wrap; min-height: 46px; padding: 0.4rem 0.85rem; background: var(--q-paper); border-top: 1px solid var(--cream-border); }
.q-tot-n { font-size: 0.78rem; color: var(--ink-mid); }
.q-tot-n b { color: var(--ink); }
.q-az { padding: 0.65rem 0 0.75rem; border-bottom: 1px solid var(--q-linea); }
.q-az:last-of-type { border-bottom: 0; }
.q-az-t { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; font-size: 0.84rem; font-weight: 600; color: var(--ink); }
.q-az-t b { font-family: var(--font-display); font-size: 1.2rem; font-weight: 600; }
.q-az .q-imp { margin-top: 0.4rem; height: 24px; }
.q-arr { display: flex; align-items: flex-start; gap: 0.75rem; padding: 0.6rem 0; border-top: 1px solid var(--q-linea); }
.q-arr-n { flex: 0 0 92px; font-size: 0.8rem; font-weight: 600; line-height: 1.3; color: var(--ink); }
.q-arr-b { flex: 1 1 auto; min-width: 0; }
.q-arr-t { display: block; margin-top: 4px; font-size: 0.72rem; color: var(--ink-mid); }

/* ── COMPUTER ── */
.q-card--tab { padding: 0; overflow: hidden; margin-top: 0.4rem; }
.q-card--tab .q-card-testa { padding: 0.9rem 1.25rem 0.6rem; justify-content: space-between; }
.q-leg--r { margin: 0; }
.q-scorri { overflow-x: auto; }
.q-tabella { min-width: 1120px; }
.q-trow { display: grid; column-gap: 1rem; align-items: center; min-height: 52px; padding: 0 1.25rem; text-decoration: none; color: inherit; border-top: 1px solid var(--q-linea); }
a.q-trow:hover { background: var(--sage-faint); }
.q-trow--testa { min-height: 0; padding-bottom: 0.45rem; border-top: 0; border-bottom: 1px solid var(--cream-border); align-items: end; font-size: 0.75rem; font-weight: 600; color: var(--ink-faint); }
.q-trow--tot { background: var(--q-paper); border-top: 1px solid var(--cream-border); }
.q-dx { text-align: right; }
.q-num-cella { text-align: right; font-family: var(--font-display); font-weight: 600; font-size: 1.5rem; letter-spacing: -0.02em; color: var(--ink); }
.q-vs { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; font-size: 0.72rem; }
.q-trow .q-var { font-size: 0.84rem; }
.q-trow .q-spark { width: 146px; height: 36px; }
.q-cl { display: flex; align-items: baseline; gap: 0.5rem; font-size: 0.8125rem; color: var(--ink); min-width: 0; }
.q-cl b { min-width: 22px; text-align: right; }
.q-cl span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.q-niente { color: var(--ink-faint); font-size: 0.8rem; }
.q-isc { display: flex; flex-direction: column; font-size: 0.7rem; color: var(--ink-faint); }
.q-isc b { font-size: 0.875rem; color: var(--ink); }
.q-tot-scala { font-size: 0.7rem; line-height: 1.35; }
.q-due { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-start; }
.q-due > .q-card { margin-top: 1rem; }
.q-card--mx { flex: 1.2 1 560px; }
.q-card--rit { flex: 1 1 420px; }
.q-mx { margin-top: 0.7rem; }
.q-mx-r { display: grid; gap: 4px; align-items: center; min-height: 30px; }
.q-mx-r--testa { align-items: end; padding-bottom: 6px; font-size: 0.75rem; font-weight: 600; color: var(--ink-faint); }
.q-mx-c { text-align: center; }
.q-mx-az { font-size: 0.8125rem; color: var(--ink); }
.q-mx-v { display: flex; align-items: center; justify-content: center; height: 26px; border-radius: 5px; font-size: 0.8125rem; font-weight: 700; color: var(--ink); }
.q-t0 { background: var(--q-paper); color: var(--ink-faint); font-weight: 400; }
.q-t1 { background: #eef2ef; }
.q-t2 { background: #cfdcd5; }
.q-t3 { background: #8fb09f; }
.q-t4 { background: #4f7f69; color: #fff; }
.q-t5 { background: #0d3d2e; color: #fff; }
.q-mx-v.q-t4, .q-mx-v.q-t5 { color: #fff; }
.q-leg--mx { margin-top: 0.7rem; }
.q-rit { display: flex; align-items: center; gap: 0.75rem; padding: 0.4rem 0; border-top: 1px solid var(--q-linea); }
.q-rit:first-of-type { margin-top: 0.5rem; }
.q-rit-n { flex: 0 0 110px; font-size: 0.8125rem; font-weight: 600; color: var(--ink); }
.q-rit .q-spark { flex: 1 1 auto; width: 100%; height: 32px; }
.q-rit b { flex: 0 0 40px; text-align: right; color: var(--ink); }

/* ── PAGINA DI UN'APP ── */
.q-nav { margin: -0.4rem -0.5rem 0.3rem; }
.q-nav-su { display: flex; align-items: center; justify-content: space-between; }
.q-indietro, .q-prec, .q-succ { display: inline-flex; align-items: center; min-height: 44px; text-decoration: none; }
.q-indietro { padding: 0 0.5rem; font-size: 0.95rem; font-weight: 600; color: var(--forest); }
.q-indietro svg { width: 20px; height: 20px; }
.q-pos { padding: 0 0.6rem; font-size: 0.78rem; color: var(--ink-faint); }
.q-nav-giu { display: flex; align-items: center; justify-content: space-between; margin: 0 0.5rem; border-top: 1px solid var(--q-linea); border-bottom: 1px solid var(--q-linea); }
.q-prec, .q-succ { font-size: 0.8rem; font-weight: 500; color: var(--ink-mid); }
.q-prec svg, .q-succ svg { width: 18px; height: 18px; }
.q-dots { display: flex; align-items: center; gap: 6px; }
.q-dot { width: 6px; height: 6px; border-radius: 3px; background: #d8d0c1; }
.q-dot--qui { width: 20px; background: var(--forest); }
.q-app-alto { display: flex; flex-direction: column; }
.q-grande { padding: 1rem 1rem 0.9rem; }
.q-grande-n { display: flex; align-items: baseline; gap: 0.6rem; }
.q-num { font-family: var(--font-display); font-weight: 600; font-size: 4.2rem; line-height: 1; letter-spacing: -0.03em; color: var(--ink); }
.q-grande-u { font-size: 1.05rem; font-weight: 500; color: var(--ink-mid); }
.q-grande-var { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.7rem; margin-top: 0.7rem; font-size: 0.78rem; }
.q-pill-var { display: inline-flex; align-items: center; gap: 0.3rem; min-height: 28px; padding: 0 0.7rem; border-radius: 999px; background: var(--sage-faint); font-size: 0.84rem; font-weight: 700; }
.q-pill-var.q-var--giu { background: #f6e9e4; }
.q-graf { display: block; width: 100%; height: auto; margin-top: 0.4rem; }
.q-g-gr { stroke: var(--q-linea); stroke-width: 1; }
.q-g-base { stroke: #d8d0c1; stroke-width: 1; }
.q-g-t { font-size: 11px; fill: var(--ink-faint); }
.q-g-t--forte { fill: var(--ink); font-weight: 600; }
.q-g-n { font-size: 11px; font-weight: 700; fill: var(--ink); }
.q-g-area { fill: var(--forest); fill-opacity: 0.07; }
.q-g-linea { fill: none; stroke: var(--forest); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.q-g-pt { fill: var(--forest); }
.q-g-oggi-l { stroke: var(--forest); stroke-width: 2; stroke-dasharray: 3 3; stroke-linecap: round; }
.q-g-oggi { fill: white; stroke: var(--forest); stroke-width: 1.8; }
.q-lg-pieno { stroke: var(--forest); stroke-width: 2; stroke-linecap: round; }
.q-lg-trat { stroke: var(--forest); stroke-width: 2; stroke-dasharray: 3 3; stroke-linecap: round; }
.q-leg--g { margin-top: 0.4rem; }
.q-app-gr { display: flex; flex-direction: column; }
.q-app-col { min-width: 0; }
.q-tocchi { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 0.8rem; }
.q-tocco { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; min-height: 54px; padding: 0.5rem 0.75rem; background: white; border: 1px solid var(--q-linea); border-radius: 0.85rem; font-size: 0.8125rem; font-weight: 500; color: var(--ink); }
.q-tocco b { font-family: var(--font-display); font-weight: 600; font-size: 1.4rem; }
.q-tocco--zero { background: var(--q-paper); color: var(--ink-faint); }
.q-tocco--zero b { color: var(--ink-faint); }
.q-tocco--primo { grid-column: 1 / span 2; flex-direction: column; align-items: stretch; background: var(--forest); border-color: var(--forest); color: white; padding: 0.75rem 0.9rem 0.9rem; }
.q-tocco-r { display: flex; align-items: baseline; justify-content: space-between; font-size: 0.875rem; font-weight: 600; }
.q-tocco-q { font-size: 0.78rem; font-weight: 400; color: #d6e3dc; }
.q-tocco-v { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.3rem; }
.q-tocco--primo b { font-size: 2.6rem; line-height: 1; }
.q-tocco-b { flex: 1 1 auto; height: 8px; border-radius: 4px; background: #2a5a47; overflow: hidden; }
.q-tocco-b i { display: block; height: 100%; background: var(--cream); }
.q-leg--p { margin-top: 0.6rem; }
.q-barre { margin-top: 0.7rem; display: flex; flex-direction: column; gap: 0.55rem; }
.q-barra-r { display: flex; align-items: center; gap: 0.6rem; font-size: 0.8125rem; }
.q-barra-n { flex: 0 0 38%; max-width: 150px; color: var(--ink); overflow-wrap: anywhere; }
.q-barra-b { flex: 1 1 auto; height: 10px; border-radius: 4px; background: var(--q-paper); overflow: hidden; }
.q-barra-b i { display: block; height: 100%; background: var(--forest); }
.q-barra-v { flex: 0 0 74px; text-align: right; color: var(--ink); }
.q-tiles { display: flex; gap: 8px; margin-top: 0.7rem; }
.q-tile { flex: 1 1 0; padding: 0.6rem 0.65rem 0.7rem; border-radius: var(--radius-sm); background: var(--sage-faint); font-size: 0.75rem; line-height: 1.25; color: var(--ink-mid); }
.q-tile b { display: block; font-family: var(--font-display); font-weight: 600; font-size: 1.9rem; line-height: 1.05; color: var(--ink); }
.q-tile--zero { background: var(--q-paper); border: 1px solid var(--q-linea); }
.q-tile--zero b { color: var(--ink-faint); }
.q-tecnici { margin-top: 1.4rem; padding-top: 0.75rem; border-top: 1px dashed var(--cream-border); font-size: 0.75rem; line-height: 1.5; color: var(--ink-faint); }
@media (min-width: 1024px) {
  .q-nav { margin: 0 0 0.3rem; max-width: 520px; }
  .q-app-alto { flex-direction: row; gap: 1rem; align-items: stretch; }
  .q-app-alto > .q-grande { flex: 0 0 300px; }
  .q-app-alto > .q-card--graf { flex: 1 1 auto; min-width: 0; }
  .q-app-gr { flex-direction: row; gap: 1rem; align-items: flex-start; }
  .q-app-gr > .q-app-col { flex: 1 1 0; }
  .q-app-gr > .q-app-col:first-child { flex: 1.3 1 0; }
  .q-tocchi { grid-template-columns: 1fr 1fr; }
}
@media (prefers-reduced-motion: reduce) { .q-corpo-i { transition: none; } }

/* ── T2 · quadro e Per Blasat (builder 1: aggiungere SOLO qui sotto) ── */

/* Nomi di azione nello spazio stretto: la forma breve (qEtichettaBreve) con i puntini
   solo se non basta nemmeno quella; l'etichetta intera e' nel title. */
.q-cl-n { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.q-altre { margin: 0.7rem 0 0; padding-top: 0.6rem; border-top: 1px dashed var(--cream-border); color: var(--ink-mid); }

/* Tabella del computer: le colonne si stringono, la Provenienza resta dentro il
   riquadro. Le celle possono restringersi (min-width: 0) e la tabella ha un minimo
   piu' basso; sotto quel minimo scorre DENTRO .q-scorri, mai fuori dal bordo. */
.q-tabella { min-width: 932px; }
.q-trow { column-gap: 0.75rem; }
.q-trow > * { min-width: 0; }
.q-trow .q-spark { width: 100%; max-width: 146px; height: 36px; }
.q-trow .q-prov, .q-trow .q-prov .q-imp { max-width: 100%; }

/* «Per Blasat»: la striscia sopra il quadro (solo admin). Una colonna sul
   telefono; dal computer «Da fare» e «Il mese» affiancati. */
.q-bl { margin-top: 1rem; }
.q-bl-t { font-family: var(--font-display); font-weight: 600; font-size: 1.05rem; line-height: 1.2; letter-spacing: -0.022em; color: var(--ink); }
.q-bl-cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.6rem; margin-top: 0.4rem; }
.q-bl-col { min-width: 0; }
.q-bl-st { margin: 0 0 0.3rem 0.2rem; font-size: 0.72rem; font-weight: 600; letter-spacing: 0.02em; color: var(--ink-faint); }
.q-bl .rip-gruppo { border: 1px solid var(--q-linea); box-shadow: none; }
.q-bl-riga { text-decoration: none; }
.q-bl-riga:focus-visible { outline: 2px solid var(--forest); outline-offset: -2px; }
.q-bl-pagina .fg-azioni { margin-top: 0; }
@media (min-width: 1024px) {
  .q-bl-cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1rem; align-items: start; }
}

/* ── T2 · pagina di un'app (builder 2: aggiungere SOLO qui sotto) ── */
/* Le sezioni in piu': domande, risposte ufficiali, opinioni, report, «Solo per Blasat». */
.q-app-sotto { display: flex; flex-direction: column; }
.q-sez .q-card-testa { align-items: center; }
.q-sez .q-card-testa .widget-link { margin-left: auto; }
/* I corpi dei riquadri di prima (card-sub, empty…) hanno margini negativi pensati per
   stare sotto la testa di una card: il padding li compensa e lascia un filo d'aria. */
.q-sez-c { padding-top: 0.8rem; min-width: 0; }
.q-sez-c .qd-grid { margin-top: 0; }
.q-nota--s { margin: -0.2rem 0 0.6rem; }
.q-sk { margin-top: 0.2rem; }
.q-sk .skel-barra { height: 14px; margin-top: 0.6rem; }
.q-csv { margin-top: 0.9rem; text-decoration: none; }

/* Confronta con un altro periodo (solo con «Date») */
.q-cmp-campi { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.q-cmp-c { flex: 1 1 140px; max-width: 240px; min-width: 0; display: flex; flex-direction: column; gap: 0.15rem; font-size: 0.72rem; font-weight: 600; color: var(--ink-faint); }
.q-cmp-c input { width: 100%; min-width: 0; min-height: 44px; padding: 0 0.6rem; border: 1.5px solid var(--q-linea); border-radius: var(--radius-sm); background: white; color: var(--ink); font: inherit; font-size: 1rem; font-weight: 400; }
.q-cmp-c input:focus-visible, .q-cmp-togli:focus-visible { outline: 2px solid var(--forest); outline-offset: 2px; }
.q-cmp-err { margin-top: 0.6rem; font-size: 0.8125rem; line-height: 1.45; color: var(--q-giu); }
.q-cmp-err[hidden] { display: none; }
.q-cmp-togli { min-height: 44px; }
.q-cmp-ris { margin-top: 0.9rem; }
.q-cmp-ap { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0.6rem; }
.q-cmp-col { min-width: 0; padding: 0.7rem 0.8rem 0.75rem; border-radius: var(--radius-sm); background: var(--q-paper); border: 1px solid var(--q-linea); }
.q-cmp-tag { display: block; font-size: 0.78rem; font-weight: 700; color: var(--ink-mid); }
.q-cmp-quando { display: block; margin-top: 0.1rem; font-size: 0.72rem; line-height: 1.35; color: var(--ink-faint); }
.q-cmp-n { display: block; margin-top: 0.4rem; font-family: var(--font-display); font-weight: 600; font-size: 2.3rem; line-height: 1; letter-spacing: -0.02em; color: var(--ink); }
.q-cmp-u { font-size: 0.8125rem; color: var(--ink-mid); }
.q-cmp-var { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.7rem; margin-top: 0.7rem; font-size: 0.78rem; }
.q-cmp-ris .caveat { margin-top: 0.6rem; }
.q-cmp-ris .cmp { margin-top: 1.1rem; }
.q-cmp-ris .cmp-row { font-size: 0.8125rem; }

/* Con la barra di navigazione in basso (telefono e tablet, sotto i 1024px) niente
   deve finire sotto: il corpo gia' lascia i suoi 57px (body, vedi sopra), qui si
   dice anche al browser dove sta il bordo utile, cosi' lo scorrimento verso un
   campo o una riga del feed (fuoco, Tab, scrollIntoView) non li ferma sotto la barra. */
@media (max-width: 1023px) {
  html:has(body.q-on) { scroll-padding-bottom: calc(57px + var(--sab) + 1rem); }
  body.q-on .q-app { padding-bottom: 1rem; }
}
