/* ================================================================
   BabyFox — App Design System (mobile, ruolo genitore)
   Estratto da app/mockup-genitore.html (bozza congelata, invariata).
   Spostato in docs/design/mockup/mockup-genitore.html il 26/07/2026 (commit
   4455816, rinomina pura) quando i mockup storici sono stati raccolti fuori
   da /app/.
   Documentato in /docs/APP-DESIGN.md — stessi nomi di variabile/classe.

   Struttura del file:
   1. Reset & base
   2. Token (:root)
   3. Schermate — .screen, il contenitore a schermo pieno
   4. Core — componenti riusabili (validi anche per la futura versione
      insegnante)
   5. Specifico app genitore — screen/componenti di dominio (diario,
      comunicazioni, attività/gite, gallery). Nota: nonostante il nome,
      alcuni componenti qui dentro sono in realtà condivisi anche da
      insegnante.html, semplicemente non sono mai stati spostati in
      sezione 4 — schermata di login (.login-hero/.login-logo/.login-body/
      .login-heading/.login-sub/.login-form/.login-row/.chk-label/.forgot),
      .week-strip/.week-arrow (solo genitore, dal 24/07/2026 — v. §4/§7 per
      il dettaglio) / .week-days/.week-day (entrambe le app),
      .comm-card/.comm-kicker/.comm-status/.comm-title/.comm-text
      (Comunicazioni), .stat-value/.stat-label/.stat-hint/.card-stack
      (Home, Altro, Corsi/gite, bambino-detail). V. anche docs/APP-DESIGN.md
      §7, che li elenca correttamente sotto "Core".
   ================================================================ */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Rimuove il rettangolo azzurro di feedback tap di Chrome/Android
   (-webkit-tap-highlight-color) su bottoni, voci di menu e ogni elemento
   toccabile: antiestetico e ridondante rispetto agli stati :active/.active
   già definiti dal design system. */
* { -webkit-tap-highlight-color: transparent; }

/* ── 2. Token ─────────────────────────────────────────────────── */
:root {
  --accent: #ff870f;
  /* #e66f00 → #e36c00 il 25/07/2026: scarto impercettibile (3 punti di hex) ma
     porta il token da 2.99:1 a 3.09:1 sul fondo crema, cioè sopra la soglia AA
     per testo grande — serve a .login-heading (34px), che punta qui. */
  --accent-dark: #e36c00;
  --accent-light: #fff7f0;
  --accent-gradient: linear-gradient(135deg, #ff870f 0%, #e66f00 100%);
  /* Scurito il 25/07/2026 da #e66f00 (3.16:1, sotto AA) per il testo piccolo
     che lo usa — .diario-pct 13px/700, .roster-step-val 12px/700, .btn-outline,
     .bb-item.active, .diario-chip.active, .comm-status.unread, .forgot. Ora 5.0:1
     su bianco e 4.7:1 su --color-bg/--accent-light. L'arancio brand vivo resta
     su --accent/--accent-dark: l'unico uso di testo grande (.login-heading,
     34px, dove basta 3:1) è stato ripuntato su --accent-dark, che vale
     esattamente il vecchio #e66f00 — quel titolo è invariato. */
  --accent-text: #b35400;

  --color-bg: #fff7f0;
  --color-surface: #ffffff;
  --color-border: rgba(0,0,0,0.08);

  /* Rampa grigi ricalibrata il 25/07/2026 per conformità WCAG AA (4.5:1 su
     testo normale). I valori precedenti (text-2 #5e6e82, text-3 #748194,
     text-4 #9da9bb) fallivano: text-3 stava a 3.95:1 e text-4 a 2.4:1 su
     bianco, peggio ancora sul fondo crema --color-bg. Vincolo emerso nel
     ricalcolo: sul crema il tetto di luminanza per passare 4.5:1 lascia
     spazio a UN solo livello sotto text-2, non due — perciò non bastava
     scurire l'ultimo gradino, è stata riscalata l'intera rampa mantenendo
     quattro passi ancora distinguibili. text-1 era già conforme. */
  --color-text-1: #344050;
  --color-text-2: #4a5768;  /* 6.9:1 su --color-bg */
  --color-text-3: #5b6a7f;  /* 5.3:1 su --color-bg */
  --color-text-4: #66738a;  /* 4.5:1 su --color-bg */

  --color-success: #00864e;
  --color-success-bg: #ccf6e4;
  --color-success-gradient: linear-gradient(135deg, #22b573 0%, #00864e 100%);
  --color-danger: #e63757;
  --color-danger-bg: #fdd8df;
  --color-warning: #f5803e;
  --color-warning-bg: #fde3d1;
  --color-info: #27bcfd;
  --color-info-bg: #d0f2ff;

  /* Tint decorativi per avatar bambini (1-2) e tile gallery (3-7) —
     valori prima hardcoded come hex sparsi nell'HTML, ora nominati. */
  --tint-1: #ff9f4a;
  --tint-2: #8b5cf6;
  --tint-3: #d7f5e6;
  --tint-4: #ffe3c2;
  --tint-5: #ffd6df;
  --tint-6: #d6ecff;
  --tint-7: #fff3b0;

  --radius-md: 12px;
  /* Documentato in docs/APP-DESIGN.md §token fin dall'inizio ma mai definito
     qui: .roster-step/.roster-flag lo usavano e il browser scartava la
     dichiarazione, rendendoli ad angoli vivi invece che a pillola. */
  --radius-pill: 160px;
  --app-radius-lg: 22px;
  --app-radius-md: 16px;
  --app-shadow: 0 10px 24px rgba(52,64,80,.10), 0 2px 6px rgba(0,0,0,.04);

  --font-body: 'Inter', system-ui, sans-serif;
  --font-head: 'Baloo 2', system-ui, sans-serif;
}

/* ── 1. Base ──────────────────────────────────────────────────── */
/* 100% resta il fallback per browser senza dvh (nessuno di
   rilievo oggi, ma costa zero tenerlo). Senza dvh, in Chrome mobile la
   barra degli indirizzi dinamica fa sì che 100% non segua il viewport
   realmente visibile: .screen e .bottombar (entrambi position:absolute
   dentro questo contenitore) si scollano dal fondo vero. Non capita
   nella PWA standalone, dove il viewport è fisso. */
html, body { height: 100%; height: 100dvh; }

/* Un punto solo per tutte le transizioni/animazioni dell'app, esistenti e
   future: chi preferisce ridurre il movimento (impostazione di sistema) le
   ottiene tutte quasi istantanee, senza bisogno di una guardia ripetuta a
   ogni nuova regola aggiunta qui o altrove nel file. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
  }
}
body {
  font-family: var(--font-body);
  color: var(--color-text-2);
  /* Sfondo crema del design (non bianco): senza questa riga la vista reale
     /app rende su bianco, perché prima del Task 7 l'unico elemento a portare
     var(--color-bg) era la cornice .device, che le viste reali non usano più
     (e il cui CSS è uscito da questo file il 14/08/2026 — v. sezione 3). */
  background: var(--color-bg);
}

/* Nessun link vestito da link del browser (APP-DESIGN.md §7.1): un'app non
   sottolinea, distingue con colore e peso. Reset globale dal 14/08/2026 —
   prima ogni link ereditava l'underline di default e i pochi punti dove dava
   fastidio lo spegnevano a mano (.hub-item, .inline-popup a), che è il modo
   di dimenticarsene sul prossimo link nuovo. */
a { text-decoration: none; }

/* ================================================================
   3. SCHERMATE — contenitore a schermo pieno
   ================================================================ */

/* Qui stava la "device chrome": body.app-shell, .device (cornice telefono
   390x844 su fondo nero), .statusbar/.statusbar.on-header, .app-body e la
   media query che la sfilava sotto i 480px. Rimossa il 14/08/2026: nessuna
   vista reale usava quelle classi — app/views/app.php è <body data-role>
   nudo — e i mockup, unici a montare la cornice, hanno la propria copia del
   CSS in app/mockup/css/. Restava solo a far credere che /app rendesse
   incorniciato da desktop. Recuperabile da git se dovesse servire.
   Nota: app/js/common.js cerca ancora #statusbar (con guardia sul null) e
   login.js ne parla in un commento — JS, fuori dalla pulizia di questo file. */

.screen {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  display: none;
  transition: opacity .18s ease;
  /* La bottombar (pill flottante) copre fino a ~76px dal fondo: un margine
     troppo stretto sotto l'ultimo bottone (es. "Salva") espone a tap
     involontari sulla navbar mentre si scorre — 128px lascia ~52px liberi
     sopra la pill. */
  padding-bottom: 128px;
  -ms-overflow-style: none;
  scrollbar-width: none;
}
.screen::-webkit-scrollbar { display: none; }
/* "active" a riposo è sempre opaca: è lo stato strutturale (anche quella
   stampata già attiva dal PHP al boot — screen-home, screen-login,
   screen-resetpass, screen-register — che non passa mai da showScreen()).
   Solo una transizione avviata da JS deve animare da 0 a 1: showScreen()
   aggiunge "screen-entering" un frame dopo aver reso attiva la schermata, poi
   lo rimuove al frame successivo, così la dissolvenza parte da opacity:0 senza
   che quello sia lo stato di riposo di ".active". */
.screen.active { display: block; opacity: 1; }
.screen.active.screen-entering { opacity: 0; }

/* ================================================================
   4. CORE — componenti riusabili
   ================================================================ */

/* Header standard (arancio, sticky, logo in alto a dx).
   Curvatura "invertita": l'header resta un rettangolo dai bordi
   dritti (resta fermo in cima mentre il contenuto scorre sotto);
   è il contenuto sottostante a "risalire" con gli angoli superiori
   arrotondati (::after), rivelando l'arancio nelle due punte —
   l'opposto di un header che arrotonda i propri angoli inferiori. */
.app-header {
  background: linear-gradient(136deg, #e66f00 0%, #ffb366 100%);
  padding: 40px 16px 18px;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  color: #fff;
  position: sticky;
  top: 0;
  /* z-index: 25; */
  margin-bottom: 2px;
}
/* .app-header::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 18px;
  background: var(--color-bg);
  border-radius: 18px 18px 0 0;
} */
.app-header .back-btn {
  width: 32px; height: 32px; border-radius: 50%;
  background: rgba(255,255,255,.22);
  border: none; color: #fff; font-size: 15px;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; flex-shrink: 0; margin-top: 2px;
}
.app-header .back-btn:hover { background: rgba(255,255,255,.34); }
.app-header-text { flex: 1; min-width: 0; padding-top: 4px; }
.app-header-title { font-family: var(--font-head); font-weight: 700; font-size: 19px; display:flex; align-items:center; gap:7px; }
.app-header-school { font-family: var(--font-head); font-weight: 400; font-size: 15px; color: rgba(255,255,255,.9); margin-top: 2px; }
.app-header-school strong { font-weight: 700; color: #fff; }
.app-header-sub { font-size: 12px; color: rgba(255,255,255,.85); margin: 2px 0 10px; }
.app-header-logo { width: 110px; height: auto; object-fit: contain; flex-shrink: 0;}

/* Header della Home: più alto delle altre schermate (--header-pad-top eredita
   già il safe-area-inset-top, v. sezione 6 del file), logo della scuola più
   grande perché è l'unico punto in cui l'app mostra il brand della scuola. */
.app-header-home { --header-pad-top: 56px; padding-bottom: 26px; }
.app-header-home .app-header-logo { width: 130px; }

.app-page-content{ position: relative; z-index: 1; background: var(--accent-light); padding-top: 20px; border-radius: 20px; margin-top: -20px; }

.px { padding-left: 16px; padding-right: 16px; }
.row { display: flex; align-items: center; gap: 12px; }
.flex-1 { flex: 1; min-width: 0; }
.ic-success { color: var(--color-success); }

/* Toggle stile iOS (sostituisce il checkbox quadrato per ogni on/off) */
.toggle { appearance: none; -webkit-appearance: none; width: 40px; height: 24px; border-radius: 160px; background: var(--color-border); border: none; position: relative; cursor: pointer; flex-shrink: 0; transition: background .15s; }
.toggle::after { content: ''; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.25); transition: left .15s; }
.toggle:checked { background: var(--accent-gradient); }
.toggle:checked::after { left: 18px; }

/* Chip / pillola (filtri, tag, switch figlio) */
.chip { padding: 7px 14px; border-radius: 160px; font-size: 13px; font-weight: 600; background: var(--color-surface); color: var(--color-text-2); border: 1px solid var(--color-border); cursor: pointer; white-space: nowrap; }
.chip.active { background: var(--accent-gradient); color: #fff; border-color: transparent; }

/* Card */
.card { background: var(--color-surface); border-radius: var(--app-radius-lg); box-shadow: var(--app-shadow); padding: 15px; margin: 0 16px 14px; }
.section-title { font-family: var(--font-head); font-weight: 600; font-size: 14px; color: var(--color-text-1); display: flex; align-items: center; gap: 7px; margin: 2px 16px 9px; }
.section-title .cnt { margin-left: auto; font-family: var(--font-body); font-size: 11px; font-weight: 700; color: var(--color-text-4); }
.section-title.center { justify-content: center; }

/* Tile riepilogo con link (usata in Home, ma pattern generico) */
.preview-tile { display:flex; align-items:center; gap:12px; padding:16px 14px; background:var(--color-surface); border-radius:var(--app-radius-md); box-shadow:var(--app-shadow); margin:0 16px 12px; cursor:pointer; }
.preview-icon { width:38px; height:38px; border-radius:12px; background:var(--accent-light); display:flex; align-items:center; justify-content:center; font-size:18px; flex-shrink:0; }
.preview-body { flex:1; min-width:0; }
.preview-title { font-size:13.5px; font-weight:700; color:var(--color-text-1); }
.preview-text { font-size:12px; color:var(--color-text-3); margin-top:1px; }
/* Stessa condizione che accende il pallino rosso in bottombar
   (aggiornaDotAttivita/aggiornaDotAvvisi): qui è solo un riflesso visivo
   dello stesso stato, nessuna logica nuova. */
.preview-tile.has-novita { box-shadow: var(--app-shadow), 0 0 1px 1px var(--accent); border-left: 4px solid var(--accent); }
/* Tile "Attiva le notifiche" della Home: stesso accent-light/accent-text gia'
   validati AA per .install-hint, non un colore nuovo (v. push.js). */
.preview-tile.tile-highlight { background: var(--accent-light); box-shadow: none; }
.preview-tile.tile-highlight .preview-title { color: var(--accent-text); }
.preview-badge { background:var(--color-danger); color:#fff; font-size:10px; font-weight:700; border-radius:160px; min-width:18px; height:18px; display:flex; align-items:center; justify-content:center; padding:0 5px; flex-shrink:0; }

/* Bottombar (pillola, 5 voci) */
.bottombar { position: absolute; left: 10px; right: 10px; bottom: 14px; height: 62px; background: var(--color-surface); border-radius: 160px; box-shadow: 0 12px 24px rgba(0,0,0,.12); display: flex; align-items: center; justify-content: space-around; z-index: 40; }
/* `flex: 0 1 72px` e non `min-width: 72px`: con un minimo rigido le 5 voci
   chiedono 360px, piu' i 20px di margini della barra fanno 380px di viewport
   minimo — sotto, l'ultima voce ("Altro") finisce fuori schermo. Misurato:
   60px fuori a 320px di viewport, 20px fuori a 360px. Con flex-basis le voci
   restano 72px finche' c'e' spazio (aspetto invariato sopra i 380px) e si
   stringono in modo proporzionale sotto. */
.bb-item { display: flex; flex-direction: column; align-items: center; gap: 2px; background: none; border: none; cursor: pointer; color: var(--color-text-3); font-family: var(--font-body); padding: 7px 10px; border-radius: 160px; transition: background .15s, color .15s; flex: 0 1 72px; min-width: 0; }
.bb-item .bb-icon { font-size: 20px; line-height: 1; }
/* L'etichetta piu' lunga misura 45px ("Presenze" insegnante, "Attivita'"
   genitore): a 320px la voce e' 60px, quindi il padding stretto sotto lascia
   52px di spazio utile e non taglia nulla. L'ellissi e' una rete di sicurezza
   per traduzioni piu' lunghe (l'inglese "Attendance"), che altrimenti
   manderebbero l'etichetta a capo alzando la barra. */
.bb-item .bb-label { font-size: 9px; font-weight: 600; letter-spacing: .5px; text-transform: uppercase; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Schermi stretti (iPhone SE 375, Android piccoli 360, 320 ancora in giro):
   solo il padding orizzontale si stringe. Il bersaglio di tocco resta 47px di
   altezza, sopra i 44px raccomandati. */
@media (max-width: 380px) {
  .bb-item { padding: 7px 4px; }
}
.bb-item.active { color: var(--accent-light); background: var(--accent); }
.bb-icon-wrap { position: relative; display: inline-flex; }
.bb-dot { position: absolute; top: -3px; right: -6px; width: 10px; height: 10px; border-radius: 50%; background: var(--color-danger); border: 2px solid var(--color-surface); }

/* Bottoni */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 40px; padding: 0 18px; border-radius: 160px; font-size: 14px; font-weight: 600; font-family: var(--font-body); text-transform: uppercase; border: none; cursor: pointer; white-space: nowrap; }
.btn-primary { background: var(--accent-gradient); color: #fff; }
.btn-success { background: var(--color-success-gradient); color: #fff; }
.btn-secondary { background: var(--color-surface); color: var(--color-text-2); border: 1px solid var(--color-border); }
.btn-outline { background: var(--accent-light); color: var(--accent-text); }
.btn-danger { background: var(--color-danger-bg); color: var(--color-danger); }
/* I bottoni a tutta larghezza sono le azioni principali di una schermata e si
   toccano col pollice: min-height 44px e' il minimo raccomandato per un bersaglio
   tattile, l'altezza base di .btn (40px) resta per i bottoni piccoli e inline.
   Due bottoni impilati non si toccano mai fra loro (rilievo del 2026-08-01). */
.btn-block { width: 100%; min-height: 44px; }
.btn-block + .btn-block { margin-top: 10px; }
/* ...ma non quando i due bottoni stanno gia' in una riga flex: li' la distanza
   la fa il gap, e il margine superiore sposterebbe il secondo 10px piu' in
   basso del primo. Successo davvero il 2026-08-01 nel foglio di conferma, dove
   "Elimina" si e' ritrovato disallineato rispetto ad "Annulla". */
.btn-row > .btn-block + .btn-block,
.confirm-actions > .btn-block + .btn-block { margin-top: 0; }

/* Riga di azioni in fondo a un form. Due regole, entrambe sulla mano che tocca:
   1. l'azione PRINCIPALE sta a DESTRA. Il telefono si tiene in mano destra e si
      tocca col pollice destro, che arriva comodo a destra e attraversa tutto lo
      schermo per andare a sinistra: la destra e' la posizione privilegiata, e va
      a cio' che si fa quasi sempre. La secondaria/distruttiva sta a sinistra,
      dove ci si arriva con un movimento in piu' — e' un ostacolo utile.
   2. La principale prende lo spazio che avanza (.btn-grow), la distruttiva resta
      della sua larghezza: due bottoni identici affiancati sono il modo piu'
      facile per toccare quello sbagliato.
   Affiancare non toglie sicurezza: la cancellazione passa comunque da
   confirmAction() (v. CLAUDE.md §UX). */
.btn-row { display: flex; gap: 10px; }
.btn-row .btn { min-height: 44px; }
.btn-row .btn-grow { flex: 1; }
.btn-icon { width: 40px; padding: 0; flex-shrink: 0; }
/* Bottone a piena larghezza fuori da un contenitore .px/.card (che già
   inseriscono il padding orizzontale): compensa il margin coi calc(). */
.btn-block-px { width: calc(100% - 32px); margin: 0 16px; }
.btn-sm { height: 32px; padding: 0 12px; font-size: 12px; }
.btn:disabled { opacity: .4; cursor: not-allowed; }

/* Avatar. Sfondo di default = accent-gradient (insegnante, account
   proprio); i bambini usano un tint dedicato (v. .tint-1/.tint-2). */
.avatar { width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 17px; flex-shrink: 0; color: #fff; font-weight: 700; background: var(--accent-gradient); }
.avatar i.ph { font-size: 1.5rem; }
.avatar-lg { width: 54px; height: 54px; font-size: 23px; }
.avatar-sm { width: 30px; height: 30px; font-size: 12px; }

/* Tint (v. token in :root): 1-2 usati sugli avatar dei bambini,
   3-7 sulle tile di gallery/album. Devono seguire .avatar nel file
   perché a parità di specificità (una sola classe) vince l'ultima
   regola dichiarata. */
.tint-1 { background: var(--tint-1); }
.tint-2 { background: var(--tint-2); }
.tint-3 { background: var(--tint-3); }
.tint-4 { background: var(--tint-4); }
.tint-5 { background: var(--tint-5); }
.tint-6 { background: var(--tint-6); }
.tint-7 { background: var(--tint-7); }

/* Liste hub (menu, dettagli a righe) */
.hub-list { background: var(--color-surface); border-radius: var(--app-radius-lg); margin: 0 16px 14px; box-shadow: var(--app-shadow); overflow: hidden; }
.hub-item { display: flex; align-items: center; gap: 12px; padding: 13px 16px; border-bottom: 1px solid var(--color-bg); cursor: pointer; color: var(--color-text-1); font-size: 14px; font-weight: 500; text-decoration: none; }
.hub-item:last-child { margin-bottom: 0; }
.hub-item:hover { background: var(--accent-light); }
.hub-icon { font-size: 16px; width: 22px; text-align: center; color: var(--accent-text); flex-shrink:0; }
/* .chev: freccia "porta a" — riusabile anche fuori da .hub-item
   (preview-tile, home-child-row); dentro hub-item si spinge a destra. */
.chev { color: var(--color-text-4); flex-shrink:0; }
.hub-item .chev { margin-left: auto; }
.hub-item.danger { color: var(--color-danger); }
.hub-item.danger .hub-icon { color: var(--color-danger); }
.hub-item .meta { font-size: 11px; color: var(--color-text-4); font-weight: 400; margin-top: 1px; }
.hub-group-label { font-size: 11px; font-weight: 700; color: var(--color-text-4); text-transform: uppercase; letter-spacing: .04em; margin: 14px 16px 6px; }
.hub-group-label.tight { margin-top: 2px; }

#gita-classi-list { margin: 0; }
.gita-date{ display: flex; justify-content: space-between; }

/* Badge */
.badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px 10px; border-radius: 160px; font-size: 11px; font-weight: 700; }
.badge-success { background: var(--color-success-bg); color: var(--color-success); }
.badge-warning { background: var(--color-warning-bg); color: var(--color-warning); }
.badge-neutral { background: var(--color-bg); color: var(--color-text-3); }

/* Form */
.field { margin-bottom: 14px; }
.field label { display: flex; font-size: 12px; font-weight: 600; color: var(--color-text-2); margin-bottom: 5px; }
.field-row { display: flex; gap: 10px; }
.field-row .field { flex: 1; min-width: 0; }
.input, textarea, select.input { width: 100%; border: 1px solid var(--color-border); background: var(--color-surface); border-radius: var(--radius-md); padding: 10px 12px; font-size: 13px; font-family: var(--font-body); color: var(--color-text-1); outline: none; }
.input:focus, textarea:focus { border-color: var(--accent); }
textarea { resize: none; min-height: 64px; }
.field-icon-wrap { position: relative; }
.field-icon-wrap .input { padding-right: 40px; }
.field-icon-btn { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); background: none; border: none; color: var(--color-text-4); font-size: 16px; cursor: pointer; padding: 4px; line-height: 1; }
.field-hint { font-size: 11px; color: var(--color-text-4); margin-top: 4px; }

/* Bottone icona inline in una lista (es. cestino su "Comunicazioni inviate").
   44px di bersaglio tattile (APP-DESIGN.md §7.1), fantasma finché non si
   preme: non deve competere visivamente con l'azione primaria della riga. */
.icon-btn {
  width: 44px; height: 44px; min-height: 44px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; border-radius: 50%;
  color: var(--color-text-4); font-size: 17px; cursor: pointer; flex-shrink: 0;
}
.icon-btn:hover, .icon-btn:active { background: var(--color-bg); color: var(--color-danger); }
.field.disabled .input { color: var(--color-text-4); background: var(--color-bg); }
.field.has-error .input { border-color: var(--color-danger); }
.field-error { font-size: 11px; color: var(--color-danger); margin-top: 3px; }

/* Segmented control */
.segmented { display: flex; gap: 4px; background: var(--color-bg); border-radius: 160px; padding: 4px; margin: 0 16px 16px; }
.segmented button { flex: 1; border: 1px solid var(--color-border); background: var(--color-surface); padding: 9px 0; border-radius: 160px; font-size: 13px; font-weight: 600; color: var(--color-text-3); cursor: pointer; }
.segmented button.active { background: var(--accent-gradient); color: #fff; border-color: transparent; }

/* Stati vuoti / placeholder */
.empty-hint { text-align: center; padding: 30px 24px; color: var(--color-text-4); font-size: 13px; }
.empty-hint .ic-lg { font-size: 20px; }
.placeholder-card { margin: 30px 16px; text-align: center; padding: 28px 20px; background: var(--color-surface); border-radius: var(--app-radius-lg); box-shadow: var(--app-shadow); }
.placeholder-card .emoji { font-size: 32px; margin-bottom: 10px; }
.placeholder-card h2 { font-family: var(--font-head); font-size: 15px; color: var(--color-text-1); margin-bottom: 6px; }
.placeholder-card p { font-size: 12.5px; color: var(--color-text-3); }

/* Banner "aggiungi a schermata Home" (hub Altro, entrambe le app): iOS non ha
   il prompt automatico di installazione, va spiegato esplicitamente. Nascosto
   di default via style="display:none" inline nell'HTML — stato gestito da
   isIosStandaloneCandidate() in common.js, non stile (v. §8). */
.install-hint { display: flex; align-items: center; gap: 10px; margin: 0 16px 14px; padding: 12px 14px; background: var(--accent-light); color: var(--accent-text); border-radius: var(--app-radius-md); font-size: 12.5px; line-height: 1.4; }
.install-hint i { font-size: 18px; flex-shrink: 0; }

/* Toast — feedback delle azioni. Sta sopra la bottombar (che copre ~76px) e
   rispetta la safe area, altrimenti su iPhone finisce sotto la home indicator. */
.app-toast {
  position: fixed;
  left: 50%;
  bottom: calc(88px + env(safe-area-inset-bottom, 0px));
  transform: translate(-50%, 12px);
  max-width: calc(100% - 32px);
  padding: 12px 18px;
  border-radius: 14px;
  background: var(--color-text-1);
  color: #fff;
  font-size: 14px;
  line-height: 1.35;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
  opacity: 0;
  transition: opacity .22s ease, transform .22s ease;
  z-index: 90;
}
.app-toast.show   { opacity: 1; transform: translate(-50%, 0); }
.app-toast.is-err { background: var(--color-danger); }

/* Conferma per le azioni distruttive (confirmAction() in common.js). Foglio che
   sale dal basso, dove sta il pollice: la scelta si fa senza allungarsi in cima
   allo schermo. z-index 100, sopra il toast (90) e la bottombar (40) — se una
   conferma e' aperta e' l'unica cosa con cui si puo' interagire.
   Annulla e' a SINISTRA e neutro, Elimina a destra e .btn-danger: l'azione
   distruttiva non deve mai essere quella che il pollice trova per prima
   tornando dal bordo. */
.confirm-back {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(52, 64, 80, .38);
  opacity: 0; transition: opacity .2s ease;
}
.confirm-back.show { opacity: 1; }
.confirm-sheet {
  width: 100%; max-width: 440px;
  margin: 0 10px calc(14px + env(safe-area-inset-bottom, 0px));
  padding: 20px 18px 18px;
  background: var(--color-surface);
  border-radius: 22px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, .22);
  transform: translateY(14px); transition: transform .22s ease;
}
.confirm-back.show .confirm-sheet { transform: translateY(0); }
.confirm-title   { font-family: var(--font-heading); font-size: 17px; color: var(--color-text-1); }
.confirm-text    { margin-top: 6px; font-size: 14px; line-height: 1.4; color: var(--color-text-2); }
.confirm-actions { display: flex; gap: 10px; margin-top: 18px; }

/* Foglio dei documenti legali (app/views/resetpass.php + app/js/reset.js):
   stesso overlay di .confirm-back, ma quasi a tutto schermo e scrollabile —
   il contenuto e' un documento intero, non un messaggio di conferma. La X
   e' a 44px (bersaglio tattile, v. APP-DESIGN.md §7.1) in alto a destra. */
.legal-sheet {
  width: 100%; max-width: 640px;
  height: calc(100% - 24px);
  margin: 12px 10px calc(12px + env(safe-area-inset-bottom, 0px));
  display: flex; flex-direction: column;
  background: var(--color-surface);
  border-radius: 22px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, .22);
  overflow: hidden;
  transform: translateY(14px); transition: transform .22s ease;
}
.confirm-back.show .legal-sheet { transform: translateY(0); }
.legal-sheet-header {
  flex: none; display: flex; justify-content: flex-end;
  padding: 6px 6px 0;
}
.legal-sheet-close {
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; font-size: 20px; color: var(--color-text-2);
}
.legal-sheet-body { flex: 1 1 auto; overflow-y: auto; padding: 0 6px 12px; }
.legal-sheet-body .legal-page { padding: 8px 14px 24px; }

/* Safe area — sul notch/Dynamic Island l'header deve estendersi SOTTO la tacca
   (fondo colorato fin sopra) e la bottombar stare sopra la home indicator.
   Con viewport-fit=cover (v. app/views/app.php, login.php, resetpass.php) il
   layout parte a filo schermo: senza questi padding/offset il titolo finisce
   sotto la tacca. Il fallback 0px di env() serve ai browser desktop, dove la
   safe area non e' definita.
   I fallback numerici sotto (40px / 14px / 128px) sono i valori GIA' in uso
   da .app-header/.bottombar/.screen (verificati, non gli ipotetici 14px/8px
   della bozza iniziale di questo task): servono a lasciare il layout
   identico su desktop, dove env() vale 0. */
.app-header {
  padding-top: calc(var(--header-pad-top, 40px) + env(safe-area-inset-top, 0px));
}
/* .bottombar e' posizionata in absolute con `bottom` (v. sopra in questa
   sezione), non con un padding interno: la safe area va sommata a
   quell'offset. Un padding-bottom qui, con altezza fissa (62px) e
   box-sizing:border-box (v. §1), sposterebbe solo le icone in alto dentro
   la pillola senza allontanare la pillola stessa dal bordo schermo — cioe'
   esattamente il difetto che questa regola deve evitare. */
.bottombar {
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
}
.screen {
  padding-bottom: calc(128px + env(safe-area-inset-bottom, 0px));
}

/* ================================================================
   5. SPECIFICO APP GENITORE
   (nota: alcuni componenti qui — login, week-strip, comm-card,
   stat-value/stat-label/stat-hint/card-stack — sono in realtà condivisi
   anche da insegnante.html, mai spostati in sezione 4. V. commento in
   testa al file e docs/APP-DESIGN.md §7.)
   ================================================================ */

/* Login. Le quattro schermate di autenticazione (accesso, richiesta del link,
   nuova password, registrazione) condividono markup, fondo e impaginazione,
   e per questo portano tutte .auth-screen: da mobile sono bianche a tutto
   schermo, da desktop diventano crema con la card sopra (sezione 6). */
.auth-screen { background: var(--color-surface); }
.login-hero {
  position: relative; overflow: hidden;
  display: flex; align-items: center; justify-content: center; flex-direction: column;
}
/* .login-hero::before {
  content: ''; position: absolute; left: 50%; top: 0; width: 460px; height: 320px;
  background: radial-gradient(ellipse at center, var(--color-bg) 0%, var(--color-bg) 38%, rgba(255,247,240,0) 78%);
  transform: translateX(-50%); z-index: 0;
} */
/* .login-hero::after {
  content: ''; position: absolute; left: 50%; top: 58%; width: 260px; height: 260px; border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, #fff 42%, rgba(255,255,255,0) 76%);
  transform: translate(-50%, -50%); z-index: 0;
} */
.login-leaf-sprig { position: absolute; top: 0; right: 0; width: 90px; height: 170px; z-index: 0; pointer-events: none; }
.login-leaf-sprig i { position: absolute; color: #a3bf74; opacity: .55; }
.login-leaf-sprig i:nth-child(1) { top: -4px; right: 32px; font-size: 17px; transform: rotate(8deg); }
.login-leaf-sprig i:nth-child(2) { top: 18px; right: 12px; font-size: 25px; transform: rotate(35deg); }
.login-leaf-sprig i:nth-child(3) { top: 50px; right: 34px; font-size: 19px; transform: rotate(64deg); }
.login-logo { width: 120px; max-width: 78%; margin-top: 20px; object-fit: contain; position: relative; z-index: 1; filter: drop-shadow(0 6px 14px rgba(230,111,0,.15)); }
/* --accent-dark, non --accent-text: a 34px basta 3:1 (AA testo grande), quindi
   il titolo tiene l'arancio brand vivo mentre --accent-text è scurito per il
   testo piccolo. Il valore reso è lo stesso di prima del 25/07/2026.
   margin-top era -20px fino al 14/08/2026: recuperava il bianco che il vecchio
   logomark aveva dentro l'immagine. Il nuovo è ritagliato sul contenuto, quindi
   quei -20px finivano sopra le zampe della volpe. */
.login-heading { font-family: var(--font-head); font-weight: 700; font-size: 34px; color: var(--accent-dark); margin-top: 4px; }
.login-sub { font-size: 13px; color: var(--color-text-3); margin-top: 2px; }
.login-body { padding: 0 28px; text-align: center; }
.login-form { text-align: left; margin-top: 26px; }
.login-row { display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: var(--color-text-3); margin-bottom: 20px; }
.login-row .forgot { color: var(--accent-text); font-weight: 600; }

/* Freccia indietro di "Recupera password": sta dentro il form, in alto a
   sinistra, perche' quella schermata e' vestita come il login e un login non
   ha l'header arancio dove le altre schermate tengono il back-btn. Da desktop
   il form e' una card e la freccia ne occupa l'angolo. */
.auth-back {
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--color-bg); border: none;
  color: var(--accent-text); font-size: 15px;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; margin-bottom: 14px;
}
/* label.chk-label (non .chk-label): dentro un .field la regola generica
   ".field label { display: block; ... }" (riga 352) ha la STESSA specificita'
   di un selettore a singola classe solo se lo si eguaglia con un tag — senza
   ".field label" vince comunque perche' classe+tag (0,1,1) batte una singola
   classe (0,1,0), e il flex qui sotto non si applicava mai: era in blocco/
   inline flow con vertical-align baseline dell'input, causa del
   disallineamento segnalato su #com-auth (2026-08-27, task 8). */
label.chk-label { display: flex; align-items: center; gap: 6px; }
label.chk-label.chk-wrap { align-items: flex-start; flex-wrap: wrap; }
label.chk-label a { color: var(--accent-text); font-weight: 600; }

/* Home: tile riepilogo figlio */
.home-child-row { display: flex; align-items: center; gap: 12px; padding: 16px 15px; margin: 0 16px 12px; border-radius: var(--app-radius-lg); box-shadow: var(--app-shadow); cursor: pointer; background: var(--accent-gradient); }
.home-child-row .avatar { background: var(--accent-light); color: var(--accent-dark); }
.home-child-row .label { font-size: 11.5px; color: var(--accent-light); }
.home-child-row .name { font-size: 15px; font-weight: 700; color: var(--accent-light); }
.home-child-row .sub { font-size: 11.5px; color: var(--accent-light); }
.home-child-row .chev { color: var(--accent-light); }

.child-switch { display: flex; gap: 8px; margin-top: 12px; margin-bottom: 14px; }

/* Diario: week strip */
.week-strip { display: flex; align-items: center; gap: 4px; margin: 0 16px 14px; }
.week-arrow { width: 26px; height: 26px; border-radius: 50%; background: var(--color-surface); border: 1px solid var(--color-border); display: flex; align-items: center; justify-content: center; font-size: 12px; color: var(--color-text-3); cursor: pointer; flex-shrink: 0; }
.week-days { flex: 1; display: flex; gap: 5px; }
.week-day { flex: 1; text-align: center; padding: 7px 0; border-radius: 12px; background: var(--color-surface); border: 1px solid var(--color-border); cursor: pointer; }
.week-day .dow { font-size: 9.5px; font-weight: 700; color: var(--color-text-4); }
.week-day .dom { font-size: 14px; font-weight: 700; color: var(--color-text-1); margin-top: 1px; }
.week-day.selected { background: var(--accent-gradient); border-color: transparent; }
.week-day.selected .dow, .week-day.selected .dom { color: #fff; }
.week-day.absent { opacity: .45; }
/* Giorno fuori dalla finestra utile: visibile ma non selezionabile. Quale
   sia la finestra dipende dalla schermata, quindi la direzione è opt-in per
   pannello via data-block-future / data-block-past (v. initWeekPanel in
   common.js): Diario e Presenze bloccano il futuro (compilare una giornata non
   ancora avvenuta non ha senso), "Avvisa la scuola" blocca il passato (si
   annuncia un'assenza che deve ancora avvenire). Una sola classe per
   entrambi i casi: la resa è la stessa, cambia solo chi la riceve. */
.week-day.locked { opacity: .3; cursor: default; }
/* Giorno di CHIUSURA della scuola (Natale, ponti — v. /anno-scolastico nel
   gestionale). Diverso dal .locked, e la differenza dev'essere visibile prima
   di provare a toccarli: il .locked è inerte, questo si SELEZIONA e mostra il
   motivo della chiusura. Tratteggio invece di opacità: dice "c'è qualcosa da
   guardare, ma non è un giorno di scuola" senza spegnere il giorno. */
.week-day.closed { border-style: dashed; border-color: var(--color-text-4); background: var(--color-bg); }
.week-day.closed .dow, .week-day.closed .dom { color: var(--color-text-4); }
/* Giorni interni a un intervallo selezionato (Avvisa la scuola, assenza di
   più giorni): estremi con .selected come sempre, il mezzo con un riempimento
   tenue che li lega senza competere con gli estremi. */
.week-day.in-range { background: var(--accent-light); border-color: var(--accent); }
.week-day.in-range .dow, .week-day.in-range .dom { color: var(--accent-text); }
.week-day .dom::after { content: ''; display: block; width: 50%; height: 4px; border-radius: 10px; background: transparent; margin: 2px auto 0; }
.week-day.absent .dom::after { background: var(--color-danger); }
.week-day.saved .dom::after { background: var(--color-success); }
.week-day.selected.saved .dom::after,.week-day.selected.absent .dom::after { background: #fff; }

/* Pannello settimane: contiene SEMPRE le 8 settimane precedenti + quella di
   oggi, un'unica lista scorrevole (mai due contenitori separati) — così si
   può risalire nel passato e tornare avanti fino a oggi nello stesso
   blocco. Da chiuso è clippato all'altezza di una riga sola, misurata via
   JS (mai una stima statica: l'altezza reale della riga dipende da cosa
   contiene — la riga di Presenze è più bassa di quella del Diario, che ha
   anche lo slider — quindi 46px qui è solo il fallback CSS prima che JS
   corregga la misura al primo showScreen() su questa schermata, v.
   common.js `resizeWeekPanel()`; ricalcolata anche da insegnante.js
   collapseWeekPanel()/expandWeekPanel() a ogni apri/chiudi manuale); da
   aperto ~3 righe (170px).
   `position: relative` NON è cosmetico: rende il pannello l'offsetParent delle
   sue righe, così `row.offsetTop` (usato per calcolare scrollTop in
   common.js/insegnante.js) è relativo al contenuto scorrevole e non a `.screen`
   (position:absolute). Senza, scrollTop clampa sempre al fondo: la settimana
   passata selezionata non viene mostrata e la riga appare tagliata. Non
   rimuoverlo.
   `overflow-y: hidden` da chiuso (correzione 24/07/2026): impedisce lo
   scroll a dito sulla singola riga visibile, che altrimenti poteva
   scostare il pannello dalla settimana corrente/scelta senza passare da
   `expandWeekPanel()`. Si riabilita solo da aperto (selettore `:has()` sul
   toggle adiacente, niente JS aggiuntivo) — lo scrollTop impostato via JS
   in `collapseWeekPanel()`/`expandWeekPanel()` funziona comunque anche con
   overflow:hidden.
   Componente condiviso da insegnante (Diario/Presenze) e genitore
   (Diario/Avvisa la scuola). Direzione delle settimane parametrica via
   `data-weeks-before`/`data-weeks-after` (default 8/0 = passato + oggi;
   Avvisa usa 0/8 = oggi + future). Motore in `common.js`.
   `transition: max-height` per l'apri/chiudi smooth: il ridimensionamento di
   servizio (ingresso schermata) la sopprime, v. `resizeWeekPanel()`. Dentro
   un `.field` (Avvisa) i margini orizzontali sono azzerati per allinearsi
   all'etichetta del campo. */
.week-panel { position: relative; max-height: 46px; overflow-y: hidden; margin: 0 16px 8px; -ms-overflow-style: none; scrollbar-width: none; border: 1px solid var(--color-border);  border-radius: 12px; transition: max-height .25s ease; }
.week-panel:has(+ .week-panel-toggle.open) { overflow-y: auto; }
.week-panel::-webkit-scrollbar { display: none; }
.field .week-panel { margin-left: 0; margin-right: 0; }
.week-panel .week-days { margin-bottom: 6px; }
.week-panel-toggle { display: flex; align-items: center; justify-content: center; width: 50%; height: 24px; margin: 0 auto 16px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 0 0 15px 15px; color: var(--color-text-1); font-size: 20px; cursor: pointer; transform: translateY(-8px);}
.week-panel-toggle.open { transform: translateY(-8px) rotateZ(180deg); border-radius: 15px 15px 0 0; }
.week-panel-toggle .ph {font-weight: 900;}

.diario-child-head { display:flex; align-items:center; gap:11px; margin: 0 16px 14px; padding: 12px 14px; background: var(--color-surface); border-radius: var(--app-radius-lg); box-shadow: var(--app-shadow); cursor:pointer; }
.diario-child-head .n { font-size: 14px; font-weight:700; color:var(--color-text-1); }
.diario-child-head .c { font-size: 11.5px; color:var(--color-text-3); margin-top:1px; }

.diario-row { display:flex; align-items:center; gap:12px; padding:11px 0; border-bottom:1px solid var(--color-bg); cursor:default; }
.diario-row:last-child { border-bottom:0; }
.diario-row.tappable { cursor:pointer; }
.diario-icon { width:34px; height:34px; border-radius:11px; background:var(--accent-light); display:flex; align-items:center; justify-content:center; font-size:16px; flex-shrink:0; }
.diario-body { flex:1; min-width:0; }
.diario-title { font-size:13.5px; font-weight:600; color:var(--color-text-1); }
.diario-desc { font-size:11.5px; color:var(--color-text-3); margin-top:1px; }
.diario-pct { font-size:13px; font-weight:700; color: var(--accent-text); flex-shrink:0; }
.diario-yn { font-size:16px; flex-shrink:0; }
/* Popup inline: apre i dettagli sotto la riga che si tocca, senza cambiare
   schermata. Nato per il menu' della mensa, dal 2026-07-29 e' anche il blocco
   dei delegati (v. spec fase 3B §7) — da qui il nome neutro.
   .menu-popup resta come alias del vecchio nome perche' e' ancora nel markup
   dei mockup app/mockup/genitore.html e app/mockup/insegnante.html, che l'utente mantiene a
   mano e che non vanno toccati: senza l'alias quei popup perderebbero lo stile. */
.inline-popup, .menu-popup { display:none; margin-top:8px; padding:10px 12px; background:var(--accent-light); border-radius:12px; font-size:12px; color:var(--color-text-2); line-height:1.5; }
.inline-popup.open, .menu-popup.open { display:block; }
/* Righe del popup dei delegati: icona + testo, come ogni altra riga dell'app.
   Il telefono resta toccabile ma NON si veste da link del browser — blu e
   sottolineato era l'unico punto dell'app con quell'aspetto (rilievo del
   2026-08-01): qui prende il colore d'accento e il peso del resto del design. */
.inline-popup .ip-row { display:flex; align-items:center; gap:8px; }
.inline-popup .ip-row + .ip-row { margin-top:6px; }
.inline-popup .ip-row i { font-size:14px; width:16px; text-align:center; flex-shrink:0; color:var(--color-text-4); }
.inline-popup a { color:var(--accent-text); font-weight:600; text-decoration:none; }
.diario-note { font-size:12px; color:var(--color-text-2); background:var(--color-bg); border-radius:12px; padding:10px 12px; margin-top:2px; }
/* Trattino di un blocco non ancora inviato (genitore, sola lettura): stesso
   grigio "in sospeso" gia' usato altrove nel diario (.diario-chip.pending,
   .block-row-note-preview.pending) invece del colore d'accento dei valori
   veri — il trattino non deve leggersi come un dato. */
.diario-pct.pending { color: var(--color-text-4); font-weight:600; }
/* Didascalia sotto la card del Diario genitore: spiega il trattino dei blocchi
   non ancora compilati. Secondaria di proposito — informa senza competere coi
   dati (v. spec 3C-2 §7). Font-size e margine come .cal-note/.field-hint: in
   questo file non esistono token di spaziatura né di scala tipografica
   (v. §2 Token), quindi si riusa la stessa scala px già in uso per le
   didascalie della schermata.
   NB: qui c'era la sequenza "--space-<asterisco><slash>", che chiudeva il
   commento in anticipo — il resto del testo finiva nel parser e si portava via
   la regola .diario-hint qui sotto, che non veniva mai applicata. */
.diario-hint {
  margin: 6px 16px 0;
  font-size: 11px;
  color: var(--color-text-4);
  text-align: center;
}

/* Comunicazioni */
.search-box { margin: 0 16px 12px; position: relative; }
.search-box input { width:100%; height:38px; border-radius:160px; border:1px solid var(--color-border); background:var(--color-surface); padding:0 14px 0 34px; font-size:13px; font-family:var(--font-body); outline:none; }
.search-box .ic { position:absolute; left:12px; top:50%; transform:translateY(-50%); font-size:13px; color:var(--color-text-4); }
.chip-row { display:flex; gap:8px; overflow-x:auto; padding: 0 16px 12px; }
.chip-row .chip { flex-shrink:0; }

.comm-card { background: var(--color-surface); border-left: 4px solid var(--color-info); border-radius: var(--app-radius-md); padding: 12px 14px; margin: 0 16px 12px; box-shadow: var(--app-shadow); cursor: pointer; }
.comm-card.warning { border-left-color: var(--color-danger); }
.comm-card.reminder { border-left-color: var(--color-warning); }
.comm-card.birthday { border-left-color: #f6c343; background: #fffaf0; }
/* Non letto: tinta di fondo + pallino accanto al titolo, per distinguerlo
   a colpo d'occhio dalle altre card (prima la differenza stava solo nella
   piccola etichetta "Da leggere", poco visibile scorrendo l'elenco). */
.comm-card.unread { background: var(--accent-light); }
.comm-card.unread .comm-title::before { content: ''; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); margin-right: 6px; vertical-align: middle; }
.comm-kicker { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--color-info); }
.comm-card.warning .comm-kicker { color: var(--color-danger); }
.comm-card.reminder .comm-kicker { color: var(--color-warning); }
.comm-card.birthday .comm-kicker { color: #c9860b; }
.comm-status { margin-left: auto; font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 160px; text-transform: none; letter-spacing: 0; background: var(--color-bg); color: var(--color-text-4); }
.comm-status.unread { background: var(--accent-light); color: var(--accent-text); }
.comm-title { font-size: 14px; font-weight: 700; color: var(--color-text-1); margin-top: 4px; }
.comm-text { font-size: 12.5px; color: var(--color-text-3); margin-top: 3px; line-height: 1.4; }
.comm-data { font-size: 11px; color: var(--color-text-4); margin-top: 2px; }
/* Domanda fissa di "conferma presa visione": distinta dal testo libero
   dell'avviso (.comm-text), cosi' si legge come un prompt del sistema e non
   come farina del sacco di chi ha scritto l'avviso. */
.comm-domanda { font-size: 12.5px; font-weight: 600; color: var(--color-text-2); margin-top: 10px; }
.comm-extra { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--color-border); display: none; }
.comm-card.open .comm-extra { display: block; }
.comm-actions { display: flex; gap: 8px; margin-top: 8px; }
/* Le azioni della card avviso usano .btn-row (bersaglio 44px, principale a
   destra col .btn-grow) invece di .comm-actions: qui serviva solo staccarle dal
   testo. Il gap di 8px di .comm-actions starebbe sotto i 10px minimi fra due
   bersagli adiacenti (APP-DESIGN.md §7.1). */
.comm-card .btn-row { margin-top: 10px; }
/* Riga di risposta si'/no del genitore, una per figlio: il nome a sinistra
   prende lo spazio, i due bottoni restano a destra. */
.comm-resp { display: flex; align-items: center; gap: 10px; margin-top: 10px; font-size: 13px; color: var(--color-text-2); }
.comm-resp .btn-row { margin-top: 0; margin-left: auto; }
/* Messaggio indirizzato alla singola famiglia: si stacca dalle circolari, ed e'
   l'unica evidenza che ha — niente chip dedicata, sono pochi all'anno. */
.comm-card.comm-personal { border-left-color: var(--accent); }
/* Le card dell'app genitore non si aprono: il testo e' gia' tutto a schermo e
   le azioni sono sempre visibili, cosi' "segna come letto" costa un tocco e non
   due. Il cursor:pointer di .comm-card serve al mockup, dove invece si aprono. */
#screen-avvisi .comm-card { cursor: default; }

/* Coppia label/valore dentro una card (insegnante in Classe, titolo/data
   di gita o corso in Attività) + stack di bottoni/popup che segue. */
.stat-value { font-size: 14px; font-weight: 700; color: var(--color-text-1); }
.stat-label { font-size: 12px; color: var(--color-text-3); margin-top: 2px; }
.stat-hint { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--color-text-4); margin-top: 4px; }
.stat-hint:first-of-type { margin-top: 8px; }
.card-stack { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.card-stack .inline-popup, .card-stack .menu-popup { margin-top: 0; }

/* Attività: day-chips (selettore giorno alternativo al week-strip) */
.day-chips { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 2px; }
.day-chip { flex-shrink: 0; width: 46px; padding: 8px 0; text-align: center; border-radius: 12px; background: var(--color-surface); border: 1px solid var(--color-border); cursor: pointer; }
.day-chip .dow { font-size: 10px; color: var(--color-text-4); font-weight: 600; }
.day-chip .dom { font-size: 15px; font-weight: 700; color: var(--color-text-1); }
.day-chip.selected { background: var(--accent-gradient); border-color: transparent; }
.day-chip.selected .dow, .day-chip.selected .dom { color: #fff; }

/* Gallery: grid foto / album */
.album-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 0 16px 6px; }
.album-tile { border-radius: var(--app-radius-md); overflow: hidden; box-shadow: var(--app-shadow); background: var(--color-surface); }
.album-cover { height: 78px; display: flex; align-items: center; justify-content: center; font-size: 26px; }
.album-meta { padding: 8px 10px; }
.album-meta .t { font-size: 12px; font-weight: 700; color: var(--color-text-1); }
.album-meta .s { font-size: 11px; color: var(--color-text-4); }
.photo-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; padding: 0 16px 16px; }
.photo-tile { aspect-ratio: 1; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 22px; }

/* ================================================================
   6. SPECIFICO APP INSEGNANTE
   ================================================================ */

/* Diario/Presenze: riga bambino.
   Presenze usa .roster-row per intero (nome + 3 bottoni stato, un'unica
   scelta per riga: la modifica resta inline). Diario (dal 26/07/2026, terza
   revisione) è organizzato invece **a blocchi**: un blocco per campo
   (Pranzo/Nanna/Cambi/Note, v. .block-tabs sotto) perché la compilazione
   segue le fasce orarie della giornata — a mezzogiorno l'insegnante compila
   il pranzo di venti bambini, non i quattro campi di un bambino. Ogni
   blocco mostra un solo controllo per riga, quindi torna a essere inline: a
   un campo alla volta il vincolo di spazio orizzontale che aveva reso
   necessario un pannello di modifica separato (revisione precedente,
   cronologia in docs/superpowers/specs/2026-07-26-diario-riga-sola-lettura-
   design.md) semplicemente non c'è. */
.roster-row { padding: 12px 0; border-bottom: 1px solid var(--color-bg); }
.roster-row:last-child { border-bottom: 0; }
.roster-name { font-size: 13.5px; font-weight: 600; color: var(--color-text-1); }
.roster-meta { margin-top: 3px; }

/* Selettore di blocco: 4 voci, ognuna con icona del campo + barra di
   avanzamento. **L'icona sta qui e non sulle righe**: dentro un blocco tutte
   le righe hanno lo stesso campo, quindi venti forchette identiche accanto ai
   nomi sarebbero rumore — l'icona serve dove distingue davvero qualcosa (i
   quattro blocchi tra loro), mentre sulla riga il segno che accelera la
   lettura è quello che cambia da riga a riga, cioè l'avatar del bambino.
   La barra (sostituisce il pallino 6px del primo tentativo, illeggibile e di
   significato non immediato — verde e grigio si confondevano a quella
   dimensione): parziale arancione = compilazione in corso, piena arancione =
   tutti compilati ma da inviare, piena verde = inviato, vuota = blocco
   intatto. Il colore resta necessario per separare "tutti compilati" da
   "inviato", che a barra piena sono indistinguibili. */
.block-tabs { display: flex; gap: 6px; margin: 0 16px 14px; }
.block-tab { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 8px 4px; border-radius: 14px; border: 1px solid var(--color-border); background: var(--color-surface); font-family: var(--font-body); cursor: pointer; }
/* Icona e testo sono un <i> e uno <span> senza classe dentro .block-tab
   (v. insegnante.php) — non .block-tab-icon/.block-tab-label, che qui sotto
   non hanno mai avuto un elemento a cui applicarsi: le due righe restavano
   morte, e il colore su tab attiva (bianco su sfondo arancione) non
   scattava mai — icona e testo restavano nel colore scuro ereditato,
   illeggibili. */
.block-tab > i { font-size: 17px; color: var(--color-text-3); line-height: 1; }
.block-tab > span { font-size: 11px; font-weight: 700; color: var(--color-text-3); }
.block-tab.active { background: var(--accent-gradient); border-color: transparent; }
.block-tab.active > i, .block-tab.active > span { color: #fff; }
.block-progress { width: 66%; height: 6px; margin-top: 3px; border-radius: 2px; background: var(--color-bg); overflow: hidden; box-shadow: inset 0 0 1px 1px var(--accent); }
/* Traccia scura sulla linguetta attiva (non il bianco traslucido di prima):
   su sfondo arancione un fondo bianco tenue si confondeva con lo sfondo
   stesso, e il riempimento bianco "in sospeso" spariva quasi del tutto —
   verificato nel Diario con un blocco parzialmente compilato. Una traccia
   scura crea un solco visibile in cui il riempimento bianco risalta, mentre
   il verde "inviato" (esplicito, riga sotto) non ne ha bisogno: era già
   leggibile. */
/* .block-tab.active .block-progress { background: rgba(0,0,0,.18); } */
.block-progress-fill { display: block; height: 100%; width: 0; border-radius: 2px; background: var(--accent); transition: width .25s ease; }
.block-progress-fill.sent { background: var(--color-success); }
.block-tab.active .block-progress { background: transparent; box-shadow: inset 0 0 1px 1px #fff; }

/* Sulla linguetta attiva il verde deve restare distinguibile dal bianco di
"compilato non inviato": --color-success-bg (verde pallido) su fondo
arancione si legge quasi come bianco, quindi qui serve il verde pieno. */
.block-tab.active .block-progress-fill { background: #fff; }
.block-tab.active .block-progress-fill.sent { background: var(--accent-text); }

/* Corpo del blocco: una riga per bambino con il solo controllo del campo
   attivo. Il Pranzo (5 chip) va a due piani (nome sopra, chip sotto a
   piena larghezza, .block-row-2l) perché è l'unico caso che non sta
   accanto al nome sulla stessa riga — "Niente" da sola occupa già 44px.
   Nanna/Cambi/Note stanno su una riga sola. */
.block-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 0; border-bottom: 1px solid var(--color-bg); }
.block-row:last-child { border-bottom: 0; }
.block-row-name { font-size: 13.5px; font-weight: 600; color: var(--color-text-1); flex: 1; min-width: 0; }
/* Avatar del bambino: tornato sulla riga il 26/07/2026 (era stato tolto
   nella revisione a tre colonne "per fare spazio"). Quel motivo è decaduto —
   un solo controllo per riga lascia spazio in abbondanza — ed è il segno che
   fa davvero da àncora nello scorrere venti nomi, a differenza dell'icona di
   campo che dentro un blocco è identica su ogni riga (v. .block-tabs). */
.block-row-head { display: flex; align-items: center; gap: 9px; flex: 1; min-width: 0; }
.block-row-2l .block-row-head { flex: none; }
/* Assente: disattivato, badge al posto del controllo — mai nascosto (v.
   spec §3): sparire dalla lista toglierebbe la conferma che l'assenza è
   registrata. Escluso da conteggi/predefiniti/invio via JS. */
.block-row.absent .block-row-name { color: var(--color-text-4); }
.block-row-2l { flex-direction: column; align-items: stretch; gap: 8px; }
.block-row-2l .block-row-name { flex: none; }

/* Chip Pranzo/Nanna: 1 tap per impostare. Nessuna chip attiva = campo non
   compilato ("—"); ri-toccare la chip attiva la spegne — annullare è
   gratis. Nel blocco Pranzo occupano l'intera riga sotto il nome; nel
   blocco Nanna (.diario-chip-row-inline) restano strette e stanno accanto
   al nome, essendo solo due. */
.diario-chip-row { display: flex; gap: 6px; }
.diario-chip { flex: 1; text-align: center; padding: 10px 2px; border-radius: var(--radius-pill); border: 1px solid var(--color-border); background: var(--color-surface); font-size: 11.5px; font-weight: 700; color: var(--color-text-2); cursor: pointer; user-select: none; }
.diario-chip.active { background: var(--accent-light); border-color: transparent; color: var(--accent-text); }
.diario-chip-row-inline { flex-shrink: 0; }
.diario-chip-row-inline .diario-chip { flex: none; width: 58px; }
/* Micro-feedback di pressione (Task 13): un piccolo "schiacciamento" al tap,
   annullato quasi del tutto da prefers-reduced-motion (regola globale in
   cima al file). */
.diario-chip:active, .btn:active { transform: scale(.96); transition: transform .1s ease; }

/* Cambi: stepper −/+ compatto accanto al nome (non più a piena larghezza:
   qui condivide la riga col nome, non un pannello dedicato). Da "—" il "+"
   porta a 0 — primo valore della scala — e il tap dopo lo alza; il "−" è
   disabilitato sia su "—" sia su 0. Una volta compilato è un normale
   stepper 0–9 e non si torna più a "—" — v. stepDiarioCambi() in
   insegnante.js. */
.block-row-step { flex-shrink: 0; width: 132px; height: 38px; }
.roster-step { display: flex; align-items: center; justify-content: space-between; gap: 2px; min-width: 0; padding: 0 4px; border: 1px solid var(--color-border); border-radius: var(--radius-pill); background: var(--color-surface); user-select: none; }
.roster-step-btn { position: relative; flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 50%; color: var(--accent); font-size: 20px; font-weight: 900; line-height: 1; cursor: pointer; }
.roster-step-btn::after { content: ''; position: absolute; inset: -6px -6px; }
.roster-step-btn.disabled { opacity: .25; pointer-events: none; }
.roster-step-val { flex: 1; text-align: center; font-size: 13px; font-weight: 700; color: var(--accent-text); white-space: nowrap; overflow: hidden; }

/* Note: riga con anteprima troncata (stesso "—" quando vuota) + tap per
   aprire il campo di testo sotto — stessa meccanica a scorrimento del
   pannello della revisione precedente, qui ridotta al solo campo nota.
   È l'unico blocco senza tetto: una nota vuota è un valore valido e
   definitivo (la maggior parte dei bambini non ne ha una la maggior parte
   dei giorni), quindi non richiede "Applica predefiniti" — v. spec §4/§5. */
.block-row-note { flex-direction: column; align-items: stretch; }
.block-row-note-summary { display: flex; align-items: center; gap: 10px; cursor: pointer; }
.block-row-note-preview { flex: 1; min-width: 0; font-size: 12px; color: var(--color-text-1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.block-row-note-preview.pending { color: var(--color-text-4); }
.block-row-note .ph-pencil-simple { color: var(--color-text-4); font-size: 15px; flex-shrink: 0; }
/* Apertura morbida via max-height, **stesso meccanismo di .week-panel** —
   `display:none` non è animabile e `height:auto` nemmeno. Perché funzioni,
   toggleDiarioNote()/saveDiarioNota() NON ri-renderizzano il blocco: si
   limitano a togliere/mettere .open sull'elemento già in pagina, altrimenti
   il nodo verrebbe distrutto e ricreato e non ci sarebbe nulla da animare
   (era il difetto della prima stesura). Il margine del textarea sta dentro
   il contenitore clippato, così a collassato non lascia spazio residuo. */
.block-row-note-editor { max-height: 0; overflow: hidden; transition: max-height .25s ease; }
/* 90px = altezza reale del campo (textarea 64px + padding + margine = 78px)
   più un margine di sicurezza minimo. NON alzare a caso: `max-height` anima
   fino al valore dichiarato, ma il contenuto smette di crescere alla sua
   altezza vera — con un tetto molto più alto (era 200px) l'apertura appare
   conclusa nel primo 40% della durata e il resto anima qualcosa che non si
   vede, facendo sembrare l'animazione scattosa e più veloce del previsto. */
.block-row-note.open .block-row-note-editor { max-height: 90px; }
.block-row-note-editor textarea { width: 100%; margin-top: 10px; border: 1px solid var(--color-border); border-radius: 12px; padding: 10px 12px; font-size: 12.5px; font-family: var(--font-body); color: var(--color-text-1); background: var(--color-bg); }

/* "Applica predefiniti" sta IN CIMA ALLA CARD, non nel footer: con venti
   bambini il flusso è lineare e segue lo scroll — entri nel blocco, applichi
   i predefiniti subito (lo trovi senza scorrere), scendi correggendo le
   eccezioni, e in fondo trovi "Invia". In fondo alla lista sarebbe
   raggiungibile solo dopo aver scorso tutto, cioè dopo il momento in cui
   serve. L'etichetta è generata da JS e dice cosa scrive e su quanti
   («Applica "Tutto" a 3 bambini»), così il predefinito del blocco non va
   ricordato; la riga sparisce quando non resta nulla da riempire. */
.block-defaults { padding-bottom: 12px; margin-bottom: 4px; border-bottom: 1px solid var(--color-bg); }
.block-defaults .btn { width: 100%; }

/* Indicatore di salvataggio del Diario: compare solo quando c'è un dato non
   salvato (v. app/js/insegnante/diario.js). Discreto di proposito — in
   condizioni normali non deve esistere.
   L'unico stato che chiede un'azione — quello in cui nessun ritentativo è
   programmato — mostra un pulsante VERO, non del testo con `cursor: pointer`:
   su un telefono il puntatore non esiste, quindi quel segnale non arriva a
   nessuno. Bersaglio 44px e posizione a destra come ogni azione principale
   dell'app (APP-DESIGN.md §7.1); il testo prende lo spazio che resta, così il
   bersaglio non si sposta al cambiare del messaggio. */
.diario-sync { display: flex; align-items: center; gap: 10px; margin: 4px 16px 0; font-size: 12px; color: var(--color-text-4); }
.diario-sync.is-err { color: var(--color-danger); }
.diario-sync-text { flex: 1; min-width: 0; }
.diario-sync-retry { min-height: 44px; flex-shrink: 0; }

/* Footer: solo "Invia ai genitori" (v. spec §5). Primario, resta
   disattivato finché un bambino presente non ha un valore — niente conferma
   a scatola chiusa, perché a quel punto l'insegnante ha già visto ogni riga
   passando da "Applica predefiniti" sopra. */
.diario-footer-actions { display: flex; flex-direction: column; gap: 8px; margin: 0 16px 14px; }

/* Presenze: DUE bottoni stato (presente/assente), colori sui token
   esistenti. Il terzo pulsante "ritardo" è stato rimosso il 26/07/2026:
   ritardo e uscita anticipata non sono stati alternativi alla presenza ma
   **orari su una presenza**, e possono coesistere nello stesso giorno —
   con tre pulsanti esclusivi "arrivato tardi E uscito prima" era
   inesprimibile. Gli orari stanno in .presenza-orari, sotto. */
.status-btns { display: flex; gap: 6px; flex-shrink: 0; }
.status-btn { position: relative; width: 36px; height: 36px; border-radius: 50%; border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text-4); display: flex; align-items: center; justify-content: center; font-size: 14px; cursor: pointer; }
/* Come .roster-step-btn: area di tocco estesa in verticale a ~44px. Qui i tre
   bottoni distano 6px (gap di .status-btns), quindi nemmeno in orizzontale
   ci sarebbe margine per estendere senza sovrapporli. */
.status-btn::after { content: ''; position: absolute; inset: -4px 0; }
.status-btn.active.st-present { background: var(--color-success-bg); border-color: transparent; color: var(--color-success); }
.status-btn.active.st-absent { background: var(--color-danger-bg); border-color: transparent; color: var(--color-danger); }

/* Orari di ingresso/uscita: riga che si apre toccando il NOME (i due
   pulsanti di stato restano indipendenti, come nel blocco Note del Diario
   dove il tap sulla riga apre e i controlli restano separati). Stessa
   meccanica max-height, e stesso vincolo: togglePresenzaRow() NON
   ri-renderizza, altrimenti la transizione non avrebbe nulla da animare. */
.presenza-nome { cursor: pointer; min-width: 0; }
.presenza-orari { max-height: 0; overflow: hidden; transition: max-height .25s ease; }
.roster-item.open .presenza-orari { max-height: 110px; }
.presenza-orari-campi { display: flex; gap: 10px; padding: 12px 0 4px; }
.presenza-orari-campi label { flex: 1; font-size: 10.5px; font-weight: 700; color: var(--color-text-4); text-transform: uppercase; letter-spacing: .02em; }
.presenza-orari-campi .input { margin-top: 5px; height: 38px; font-size: 13px; }
.presenza-orari-hint { padding: 12px 0 4px; font-size: 12px; color: var(--color-text-4); }

/* "Avvisa la scuola": periodo dell'assenza. Un vero toggle (checkbox
   .toggle, stesso stile di "Notifiche sul telefono"), non più un link a
   pillola — v. DECISIONI.md 2026-08-30. Il caso di gran lunga più frequente
   resta l'assenza di un giorno solo, e attivandolo NON compaiono campi data:
   si continua a toccare i giorni nello stesso pannello settimane, perché
   passare al compilare due date native cambierebbe tipo di interazione a
   metà della stessa scelta. */
.avviso-periodo-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; }
.avviso-periodo-row span { font-size: 13px; font-weight: 600; color: var(--color-text-2); }

/* Storico presenze per bambino: calendario mensile a quadretti + elenco dei
   soli giorni anomali (v. spec). L'elenco cronologico completo che c'era
   prima mostrava soprattutto righe "Presente" identiche — cioè ciò che NON
   varia — e l'unica riga che conta ci si perdeva dentro; stesso principio per
   cui nel Diario l'icona di campo non sta sulle righe.
   **Tre colori, non quattro**, benché gli stati siano quattro (intera,
   ritardo, uscita anticipata, assenza): scorrendo la griglia la domanda è
   "quel giorno c'era tutto o no?", mentre da quale estremo la giornata sia
   stata tagliata e a che ora è dettaglio e vive nell'elenco sotto. Ritardo e
   uscita anticipata sono infatti **annotazioni su una presenza**, non stati
   alternativi: possono coesistere nello stesso giorno. */
.cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.cal-title { font-family: var(--font-head); font-size: 15px; font-weight: 600; color: var(--color-text-1); }
.cal-nav { width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text-2); font-size: 14px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.cal-nav:disabled { opacity: .3; cursor: default; }

.cal-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 5px; }
.cal-dow { text-align: center; font-size: 10px; font-weight: 700; color: var(--color-text-4); text-transform: uppercase; letter-spacing: .02em; padding-bottom: 2px; }
/* Tre stati, tre tinte diverse per famiglia — verde / arancio / rosso — non
   tre gradazioni della stessa. È la scala che si legge senza legenda.
   Due tentativi scartati prima di arrivarci: (1) crema per l'intera, quasi
   indistinguibile dal pesca del parziale, due tinte calde e chiare della
   stessa famiglia su quadretti da ~55px; (2) solo contorno per l'intera, che
   distingueva bene ma **si leggeva come "da compilare"** — in un calendario
   una cella vuota vuol dire che manca qualcosa, non che è andato tutto bene.
   Il riempimento verde dice la cosa giusta e coincide col significato che il
   verde ha già in tutta l'app.
   Resta così anche una regola utile: **pieno = giorno di cui sappiamo com'è
   andato, scarico = giorno non ancora arrivato** (.futuro). */
.cal-day { aspect-ratio: 1; display: flex; align-items: center; justify-content: center; border: 1px solid transparent; border-radius: 10px; font-size: 12.5px; font-weight: 600; }
.cal-day.intera { background: var(--color-success-bg); color: var(--color-success); }
.cal-day.parziale { background: var(--color-warning-bg); color: var(--color-warning); font-weight: 700; }
.cal-day.assente { background: var(--color-danger-bg); color: var(--color-danger); font-weight: 700; }
/* Giorno scolastico non ancora arrivato: presente nella griglia (il mese è
   quello, e vederlo intero dà il senso della proporzione) ma senza stato —
   segnarlo come "intera" sarebbe un dato inventato. */
.cal-day.futuro { background: transparent; color: var(--color-text-4); opacity: .4; }
.cal-day.vuoto { background: transparent; }

.cal-legend { display: flex; gap: 14px; margin-top: 12px; font-size: 11px; color: var(--color-text-3); }
.cal-legend span { display: flex; align-items: center; gap: 5px; }
.cal-swatch { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
.cal-swatch.intera { background: var(--color-success-bg); }
/* Il calendario salta sabato e domenica: se il mese inizia nel weekend
   (maggio 2027 comincia di sabato) la griglia parte dal 3 e sembra che
   manchino dei giorni. Le colonne L M M G V lo dicono già, ma solo a chi le
   guarda: questa riga lo rende esplicito. */
.cal-note { margin-top: 8px; font-size: 11px; color: var(--color-text-4); }
.cal-swatch.parziale { background: var(--color-warning-bg); }
.cal-swatch.assente { background: var(--color-danger-bg); }

/* Righe dell'elenco: riprendono la tinta del quadretto corrispondente nel
   calendario, così la stessa data è rossa lassù e rossa quaggiù e lo sguardo
   passa dall'uno all'altro senza tradurre. Serve perché le due icone
   (`ph-x-circle` e `ph-clock`) sono entrambe tonde e a colpo d'occhio si
   somigliano: a separarle è il colore. Il testo — "Assenza" contro "Ingresso
   09:20" — resta la lettura non cromatica per chi il colore non lo
   distingue, quindi l'informazione non è mai affidata al solo colore.
   Righe non toccabili: niente cursore a mano né tinta al passaggio. */
.storico-riga { cursor: default; }
.storico-riga.assente, .storico-riga.assente:hover { background: var(--color-danger-bg); }
.storico-riga.assente .hub-icon { color: var(--color-danger); }
.storico-riga.parziale, .storico-riga.parziale:hover { background: var(--color-warning-bg); }
.storico-riga.parziale .hub-icon { color: var(--color-warning); }
.storico-riga .meta { color: var(--color-text-2); }

/* Contatori: danno il denominatore, che il solo elenco delle eccezioni
   perderebbe — "3 assenze" non è un'informazione finché non si sa su quanti
   giorni. Conta i giorni scolastici GIÀ TRASCORSI, non quelli del mese. */
.storico-counters { display: flex; gap: 8px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--color-bg); }
.storico-counter { flex: 1; text-align: center; }
.storico-counter .n { font-size: 17px; font-weight: 700; color: var(--color-text-1); }
.storico-counter .l { font-size: 10.5px; color: var(--color-text-4); margin-top: 1px; }

/* Gallery: dropzone upload — mockup statico, nessuna vera upload */
.upload-dropzone { margin: 0 16px 14px; border: 2px dashed var(--color-border); border-radius: var(--app-radius-lg); padding: 34px 20px; text-align: center; color: var(--color-text-4); }
.upload-dropzone i { font-size: 26px; color: var(--accent-text); margin-bottom: 8px; display: block; }
.upload-dropzone .t { font-size: 13px; font-weight: 600; color: var(--color-text-2); }
.upload-dropzone .s { font-size: 11.5px; color: var(--color-text-4); margin-top: 2px; }

/* ================================================================
   6. DESKTOP — login, resetpass e recupero password
   ================================================================
   L'app resta mobile-first: queste regole non toccano il telefono, si
   limitano a raccogliere il contenuto in una colonna centrata quando la
   finestra è più larga di un telefono, invece di stirare un campo email
   su tutta la riga.

   Soglia 481px: il telefono in portrait più largo è 430px (iPhone Pro Max)
   e sotto ~452px una colonna da 420px non avrebbe comunque spazio per
   restringersi — sarebbe cambiato solo il vestito (crema + card) su un
   telefono vero. Un telefono in ORIZZONTALE è largo 844px e prende quindi
   il trattamento desktop: è voluto, è proprio il caso in cui il form a
   tutta larghezza sta peggio. */
@media (min-width: 481px) {

  /* Centratura verticale con margini auto invece di justify-content:center:
     quando il contenuto supera l'altezza della finestra i margini auto si
     annullano, mentre "center" taglierebbe la testa dello scroll.

     Scoped a .auth-screen e non a .screen.active: .screen è anche il
     contenitore delle schermate dell'app, che da desktop finirebbero centrate
     in verticale con mezzo schermo vuoto sopra l'header. La classe esiste
     apposta per tenere insieme le quattro schermate di autenticazione —
     un elenco di id si dimentica alla quinta. */
  .auth-screen.active { display: flex; flex-direction: column; }

  /* Senza flex-shrink:0, su una finestra più bassa del contenuto i figli si
     comprimono (il default è 1) e .login-hero — che ha overflow:hidden —
     mangia la scritta "BabyFox". Lo screen ha già overflow-y:auto: deve
     scorrere, non schiacciare. */
  .auth-screen.active > * { flex-shrink: 0; }
  .auth-screen.active > :first-child { margin-top: auto; }
  .auth-screen.active > :last-child  { margin-bottom: auto; }

  /* Colonna centrata. La card veste il solo form: logo, nome e sottotitolo
     restano fuori, sul crema. Le tre schermate condividono lo stesso markup
     (.login-hero + .login-body), quindi bastano due selettori. */
  .login-hero,
  .login-body { width: 100%; max-width: 420px; margin-left: auto; margin-right: auto; }

  /* Di default questi screen sono bianchi come la card: senza il crema la
     card non si vedrebbe. */
  .auth-screen { background: var(--color-bg); }

  .login-form {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 20px;
    padding: 22px 20px;
    box-shadow: 0 10px 30px rgba(230,111,0,.07);
  }

  /* ── L'app vera (/app) ────────────────────────────────────────
     Niente card qui: le schermate dell'app sono già fatte di card, e
     incorniciarle darebbe card dentro card. Serve solo che smettano di
     stirarsi — una riga di elenco larga 1400px non è più leggibile, è solo
     più lunga da percorrere con l'occhio.

     430px è la misura per cui l'app è disegnata (il telefono più largo),
     quindi da desktop si vede l'app a grandezza naturale, centrata.

     Scoped a body[data-role], che è solo di app/views/app.php: login,
     resetpass e registrazione hanno <body> nudo e restano governati dalle
     regole qui sopra. .screen e .bottombar sono entrambe in position:absolute
     sul viewport (non c'è un contenitore posizionato in mezzo), quindi si
     centrano con left/width e non con margin:auto. */
  body[data-role] .screen {
    left: calc(50% - 215px);
    right: auto;
    width: 430px;
  }
  body[data-role] .bottombar {
    left: calc(50% - 205px);   /* 430 meno i 10px di margine per lato */
    right: auto;
    width: 410px;
  }
}

/* ── Utility spaziature — DEVONO stare in fondo al file ──────────
   Molti componenti (.card, .hub-list, .comm-card...) impostano il
   margin-top a 0 dentro lo shorthand "margin: 0 16px 14px". A parità di
   specificità (una sola classe) vince chi viene dopo nel file: se questi
   utility fossero dichiarati prima del componente a cui si applicano, il
   margin-top del componente li sovrascriverebbe silenziosamente (bug
   reale del 24/07/2026: .mt-xl su .hub-list non produceva alcuno stacco). */
.mb-sm { margin-bottom: 8px; }
.mt-md { margin-top: 16px; }
.mt-lg { margin-top: 18px; }
.mt-xl { margin-top: 28px; }

/* Pagine legali pubbliche (privacy.php, termini.php) — passo 18 */
.legal-page { max-width: 640px; margin: 0 auto; padding: 24px 20px 48px; }
.legal-page h1 { font-family: var(--font-head); font-size: 20px; margin: 0 0 4px; }
.legal-page h2 { font-family: var(--font-head); font-size: 16px; margin: 24px 0 8px; }
.legal-page p, .legal-page li { font-family: var(--font-body); font-size: 14px; line-height: 1.6; color: var(--color-text-1); }
.legal-page .legal-meta { background: var(--color-surface); border-radius: var(--app-radius-lg); box-shadow: var(--app-shadow); padding: 15px; }
