:root {
  --bg: #0e1116;
  --panel: #161b22;
  --panel-2: #1c232d;
  --ink: #e6edf3;
  /* --ink-3 era #5f6b78: 3.1:1 sul pannello, sotto AA per il testo piccolo di
     legende e note. Alzato al minimo che resta "secondario" ma si legge. */
  --ink-2: #9aa7b4;
  --ink-3: #7d8896;
  --line: #232b36;
  --line-2: #2c3642;
  --hover: #232c38;
  --canvas: #10151c;
  --accent: #9fb6d4;
  --c-blue: #4c8dff;
  --c-green: #35c46b;
  --c-yellow: #f5c542;
  --c-orange: #ff8c42;
  --c-red: #ff5252;
  --c-stale: #4a5361;
  /* inchiostro leggibile SOPRA --c-yellow: il giallo cambia luminosità fra i
     temi (chiaro sul dark, ocra sul light) e un testo fisso resterebbe
     illeggibile in uno dei due */
  --on-yellow: #241c00;
  /* sismografo in quiete: bianco pieno con bordo nero, uguale nei due temi —
     è una convenzione di lettura della mappa, non un colore d'interfaccia */
  --c-quiet: #ffffff;
  --c-quiet-line: #000000;
  /* sismografo offline mostrato dalla legenda: grigio scuro in entrambi i temi */
  --c-offline: #343b46;
  /* anello dei sismografi aperti nella sidebar: ciano, l'unica tinta che non
     appartiene a nessuna scala della mappa (livelli, faglie, storici) */
  --c-watch: #22d3ee;
  --shadow: 0 18px 60px rgba(0, 0, 0, .6);
  color-scheme: dark;
}

/* Tema chiaro: di giorno, all'aperto, sul telefono il dark è illeggibile.
   Le variabili erano già tutte qui, quindi il tema è solo un altro set.
   `data-theme` sulla radice (scelta esplicita in Vista) batte il sistema. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #eef1f6;
    --panel: #f7f9fc;
    --panel-2: #ffffff;
    --ink: #10151c;
    --ink-2: #4a5563;
    --ink-3: #6b7684;
    --line: #d7dde6;
    --line-2: #c3ccd8;
    --hover: #e6ebf2;
    --canvas: #ffffff;
    --c-blue: #1f6feb;
    --c-green: #1a7f43;
    --c-yellow: #a67500;
    --c-orange: #c2540a;
    --c-red: #cf222e;
    --c-stale: #97a1ad;
    --c-watch: #0e8fa8;   /* sul chiaro il ciano acceso sbianca */
    --on-yellow: #ffffff;
    --shadow: 0 18px 50px rgba(16, 21, 28, .18);
    color-scheme: light;
  }
}
:root[data-theme="light"] {
  --bg: #eef1f6;
  --panel: #f7f9fc;
  --panel-2: #ffffff;
  --ink: #10151c;
  --ink-2: #4a5563;
  --ink-3: #6b7684;
  --line: #d7dde6;
  --line-2: #c3ccd8;
  --hover: #e6ebf2;
  --canvas: #ffffff;
  --c-blue: #1f6feb;
  --c-green: #1a7f43;
  --c-yellow: #a67500;
  --c-orange: #c2540a;
  --c-red: #cf222e;
  --c-stale: #97a1ad;
  --c-watch: #0e8fa8;   /* sul chiaro il ciano acceso sbianca */
  --on-yellow: #ffffff;
  --shadow: 0 18px 50px rgba(16, 21, 28, .18);
  color-scheme: light;
}
/* le tile scure di CARTO sotto un tema chiaro sarebbero un buco nero: il
   layer viene schiarito. Solo quelle: `base-invert` lo mette il JS sullo
   sfondo che si può rovesciare, perché rovesciare un'ortofoto o una carta del
   rilievo darebbe un negativo fotografico (vedi BASEMAPS in app.js). */
.leaflet-tile-pane { filter: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]).base-invert .leaflet-tile-pane {
    filter: invert(1) hue-rotate(180deg) brightness(1.05);
  }
}
:root[data-theme="light"].base-invert .leaflet-tile-pane {
  filter: invert(1) hue-rotate(180deg) brightness(1.05);
}

/* Chi ha chiesto meno movimento al sistema non vuole cinque cose che pulsano
   dietro a un terremoto. Il colore resta, l'animazione no. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
}
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  background: var(--bg); color: var(--ink);
  font: 14px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif;
  display: flex; flex-direction: column;
}
/* wrap: sotto i ~1300 px la fila (titolo, copertura, stato, ricerca, sei
   pulsanti, e il badge della sessione Claude quando c'è) non entra e sfondava
   in orizzontale, portando in scroll tutta la pagina. Meglio una seconda riga
   in testata che una pagina più larga dello schermo.
   La seconda riga però resta l'ultima spiaggia: prima che si formi, i pulsanti
   escono dalla fila uno alla volta e passano nel menù «Altro» (⚙) — lo fa
   syncHeaderOverflow in app.js, che misura la riga e decide quanti sacrificarne
   (la testata alta il doppio ruba spazio alla carta, che è il pezzo che conta). */
header {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap; row-gap: 6px;
  padding: 8px 14px; background: var(--panel);
  border-bottom: 1px solid var(--line);
}
header h1 { font-size: 16px; margin: 0; letter-spacing: .5px; }
.spacer { flex: 1; }
/* cifre a larghezza fissa: senza, l'orologio si allarga e si stringe a ogni
   secondo e trascina con sé tutto ciò che ha accanto in testata */
#clock {
  display: flex; align-items: baseline; gap: 5px; flex-shrink: 0;
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum";
}
#clock-time, #clock-date { font-size: 14px; letter-spacing: .5px; }
.muted { color: var(--ink-2); }
.small { font-size: 12px; }
/* `a.toggle` per i due indirizzi esterni della testata (diretta YouTube e
   canale Telegram): un link e non un pulsante, ma nella fila deve essere
   indistinguibile dai vicini. */
button.toggle, a.toggle {
  background: var(--panel-2); color: var(--ink); border: 1px solid var(--line-2);
  border-radius: 6px; padding: 5px 12px; cursor: pointer; font-size: 13px;
}
button.toggle:hover, a.toggle:hover { background: var(--hover); }
button.toggle.on { border-color: var(--c-green); color: var(--c-green); }

/* selettore copertura (italia/europa/mondo) */
#mode-select {
  background: var(--panel-2); color: var(--ink); border: 1px solid var(--line-2);
  border-radius: 6px; padding: 4px 8px; cursor: pointer; font-size: 13px;
}
#mode-select:disabled { opacity: .5; cursor: wait; }
#mode-overlay {
  position: fixed; inset: 0; z-index: 1200;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6, 9, 13, .82); backdrop-filter: blur(2px);
}
#mode-overlay.hidden { display: none; }

/* Badge replay: mappa, sismografi e drum sono ancorati all'orologio del server
   (vedi syncClock), quindi in replay disegnano il passato senza nessun segno
   distintivo. Un replay è così indistinguibile dal live — stesse scosse, stessi
   colori — ed è un ottimo modo per allarmarsi per un terremoto di sei mesi fa.
   Da qui il tono d'ambra e il punto che pulsa: devono dare fastidio. */
#replay-badge {
  display: flex; align-items: center; gap: 8px;
  background: rgba(245, 197, 66, .12); color: var(--c-yellow);
  border: 1px solid var(--c-yellow); border-radius: 6px;
  padding: 3px 10px; font-size: 13px;
}
#replay-badge b { letter-spacing: 1px; font-size: 11px; }
#rb-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--c-yellow); animation: rb-pulse 1.4s ease-in-out infinite;
}
@keyframes rb-pulse { 50% { opacity: .2; } }
#rb-time { font-variant-numeric: tabular-nums; color: var(--ink); }
#rb-speed { color: var(--ink-2); }
#rb-bar {
  width: 90px; height: 4px; border-radius: 2px;
  background: rgba(245, 197, 66, .25); overflow: hidden;
}
#rb-fill { display: block; height: 100%; width: 0; background: var(--c-yellow); }
#replay-badge.preroll { opacity: .75; }
#replay-badge.preroll #rb-fill { width: 100%; opacity: .35; }
/* caricamento: l'orologio virtuale è fermo e la barra misura il download.
   Più larga, perché è l'unica cosa da guardare finché i dati non ci sono. */
#replay-badge.loading #rb-time { opacity: .5; }
#replay-badge.loading #rb-bar { width: 140px; }
#replay-badge.loading #rb-fill { transition: width .3s linear; }
/* l'id batte .hidden per specificità: serve il selettore combinato, come già
   fanno i modali qui sotto */
#replay-badge.hidden { display: none; }
#replay-badge { cursor: pointer; }
.rb-live {
  background: transparent; color: var(--c-yellow); cursor: pointer;
  border: 1px solid var(--c-yellow); border-radius: 5px;
  padding: 1px 8px; font-size: 12px; margin-left: 2px;
}
.rb-live:hover { background: rgba(245, 197, 66, .2); }
body.replay header { border-bottom: 1px solid var(--c-yellow); }

/* Pannello replay: due colonne — la lista del Bollettino a sinistra (clic e
   parte), il form manuale a destra per gli istanti non a catalogo. */
.modal-box.rp-box { width: min(760px, 95vw); height: auto; max-height: 92vh; }
.modal-body.rp-body { display: flex; gap: 0; overflow: hidden; }
.rp-col { padding: 12px 14px; overflow-y: auto; min-height: 0; }
.rp-col:first-child { flex: 1; border-right: 1px solid var(--line); min-width: 0; }
.rp-manual { width: 280px; flex: none; }
.rp-catfilter { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-bottom: 10px; }
.rp-catfilter select, .rp-field select, .rp-field input {
  background: var(--bg); color: var(--ink); border: 1px solid var(--line-2);
  border-radius: 5px; padding: 3px 6px; font: inherit; font-size: 13px;
}
.rp-catalog { display: flex; flex-direction: column; gap: 6px; max-height: 48vh; }
.rp-ev {
  display: flex; align-items: baseline; gap: 10px; cursor: pointer;
  padding: 7px 9px; border-radius: 6px; border: 1px solid var(--line-2);
  background: var(--panel-2);
}
.rp-ev:hover { border-color: var(--c-yellow); background: var(--hover); }
.rp-ev .mag {
  font-weight: 700; font-variant-numeric: tabular-nums; min-width: 42px;
  font-family: ui-monospace, monospace;
}
.rp-ev .mag.m2 { color: var(--c-yellow); }
.rp-ev .mag.m3 { color: var(--c-orange); }
.rp-ev .mag.m4 { color: var(--c-red); }
.rp-ev .meta { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.rp-ev .when { font-variant-numeric: tabular-nums; }
.rp-ev .where { color: var(--ink-2); font-size: 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rp-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px;
  font-size: 12px; color: var(--ink-2); }
.rp-actions { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
#rp-go { background: var(--c-yellow); border: none; color: var(--canvas);
  border-radius: 6px; padding: 7px 16px; cursor: pointer; font-weight: 600; }
#rp-go:disabled { opacity: .5; cursor: default; }
#rp-msg.err { color: var(--c-red); }
@media (max-width: 620px) {
  .modal-body.rp-body { flex-direction: column; overflow-y: auto; }
  .rp-manual { width: auto; border-top: 1px solid var(--line); }
  .rp-col:first-child { border-right: none; }
}

#layout { flex: 1; display: flex; min-height: 0; position: relative; }

/* Loader d'avvio: copre mappa e sidebar finché i flussi non sono entrati (la
   rete FDSN si popola in ~1-2 min). Si toglie da solo quando una quota
   significativa di stazioni e' attiva, o su "Mostra comunque", o dopo 120 s. */
#loader {
  position: absolute; inset: 0; z-index: 900;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6, 9, 13, .82); backdrop-filter: blur(2px);
  transition: opacity .35s ease;
}
#loader.gone { opacity: 0; pointer-events: none; }
.loader-box {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  text-align: center; max-width: 320px; padding: 0 20px;
}
.loader-box .spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--line-2); border-top-color: var(--c-blue);
  animation: ldr-spin .9s linear infinite;
}
@keyframes ldr-spin { to { transform: rotate(360deg); } }
.loader-title { font-size: 14px; font-weight: 600; color: var(--ink); }
.loader-sub { font-variant-numeric: tabular-nums; }
.loader-note { line-height: 1.35; }
#loader-skip { margin-top: 4px; }
#map { flex: 1; min-width: 0; background: var(--canvas); }
/* Due sidebar indipendenti: eventi a sinistra, sismografi live a destra.
   Quella di destra esiste solo quando c'è almeno un sismografo aperto
   (classe .collapsed, gestita dal JS). */
#side, #side-left {
  flex: 0 0 auto; background: var(--panel); padding: 6px;
  display: flex; flex-direction: column; gap: 6px; overflow: hidden;
}
#side { width: 420px; border-left: 1px solid var(--line); }
#side-left { width: 340px; border-right: 1px solid var(--line); }
#side.collapsed, #resizer.collapsed { display: none; }
#ev-panel { flex: 1 1 auto; min-height: 0; display: flex;
  flex-direction: column; }
#events { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
#wave-panel { flex: 1 1 auto; min-height: 0; display: flex;
  flex-direction: column; }
/* --chan-base è l'altezza MINIMA di una traccia; con poche tracce aperte il
   JS (fitChanHeights) alza --chan-h fino al doppio, finché tutto sta nella
   sidebar senza scroll. Il CSS tiene la base, il JS solo il moltiplicatore. */
#watches { flex: 1; min-height: 0; overflow-y: auto; --chan-base: 34px; }
#resizer, #resizer-left {
  flex: 0 0 6px; cursor: col-resize; background: var(--line);
  transition: background .15s;
}
#resizer:hover, #resizer.dragging,
#resizer-left:hover, #resizer-left.dragging { background: var(--c-blue); }
/* durante il trascinamento: niente selezione di testo né cursori a caso */
body.resizing { user-select: none; cursor: col-resize; }
body.resizing #map { pointer-events: none; }
.panel {
  background: var(--panel-2); border-radius: 6px; padding: 6px 8px;
}
.panel h2 { font-size: 12px; margin: 0 0 5px; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .8px; }
/* le note dell'intestazione non gridano: sono contesto, non titolo */
.panel h2 .small { text-transform: none; letter-spacing: 0; }
/* la spiegazione per esteso sta nel title: scritta qui costerebbe una riga
   di sidebar per sempre, per dire una cosa che si impara una volta sola */
#wave-help { float: right; cursor: help; font-size: 10px; font-weight: 700;
  width: 14px; height: 14px; line-height: 13px; text-align: center;
  border: 1px solid var(--ink-3); border-radius: 50%; color: var(--ink-2); }
#wave-help:hover { color: var(--ink); border-color: var(--ink-2); }
/* Testata su UNA riga: titolo, finestra, modalità, "?". In fila e non a
   galleggiamento perché con quattro cose il float mandava la modalità a capo
   da sola, e la sidebar perdeva una riga per sempre. A stringersi è la sola
   finestra (l'unica il cui testo cambia lunghezza), con l'ellissi. */
#wave-panel h2 { display: flex; align-items: center; gap: 5px; }
#wave-panel h2 .t { flex: 0 0 auto; }
#wave-window { flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
#wave-panel h2 #wave-help { float: none; flex: 0 0 auto; }
/* Modalità della sidebar, due posizioni sole come il paddle del tremore:
   l'automatismo o l'utente. Piccolo e in grigio finché non lo si guarda —
   dice uno stato, non chiede di essere premuto a ogni apertura. */
#wave-mode { flex: 0 0 auto; display: inline-flex; overflow: hidden;
  border: 1px solid var(--line-2); border-radius: 5px; }
#wave-mode button { appearance: none; border: 0; background: transparent;
  color: var(--ink-3); cursor: pointer; padding: 1px 6px;
  font: 600 10px ui-monospace, monospace; letter-spacing: .3px;
  text-transform: none; }
#wave-mode button + button { border-left: 1px solid var(--line-2); }
#wave-mode button:hover { color: var(--ink); }
#wave-mode button.on { background: var(--accent); color: var(--canvas); }
.hidden { display: none; }

.legend .row { display: flex; align-items: center; gap: 8px; margin: 4px 0; }
.legend input { width: 70px; background: var(--bg); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: 4px; padding: 2px 6px; }
.dot { width: 14px; height: 14px; border-radius: 50%; display: inline-block; }
#th-save { background: var(--c-blue); border: none;
  color: #fff; border-radius: 6px; padding: 5px 14px; cursor: pointer; }
.th-actions { display: flex; align-items: center; gap: 10px; margin-top: 10px; }

.event {
  border-left: 3px solid var(--c-orange); background: var(--panel);
  border-radius: 5px; padding: 5px 7px; margin-bottom: 4px;
  display: flex; gap: 8px; align-items: flex-start;
}
.event .mag { font-size: 16px; font-weight: 600; min-width: 52px; }
/* magnitudo preferita misurata su meno di 3 stazioni: il numero c'è ma non ha
   dispersione dietro. Tratteggio invece di un colore diverso: la scala di
   colore della magnitudo è già occupata dalla severità */
.mag-ind { border-bottom: 1px dotted currentColor; }
.event .det { flex: 1; font-size: 11px; color: var(--ink-2); }
.event .det b { color: var(--ink); }
.event.is-anthro { border-left-color: var(--c-stale); opacity: .78; }
/* il dubbio in grazia non è sbiadito: è ancora una scossa da guardare, solo
   con una stima di cui non fidarsi. Il bordo lo dice in rosso. */
.event.is-doubtful { border-left-color: var(--c-red); opacity: 1; }
/* stato della soluzione: "Probabile falso positivo" (voto E o F), "Soluzione
   dubbia" (C o D consolidata) e "PRELIMINARE" (nessuna revisione passata).
   Stessa forma — pastiglia piena, piccola — perché sono la stessa classe di
   informazione: quanto ci si può appoggiare a questi numeri. Ne compare una
   sola per volta, nell'ordine di gravità qui sopra.
   I colori sono quelli del voto che le motiva (q-badge, sotto): giallo scuro
   come la C per il dubbio, rosso come la E per il probabile falso positivo,
   grigio per l'attesa. Il dubbio in rosso rubava l'unico colore d'allarme
   della GUI a una soluzione che un terremoto ce l'ha, ed era solo mal
   vincolata. */
.sol-badge {
  display: inline-block; border-radius: 4px; padding: 1px 6px;
  font-weight: 700; font-size: 10px; line-height: 15px;
  letter-spacing: .02em; white-space: nowrap;
  vertical-align: 1px; cursor: help;
}
.sol-doubt { color: #1b1f24; background: var(--c-yellow); }
.sol-fp { color: #fff; background: var(--c-red); }
.sol-prelim { color: #fff; background: var(--c-stale); }
/* nella sidebar sta sotto la riga della qualità, rientrata come lei */
.event .ev-sol { margin-top: 3px; }
/* e sotto la qualità sta anche la targhetta di chi ha rivisto (stessa riga
   propria, stesso rientro): vedi renderEvents */
.event .ev-rev { margin-top: 3px; }
/* minimappa dell'epicentro: mosaico di mattonelle ritagliato, con la stellina
   ferma al centro. `overflow:hidden` è la finestra; le immagini dentro sono
   posizionate in pixel dal JS (vedi miniMapHtml). */
.ev-mini { position: relative; flex-shrink: 0; overflow: hidden;
  border-radius: 4px; border: 1px solid var(--line-2);
  background: var(--canvas); }
.ev-mini-tiles { position: absolute; inset: 0; }
.ev-mini-tiles img { position: absolute; width: 256px; height: 256px;
  image-rendering: auto; }
.ev-mini-pin { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -55%);
  color: var(--c-yellow); font-size: 15px; line-height: 1;
  text-shadow: 0 0 3px #000, 0 0 1px #000; pointer-events: none; }
/* le mattonelle scure sotto tema chiaro si schiariscono come quelle della
   carta grande: stessa regola, altro contenitore (non sono in un pane Leaflet) */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]).base-invert .ev-mini-tiles {
    filter: invert(1) hue-rotate(180deg) brightness(1.05);
  }
}
:root[data-theme="light"].base-invert .ev-mini-tiles {
  filter: invert(1) hue-rotate(180deg) brightness(1.05);
}
/* "25 minuti fa" accanto all'orario assoluto: stessa riga, meno peso */
.event .ev-ago { font-weight: 400; color: var(--ink-2); }
.event .ev-q { display: inline-flex; align-items: center; gap: 4px;
  flex-wrap: wrap; }
/* voto da C in giù su soluzione consolidata: la riga della qualità è anche
   l'avviso (prima c'era una seconda riga che ripeteva la lettera) */
.event .ev-q-low { color: var(--c-yellow); font-weight: 600; cursor: help; }
/* il motivo per cui un evento è "sospetto" (qualità scadente, falso positivo) */
.cls { margin-top: 3px; font-size: 12px; font-weight: 600; }
.cls.anthro { color: var(--c-yellow); }
/* la data/ora è l'informazione principale della scheda evento */
.event .ev-when { font-size: 12px; font-weight: 600; color: var(--ink);
  margin-bottom: 2px; }
/* magnitudo ufficiale di catalogo (INGV/EMSC) */
.ingv { color: var(--c-green); font-size: 11px; font-weight: 600; }
a.ingv { text-decoration: none; }
/* conferma dell'Osservatorio Vesuviano (gossip.py): stessa forma della riga
   INGV ma colore diverso — è un'altra fonte, non lo stesso catalogo prima */
.ov { color: var(--c-orange); font-size: 11px; font-weight: 600; }
a.ov { text-decoration: none; }
/* crediti delle fonti esterne, in calce all'elenco eventi: le loro scosse
   stanno nell'elenco insieme alle nostre, e la licenza CC-BY vuole che la
   fonte sia detta da qualche parte visibile. Ogni riga compare solo quando
   nell'elenco c'è almeno una card di quella fonte: un credito senza niente da
   attribuire è rumore, e per giunta lascia credere che quelle scosse ci siano */
#ov-foot, #cat-foot { margin-top: 6px; line-height: 1.4; }
#ov-foot:empty, #cat-foot:empty { display: none; }
/* i tre stadi di revisione: l'automatica è rapida ma provvisoria, e il colore
   lo dice invece di lasciarlo leggere nella parola */
.ov-auto { color: var(--c-yellow); font-size: 10px; font-weight: 600; }
.ov-rev { color: var(--c-orange); font-size: 10px; font-weight: 600; }
.ov-bull { color: var(--c-green); font-size: 10px; font-weight: 600; }
a.ingv:hover { text-decoration: underline; }

/* evento rivisto manualmente: deve saltare all'occhio in ogni elenco */
.rev-badge {
  display: inline-block; color: var(--c-blue); border: 1px solid var(--c-blue);
  border-radius: 4px; padding: 0 5px; font-size: 10px; font-weight: 700;
  line-height: 15px; white-space: nowrap; vertical-align: 1px;
}
/* revisione automatica Claude: stesso peso visivo, colore suo;
   la sola presa visione (nessuna correzione) è verde, la correzione arancio */
.rev-badge.rev-claude { color: var(--c-orange); border-color: var(--c-orange); }
.rev-badge.rev-claude.rev-ok { color: var(--c-green); border-color: var(--c-green); }
/* correzione provata e ripristinata: non è una conferma della soluzione
   (verde) né una correzione attiva (arancio) — vale meno di entrambe */
.rev-badge.rev-claude.rev-undone { color: var(--c-stale); border-color: var(--c-stale); }
/* revisione non necessaria: il triage l'ha fermata prima di aprirla. È un
   "guardato e archiviato", non un giudizio sulla soluzione: tono spento e
   nessun bordo pieno, non deve competere con i badge che raccontano un esame
   vero. Il testo è lungo apposta — qui l'ambiguità costerebbe fiducia */
/* revisione non necessaria: il triage l'ha guardata e ha deciso di non
   aprirla. È comunque un passaggio avvenuto — quindi un badge come gli altri,
   solo grigio: dice "Claude c'è stato" senza rivendicare un esame che non è
   stato fatto. Il perché sta nel tooltip, dove non ruba una riga a ogni
   elenco. */
.rev-badge.rev-claude.rev-skip {
  color: var(--c-stale); border-color: var(--c-stale);
}
.event.is-reviewed { border-left-color: var(--c-blue); }

/* indice di qualità A-F della soluzione (quality.py). Pastiglia PIENA, non a
   bordo come i badge di revisione: quelli dicono chi è passato, questa dice
   quanto vale la soluzione, e le due cose stanno spesso sulla stessa riga —
   devono distinguersi senza leggere. La scala di colore è quella del semaforo
   già usata altrove (verde=buono, rosso=no), così non c'è una legenda nuova
   da imparare. */
.q-badge {
  display: inline-block; margin-left: 5px; border-radius: 4px;
  padding: 0 5px; font-size: 11px; font-weight: 800; line-height: 15px;
  vertical-align: 1px; color: #fff; background: var(--c-stale);
  cursor: help; font-family: ui-monospace, monospace;
}
.q-badge.q-sm { font-size: 10px; padding: 0 4px; }
.q-badge.q-A { background: var(--c-green); }
/* la B è la stessa famiglia della A, smorzata: sono entrambe soluzioni
   di cui ci si può fidare, e due verdi diversi lo dicono meglio di due
   colori diversi */
.q-badge.q-B { background: color-mix(in srgb, var(--c-green) 72%, var(--c-yellow)); }
.q-badge.q-C { background: var(--c-yellow); color: #1b1f24; }
.q-badge.q-D { background: var(--c-orange); }
.q-badge.q-E { background: var(--c-red); }
.q-badge.q-F { background: #8b1a1a; }

/* divisorio fra i due gruppi della sidebar (in attesa di revisione / rivisti):
   una riga sottile con l'etichetta dentro, non un titolo pieno — deve separare
   senza pesare quanto le schede che divide */
.ev-split {
  display: flex; align-items: center; gap: 7px;
  margin: 7px 2px 5px; color: var(--ink-2);
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em;
}
.ev-split::after {
  content: ""; flex: 1; height: 1px; background: var(--line-2);
}
.ev-split:first-child { margin-top: 0; }

/* piede della sidebar eventi: rimando allo storico completo */
.ev-more {
  flex: 0 0 auto; margin-top: 4px; padding: 3px 6px; cursor: pointer;
  color: var(--ink-2); font-size: 12px; text-align: center;
  border: 1px dashed var(--line-2); border-radius: 5px;
}
.ev-more:hover { color: var(--ink); border-color: var(--c-blue); }

/* barra e stati della revisione manuale nel modal evento */
.mrev { display: flex; align-items: center; gap: 6px; margin: 4px 0 8px;
  flex-wrap: wrap; }
/* eventi ripescati dall'archivio: niente revisione (i dati non ci sono più).
   Il combinato serve perché .mrev sta dopo .hidden e a pari specificità
   vincerebbe il display:flex. */
.mrev.hidden { display: none; }
.mrev button.on { background: var(--c-blue); color: #fff; }
#mr-apply { background: var(--c-blue); border: none; color: #fff;
  border-radius: 6px; padding: 4px 12px; cursor: pointer; }
/* Modifiche non ancora applicate: il pulsante passa al giallo e lampeggia.
   In blu era indistinguibile dal suo stato di riposo, e una revisione fatta
   ma mai applicata (che il refresh butterebbe via) non si vedeva. */
#mr-apply.dirty { background: var(--c-yellow); color: var(--on-yellow);
  animation: mr-dirty 1.1s ease-in-out infinite; }
@keyframes mr-dirty { 50% { opacity: .45; } }
/* chi ha chiesto meno animazioni tiene il giallo, senza il lampeggio */
@media (prefers-reduced-motion: reduce) {
  #mr-apply.dirty { animation: none; }
}
/* ricalcolo in corso: i comandi che lo rifarebbero partire restano visibili
   ma spenti — sparire farebbe saltare la barra sotto al puntatore */
.mrev button:disabled { opacity: .45; cursor: default; }
.mrev-busy { display: inline-flex; align-items: center; flex: 0 0 auto; }
.mrev-busy.hidden { display: none; }
.mrev-busy .spinner { width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--line-2); border-top-color: var(--c-blue);
  animation: ldr-spin .9s linear infinite; }
/* chi ha chiesto meno animazioni non vuole un cerchio che gira davanti a un
   ricalcolo lento: resta l'indicatore, senza rotazione (come .tr-busy) */
@media (prefers-reduced-motion: reduce) {
  .mrev-busy .spinner { animation: none; }
}
/* Velo del ricalcolo dopo "Applica": copre TUTTA la scheda, testata compresa.
   Sotto ci sono i numeri della soluzione VECCHIA — leggerli mentre il worker
   li sta rifacendo è peggio che non vederli — e i comandi che rifarebbero
   partire il ricalcolo. Chi vuole comunque guardare i sismogrammi ha il
   pulsante "Nascondi": il ricalcolo prosegue e la barra continua a dirlo. */
.mr-load {
  position: absolute; inset: 0; z-index: 20; border-radius: 10px;
  display: flex; align-items: center; justify-content: center; padding: 16px;
  /* niente backdrop-filter: sotto ci sono venti canvas di sismogrammi e una
     mappa, e sfocarli costa un ricalcolo a ogni fotogramma dei push live */
  background: color-mix(in srgb, var(--bg) 82%, transparent);
}
.mr-load.hidden { display: none; }
.mr-load-card {
  width: min(420px, 94%); background: var(--panel);
  border: 1px solid var(--line-2); border-radius: 10px; padding: 14px 16px;
  box-shadow: 0 18px 60px rgba(0, 0, 0, .5);
}
.mr-load-head { display: flex; align-items: center; gap: 8px; }
.mr-load-head b { font-size: 14px; }
.mr-load .spinner {
  flex: 0 0 auto; width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--line-2); border-top-color: var(--c-blue);
  animation: ldr-spin .9s linear infinite;
}
/* ricalcolo concluso: il cerchio si ferma e dice come è andata (verde = fatto,
   rosso = nessuna soluzione nuova). Il velo resta ancora un paio di secondi:
   quello è il tempo per leggere l'ultima riga, non per credere che stia
   ancora lavorando */
.mr-load.end .mr-load-head .spinner {
  animation: none; border-color: var(--c-green);
}
.mr-load.end.ko .mr-load-head .spinner { border-color: var(--c-red); }
.mr-load-sub { margin: 5px 0 10px; }
/* un passo per riga: l'elenco è la sola cosa che dica quanto manca */
.mr-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; }
.mr-step { display: flex; align-items: center; gap: 8px; font-size: 12px;
  color: var(--ink-2); }
.mr-step-ic { flex: 0 0 auto; width: 14px; height: 14px; display: inline-flex;
  align-items: center; justify-content: center; }
.mr-step .spinner { width: 12px; height: 12px; }
.mr-step.wait { opacity: .5; }
.mr-step.run { color: var(--ink); font-weight: 600; }
.mr-step.done { color: var(--ink); }
.mr-step.done .mr-step-ic { color: var(--c-green); }
/* saltato, non fatto: sugli eventi d'archivio conferma locale e ricattura dei
   sismogrammi non si eseguono, e spacciarle per fatte racconterebbe un calcolo
   che non è avvenuto */
.mr-step.skip { opacity: .45; }
.mr-step.fail { color: var(--c-red); }
.mr-step.fail .mr-step-ic { color: var(--c-red); }
.mr-load-note {
  margin-top: 10px; font-size: 11px; line-height: 1.45; color: var(--ink-2);
  border-left: 2px solid var(--line-2); padding-left: 8px;
}
.mr-load-note.err { color: var(--c-orange);
  border-color: color-mix(in srgb, var(--c-orange) 60%, transparent); }
.mr-load-note.hidden { display: none; }
@media (prefers-reduced-motion: reduce) {
  .mr-load .spinner { animation: none; }
}

/* il margine sinistro lo dà il `gap` di .mw-act, che le sta attorno */
.mwave .mrev-use { display: inline-flex; align-items: center; gap: 3px;
  cursor: pointer; color: var(--ink-2); }
.mwave .mrev-use input { margin: 0; cursor: pointer; }
.mwave.excluded canvas { opacity: .35; }
.mwave.excluded .lbl { opacity: .6; }
/* scheda di una segnalazione altrui: qui la spunta AGGIUNGE una stazione a una
   soluzione che ancora non esiste, e il verso del segnale è l'opposto di
   .excluded — si accende la riga che entra nel conto */
.mwave.using .lbl { color: var(--ink); box-shadow: inset 2px 0 0 var(--c-blue); }
.mwave.using .mrev-use { color: var(--c-blue); }
.mw-pick { color: var(--c-blue); white-space: nowrap; }
/* falso positivo: la conclusione estrema di una revisione. Rosso e con la ✕,
   perché è l'unico comando della barra che CANCELLA */
.mrev-false {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  background: none; border: 1px solid var(--c-red); color: var(--c-red);
  border-radius: 6px; padding: 4px 10px; font: inherit; font-size: 12px;
}
.mrev-false:hover { background: color-mix(in srgb, var(--c-red) 16%, transparent); }
.mrev-x { font-weight: 700; line-height: 1; }
/* motivazione della revisione: larga quanto la riga dei pulsanti, perché la
   si scrive a frasi, non a parole */
.mrev-note-box.hidden { display: none; }
.mrev-note-cap { display: block; margin: 0 0 3px; font-size: 10px;
  text-transform: uppercase; letter-spacing: .5px; color: var(--ink-2); }
.mrev-note {
  display: block; width: 100%; box-sizing: border-box; margin: 0 0 8px;
  padding: 5px 8px; font: inherit; font-size: 12px;
  color: var(--ink); background: var(--bg);
  border: 1px solid var(--line); border-radius: 6px;
}
.mrev-note::placeholder { color: var(--ink-3); }
.mrev-note:focus { outline: none; border-color: var(--c-blue); }
/* sismografo arruolato a mano — col "+" o riacceso fra quelli che la
   soluzione automatica non usava: si distingue da quelli che il sistema ha
   scelto da sé, altrimenti dopo tre aggiunte non si sa più che cosa è cosa */
.mwave.added { outline: 1px dashed var(--c-blue); outline-offset: 2px;
  border-radius: 5px; }
.mw-tag { background: var(--c-blue); color: #fff; border-radius: 3px;
  padding: 0 4px; font-size: 9px; text-transform: uppercase;
  letter-spacing: .4px; }
/* fuori dalla soluzione: la targhetta è grigia come tutto ciò che non pesa
   sulla stima, e il tracciato resta pienamente leggibile — è lì per essere
   confrontato con gli altri, non per essere ignorato */
.mw-unused { background: var(--c-stale); color: #fff; }
.mwave.unused .lbl { color: var(--ink-3); }
.mwave.unused canvas { opacity: .78; }
/* stessa cornice tratteggiata degli aggiunti, ma rossa: il riquadro dice
   "questa riga sta fuori dal conto", il colore dice da che parte sta */
.mwave.unused { outline: 1px dashed var(--c-red); outline-offset: 2px;
  border-radius: 5px; }
/* canali estesi: la stazione diventa un blocco di tre o più tracce, e senza
   una cornice non si vede più dove finisce la stazione e comincia la
   successiva. Stessa cornice di «aggiunto» e «non usato» — l'outline avvolge
   anche le componenti, che sono figlie della .mwave — ma neutra: qui non dice
   uno stato, dice "queste righe sono la stessa stazione". Per questo cede il
   passo alle due cornici che uno stato ce l'hanno: quelle inquadrano già il
   blocco intero, e due riquadri sovrapposti sarebbero solo rumore. */
.mwave.chans-open:not(.added):not(.unused) {
  outline: 1px dashed var(--ink-2); outline-offset: 2px; border-radius: 5px; }
.mw-loading { padding: 4px 2px; }

/* ---- i due spoiler in fondo alla scheda: "parametri secondari" e "storico
   delle revisioni". Erano due righe di testo maiuscoletto una sopra l'altra,
   indistinguibili fra loro e dai titoli delle sezioni aperte: non si capiva
   dove finiva l'una e cominciava l'altra. Ora sono due BARRE, con un filetto
   di separazione sopra e una fascia di colore a sinistra diversa per ciascuna
   — azzurro i parametri (numeri della soluzione), giallo lo storico (la sua
   storia nel tempo). La veste è dichiarata una volta per tutti e due, così non
   possono divergere. ---- */
.m-sec { margin: 12px 0 4px; border-top: 1px solid var(--line);
  padding-top: 10px; }
.m-sec.hidden { display: none; }
.m-sec > summary, .m-hist-tog {
  display: flex; align-items: center; gap: 7px; width: 100%;
  box-sizing: border-box; padding: 6px 9px; border-radius: 6px;
  background: var(--panel-2); border: 1px solid var(--line-2);
  border-left: 3px solid var(--accent);
  font: inherit; font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .6px; color: var(--ink-2); cursor: pointer; text-align: left;
}
.m-sec > summary:hover, .m-hist-tog:hover {
  background: var(--hover); color: var(--ink); }
/* la freccia: quella nativa del <summary> è di un altro disegno e di un'altra
   misura rispetto allo span che lo storico si scrive da sé in JS. Qui si toglie
   e si rifà con lo stesso glifo, così le due barre hanno la stessa freccia */
.m-sec > summary { list-style: none; }
.m-sec > summary::-webkit-details-marker { display: none; }
.m-sec > summary::before, .m-hist-tog #m-hist-caret {
  flex: 0 0 auto; display: inline-block; width: .8em; font-size: 11px;
  line-height: 1; }
.m-sec > summary::before { content: "▸"; }
.m-sec[open] > summary::before { content: "▾"; }
.m-sec > summary { border-left-color: var(--c-blue); }
.m-hist-tog { border-left-color: var(--c-yellow); }

/* I due comandi (terremoti dubbi e soglia di magnitudo) galleggiano a destra
   della riga del titolo come un blocco solo: da float separati andava a capo
   soltanto quello che non ci stava più, e la testata restava sfalsata su due
   righe. Insieme, o stanno accanto al titolo o scendono tutti e due, su una
   riga sola. Senza un contesto di formattazione proprio il float uscirebbe
   dall'h2 finendo sopra alla prima card: a questo serve `flow-root`.
   Le etichette scendono a 11 px perché con la sidebar alla larghezza tipica
   "Magnitudo" e "Nascondi terremoti dubbi" per pochi pixel non ci stavano. */
#ev-panel h2 { display: flow-root; }
#ev-filters { float: right; display: inline-flex; align-items: center;
  gap: 10px; }
#ev-filters .small { font-size: 11px; }
#ev-filter { display: inline-flex; align-items: center; gap: 4px;
  text-transform: none; letter-spacing: 0; cursor: pointer; font-weight: 400; }
#ev-filter input { margin: 0; cursor: pointer; }
#ev-magfilter { display: inline-flex; align-items: center; gap: 4px;
  text-transform: none; letter-spacing: 0; font-weight: 400; }
/* stesso vestito degli altri menu dell'interfaccia (.rp-field select), un
   filo più stretto: qui sta dentro l'intestazione di un pannello */
.mag-min { background: var(--bg); color: var(--ink-2);
  border: 1px solid var(--line-2); border-radius: 5px; padding: 1px 4px;
  font: inherit; font-size: 11px; cursor: pointer; }
.mag-min:hover { color: var(--ink); }
/* soglia attiva: il menu si accende, altrimenti un filtro lasciato acceso
   settimana scorsa è indistinguibile da una GUI che non mostra niente */
.mag-min.on { color: var(--ink); border-color: var(--c-blue); }

/* Compattezza: con 10 stazioni aperte ogni riga sprecata è una traccia in
   meno a schermo. L'intestazione sta su UNA riga (il nome del sito cede per
   primo) e l'etichetta del canale galleggia sul canvas invece di occupare
   una riga propria. */
.watch {
  background: var(--panel); border-radius: 5px; padding: 2px 5px;
  margin-bottom: 3px;
}
.watch .head { display: flex; align-items: center; gap: 6px;
  flex-wrap: nowrap; min-width: 0; line-height: 15px; }
.watch .head b { font-size: 11px; flex-shrink: 0; }
.watch .head .site { min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
/* distanza dall'epicentro: c'è solo sui sismografi aperti dall'automatismo,
   e cede il posto al nome del sito solo quando è vuota */
.watch .head .dist { flex-shrink: 0; color: var(--ink-3); font-size: 10px;
  font-family: ui-monospace, monospace; }
.watch .head .dist:empty { display: none; }
.watch .head button.x {
  background: none; border: none; color: var(--ink-3);
  cursor: pointer; font-size: 14px; padding: 0 2px; flex-shrink: 0;
}
/* selettore dei canali: subito a destra del nome della stazione */
.chansel { margin-left: auto; display: flex; flex-wrap: nowrap; gap: 0 7px;
  justify-content: flex-end; flex-shrink: 0; }
.chanbox { display: inline-flex; align-items: center; gap: 3px; cursor: pointer;
  font-size: 10px; color: var(--ink-2); font-family: ui-monospace, monospace; }
.chanbox input { margin: 0; cursor: pointer; width: 11px; height: 11px; }
.chanbox:hover { color: var(--ink); }

/* asse dei tempi condiviso, appiccicato in cima al pannello che scorre.
   Il padding orizzontale è lo stesso di `.watch`, così le tacche cadono
   esattamente sopra la griglia delle tracce. */
#wave-axis-row { position: sticky; top: 0; z-index: 3; padding: 0 5px 2px;
  background: var(--panel-2); }
#wave-axis-row.hidden { display: none; }
#wave-axis { width: 100%; height: 20px; display: block; cursor: ew-resize; }

.chan { margin: 1px 0; position: relative; }
/* right = PAD_R + 3: il testo si ferma PRIMA della linea di ORA, che
   altrimenti taglia in due l'ultima parola */
.chan .lbl { position: absolute; top: 0; left: 5px; right: 13px;
  display: flex; justify-content: space-between; gap: 8px;
  pointer-events: none; font-size: 10px; line-height: 12px;
  color: var(--ink-2); font-family: ui-monospace, monospace;
  white-space: nowrap; overflow: hidden;
  /* la traccia passa sotto il testo: senza alone diventa illeggibile */
  text-shadow: 0 0 3px var(--canvas), 0 0 3px var(--canvas), 0 0 3px var(--canvas);
}
.chan .lbl span:last-child { flex-shrink: 0; }
.chan canvas { width: 100%; display: block; cursor: pointer;
  height: var(--chan-h, var(--chan-base, 34px));
  background: var(--canvas); border-radius: 4px; }
.chan canvas:hover { outline: 1px solid var(--line-2); }

.event { cursor: pointer; }
.event:hover { background: var(--hover); }

/* ---------- modal evento / drum / soglie ---------- */
#modal, #drum, #tremor, #rp-modal, #snd-modal, #evlist, #help, #sheet,
#pm-modal, #cl-modal, #us-modal, #ovm, #credits, #disc {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center; }
#evlist { z-index: 950; }   /* il dettaglio evento si apre SOPRA l'elenco */
#drum, #tremor { z-index: 1100; }   /* si aprono anche sopra il modal evento */
/* …ma dal drum si apre un evento (clic su un picco): quella scheda è la
   risposta alla domanda appena fatta e deve stare SOPRA il drum, che resta
   aperto sotto. Chiusa la scheda si torna al drum, senza aver perso il posto */
#modal.over-drum { z-index: 1150; }
#rp-modal, #snd-modal, #help, #pm-modal, #cl-modal, #us-modal { z-index: 1200; }   /* si aprono dall'header: sopra al resto */
#sheet { z-index: 1250; align-items: flex-end; }
/* i crediti stanno sopra a tutti: si aprono ANCHE da "Come si legge" (che
   resta aperto sotto, e a cui Esc riporta) e dal foglio "Altro" */
#credits { z-index: 1300; }
/* le avvertenze sopra ai crediti, e sopra a tutto: si aprono da aiuto, crediti
   e foglio "Altro", e alla prima visita fanno da gate a schermo intero — sotto
   a qualunque altra superficie sarebbero un gate scavalcabile */
#disc { z-index: 1400; }
#modal.hidden, #drum.hidden, #tremor.hidden, #rp-modal.hidden,
#snd-modal.hidden, #evlist.hidden, #help.hidden, #sheet.hidden,
#pm-modal.hidden, #cl-modal.hidden, #us-modal.hidden, #ovm.hidden,
#credits.hidden, #disc.hidden { display: none; }

/* ---------- scheda di una segnalazione INGV-OV ---------- */
#ovm-map { height: 260px; border-radius: 6px; border: 1px solid var(--line);
  background: var(--canvas); }
/* velo di attesa su TUTTO il corpo della scheda: qui non c'è niente da
   mostrare prima che arrivi il dettaglio — carta, tabella e sismogrammi sono
   la stessa risposta — e una scheda a metà, con i residui della segnalazione
   aperta prima, si legge come rotta invece che come "sto caricando" */
#ovm .modal-body { position: relative; }
.ovm-load {
  position: absolute; inset: 0; z-index: 6;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  background: var(--panel); color: var(--ink-2); font-size: 13px;
  padding: 0 24px; text-align: center;
}
/* `.hidden` sta più in alto nel foglio e ha la stessa specificità (0,1,0):
   senza questa riga il `display:flex` qui sopra vinceva e il velo non se ne
   andava più. Non si vedeva a scheda intera — sembrava semplicemente vuota —
   ma su telefono, dove la .modal-body scorre, un `inset:0` copre solo il primo
   schermo: restava una fascia opaca alta quanto lo schermo sopra la carta, al
   posto dell'intestazione e dell'avvertenza. Stesso rimedio di a.toggle.hidden
   e di #ovm.hidden qui sotto. */
.ovm-load.hidden { display: none; }
.ovm-load .spinner {
  flex: 0 0 auto;
  width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid var(--line-2); border-top-color: var(--c-blue);
  animation: ldr-spin .9s linear infinite;
}
/* a errore avvenuto il velo resta, ma senza rotella: quello che c'è da dire è
   il motivo, non l'attesa */
.ovm-load.err .spinner { display: none; }
.ovm-load.err { color: var(--c-orange); }
/* l'avvertenza sulla fonte non è una nota a piè di pagina: è la prima cosa
   che va letta, o si prendono i numeri dell'OV per misure nostre */
.ovm-warn {
  margin: 6px 0 10px; padding: 7px 9px; border-radius: 6px; font-size: 12px;
  color: var(--ink-2); line-height: 1.4;
  background: color-mix(in srgb, var(--c-orange) 12%, transparent);
  border-left: 3px solid var(--c-orange);
}
.ovm-warn b { color: var(--ink); }
.ovm-radius { display: inline-flex; align-items: center; gap: 5px; }
.ovm-radius select { background: var(--bg); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: 5px; padding: 2px 5px;
  font: inherit; font-size: 12px; }
#ovm-go { background: var(--c-blue); border: none; color: #fff;
  border-radius: 6px; padding: 4px 12px; cursor: pointer; font: inherit; }
#ovm-go:disabled { opacity: .45; cursor: default; }
/* il messaggio va a riga sua: quando il gate respinge la soluzione elenca i
   motivi (geometria, rms, stazioni), ed è una frase, non una parola */
#ovm-msg { flex: 1 1 100%; line-height: 1.35; }
/* card di una segnalazione nell'elenco eventi: stessa forma delle nostre, ma
   la fonte si vede prima di leggere i numeri */
.event.is-ov { background: color-mix(in srgb, var(--c-orange) 6%, var(--panel)); }
.ov-src {
  background: var(--c-orange); color: #1b1f24; border-radius: 3px;
  padding: 0 4px; font-size: 9px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .4px;
}
.ov-src-line { margin-top: 3px; color: var(--ink-3); }

/* esito della rielaborazione con dati nostri (reprocess.py): la riga che
   toglie la card dall'attesa. Tre colori per tre esiti diversi — l'abbiamo
   localizzata, non ci siamo riusciti, non l'abbiamo ancora potuta guardare —
   perché "rielaborata" da solo non dice niente. */
.qm-line { margin-top: 3px; font-size: 11px; line-height: 1.3; }
.qm-ok { color: var(--c-blue); }
.qm-no { color: var(--ink-3); }
.qm-wait { color: var(--c-yellow); }

/* card e righe degli eventi che l'INGV ha pubblicato e QuakeMon non ha
   rilevato: stessa forma delle nostre e stesso trattamento delle segnalazioni
   INGV-OV — quel che cambia è la fonte, e si legge prima dei numeri. Il verde
   è quello con cui la GUI marca già la magnitudo di catalogo (.ingv). */
.event.is-cat { background: color-mix(in srgb, var(--c-green) 6%, var(--panel)); }
.cat-src {
  display: inline-block; background: var(--c-green); color: #1b1f24;
  border-radius: 3px; padding: 0 4px; font-size: 9px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .4px; white-space: nowrap;
}
.cat-src-line { margin-top: 3px; color: var(--ink-3); }
#evl-rows tr.is-cat {
  background: color-mix(in srgb, var(--c-green) 6%, transparent);
}

/* ---------- rapporti post-mortem INGV ---------- */
.modal-box.pm-box { width: min(860px, 95vw); height: auto; max-height: 92vh; }
#pm-day { max-width: 160px; }
.modal-body.pm-body {
  display: block; overflow-y: auto; padding: 4px 16px 14px;
  font-size: 13px; line-height: 1.45;
}
/* fuori dal corpo che scorre: l'avvertenza sul rapporto non deve poter finire
   sotto due schermate di testo */
.pm-disc {
  flex: 0 0 auto; padding: 8px 16px 10px; line-height: 1.45;
  border-top: 1px solid var(--line); background: var(--panel);
}
.pm-disc b { color: var(--ink-2); }
.pm-body h2 { font-size: 17px; margin: 12px 0 6px; }
.pm-body h3 {
  font-size: 12px; margin: 14px 0 6px; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .8px;
}
.pm-body p { margin: 6px 0; }
.pm-body ul { margin: 6px 0; padding-left: 20px; }
.pm-body li { margin: 3px 0; }
.pm-body code {
  background: var(--hover); border-radius: 4px; padding: 0 4px;
  font-size: 12px;
}
.pm-body table {
  width: 100%; border-collapse: collapse; font-size: 12px; margin: 6px 0;
}
.pm-body th {
  text-align: left; color: var(--ink-3); font-size: 10px;
  text-transform: uppercase; letter-spacing: .6px; font-weight: 600;
  padding: 5px 8px 3px; border-bottom: 1px solid var(--line);
}
.pm-body td { padding: 4px 8px; border-bottom: 1px solid var(--line);
  vertical-align: top; }
/* Badge "Claude sta lavorando": stesso impianto del badge replay (punto che
   pulsa + testo), colore della revisione automatica. Persistente per tutta la
   sessione headless, perché è l'unico modo di sapere che il processo esiste
   prima che finisca e finisca nel ledger. */
#claude-live {
  display: flex; align-items: center; gap: 7px; cursor: pointer;
  background: rgba(255, 146, 43, .12); color: var(--c-orange);
  border: 1px solid var(--c-orange); border-radius: 6px;
  /* 320: ci sta per intero anche l'etichetta più lunga
     ("post-mortem 2026-08-04 · 14 min 27 s"); oltre, tronca con l'ellissi */
  padding: 4px 10px; font-size: 12px; max-width: 320px;
}
#claude-live:hover { background: rgba(255, 146, 43, .22); }
#claude-live.hidden { display: none; }
/* Su desktop il badge non sta più in testata ma sulla carta, in alto a sinistra
   sotto alla pill dell'ultimo evento: in testata era una riga di testo arancione fra
   i comandi di tutti i giorni, e per farci stare "post-mortem 2026-08-04 ·
   14 min 27 s" la fila si ristringeva tutta. Qui è una notifica, che è quello
   che è. Sfondo pieno e ombra: sotto c'è la cartografia, non un pannello. */
#claude-live.cl-on-map {
  background: color-mix(in srgb, var(--panel) 94%, transparent);
  box-shadow: 0 3px 10px rgba(0, 0, 0, .35); backdrop-filter: blur(2px);
  padding: 6px 10px; max-width: min(46vw, 300px);
}
#claude-live.cl-on-map:hover { background: var(--hover); }
#cll-dot {
  flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%;
  background: var(--c-orange); animation: rb-pulse 1.4s ease-in-out infinite;
}
#cll-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-variant-numeric: tabular-nums; }

/* ---------- consumo di Claude ---------- */
.modal-box.cl-box { width: min(720px, 95vw); height: auto; max-height: 92vh; }
.cl-acc { display: flex; align-items: center; gap: 6px; }
#cl-account { max-width: 180px; }
#cl-account:disabled { opacity: .5; cursor: wait; }
.cl-bar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 8px 16px; border-bottom: 1px solid var(--line);
  background: var(--panel);
}
.cl-ctl { display: flex; align-items: center; gap: 6px; }
.cl-ctl select { max-width: 220px; }
.cl-switch { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.cl-switch input { cursor: pointer; }
/* Claude spento: il pannello resta leggibile (i consumi passati contano
   ancora) ma si vede a colpo d'occhio che non partirà più niente */
.cl-bar.off { background: rgba(255, 82, 82, .08); }
.cl-bar.off b { color: var(--c-red); }
.cl-bar select:disabled { opacity: .5; cursor: wait; }
.modal-body.cl-body {
  display: block; overflow-y: auto; padding: 4px 16px 14px; font-size: 13px;
}
.cl-sec {
  font-size: 10px; margin: 14px 0 8px; color: var(--ink-3);
  text-transform: uppercase; letter-spacing: .8px; font-weight: 600;
}
.cl-gauge { margin: 10px 0 12px; }
.cl-gauge .cl-lab {
  display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px;
}
.cl-gauge .cl-lab b { font-size: 13px; }
.cl-gauge .cl-pct {
  margin-left: auto; font-variant-numeric: tabular-nums; font-size: 15px;
  font-weight: 600;
}
.cl-track {
  height: 8px; border-radius: 4px; background: var(--line-2);
  overflow: hidden;
}
.cl-fill { display: block; height: 100%; width: 0; background: var(--c-green);
  transition: width .3s ease; }
/* il colore è la severità della finestra: a colpo d'occhio dice se le
   prossime revisioni partiranno o resteranno a terra */
.cl-lv1 .cl-fill { background: var(--c-green); }
.cl-lv2 .cl-fill { background: var(--c-yellow); }
.cl-lv3 .cl-fill { background: var(--c-orange); }
.cl-lv4 .cl-fill { background: var(--c-red); }
.cl-lv1 .cl-pct { color: var(--c-green); }
.cl-lv2 .cl-pct { color: var(--c-yellow); }
.cl-lv3 .cl-pct { color: var(--c-orange); }
.cl-lv4 .cl-pct { color: var(--c-red); }
.cl-sub { margin-top: 4px; display: flex; gap: 10px; flex-wrap: wrap; }
.cl-tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 8px; margin: 8px 0;
}
.cl-tile {
  border: 1px solid var(--line); border-radius: 7px; padding: 7px 9px;
  background: var(--panel);
}
.cl-tile .v { font-size: 16px; font-variant-numeric: tabular-nums; }
.cl-tile .k {
  font-size: 10px; color: var(--ink-3); text-transform: uppercase;
  letter-spacing: .6px;
}
.cl-note { margin: 6px 0; color: var(--ink-2); font-size: 12px; }
.cl-err {
  border: 1px solid var(--c-orange); border-radius: 7px; padding: 7px 9px;
  color: var(--c-orange); font-size: 12px; margin: 8px 0;
}
.cl-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.cl-table th {
  text-align: left; color: var(--ink-3); font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .6px;
  padding: 5px 8px 3px; border-bottom: 1px solid var(--line);
}
.cl-table td { padding: 4px 8px; border-bottom: 1px solid var(--line);
  vertical-align: top; font-variant-numeric: tabular-nums; }
.cl-table td.ko { color: var(--c-red); }
/* sessioni in corso: stessa tabella delle passate, ma accese — il punto che
   pulsa è lo stesso segnale del badge in intestazione */
.cl-table.cl-live td { color: var(--c-orange); }
.cl-run {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  background: var(--c-orange); margin-right: 6px; vertical-align: 1px;
  animation: rb-pulse 1.4s ease-in-out infinite;
}

.th-body { display: block; }
.d-body { display: block; overflow-y: auto; padding: 0 10px; }
#d-canvas { width: 100%; display: block; background: var(--canvas);
  border-radius: 6px; }
.d-foot { padding: 8px 14px; border-top: 1px solid var(--line); }
/* apre/chiude la nota del pannello vulcani: esiste solo su schermo stretto
   (vedi la media query a fondo foglio), sul desktop la nota sta sempre aperta */
.tr-note-btn { display: none; width: 100%; text-align: left;
  padding: 7px 12px; border: none; border-top: 1px solid var(--line);
  background: var(--panel); color: var(--ink-3); font: inherit; font-size: 12px;
  cursor: pointer; }
.tr-note-btn[aria-expanded="true"] { color: var(--ink-2); }
/* Modal vulcani: SCHERMO PIENO, non una finestra. Qui dentro ci sono quattro
   viste (serie, mappa dei sensori, griglia delle telecamere, bollettino) e
   tutte e quattro vogliono spazio — una griglia di dieci fotogrammi o una
   serie di vent'anni in un riquadro da 560 px non si consultano. */
.modal-box:has(#tr-canvas) { width: 100vw; height: 100dvh; max-height: 100dvh;
  border-radius: 0; border: none; }
.modal-body.tr-body { display: block; padding: 8px 12px; position: relative; }
#tr-canvas { width: 100%; height: 100%; display: block; background: var(--canvas);
  border-radius: 6px; cursor: crosshair; }
/* --- paddle della scala dei valori (lineare / logaritmica) ---
   Appoggiato SOPRA il grafico, all'angolo alto sinistro dell'area disegnata
   (TR_MARGIN: 52 px di asse + 12 px di padding del corpo). Fondo semitrasparente
   e non opaco: sotto ci passa la curva, e coprirla del tutto per due pulsanti
   sarebbe un cattivo affare. Piccolo e in monospazio, come le etichette
   dell'asse: è un comando del grafico, non della testata. */
.tr-paddle { position: absolute; left: 68px; top: 15px; z-index: 2;
  display: inline-flex; overflow: hidden; border-radius: 5px;
  border: 1px solid var(--line-2);
  background: color-mix(in srgb, var(--panel) 82%, transparent); }
/* `display: inline-flex` sta dopo `.hidden` nel foglio e lo vincerebbe: le
   altre viste del pannello nascondono il paddle, e deve sparire davvero */
.tr-paddle.hidden { display: none; }
.tr-paddle button { appearance: none; border: 0; background: transparent;
  color: var(--ink-3); cursor: pointer; padding: 2px 8px;
  font: 600 10px ui-monospace, monospace; letter-spacing: .3px; }
.tr-paddle button + button { border-left: 1px solid var(--line-2); }
.tr-paddle button:hover:not(:disabled) { color: var(--ink); }
.tr-paddle button.on { background: var(--accent); color: var(--canvas); }
/* parametro con segno: il log non si applica e il paddle non finge di poterlo
   fare — resta visibile (dice qual è la scala) ma non si tocca */
.tr-paddle.off { opacity: .45; }
.tr-paddle.off button { cursor: default; }

/* --- lettura del punto sotto il cursore sul grafico del tremore ---
   Riga, pallino e riquadro sono DOM sopra al canvas, non disegno: ridipingere
   la serie a ogni movimento del mouse vorrebbe dire rifare drawTremor (che
   percorre tutti i bucket) sessanta volte al secondo. `pointer-events: none`
   su tutti e tre, o il riquadro si metterebbe fra il cursore e il canvas e lo
   zoom con la rotella smetterebbe di funzionare dove sta il riquadro. */
.tr-cross { position: absolute; width: 1px; pointer-events: none; z-index: 2;
  background: color-mix(in srgb, var(--ink) 45%, transparent); }
.tr-dot { position: absolute; width: 7px; height: 7px; margin: -4px 0 0 -4px;
  border-radius: 50%; pointer-events: none; z-index: 2;
  background: var(--panel); border: 2px solid var(--accent); }
.chart-tip { position: absolute; pointer-events: none; z-index: 3;
  max-width: 320px; padding: 5px 8px; border-radius: 6px;
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  border: 1px solid var(--line-2); box-shadow: var(--shadow);
  color: var(--ink-2); font-family: ui-monospace, monospace; font-size: 11px;
  line-height: 1.45; white-space: nowrap; }
.chart-tip .v { color: var(--ink); font-size: 13px; font-weight: 600; }
/* il dettaglio del parossismo è l'unica riga che può essere lunga: va a capo
   invece di allargare il riquadro fino a coprire mezzo grafico */
.chart-tip .w { white-space: normal; color: var(--ink-3); }
.chart-tip .hidden { display: none; }
.tr-cross.hidden, .tr-dot.hidden, .chart-tip.hidden { display: none; }
.tr-zoom { position: absolute; right: 58px; top: 16px; pointer-events: none;
  background: color-mix(in srgb, var(--panel) 82%, transparent); padding: 1px 6px; border-radius: 4px; }
#tr-volcano, #tr-range, #tr-sensor, #tr-param, #tr-band, #tr-cam-speed,
#tr-cam-step, #tr-cam-date, #tr-cam-time, #tr-t0, #tr-t1 {
  background: var(--panel-2, var(--panel-2));
  color: var(--ink); border: 1px solid var(--line-2); border-radius: 6px;
  padding: 3px 6px; font-size: 12px; }
#tr-sensor { max-width: 200px; }
#tr-sensor.hidden { display: none; }
/* ogni selettore della testata porta la sua etichetta: da soli "ECPN · Cratere
   del Piano" e "tutte le bande" non dicono di che cosa sono il valore */
.tr-field { display: inline-flex; align-items: center; gap: 5px; min-width: 0; }
.tr-field > span { color: var(--ink-2); font-size: 11px;
  text-transform: uppercase; letter-spacing: .04em; white-space: nowrap; }
/* il campo sparisce con il suo selettore (sensore unico, banda unica) */
.tr-field:has(select.hidden) { display: none; }
/* e sparisce anche da sé: `.hidden` da sola non basta, perché sta PRIMA di
   .tr-field nel foglio e a pari specificità l'inline-flex la vinceva — è per
   questo che la casella dei parossismi restava a schermo sui vulcani senza
   parossismi (Vulcano, Vesuvio, Campi Flegrei) */
.tr-field.hidden { display: none; }
/* i parossismi: prima un pulsante grande quanto gli altri comandi della
   serie per un'evidenziazione opzionale del grafico, ora una casella piccola */
.tr-erupt-chk { cursor: pointer; white-space: nowrap; }
.tr-erupt-chk input { width: 14px; height: 14px; margin: 0; cursor: pointer; }
/* La testata sta su DUE righe: sopra la serie (vulcano e comandi del grafico),
   sotto i tre pulsanti che cambiano vista. Sono cose di natura diversa — i
   secondi restano sempre raggiungibili, i primi spariscono quando la vista
   aperta non disegna nessuna serie (vedi #tr-series-ctl.hidden). */
/* `flex-wrap: nowrap` non è ornamentale: `.modal-head` va a capo (serve alle
   altre testate), ma qui la direzione è `column`, e una colonna che va a capo
   non impagina più le righe sulla larghezza del pannello — le manda in
   colonne affiancate, larghe quanto il loro contenuto. Con gli indicatori
   INGV accesi la testata diventava più larga dello schermo e la ✕ finiva
   fuori a destra. */
.modal-head.tr-head { flex-direction: column; align-items: stretch; gap: 6px;
  flex-wrap: nowrap; }
.tr-head-row { display: flex; align-items: center; gap: 10px;
  flex-wrap: wrap; min-width: 0; }
.tr-ctl { display: contents; }
.tr-ctl.hidden { display: none; }
#tr-sub, #tr-progress { white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; min-width: 0; }
#tr-sub { flex: 1 1 auto; }
/* Attesa: lo spinnerino in testata mentre una qualunque sorgente del pannello
   sta scaricando, il velo grande sul grafico ancora vuoto. Stesso disegno del
   velo della scheda evento (.m-map-load), stessa animazione (ldr-spin). */
.tr-busy { display: inline-flex; align-items: center; flex: 0 0 auto; }
.tr-busy.hidden { display: none; }
.tr-busy .spinner { width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--line-2); border-top-color: var(--c-blue);
  animation: ldr-spin .9s linear infinite; }
.tr-load { position: absolute; inset: 8px 12px; z-index: 2; border-radius: 6px;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  background: color-mix(in srgb, var(--canvas) 88%, transparent);
  color: var(--ink-2); font-size: 12px; pointer-events: none; }
.tr-load.hidden { display: none; }
.tr-load .spinner { width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid var(--line-2); border-top-color: var(--c-blue);
  animation: ldr-spin .9s linear infinite; }
/* chi ha chiesto meno animazioni non vuole due cerchi che girano in eterno
   davanti a un archivio lento: resta l'indicatore, senza rotazione */
@media (prefers-reduced-motion: reduce) {
  .tr-busy .spinner, .tr-load .spinner { animation: none; }
}
/* mappa dei sensori: stessa area del grafico, mostrata al posto suo — il
   modal copre la mappa principale, quindi "dove sono i sensori" va risposto
   qui dentro, non dietro al fondale */
#tr-map { position: absolute; inset: 8px 12px; border-radius: 6px;
  background: var(--canvas); z-index: 1; }
#tr-map.hidden { display: none; }
/* telecamere: stessa area del grafico e della mappa, una griglia che si adatta
   al numero di camere accese. Il fotogramma porta già data, ora e sigla
   impresse dall'INGV, quindi la didascalia sotto dice solo il sito e l'età. */
.tr-cams { position: absolute; inset: 8px 12px; z-index: 1;
  display: flex; flex-direction: column; gap: 8px; }
.tr-cams.hidden { display: none; }
.tr-cam-grid { flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: grid; gap: 8px; align-content: start;
  /* min-content: senza, la riga si dimensiona prima che l'immagine abbia
     applicato il suo aspect-ratio e la didascalia finisce fuori dal riquadro */
  grid-auto-rows: min-content;
  /* a schermo pieno 240 px davano sei colonne di francobolli: su una termica
     il bagliore del cratere è una macchia di pochi pixel, e va vista */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr)); }
/* Barra del tempo, in tre righe (prima erano tutti i comandi in una fila
   sola, senza etichette — vedi la revisione del 2026-08-07): QUANDO ci si
   trova, il TIMELAPSE (con i salti rapidi "ultime N ore") e la POSIZIONE
   fine, che è lo slider — il comando con cui si naviga davvero fra i
   fotogrammi vicini, gli altri sono per la precisione o per i salti grandi. */
.tr-cam-bar { flex: 0 0 auto; display: flex; flex-direction: column; gap: 6px;
  padding: 6px 8px; border: 1px solid var(--line-2);
  border-radius: 6px; background: var(--canvas); }
.cam-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* l'etichetta di riga, stesso trattamento delle etichette PARAMETRO/SENSORE
   in testata: dice a cosa serve il gruppo senza un titolo ingombrante */
.cam-cap { color: var(--ink-2); font-size: 11px; text-transform: uppercase;
  letter-spacing: .04em; white-space: nowrap; }
.cam-quick { display: inline-flex; gap: 4px; }
.cam-quick-btn { padding: 4px 10px; font-size: 12px; }
.cam-quick-btn:disabled { opacity: .35; cursor: default; }
.cam-row-slider #tr-cam-slider { flex: 1 1 220px; min-width: 140px;
  accent-color: var(--warn, #e8a33d); }
.cam-row-slider #tr-cam-when { white-space: nowrap; }
/* in diretta non c'è niente da scorrere: i comandi del timelapse restano
   visibili (dicono che cosa si può fare) ma spenti — i salti rapidi no,
   sono proprio il modo in cui si esce dalla diretta */
.tr-cam-bar.live #tr-cam-prev, .tr-cam-bar.live #tr-cam-play,
.tr-cam-bar.live #tr-cam-next, .tr-cam-bar.live #tr-cam-speed,
.tr-cam-bar.live #tr-cam-step, .tr-cam-bar.live #tr-cam-slider,
.tr-cam-bar.live #tr-cam-time { opacity: .45; }
.tr-cam { display: flex; flex-direction: column;
  border: 1px solid var(--line-2); border-radius: 6px; overflow: hidden;
  background: var(--canvas); cursor: zoom-in; }
/* contain, non cover: le termiche hanno un fotogramma di forma diversa dalle
   visibili e ritagliarle toglierebbe proprio i bordi del cratere */
.tr-cam img { width: 100%; display: block; aspect-ratio: 1024 / 624;
  object-fit: contain; background: var(--canvas); }
.tr-cam .cap { display: flex; gap: 6px; align-items: baseline;
  padding: 3px 6px; font-size: 11px; color: var(--ink-3); }
.tr-cam .cap b { color: var(--ink); font-weight: 600; }
.tr-cam .cap .age { margin-left: auto; white-space: nowrap; }
/* la stantia si vede: un fotogramma di venti minuti fa su un vulcano in
   eruzione è un'informazione vecchia travestita da diretta */
.tr-cam.stale img { filter: saturate(.4); }
.tr-cam.stale .cap .age { color: var(--warn, #e8a33d); }
.tr-cam.off { opacity: .5; cursor: default; }
/* il segnaposto vale per due casi diversi: la telecamera ferma adesso, e il
   buco d'archivio (di quell'istante non è rimasto niente sul sito INGV) */
.tr-cam .ph { aspect-ratio: 1024 / 624; display: flex; align-items: center;
  justify-content: center; font-size: 11px; color: var(--ink-3); }
.tr-cam.big .ph { aspect-ratio: auto; flex: 1 1 auto; }
/* Ingrandita: resta sola nel riquadro e lo riempie tutto. Non basta allargarla
   a tutte le colonne — il pannello è alto ~380 px e un'immagine da 70vh ne
   uscirebbe fuori con la didascalia. Qui l'altezza è quella del contenitore,
   che è definita (inset assoluto), e l'immagine ci si adatta dentro. */
.tr-cams.zoom .tr-cam-grid { display: block; }
.tr-cams.zoom .tr-cam:not(.big) { display: none; }
.tr-cam.big { height: 100%; cursor: zoom-out; }
.tr-cam.big img { flex: 1 1 auto; height: auto; min-height: 0;
  aspect-ratio: auto; object-fit: contain; }
/* Le miniature di Skyline sono 344x193 per scelta loro: ingrandite a tutto il
   pannello diventano una macchia di pixel che non dice più niente. Restano
   dentro un tetto (~2.5x), centrate, e chi vuole il dettaglio ha il
   collegamento al loro video in didascalia. */
.tr-cam.thumb.big img { max-width: 880px; align-self: center; }
/* Video, player del sito e timelapse riempiono il riquadro come il fotogramma:
   la telecamera ingrandita è sempre la stessa cosa, cambia solo cosa ci scorre
   dentro. Il cursore torna normale — dentro un video ci sono dei comandi, e
   "zoom-out" farebbe credere che ogni clic chiuda tutto. */
.tr-cam .cam-video, .tr-cam .cam-lapse {
  flex: 1 1 auto; min-height: 0; width: 100%; border: 0;
  background: #000; cursor: default; }
.tr-cam .cam-video { object-fit: contain; }
/* il flusso caduto non resta un rettangolo nero muto: il perché sta in calce */
.tr-cam .cam-video.failed { opacity: .35; }
.tr-cam .cam-lapse { display: flex; flex-direction: column; gap: 4px; }
.tr-cam .cam-lapse video { flex: 1 1 auto; min-height: 0; width: 100%;
  object-fit: contain; background: #000; }
.tr-cam .cam-lapse select { flex: 0 0 auto; align-self: flex-start;
  margin: 0 6px 4px; font-size: 11px; background: var(--panel-2);
  color: var(--ink); border: 1px solid var(--line-2); border-radius: 4px;
  padding: 2px 4px; }
/* I modi stanno in didascalia e non in una barra propria: sono una proprietà
   di QUELLA telecamera (non tutte hanno un video), e vanno letti accanto al
   nome di chi la possiede — che è il motivo per cui i modi sono diversi. */
.tr-cam .cap .modes { display: inline-flex; gap: 4px; margin-left: 6px; }
.tr-cam .cap .mode { font: inherit; font-size: 10px; line-height: 1.5;
  padding: 0 5px; border-radius: 4px; cursor: pointer; text-decoration: none;
  border: 1px solid var(--line-2); background: var(--panel-2);
  color: var(--ink-3); }
.tr-cam .cap .mode:hover { color: var(--ink); border-color: var(--line); }
.tr-cam .cap .mode.on { color: var(--ink); border-color: var(--warn, #e8a33d); }
/* Bollettino LGS: stessa area del grafico, ma qui non c'e' una serie da
   disegnare — c'e' una SCHEDA. Il bollettino esce una volta al giorno e porta
   quindici campi, quindi si legge come una tabella: parametro, livello sulla
   scala a quattro colori del LGS (le stesse classi .tr-alert degli allarmi
   INGV), valore con unita', e a destra l'andamento degli ultimi mesi. */
.tr-lgs { position: absolute; inset: 8px 12px; z-index: 1; overflow-y: auto;
  font-size: 12px; }
.tr-lgs.hidden { display: none; }
.tr-lgs .lgs-head { display: flex; align-items: baseline; gap: 10px;
  flex-wrap: wrap; margin-bottom: 8px; }
.tr-lgs .lgs-head .idx { font-size: 13px; }
/* l'indice e' il giudizio complessivo: piu' grande degli altri, perche' e' la
   riga che si guarda per prima */
.tr-lgs .lgs-head .tr-alert { font-size: 12px; padding: 3px 10px; }
.tr-lgs .lgs-head .when { color: var(--ink-3); }
.tr-lgs .lgs-head a { color: var(--ink-3); }
.lgs-row { display: grid; align-items: center; gap: 8px;
  grid-template-columns: minmax(120px, 1.1fr) 88px minmax(74px, .7fr)
    minmax(60px, 1.6fr);
  padding: 2px 0; border-top: 1px solid var(--line-2); }
.lgs-row:first-of-type { border-top: 0; }
.lgs-row .lab { color: var(--ink-2); }
.lgs-row .val { text-align: right; font-variant-numeric: tabular-nums;
  color: var(--ink); }
.lgs-row .val .u { color: var(--ink-3); font-size: 11px; margin-left: 2px; }
/* il parametro senza numero (tremore, infrasuono: il LGS li classifica ma non
   li quantifica) non ha ne' valore ne' andamento: la riga resta, vuota */
.lgs-row .val.na, .lgs-row .lab.na { color: var(--ink-3); }
/* barra di scelta del giorno: l'archivio e' profondo vent'anni, e senza un
   calendario l'unica cosa raggiungibile sarebbe l'ultimo bollettino */
.lgs-daybar { display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  margin-bottom: 8px; }
.lgs-daybar input[type="date"] { font: inherit; color: var(--ink);
  background: var(--canvas); border: 1px solid var(--line-2);
  border-radius: 4px; padding: 2px 6px; }
.lgs-daybar .toggle { padding: 2px 8px; }
.lgs-daybar .toggle:disabled { opacity: .35; cursor: default; }
/* intestazione delle colonne: dice a che periodo si riferiscono le curve, che
   e' la prima cosa da sapere per leggerle */
.lgs-cols { color: var(--ink-3); font-size: 11px; border-top: 0;
  padding-bottom: 2px; }
.lgs-cols span:nth-child(2), .lgs-cols span:nth-child(3) { text-align: center; }
.lgs-cols span:nth-child(3) { text-align: right; }
.lgs-cols .axis { display: flex; justify-content: space-between;
  align-items: baseline; gap: 6px; }
.lgs-cols .axis .mid { color: var(--ink-3); opacity: .75; }
.lgs-row svg { display: block; width: 100%; height: 22px; overflow: visible; }
/* il giorno a video, marcato sulle curve: con un bollettino d'archivio non e'
   piu' il bordo destro, e senza il segno non si saprebbe dove cade */
.lgs-row svg .sel { stroke: var(--ink-3); stroke-width: 1;
  stroke-dasharray: 2 2; vector-effect: non-scaling-stroke; opacity: .8; }
.lgs-row svg polyline { fill: none; stroke: var(--accent, #5aa9e6);
  stroke-width: 1.2; vector-effect: non-scaling-stroke; }
.lgs-row svg .last { fill: var(--accent, #5aa9e6); }
.lgs-sec { margin: 10px 0 6px; color: var(--ink-3); font-size: 11px;
  text-transform: uppercase; letter-spacing: .04em; }
/* elicorder: il PNG del LGS e' 850x600 e porta gia' impressi stazione, canale
   e ora UTC, quindi qui non serve didascalia — solo i due selettori */
.lgs-heli { display: flex; flex-direction: column; gap: 6px; }
.lgs-heli .bar { display: flex; gap: 6px; align-items: center;
  flex-wrap: wrap; }
.lgs-heli img { width: 100%; max-width: 850px; display: block;
  aspect-ratio: 850 / 600; object-fit: contain; background: #ececec;
  border: 1px solid var(--line-2); border-radius: 6px; }
/* ---- bollettini e comunicati INGV-OE (vedi quakemon/bollettini.py) ----
   Qui non c'e' niente da graficare: sono DOCUMENTI, e un documento si sfoglia.
   Due colonne — l'elenco a sinistra, il PDF aperto a destra — perche' su questo
   archivio si naviga per confronto (la settimana prima, il comunicato del
   giorno del parossismo) e un elenco che sparisce a ogni apertura obbligherebbe
   a tornare indietro ogni volta. */
.tr-bull { position: absolute; inset: 8px 12px; z-index: 1; display: flex;
  gap: 10px; font-size: 12px; min-height: 0; }
.tr-bull.hidden { display: none; }
.tr-bull .bull-list { display: flex; flex-direction: column; gap: 6px;
  flex: 0 0 330px; min-height: 0; }
.bull-filters { display: flex; gap: 4px; flex-wrap: wrap; }
.bull-filters .toggle { padding: 2px 8px; font-size: 11px; }
/* il conteggio per collana sta nel pulsante: dice quanto c'e' prima di
   filtrare, che e' l'unica cosa che serve sapere per decidere se filtrare */
.bull-filters .toggle .n { color: var(--ink-3); margin-left: 4px; }
.bull-filters .toggle.on .n { color: inherit; opacity: .75; }
.bull-items { overflow-y: auto; min-height: 0; flex: 1;
  border: 1px solid var(--line-2); border-radius: 6px; }
/* separatore d'anno: l'archivio e' profondo otto anni e senza un appiglio
   scorrere 500 righe tutte uguali non porta da nessuna parte */
.bull-year { position: sticky; top: 0; z-index: 1; padding: 3px 8px;
  background: var(--panel); border-bottom: 1px solid var(--line-2);
  color: var(--ink-3); font-size: 11px; letter-spacing: .04em; }
.bull-item { display: flex; align-items: baseline; gap: 8px; width: 100%;
  padding: 4px 8px; border: 0; border-top: 1px solid var(--line-2);
  background: none; color: var(--ink); font: inherit; text-align: left;
  cursor: pointer; }
.bull-item:first-child { border-top: 0; }
.bull-item:hover { background: color-mix(in srgb, var(--ink) 7%, transparent); }
.bull-item.on { background: color-mix(in srgb, var(--accent) 22%,
  transparent); }
.bull-item .day { font-variant-numeric: tabular-nums; color: var(--ink-2);
  flex: 0 0 auto; }
.bull-item .kind { flex: 1; color: var(--ink-3); font-size: 11px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* il comunicato e' l'ALLARME e non il resoconto: si distingue a colpo d'occhio
   dalla riga settimanale, o in elenco si perderebbe fra le altre */
.bull-item.comunicato .kind { color: var(--c-orange); }
.bull-item .dot { flex: 0 0 auto; font-size: 9px; color: var(--ink-3);
  opacity: .7; }
/* pallino pieno = il PDF e' gia' su disco e si apre all'istante; vuoto = va
   ancora chiesto all'INGV. I comunicati sono sempre pieni (li raccoglie
   l'harvester), i bollettini si riempiono man mano che si aprono */
.bull-item.cached .dot { color: var(--c-green); opacity: 1; }
.bull-doc { flex: 1; min-width: 0; display: flex; flex-direction: column;
  gap: 6px; }
.bull-doc .bar { display: flex; gap: 8px; align-items: baseline;
  flex-wrap: wrap; }
.bull-doc .bar .t { color: var(--ink); }
.bull-doc .bar a { color: var(--ink-3); }
/* il ritorno all'elenco serve solo dove l'elenco si e' fatto da parte: sul
   desktop e' li' a sinistra, e un «‹ elenco» accanto sarebbe rumore */
.bull-doc .bar .back { display: none; padding: 2px 8px; font-size: 11px; }
.bull-doc iframe { flex: 1; width: 100%; min-height: 0; border: 1px solid
  var(--line-2); border-radius: 6px; background: var(--canvas); }
.bull-doc .empty { color: var(--ink-3); margin: auto; text-align: center;
  max-width: 40ch; }
/* Schermo stretto: le due colonne non ci stanno affiancate. L'elenco prende
   tutta la larghezza finche' non si apre un documento; aperto quello, passa
   lui in primo piano e si torna con «‹ elenco». */
@media (max-width: 900px) {
  .tr-bull { flex-direction: column; }
  .tr-bull .bull-list { flex: 1 1 auto; }
  .tr-bull.doc .bull-list { display: none; }
  .tr-bull:not(.doc) .bull-doc { display: none; }
  .bull-doc .bar .back { display: inline-block; }
}
/* ---- dispersione delle ceneri (mappe TEPS dell'INGV-OE) ----
   Stessa impaginazione della griglia telecamere — è la stessa cosa, una
   parete di immagini con una barra sopra — con due differenze volute: la
   riga di avvertenza in cima, che non si nasconde mai, e riquadri più larghi,
   perché queste sono carte con toponimi e una scala da leggere. */
.tr-ash { position: absolute; inset: 8px 12px; z-index: 1;
  display: flex; flex-direction: column; gap: 8px; }
.tr-ash.hidden { display: none; }
/* L'avvertenza: sta in cima e ha il colore dell'allarme perché la lettura
   sbagliata di questa scheda (una macchia rossa presa per un'osservazione) è
   il difetto peggiore che possa produrre. Non si comprime e non si chiude. */
.ash-warn { flex: 0 0 auto; font-size: 12px; line-height: 1.35;
  color: var(--ink-2); padding: 6px 10px; border-radius: 6px;
  border: 1px solid color-mix(in srgb, var(--c-orange) 55%, var(--line-2));
  background: color-mix(in srgb, var(--c-orange) 12%, transparent); }
.ash-warn b { color: var(--c-orange); }
.ash-bar { flex: 0 0 auto; display: flex; align-items: center; gap: 6px;
  flex-wrap: wrap; padding: 6px 8px; border: 1px solid var(--line-2);
  border-radius: 6px; background: var(--canvas); }
.ash-bar button:disabled { opacity: .35; cursor: default; }
.ash-grid { flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: grid; gap: 8px; align-content: start; grid-auto-rows: min-content;
  /* 320 px: sotto, i toponimi della carta INGV non si leggono più e il
     riquadro diventa una macchia di colore senza geografia; sopra, su uno
     schermo largo le otto finestre non stanno in due righe e la giornata si
     guarda a scorrimento invece che a colpo d'occhio */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); }
.ash-cell { display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--line-2); border-radius: 6px;
  background: var(--canvas); cursor: zoom-in; }
/* le mappe sono quadrate (932x920) e vanno mostrate intere: un ritaglio
   toglierebbe la scala in g/m² o la legenda dei colori */
.ash-cell img { width: 100%; display: block; aspect-ratio: 932 / 920;
  object-fit: contain; background: var(--canvas); }
.ash-cell .ash-cap { display: flex; gap: 8px; align-items: baseline;
  padding: 3px 6px; font-size: 11px; color: var(--ink-3); }
.ash-cell .ash-cap b { color: var(--ink); font-weight: 600; }
.ash-cell .ash-cap .muted { margin-left: auto; white-space: nowrap; }
/* la finestra che contiene ADESSO: fra otto mappe è quella che si cerca */
.ash-cell.now { border-color: var(--accent); }
.ash-cell.off { cursor: default; opacity: .6; }
/* segnaposto della finestra assente: tiene il posto con la forma della mappa,
   così le altre non scorrono su di lei e il buco si vede per quello che è */
.ash-ph { display: flex; align-items: center; justify-content: center;
  aspect-ratio: 932 / 920; }
/* Ingrandimento: dentro al pannello, non un modale suo (vedi index.html). */
.ash-big { position: absolute; inset: 0; z-index: 6; display: flex;
  flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 12px; cursor: zoom-out;
  background: color-mix(in srgb, var(--panel) 92%, transparent); }
.ash-big.hidden { display: none; }
.ash-big img { max-width: 100%; min-height: 0; flex: 0 1 auto;
  object-fit: contain; cursor: default;
  border: 1px solid var(--line-2); border-radius: 6px; }
.ash-big-cap { flex: 0 0 auto; text-align: center; }
.ash-big-close { position: absolute; top: 8px; right: 10px; }
@media (max-width: 760px) {
  /* su telefono una colonna: due mappe da 190 px l'una sono illeggibili */
  .ash-grid { grid-template-columns: 1fr; }
}
.tr-legend { position: absolute; left: 20px; bottom: 16px; z-index: 2;
  display: flex; gap: 12px; flex-wrap: wrap; font-size: 11px;
  color: var(--ink-3); background: color-mix(in srgb, var(--panel) 88%, transparent);
  border: 1px solid var(--line-2); border-radius: 6px; padding: 4px 8px; }
.tr-legend.hidden { display: none; }
.tr-legend .on { color: var(--ink); font-weight: 600; }
.tr-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  vertical-align: -1px; margin-right: 3px; }
.tr-dot.cen { background: #e8a33d; border-radius: 0; }
/* sensori sulla mappa del pannello: cerchietti in testo puro come il centroide.
   Il colore lo mette il JS (uno per famiglia di parametro); i sensori di un
   parametro diverso da quello a video restano visibili ma smorzati. */
.tr-sensor { font-size: 11px; line-height: 12px; text-align: center;
  text-shadow: 0 0 3px var(--bg), 0 0 5px var(--bg); cursor: pointer; }
.tr-sensor.dim { opacity: .42; }
.tr-sensor.sel { font-size: 16px; line-height: 16px;
  text-shadow: 0 0 4px var(--bg), 0 0 9px var(--ink); }
/* indicatori di allarme INGV: livello tremore sulla scala verde/giallo/
   arancio/rosso, allarmi del dilatometro accesi in rosso e spenti in grigio —
   uno spento e' comunque un'informazione, non si nasconde */
.tr-status { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.tr-alert { font-size: 11px; padding: 2px 8px; border-radius: 12px;
  white-space: nowrap; font-weight: 600; color: #0b0f14; }
.tr-alert.a0 { background: var(--green, #3fb950); }
.tr-alert.a1 { background: var(--yellow, #d6c33a); }
.tr-alert.a2 { background: var(--orange, #e8a33d); }
.tr-alert.a3 { background: var(--red, #e5534b); }
.tr-alert.off { background: transparent; color: var(--ink-3);
  border: 1px solid var(--line-2); font-weight: 400; }
/* pallino sul pulsante Vulcani: dice che c'e' un'allerta accesa senza dover
   aprire il pannello, con lo stesso colore della scala INGV */
#btn-tremor { position: relative; }
.vc-badge { position: absolute; top: -3px; right: -3px;
  width: 9px; height: 9px; border-radius: 50%;
  box-shadow: 0 0 0 2px var(--panel);
  animation: vc-pulse 2s ease-in-out infinite; }
.vc-badge.hidden { display: none; }
.vc-badge.a0 { background: var(--c-green); animation: none; }
.vc-badge.a1 { background: var(--c-yellow); }
.vc-badge.a2 { background: var(--c-orange); }
.vc-badge.a3 { background: var(--c-red); }
@keyframes vc-pulse { 50% { opacity: 0.25; } }

/* avvisi di cambio stato dei vulcani: in alto a destra, sopra a tutto (anche
   ai modali), perche' un parossismo non deve finire dietro a una finestra */
.vc-toasts { position: fixed; top: 58px; right: 12px; z-index: 3000;
  display: flex; flex-direction: column; gap: 8px;
  width: min(360px, calc(100vw - 24px)); pointer-events: none; }
.vc-toast { pointer-events: auto; display: flex; align-items: flex-start;
  gap: 8px; padding: 8px 10px; border-radius: 10px; font-size: 13px;
  background: var(--panel); border: 1px solid var(--line-2);
  border-left: 4px solid var(--ink-3); box-shadow: var(--shadow);
  animation: vc-in 0.2s ease-out; }
.vc-toast.s1 { border-left-color: var(--c-green); }
.vc-toast.s2 { border-left-color: var(--c-yellow); }
.vc-toast.s3 { border-left-color: var(--c-orange); }
.vc-toast.s4 { border-left-color: var(--c-red);
  background: color-mix(in srgb, var(--c-red) 16%, var(--panel)); }
.vc-toast-txt { flex: 1 1 auto; cursor: pointer; min-width: 0; }
.vc-toast-txt b { display: block; margin-bottom: 2px; }
.vc-toast-x { flex: 0 0 auto; padding: 0 6px; }
@keyframes vc-in { from { opacity: 0; transform: translateX(12px); } }
@media (max-width: 820px) {
  .vc-toasts { top: auto; bottom: 70px; right: 8px; left: 8px; width: auto; }
}
.tr-live { font-variant-numeric: tabular-nums; padding: 2px 8px;
  border: 1px solid var(--line-2); border-radius: 12px; white-space: nowrap; }

/* --- diretta sotto il grafico del tremore (solo telefono) ---
   Su uno schermo alto e stretto la serie a tutta altezza era illeggibile: la
   metà bassa del pannello ospita la diretta della prima telecamera con un
   flusso video (regia INGV TV), o in mancanza il fotogramma che si rinnova
   da solo (vedi syncTremorSplitCam in app.js). */
.tr-splitcam { display: none; }
@media (max-width: 820px) {
  .tr-splitcam { position: absolute; left: 12px; right: 12px; bottom: 8px;
    height: calc(50% - 12px); z-index: 1;
    display: flex; flex-direction: column; overflow: hidden;
    background: #000; border: 1px solid var(--line-2); border-radius: 6px; }
  .tr-splitcam.hidden { display: none; }
  .tr-splitcam video, .tr-splitcam img { flex: 1 1 auto; min-height: 0;
    width: 100%; object-fit: contain; background: #000; }
  .tr-splitcam .cap { flex: 0 0 auto; display: flex; gap: 6px;
    align-items: baseline; padding: 3px 8px; font-size: 11px;
    color: var(--ink-3); background: var(--panel); }
  .tr-splitcam .cap b { color: var(--ink); font-weight: 600; }
  .tr-splitcam .cap .age { margin-left: auto; white-space: nowrap; }
}
/* --- picture-in-picture del vulcano (solo desktop) ---
   Il contrario dello split del telefono: lì lo schermo è alto e la diretta si
   prende metà pannello, qui è largo e la diretta sta in un riquadro piccolo
   in basso a sinistra, SOPRA al grafico. Qualcosa copre e per questo si chiude
   con una ✕: l'angolo in basso a sinistra è comunque il pezzo di grafico che si
   legge meno — la parte viva di una curva di tremore sta in alto e la coda a
   destra. Si scosta però di quanto bastano i margini del canvas (TR_MARGIN:
   52 px a sinistra, 34 in basso) più il padding del pannello (12 e 8), così le
   etichette dei due assi restano leggibili senza doverlo spostare; e lo si
   sposta comunque trascinandolo (trPipDrag).
   z-index 3: sopra alle viste (1) e al velo di attesa (2), sotto agli avvisi
   di parossismo (3000). */
.tr-pip { display: none; }
@media (min-width: 821px) {
  .tr-pip { position: absolute; left: 64px; bottom: 38px; z-index: 3;
    width: min(320px, 34%);
    display: flex; flex-direction: column; overflow: hidden;
    background: #000; border: 1px solid var(--line-2); border-radius: 6px;
    box-shadow: var(--shadow); }
  .tr-pip.hidden { display: none; }
  .tr-pip video, .tr-pip img { display: block; width: 100%;
    aspect-ratio: 16 / 9; object-fit: contain; background: #000; }
  .tr-pip .cap { flex: 0 0 auto; display: flex; gap: 6px; align-items: center;
    padding: 3px 4px 3px 8px; font-size: 11px; color: var(--ink-3);
    background: var(--panel); }
  .tr-pip .cap .pip-txt { min-width: 0; display: flex; gap: 6px;
    align-items: baseline; overflow: hidden; }
  .tr-pip .cap .pip-txt b { color: var(--ink); font-weight: 600;
    white-space: nowrap; }
  .tr-pip .cap .pip-txt span { overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; }
  .tr-pip .cap .age { margin-left: auto; white-space: nowrap; }
  .tr-pip .cap .pip-x { flex: 0 0 auto; padding: 0 5px; line-height: 1;
    background: none; border: 0; color: var(--ink-3); cursor: pointer;
    font-size: 12px; }
  .tr-pip .cap .pip-x:hover { color: var(--ink); }
  /* si trascina dalla didascalia e dal fermo immagine: sul video no, lì i
     comandi nativi (play, barra, volume) vogliono i loro clic */
  .tr-pip .cap, .tr-pip img { cursor: move; }
  .tr-pip .cap .pip-x { cursor: pointer; }
  /* maniglia di misura: angolo in alto a DESTRA, l'unico che cresce senza
     spostare il riquadro (è ancorato in basso a sinistra). Sta sopra al video
     ma lontana dai suoi comandi, che sono in basso. Si vede appena finché non
     ci si passa sopra: è un riquadro da guardare, non un pannello di regolazioni. */
  .tr-pip .pip-grip { position: absolute; top: 0; right: 0; width: 22px;
    height: 22px; z-index: 1; cursor: nesw-resize; touch-action: none;
    background: linear-gradient(225deg, rgba(255,255,255,.5) 0 45%,
                                transparent 45%);
    opacity: .35; transition: opacity .12s; }
  .tr-pip:hover .pip-grip, body.resizing-pip .tr-pip .pip-grip { opacity: 1; }
  /* presa per spostare: angolo in alto a sinistra, quello libero (la misura sta
     a destra, i comandi del video in basso). Vedi trPipDrag. */
  .tr-pip .pip-move { position: absolute; top: 0; left: 0; width: 22px;
    height: 22px; z-index: 1; cursor: move; touch-action: none;
    background: linear-gradient(135deg, rgba(255,255,255,.5) 0 45%,
                                transparent 45%);
    opacity: .35; transition: opacity .12s; }
  .tr-pip:hover .pip-move, body.moving-pip .tr-pip .pip-move { opacity: 1; }
}
/* durante il trascinamento del riquadro il cursore è quello della maniglia,
   non il col-resize dei resizer di layout (regola più sotto in cascata) */
body.resizing-pip { cursor: nesw-resize; }
/* mentre si sposta il riquadro il cursore resta "move" anche fuori da esso, e
   niente selezione di testo: la didascalia ha del testo, e trascinandola lo si
   evidenziava invece di muovere il riquadro */
body.moving-pip, body.moving-pip * { cursor: move !important; }
body.moving-pip { user-select: none; }

/* centroide del tremore sulla mappa: triangolo arancio, testo puro (niente
   immagini), leggibile sia su tile chiare che scure grazie all'alone */
.tr-centroid { color: #e8a33d; font-size: 14px; line-height: 16px;
  text-align: center; text-shadow: 0 0 3px var(--bg), 0 0 6px var(--bg);
  pointer-events: auto; }
.modal-back { position: absolute; inset: 0;
  background: color-mix(in srgb, var(--bg) 74%, transparent); }
.modal-box {
  position: relative; background: var(--panel); border: 1px solid var(--line-2);
  border-radius: 10px; width: min(1180px, 94vw); height: min(820px, 92vh);
  display: flex; flex-direction: column; box-shadow: 0 18px 60px rgba(0,0,0,.6);
}
.modal-head {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; border-bottom: 1px solid var(--line);
}
.modal-head b { font-size: 15px; }
.modal-body { flex: 1; display: flex; min-height: 0; }
.m-left {
  width: var(--m-left-w, 44%); flex: 0 0 auto;
  min-width: 320px; padding: 12px 14px; overflow-y: auto;
  border-right: 1px solid var(--line);
}
.m-right { flex: 1; padding: 12px 14px; overflow-y: auto; min-width: 0; }
/* Maniglia fra le due colonne della scheda evento. Larga 7 px per essere
   afferrabile col mouse, ma disegnata come la solita linea sottile al centro:
   quel che si vede è la stessa separazione di prima, non una barra in più.
   La linea la disegna la maniglia (il bordo di .m-left qui se ne va, o
   sarebbero due righe a 3 px di distanza); #ovm, che di maniglia non ne ha,
   tiene il suo. Niente margini negativi: sovrapporsi alle colonne coprirebbe
   la barra di scorrimento di .m-left. */
#modal .m-left { border-right: none; }
.m-split {
  flex: 0 0 7px;
  cursor: col-resize; position: relative; background: transparent;
  touch-action: none;   /* il drag col dito/penna non deve scorrere il modal */
}
.m-split::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 3px; width: 1px;
  background: var(--line); transition: background .12s, box-shadow .12s;
}
.m-split:hover::after, .m-split:focus-visible::after, .m-split.drag::after {
  background: var(--accent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
}
.m-split:focus-visible { outline: none; }
/* durante il trascinamento il cursore non deve cambiare passando sopra la
   carta o i sismogrammi, e la selezione del testo rovinerebbe il gesto
   (il puntatore è catturato dalla maniglia, quindi nessun altro lo riceve) */
body.m-splitting { cursor: col-resize; user-select: none; }
.modal-body h3 {
  font-size: 12px; margin: 0 0 8px; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .8px;
}
#m-map { height: 300px; border-radius: 8px; background: var(--canvas); }
/* Velo di attesa sulla carta della scheda: fra il clic e l'arrivo del
   dettaglio restava a video la carta dell'evento PRECEDENTE — mezzo secondo di
   epicentro sbagliato, che è peggio di mezzo secondo di niente. */
#m-map-wrap { position: relative; }
.m-map-load {
  position: absolute; inset: 0; z-index: 2; border-radius: 8px;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  background: color-mix(in srgb, var(--canvas) 88%, transparent);
  color: var(--ink-2); font-size: 12px;
}
.m-map-load.hidden { display: none; }
.m-map-load .spinner {
  width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid var(--line-2); border-top-color: var(--c-blue);
  animation: ldr-spin .9s linear infinite;
}
/* la riga sotto alla carta della scheda: barra dei colori più le note su
   incertezza e stazioni mute. `wrap` perché le note sono frasi, non sigle, e
   in una riga sola si spezzavano in colonne strette da tre parole l'una */
#m-scale { display: flex; align-items: center; gap: 4px 8px; flex-wrap: wrap;
  margin: 8px 0 4px;
  font-size: 11px; color: var(--ink-2); font-family: ui-monospace, monospace; }
#m-scale .bar { flex: 1 1 90px; height: 10px; border-radius: 3px; }
#m-info { margin-top: 10px; font-size: 12px; }
#m-info table { width: 100%; border-collapse: collapse; }
#m-info td { padding: 3px 0; vertical-align: top; }
#m-info td:first-child { color: var(--ink-2); width: 42%; }
#m-info .cls { margin: 0; }

/* Parametri secondari: stessa tabella, ma con l'etichetta stretta invece che
   a metà riga. Qui le etichette sono corte ("stazioni usate", "rms / gap") e
   i valori lunghi: senza una larghezza dichiarata il browser strizzava la
   colonna sinistra fino a mandare a capo "stazioni / usate" per un valore di
   due cifre — tre righe di altezza per un dato solo. E senza `vertical-align`
   l'etichetta "revisione" finiva centrata a metà del paragrafo che le sta
   accanto, lontana dalla riga a cui si riferisce. */
#m-sec-body { font-size: 12px; padding-top: 6px; }
#m-sec-body table { width: 100%; border-collapse: collapse; }
#m-sec-body td { padding: 3px 0; vertical-align: top; }
/* `:not([colspan])`: la riga a tutta larghezza (la motivazione della
   revisione) è un td unico, quindi è anche il PRIMO td della sua riga e
   prendeva le regole della colonna delle etichette — `white-space: nowrap` su
   un paragrafo di quattro frasi. Il testo restava su una riga sola, allargava
   la tabella oltre la .m-left e finiva sotto ai sismogrammi. */
#m-sec-body td:first-child:not([colspan]) { color: var(--ink-2);
  white-space: nowrap; width: 1%; padding-right: 12px; }
/* la motivazione della revisione: una frase per riga, rientrata sotto il
   filetto così si vede dove comincia e dove finisce il discorso. Sta in una
   riga a tutta larghezza (colspan), non nella colonna dei valori: lì partiva
   rientrato quanto l'etichetta più lunga della tabella e restavano una
   settantina di pixel vuoti a sinistra per tutte le sue righe. Il rientro è
   solo quello del filetto — il paragrafo comincia più o meno sotto la "e" di
   "revisione", che è l'etichetta a cui si riferisce */
.rev-note { margin-top: 2px; padding-left: 8px;
  border-left: 2px solid var(--line);
  color: var(--ink-2); font-size: 11px; line-height: 1.45;
  /* Le note di Claude portano elenchi di sigle senza spazi
     ("(FUORN,BRMO,VDL,CARE,...)") e intervalli col trattino: parole singole
     più larghe della colonna. Senza rottura forzata alzavano il min-content
     della tabella, la .m-left non taglia in orizzontale e il paragrafo
     finiva a scorrere sotto ai sismogrammi della colonna destra. */
  overflow-wrap: anywhere; }
.rev-note > div + div { margin-top: 3px; }

/* --- record section: una riga per sismografo ---
   Ogni riga è un BLOCCO: testata (chi / quanto / cosa ci faccio) incollata
   sopra al suo sismogramma, con lo stesso sfondo del canvas. Prima testata e
   traccia erano due cose slegate su fondo pagina, e con cinque elementi in
   `space-between` i numeri di righe consecutive non si incolonnavano mai. */
.mwave { margin-bottom: 10px; }
.mwave .lbl { display: flex; align-items: center; column-gap: 10px;
  row-gap: 2px; font-size: 11px; color: var(--ink-2);
  font-family: ui-monospace, monospace;
  padding: 4px 8px; background: var(--canvas);
  border-radius: 4px 4px 0 0;
  /* con le magnitudo di stazione la riga può non starci sui ~520 px del
     telefono: meglio andare a capo che tagliare */
  flex-wrap: wrap; }
/* CHI: il nome è l'unica cosa che si cerca scorrendo la colonna, e non deve
   confondersi col resto della riga */
.mwave .lbl .mw-name { flex: 0 0 auto; color: var(--ink); font-weight: 600; }
/* QUANTO: i numeri stanno insieme e prendono lo spazio che avanza, così la
   zona dei comandi resta ancorata a destra invece di galleggiare */
.mw-meta { flex: 1 1 auto; min-width: 0; display: flex; flex-wrap: wrap;
  align-items: center; column-gap: 14px; row-gap: 2px; }
.mw-peak { color: var(--ink-3); white-space: nowrap; }
/* COSA CI FACCIO: canali e spunta «usa», sempre in fondo alla riga */
.mw-act { flex: 0 0 auto; margin-left: auto; display: flex;
  align-items: center; gap: 8px; }
/* magnitudo di stazione accanto al sismogramma: il voto della singola
   stazione con lo scarto dalla mediana di rete; oltre 0.5 unità lo scarto
   si accende — è il segnale "guarda QUESTA traccia" */
.mw-mags { display: flex; gap: 10px; flex-wrap: wrap; }
.mw-sta { color: var(--ink); white-space: nowrap; cursor: help; }
.mw-delta { color: var(--ink-2); }
.mw-delta.warn { color: var(--c-orange); font-weight: 600; }
.mwave canvas { width: 100%; height: 58px; display: block;
  background: var(--canvas); border-radius: 0 0 4px 4px; }
/* un solo comando per i canali, al posto della fila di sigle */
.mw-chans-all { background: none; border: 1px solid var(--line-2);
  border-radius: 4px; padding: 1px 7px; font: inherit; font-size: 10px;
  color: var(--ink-2); cursor: pointer; white-space: nowrap; }
.mw-chans-all:hover { border-color: var(--c-blue); color: var(--ink); }
.mw-chans-all.on { color: var(--c-blue); border-color: var(--c-blue); }
.mw-chans-all.loading { opacity: .5; cursor: progress; }

/* --- altre componenti della stessa stazione ---
   Righe FIGLIE dentro la .mwave, non sorelle: così l'ordine per distanza
   resta quello delle stazioni, e gli stati excluded/added/unused (che sono
   selettori discendenti, più sopra) le prendono già senza dire nulla.
   Più basse della madre: sono di supporto, la traccia della stazione è
   quella sopra. */
/* NIENTE rientro: tutte le tracce della colonna condividono lo stesso asse dei
   tempi, e spostare una componente di qualche pixel la disallineerebbe da
   quella sopra — i pick di due canali della stessa stazione non caderebbero
   più sulla stessa verticale. L'appartenenza la dicono il corpo più piccolo e
   l'altezza ridotta del canvas. */
.mw-sub { margin-top: 4px; }
.mw-sub .lbl { display: flex; align-items: center; font-size: 10px;
  color: var(--ink-3); font-family: ui-monospace, monospace; column-gap: 10px;
  padding: 3px 8px; background: var(--canvas); border-radius: 4px 4px 0 0; }
.mw-sub .lbl .mw-name { color: var(--ink-2); font-weight: 600; }
.mw-sub canvas { width: 100%; height: 44px; display: block; cursor: default;
  background: var(--canvas); border-radius: 0 0 4px 4px; }

/* legenda cartografia (control Leaflet in basso a sinistra) + toggle colore.
   Larga in orizzontale, così le righe si affiancano e la legenda resta bassa. */
.map-legend {
  background: color-mix(in srgb, var(--panel) 92%, transparent); color: var(--ink-2);
  border: 1px solid var(--line-2); border-radius: 8px; padding: 7px 9px;
  font-size: 11px; line-height: 1.3; max-width: 520px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .4); backdrop-filter: blur(2px);
}
.map-legend .ml-modes { display: flex; align-items: center; gap: 4px;
  margin-bottom: 6px; flex-wrap: wrap; }
.map-legend .ml-cap { color: var(--ink-3); margin-right: 2px; }
.map-legend .ml-mode {
  background: var(--panel-2); color: var(--ink-2); border: 1px solid var(--line-2);
  border-radius: 5px; padding: 2px 7px; cursor: pointer; font-size: 11px;
}
.map-legend .ml-mode:hover { color: var(--ink); }
.map-legend .ml-mode.on {
  border-color: var(--c-blue); color: var(--ink); background: var(--hover);
}
.map-legend .ml-rows { display: flex; flex-wrap: wrap; gap: 3px 10px; }
.map-legend .ml-item { display: inline-flex; align-items: center; gap: 4px; }
.map-legend .ml-dot { width: 11px; height: 11px; border-radius: 50%;
  display: inline-block; flex-shrink: 0; }
.map-legend .ml-star { font-size: 13px; line-height: 1;
  -webkit-text-stroke: .6px #000; paint-order: stroke fill; }
.map-legend .ml-vc { width: 13px; height: 11px; flex-shrink: 0; cursor: default;
  filter: none; }
.map-legend .ml-note { color: var(--ink-3); margin-top: 5px; }
.map-legend .ml-err { color: var(--c-orange); }
/* simboli degli strati: triangolo (sismografo), tratto (faglia), cerchio
   vuoto (terremoto storico) — gli stessi che si vedono in mappa */
.map-legend .ml-tri { font-size: 11px; line-height: 1; }
/* il triangolo "quiete" è bianco: senza un contorno sparirebbe sul pannello
   chiaro, esattamente come sparirebbe in mappa senza il bordo nero */
.map-legend .ml-tri.quiet { -webkit-text-stroke: .8px var(--c-quiet-line); }
/* sismografo aperto nella sidebar: l'anello ciano attorno al triangolo,
   come in mappa (vedi drawWatchHalos) */
.map-legend .ml-halo {
  width: 15px; height: 15px; border-radius: 50%; flex-shrink: 0;
  border: 1.5px solid var(--c-watch);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink-2);
}
.map-legend .ml-halo .ml-tri { font-size: 8px; }
.map-legend .ml-watch { margin-top: 5px; }
.map-legend .ml-line { width: 16px; height: 3px; border-radius: 2px;
  display: inline-block; flex-shrink: 0; }
.map-legend .ml-circ { border: 1.5px solid; border-radius: 50%;
  display: inline-block; flex-shrink: 0; }
.map-legend .ml-sec { color: var(--ink-3); margin: 7px 0 3px;
  text-transform: uppercase; letter-spacing: .5px; font-size: 10px; }
/* la glossa accanto al titolo di sezione: è una frase, non un'etichetta, e
   in maiuscolo spaziato sarebbe illeggibile */
.map-legend .ml-sec .ml-sub { text-transform: none; letter-spacing: 0; }
/* la prima sezione non deve staccarsi dal toggle di modalità */
.map-legend .ml-body > .ml-sec:first-child { margin-top: 2px; }

/* comandi della carta in alto a destra: livelli e sfondo.
   Due pulsanti impilati, ciascuno con la sua tendina ancorata a destra —
   su telefono aprirla verso sinistra è l'unico modo perché non esca dallo
   schermo, e la tendina scorre invece di allungarsi oltre la mappa. */
.map-tools { display: flex; flex-direction: column; gap: 6px; }
.mt-item { position: relative; }
.mt-btn {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  cursor: pointer; width: 92px;
  background: color-mix(in srgb, var(--panel) 94%, transparent);
  color: var(--ink); border: 1px solid var(--line-2); border-radius: 8px;
  padding: 6px 10px; font: inherit; font-size: 12px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, .35); backdrop-filter: blur(2px);
}
.mt-btn:hover { background: var(--hover); }
.mt-btn[aria-expanded="true"] { border-color: var(--c-blue); }
.mt-ico { font-size: 13px; line-height: 1; }
.mt-drop {
  position: absolute; top: calc(100% + 4px); right: 0; z-index: 5;
  min-width: 190px; max-width: min(78vw, 260px);
  max-height: min(58vh, 420px); overflow-y: auto;
  background: var(--panel); border: 1px solid var(--line-2);
  border-radius: 8px; padding: 6px; box-shadow: var(--shadow);
  font-size: 12px; color: var(--ink);
}
.mt-drop.hidden { display: none; }
/* Una tendina aperta è larga fino a 78vw: su schermi stretti arriva sotto alle
   due notifiche dell'angolo opposto (pill dell'ultimo evento e badge della
   sessione Claude), che vengono dopo nel DOM e le finiscono davanti, coprendo
   le righe da spuntare. Si tolgono di mezzo finché la tendina è aperta (app.js
   mette `mt-open` sul body) — sono notifiche, non comandi, e tornano dopo. */
body.mt-open #ev-pill,
body.mt-open .map-quiet,
body.mt-open #claude-live.cl-on-map { visibility: hidden; }
.mt-cap { color: var(--ink-3); font-size: 10px; text-transform: uppercase;
  letter-spacing: .5px; padding: 2px 6px 4px; }
.mt-row {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 5px 6px; border-radius: 5px; cursor: pointer;
  background: none; border: none; color: inherit; font: inherit;
  font-size: 12px; text-align: left;
}
.mt-row:hover { background: var(--hover); }
.mt-row input { margin: 0; cursor: pointer; flex-shrink: 0; }
/* la spunta dello sfondo attivo occupa spazio anche da spenta: senza, le
   etichette ballerebbero a ogni cambio */
.mt-tick { width: 12px; flex-shrink: 0; color: var(--c-blue); visibility: hidden; }
.mt-base.on .mt-tick { visibility: visible; }
/* citazione delle mattonelle dello sfondo attivo: piccola e in grigio, ma
   dentro la tendina dello sfondo — cioè accanto alla cosa che attribuisce */
.mt-attr { border-top: 1px solid var(--line); margin-top: 4px;
  padding: 6px 6px 2px; color: var(--ink-3); font-size: 10px; line-height: 1.4; }
.mt-attr a { color: var(--ink-2); }
.mt-attr-more { background: none; border: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--c-blue); text-decoration: underline; }
.mt-base.on { color: var(--ink); font-weight: 600; }
/* opzione di uno strato: rientrata sotto la sua casella */
.mt-row-sub { margin-left: 12px; width: calc(100% - 12px); }
/* riga spenta perché lo strato padre è spento (vince su display:flex) */
.mt-row.hidden { display: none; }
.mt-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 3px;
  padding: 0 6px 6px 18px; }
.mt-sub-cap { color: var(--ink-3); font-size: 10px; width: 100%; }
.mt-chip {
  background: var(--panel-2); color: var(--ink-2);
  border: 1px solid var(--line-2); border-radius: 5px;
  padding: 2px 7px; cursor: pointer; font: inherit; font-size: 11px;
}
.mt-chip:hover { color: var(--ink); }
.mt-chip.on { border-color: var(--c-blue); color: var(--ink);
  background: var(--hover); }
/* velocità del replay: gli stessi chip, ma a misura di dito — è un comando che
   si usa mentre si sta guardando qualcosa scorrere */
.rs-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 4px; }
.rs-chip { padding: 8px 14px; font-size: 13px; }

/* elenco completo eventi (sezione "Eventi") */
/* da desktop l'elenco è largo 1176px (980 + 20%): con dieci colonne, epicentro
   e note stavano strette e andavano a capo di continuo. Il min() con 95vw
   regge comunque i portatili piccoli, e su telefono vale la regola a schermo
   pieno più sotto. */
.modal-box.evl-box { width: min(1176px, 95vw); height: auto; max-height: 92vh; }
.modal-body.evl-body { display: block; overflow-y: auto; padding: 6px 14px 12px; }
.evl-filter { display: inline-flex; align-items: center; gap: 4px;
  cursor: pointer; }
.evl-filter input { margin: 0; cursor: pointer; }
#evl-table { width: 100%; border-collapse: collapse; font-size: 12px; }
#evl-table th {
  text-align: left; color: var(--ink-3); font-size: 10px;
  text-transform: uppercase; letter-spacing: .6px; font-weight: 600;
  padding: 6px 8px 4px; border-bottom: 1px solid var(--line);
  position: sticky; top: 0; background: var(--panel);
}
#evl-table td { padding: 5px 8px; border-bottom: 1px solid var(--line);
  vertical-align: top; }
#evl-table tbody tr { cursor: pointer; }
#evl-table tbody tr:hover { background: var(--hover); }
#evl-table tbody tr.is-anthro { opacity: .68; }
.evl-when { font-variant-numeric: tabular-nums; white-space: nowrap;
  color: var(--ink); }
.evl-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* colonna qualità: punteggio, lettera e, accanto, la targhetta del dubbio */
.evl-q { white-space: nowrap; }
/* colonna note: le targhette ci stanno quasi sempre su una riga sola, e
   spezzarle in due (o mandare la seconda a capo) sfilacciava l'elenco. Con il
   nowrap la colonna si prende la larghezza che le serve e a cedere è
   l'epicentro, che il testo lo sa spezzare */
.evl-note { white-space: nowrap; }

/* pannello suoni */
.modal-box.snd-box { width: min(440px, 94vw); height: auto; max-height: 92vh; }
.modal-body.snd-body { display: block; overflow-y: auto; padding: 12px 16px; }
.snd-body h3 { font-size: 12px; margin: 14px 0 6px; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .8px; }
.snd-body h3:first-child { margin-top: 0; }
.snd-row { display: flex; align-items: center; gap: 8px; margin: 6px 0;
  cursor: pointer; }
.snd-row input { cursor: pointer; }
.snd-row .dot { width: 11px; height: 11px; }
.snd-field { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin: 9px 0; color: var(--ink-2); font-size: 13px; }
.snd-inline { display: inline-flex; align-items: center; gap: 6px; }
.snd-field select {
  background: var(--bg); color: var(--ink); border: 1px solid var(--line-2);
  border-radius: 5px; padding: 3px 6px; font: inherit; font-size: 13px;
}

.leaflet-container { font: inherit; }
.leaflet-tooltip.station-tip {
  background: var(--panel); color: var(--ink); border: 1px solid var(--line-2);
  font-family: ui-monospace, monospace; font-size: 12px;
}

/* nome del sismografo sotto al triangolo, solo da zoom 9.5 in su */
.station-label-wrap { background: none; border: none; }
.station-label {
  position: absolute; left: 0; top: 0; transform: translateX(-50%);
  font: 600 10px/1 ui-monospace, monospace; white-space: nowrap;
  color: var(--ink); text-shadow:
    0 0 3px color-mix(in srgb, var(--bg) 92%, transparent),
    0 0 5px color-mix(in srgb, var(--bg) 75%, transparent);
  pointer-events: none;
}

/* anello dei sismografi aperti nella sidebar (vedi drawWatchHalos).
   Il riquadro non riceve mai il puntatore: sta SOPRA la tela dei triangoli e
   senza questo si mangerebbe il clic proprio sulle stazioni aperte, cioè
   quelle su cui si clicca per richiuderle. Il secondo anello scuro
   (box-shadow interno) tiene il ciano leggibile anche sul satellite. */
.wt-halo-wrap { background: none; border: none; pointer-events: none; }
.wt-halo {
  display: block; width: 100%; height: 100%; border-radius: 50%;
  border: 2px solid var(--c-watch);
  box-shadow: 0 0 0 1px color-mix(in srgb, #000 55%, transparent),
              0 0 7px color-mix(in srgb, var(--c-watch) 65%, transparent);
}
/* aperto dall'automatismo di un evento: pulsa piano finché resta lì */
.wt-halo.auto { animation: wt-halo 1.8s ease-in-out infinite; }
@keyframes wt-halo {
  0%, 100% { transform: scale(.86); opacity: .7; }
  50%      { transform: scale(1.06); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .wt-halo.auto { animation: none; }
}

/* vulcani sorvegliati: cono arancione grande come un sismografo, cliccabile.
   Il contorno chiaro/scuro come lo sfondo lo stacca dalla cartografia sia sul
   rilievo che sul satellite, dove l'arancio da solo si perde. */
.vc-ico-wrap { background: none; border: none; }
.vc-ico { display: block; width: 100%; height: 100%; cursor: pointer;
  filter: drop-shadow(0 0 3px color-mix(in srgb, var(--bg) 85%, transparent)); }
.vc-ico svg { display: block; width: 100%; height: 100%; overflow: visible; }
.vc-ico svg path {
  fill: var(--c-orange);
  stroke: color-mix(in srgb, var(--bg) 80%, transparent); stroke-width: 1.2;
  paint-order: stroke fill;
}
.vc-ico:hover svg path { fill: var(--c-red); }

/* epicentri: stelline piene scalate sulla magnitudo, con la sola magnitudo */
.eq-star-wrap { background: none; border: none; }
.eq-star {
  position: relative; width: var(--sz); height: var(--sz);
  display: flex; align-items: center; justify-content: center; line-height: 1;
}
.eq-star .star {
  font-size: var(--sz); color: var(--col); transform-origin: center;
  text-shadow: 0 0 3px color-mix(in srgb, var(--bg) 90%, transparent),
               0 0 6px color-mix(in srgb, var(--bg) 55%, transparent);
  /* bordino nero: il contorno, non il riempimento, dice "evento". Il colore
     pieno resta quello della magnitudo, ma il profilo separa le stelline dai
     pallini dei sismografi anche dove i due colori si somigliano. */
  -webkit-text-stroke: 1.5px #000;
  paint-order: stroke fill;
}
.eq-star .lbl {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  margin-top: -1px; font: 700 11px/1 ui-monospace, monospace;
  color: var(--ink); background: color-mix(in srgb, var(--panel) 88%, transparent);
  padding: 1px 4px; border-radius: 4px; white-space: nowrap;
  border: 1px solid var(--col);
}
/* seconda riga sotto la magnitudo: da quanto è avvenuta. Senza riquadro —
   è contesto, non deve competere col numero. */
.eq-star .age, .eq-ripple .age {
  position: absolute; left: 50%; white-space: nowrap;
  font: 600 10px/1 ui-monospace, monospace; color: var(--age-ink);
  text-shadow: 0 0 3px var(--age-halo), 0 0 5px var(--age-halo),
               0 0 7px var(--age-halo);
}
.eq-star .age { top: 100%; transform: translateX(-50%); margin-top: 15px; }
.eq-ripple .age {
  top: 50%; transform: translate(-50%, calc(var(--core) / 2 + 20px));
}
/* "PRELIMINARE": la riga fra la magnitudo e il "da quanto", sulla stessa
   colonna e con lo stesso trattamento della scritta del dubbio. Gialla: il
   grigio la faceva leggere come il "da quanto" sotto — contesto da ignorare —
   mentre è un'avvertenza, "questo numero può ancora cambiare". Giallo e non
   rosso perché resta un'attesa, non un allarme: il rosso è del dubbio.
   Quando c'è, il "da quanto" scende di una riga. */
.eq-star .tag.prelim, .eq-ripple .tag.prelim {
  position: absolute; left: 50%; white-space: nowrap;
  font-weight: 700; font-size: 9px; line-height: 1;
  letter-spacing: .04em; color: var(--c-yellow);
  text-shadow: 0 0 3px var(--bg), 0 0 3px var(--bg), 0 0 6px var(--bg);
}
.eq-star .tag.prelim { top: 100%; transform: translateX(-50%); margin-top: 15px; }
.eq-star.has-tag .age { margin-top: 27px; }
.eq-ripple .tag.prelim {
  top: 50%; transform: translate(-50%, calc(var(--core) / 2 + 20px));
}
.eq-ripple.has-tag .age {
  transform: translate(-50%, calc(var(--core) / 2 + 32px));
}
/* senza la pastiglia della magnitudo (zoom basso, o etichetta soppressa
   dall'affollamento) la colonna risale, esattamente come sulla croce del
   dubbio: la scritta prende il posto del numero invece di restare sospesa
   sopra un buco. Serve da quando "PRELIMINARE" non aspetta più l'etichetta
   per comparire. */
.eq-star.no-lbl .tag.prelim, .eq-star.no-lbl .age { margin-top: 2px; }
.eq-star.no-lbl.has-tag .age { margin-top: 14px; }
.eq-ripple.no-lbl .tag.prelim, .eq-ripple.no-lbl .age {
  transform: translate(-50%, calc(var(--core) / 2 + 8px));
}
.eq-ripple.no-lbl.has-tag .age {
  transform: translate(-50%, calc(var(--core) / 2 + 20px));
}
.eq-star.anthro .star { opacity: .8; }
.eq-star.anthro .lbl { color: var(--ink-2); border-color: var(--c-stale); }
.eq-star.active .star { animation: eq-pulse 1.4s ease-in-out infinite; }

/* soluzione consolidata male ma ancora in grazia: croce rossa al posto della
   stella, con la targhetta del dubbio accanto alla magnitudo. Deve leggersi
   come "non fidarti di questo epicentro", non come un evento in meno. */
.eq-cross {
  position: relative; width: var(--sz); height: var(--sz);
  display: flex; align-items: center; justify-content: center; line-height: 1;
}
.eq-cross .x {
  font-size: calc(var(--sz) * .82); font-weight: 700; color: var(--c-red);
  text-shadow: 0 0 3px color-mix(in srgb, var(--bg) 90%, transparent),
               0 0 6px color-mix(in srgb, var(--bg) 55%, transparent);
}
/* sotto la croce, in colonna: magnitudo, "soluzione dubbia", "x min fa" */
.eq-cross .cap {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  margin-top: -1px; display: flex; align-items: center; gap: 3px;
}
.eq-cross .lbl {
  font: 700 11px/1 ui-monospace, monospace;
  color: var(--ink); background: color-mix(in srgb, var(--panel) 88%, transparent);
  padding: 1px 4px; border-radius: 4px; white-space: nowrap;
  border: 1px solid var(--c-red);
}
/* scritta, non pastiglia: in carta un rettangolo rosso pieno era una macchia
   larga il triplo della stella e nascondeva il territorio intorno. Il contorno
   chiaro la tiene leggibile sopra qualunque mattonella. */
.eq-cross .tag {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  margin-top: 15px; white-space: nowrap;
  font-weight: 700; font-size: 9px; line-height: 1;
  letter-spacing: .02em; color: var(--c-red);
  text-shadow: 0 0 3px var(--bg), 0 0 3px var(--bg), 0 0 6px var(--bg);
}
/* il "da quanto" del dubbio non segue la regola dello zoom delle altre icone:
   una croce rossa senza età non dice se il dubbio è di adesso o di un'ora fa,
   e a scala nazionale — dove la croce si nota per prima — spariva proprio
   quando serviva. Per questo la scritta sopra è di un corpo più piccolo: lo
   spazio sotto la croce lo dividono in tre. */
.eq-cross .age {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  margin-top: 27px; white-space: nowrap;
  font: 600 10px/1 ui-monospace, monospace; color: var(--age-ink);
  text-shadow: 0 0 3px var(--age-halo), 0 0 5px var(--age-halo),
               0 0 7px var(--age-halo);
}
/* Il "da quanto" sta sopra le mattonelle, quindi il suo contrasto lo detta lo
   SFONDO cartografico scelto, non il tema dell'interfaccia: su scuro, rilievo
   e satellite va chiaro con alone scuro; su "chiara" va quasi nero con alone
   bianco, dove il grigio di prima si perdeva sul fondo pallido. */
:root {
  --age-ink: #eef3fa;
  --age-halo: rgba(6, 9, 14, .92);
}
:root[data-base="chiara"] {
  --age-ink: #0b1017;
  --age-halo: rgba(255, 255, 255, .95);
}
/* senza la pastiglia della magnitudo (zoom basso, o etichetta soppressa
   dall'affollamento) la colonna risale: scritta e "da quanto" non devono
   restare sospesi sopra un buco */
.eq-cross.no-lbl .tag { margin-top: 2px; }
.eq-cross.no-lbl .age { margin-top: 14px; }
@keyframes eq-pulse {
  0%, 100% { transform: scale(1); filter: drop-shadow(0 0 0 var(--col)); }
  50% { transform: scale(1.18); filter: drop-shadow(0 0 6px var(--col)); }
}

/* scossa appena rilevata e non ancora consolidata: cerchi concentrici che si
   allargano dall'epicentro. Deve battere in visibilità tutto il resto della
   mappa, perché è l'unica icona che sta dicendo "sta succedendo ora". */
.eq-ripple {
  position: relative; width: var(--sz); height: var(--sz);
  display: flex; align-items: center; justify-content: center;
}
.eq-ripple .rg, .eq-ripple .core {
  position: absolute; top: 50%; left: 50%;
}
.eq-ripple .rg {
  width: var(--sz); height: var(--sz); margin: calc(var(--sz) / -2) 0 0
    calc(var(--sz) / -2);
  border: 2px solid var(--col); border-radius: 50%;
  /* Anello lento di proposito: è il movimento a richiamare l'occhio, non la
     sua velocità, e i cerchi ormai accompagnano l'ultimo evento per tutta la
     sua permanenza in carta — un'onda rapida che pulsa per ore accanto a chi
     sta leggendo i sismogrammi è la cosa da cui si scappa. I ritardi degli
     altri due anelli sono un terzo e due terzi del giro: si susseguono invece
     di accavallarsi. */
  animation: eq-ripple 7.2s cubic-bezier(.2, .6, .3, 1) infinite;
}
.eq-ripple .r2 { animation-delay: 2.4s; }
.eq-ripple .r3 { animation-delay: 4.8s; }
/* al centro la stessa stella degli eventi consolidati, non un pallino: così
   la scossa appena rilevata è già leggibile come epicentro */
.eq-ripple .core {
  width: var(--core); height: var(--core);
  margin: calc(var(--core) / -2) 0 0 calc(var(--core) / -2);
  font-size: var(--core); line-height: var(--core); text-align: center;
  color: var(--col);
  text-shadow: 0 0 8px var(--col),
               0 0 2px color-mix(in srgb, var(--bg) 70%, transparent);
  -webkit-text-stroke: 1.5px #000;  /* stesso bordino delle stelline */
  paint-order: stroke fill;
  /* battito lento come gli anelli: se la stella pulsasse tre volte più in
     fretta i due movimenti si contraddirebbero */
  animation: eq-core 3.6s ease-in-out infinite;
}
.eq-ripple .lbl {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, calc(var(--core) / 2 + 3px));
  font: 700 11px/1 ui-monospace, monospace; color: var(--ink);
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  padding: 1px 4px; border-radius: 4px; white-space: nowrap;
  border: 1px solid var(--col);
}
/* I cerchi ora accompagnano l'ultimo evento per tutta la sua permanenza in
   carta, non solo i primi minuti: il loro riquadro è largo tre volte la stella
   e, restando cliccabile, rubava il clic ai marker sotto (in uno sciame, agli
   altri epicentri della sequenza). Il riquadro non riceve più il puntatore;
   lo ricevono la stella e le sue scritte, e il clic risale comunque al marker. */
.eq-star-wrap:has(.eq-ripple) { pointer-events: none; }
.eq-ripple .core, .eq-ripple .lbl, .eq-ripple .tag, .eq-ripple .age {
  pointer-events: auto;
}
@keyframes eq-ripple {
  0%   { transform: scale(.18); opacity: 0; }
  12%  { opacity: .95; }
  100% { transform: scale(1); opacity: 0; }
}
@keyframes eq-core {
  0%, 100% { transform: scale(.85); opacity: .75; }
  50%      { transform: scale(1.15); opacity: 1; }
}
/* chi ha chiesto meno animazioni: la stella smette di battere e resta ferma,
   l'anello rallenta ancora rispetto al giro normale (che è già lento: il
   vecchio valore di questa riga, 4.8 s, oggi sarebbe più VELOCE del normale) */
@media (prefers-reduced-motion: reduce) {
  .eq-ripple .rg { animation-duration: 12s; }
  .eq-ripple .r2 { animation-delay: 4s; }
  .eq-ripple .r3 { animation-delay: 8s; }
  .eq-ripple .core { animation: none; }
}
/* In VETRINA gli anelli pulsano come qui, senza limite di tempo. Per un periodo
   si fermavano dopo cinque minuti (classe `aged`) per non tenere il compositore
   di Chrome a ridisegnare l'intero schermo a 60 fps, che in software è un core:
   la spesa è stata riaccettata, perché una diretta la si apre in un momento
   qualsiasi e un anello fermo non annuncia niente. Nessuna regola `body.stream`
   sui cerchi: quello che si vede al monitor è quello che va in onda. */

/* la tela dello scuotimento interpolato: sta nel suo riquadro, si posiziona
   con una transform (L.DomUtil.setPosition) e non deve mai rubare il clic ai
   sismografi che ci stanno sopra */
.qm-shake { position: absolute; left: 0; top: 0; pointer-events: none; }

/* Il riquadro della citazione delle mattonelle non c'è più su nessuna delle
   carte (vedi app.js, attributionControl): se Leaflet ne crea comunque uno,
   resta fuori dalla vista invece di riaffacciarsi in un angolo. */
.leaflet-control-attribution { display: none !important; }

/* I controlli Leaflet (zoom, legenda) stanno a z-index 1000: senza uno
   stacking context proprio della mappa galleggiano sopra tutto ciò che sta
   più in basso (#evlist a 950, #loader a 900). Isolarli qui li rende
   invisibili a qualunque overlay esterno, presente e futuro. */
#map { position: relative; z-index: 0; }
#m-map { position: relative; z-index: 0; }

/* ==================== mobile ==================== */

/* Trascinamento touch dei resizer: senza touch-action:none il browser
   interpreta il gesto come scroll e si prende il pointer al primo pixel
   (pointercancel), uccidendo il drag. */
#resizer, #resizer-left { touch-action: none; }
/* Niente zoom da doppio tap sui controlli e sui canvas dell'app: il doppio
   tap sui sismogrammi del modal è "torna alla finestra intera", non zoom
   della pagina. manipulation lascia comunque pan e pinch. */
button, select, input, label,
.event, .rp-ev, .ev-more, #replay-badge,
.chan canvas, .mwave canvas, #d-canvas, #tr-canvas { touch-action: manipulation; }
/* I sismogrammi della revisione manuale gestiscono da sé pizzicata (zoom
   temporale) e trascinamento orizzontale (scorrimento della finestra): al
   browser resta il solo scorrimento verticale del modal. Con `manipulation`
   la pizzicata la prendeva la pagina e la record section restava inchiodata
   alla finestra intera — da telefono non c'era modo di zoomare. */
.mwave canvas { touch-action: pan-y; }

/* Touch: bersagli più grandi. I 6px dei resizer sono intoccabili col dito. */
@media (pointer: coarse) {
  #resizer, #resizer-left { flex-basis: 16px; display: flex;
    align-items: center; justify-content: center; }
  #resizer.collapsed { display: none; }
  /* maniglia visiva: senza, una barra spessa sembra un separatore inerte */
  #resizer::after, #resizer-left::after { content: ""; width: 44px;
    height: 5px; border-radius: 3px; background: var(--line-2); }
  button.toggle { padding: 8px 12px; }
  .watch .head { line-height: 22px; }
  .watch .head button.x { font-size: 18px; padding: 2px 8px; }
  .chanbox input, #ev-filter input, .evl-filter input, .mrev-use input {
    width: 16px; height: 16px; }
  .mag-min { padding: 4px 6px; font-size: 12px; }
  #watches { --chan-base: 44px; }
  .legend input { padding: 6px; }
}
/* col layout desktop (affiancato) il #resizer resta verticale: maniglia ruotata */
@media (pointer: coarse) and (min-width: 821px) {
  #resizer::after { width: 5px; height: 44px; }
}
/* Tablet in orizzontale: la larghezza basterebbe per le tre colonne, ma con
   l'elenco eventi a sinistra (340 px) e i sismografi a destra (420) alla
   carta ne restano sì e no 500 su 1280 — cioè il pezzo che conta si prende
   meno di metà schermo. Sopra gli 820 px il telefono non c'è più, e il dito
   (pointer: coarse) distingue il tablet dal desktop, dove lo spazio c'è e la
   colonna resta. Non si perde niente: gli eventi stanno dietro al pulsante
   «Eventi» in testata e l'ultimo rilevato resta nella pill sulla carta. */
@media (pointer: coarse) and (min-width: 821px) and (max-width: 1400px) {
  #side-left, #resizer-left { display: none; }
}

/* Schermi stretti: mappa sopra, pannelli sotto (la sidebar fissa da 420px
   non entra), header su più righe, modal grandi a schermo pieno. */
@media (max-width: 820px) {
  /* Altezza del guscio: `--app-h` è la viewport MISURATA in JS (app.js,
     syncAppHeight), 100dvh il ripiego. Serve la misura perché su alcuni
     browser Android 100dvh vale più della porzione visibile — la barra
     inferiore finiva fuori dallo schermo e la pagina sembrava "troppo alta".
     Con overflow: hidden il guscio non può nemmeno scorrere via: se qualcosa
     sfora, si stringe il contenuto (che ha già i suoi min-height e scroll
     interni), non si perde la barra di navigazione dell'app. */
  html { overflow: hidden; overscroll-behavior: none; }
  body { height: 100dvh; height: var(--app-h, 100dvh); overflow: hidden; }
  header { flex-wrap: wrap; gap: 6px 10px; padding: 6px 10px;
    padding-left: calc(10px + env(safe-area-inset-left));
    padding-right: calc(10px + env(safe-area-inset-right)); }
  #layout { flex-direction: column; }
  /* La sidebar eventi su telefono non esiste: la mappa si prende tutto lo
     schermo, gli eventi vivono nell'elenco a schermo pieno (tab Eventi) e
     l'ultimo rilevato nella pill in alto a destra (#ev-pill). */
  #side-left, #resizer-left { display: none; }
  #map { order: 1; } #resizer { order: 2; } #side { order: 3; }
  /* mappa a tutta altezza; con almeno un sismografo aperto torna ad altezza
     fissa (--map-h la scrive il resizer, app.js) e il resto va alle tracce */
  #map { flex: 1 1 auto; min-height: 160px; }
  #layout:has(#side:not(.collapsed)) #map { flex: 0 0 auto;
    height: 45vh; height: var(--map-h, 45dvh); }
  #resizer { cursor: row-resize; }
  body.resizing { cursor: row-resize; }
  /* tab "Onde": i sismogrammi si prendono tutta l'altezza. La carta non si
     nasconde con display:none (Leaflet si ritroverebbe a 0×0 e al ritorno
     disegnerebbe tessere grigie): resta nel flusso, alta zero. */
  body.waves-full #layout:has(#side:not(.collapsed)) #map,
  body.waves-full #map { flex: 0 0 auto; height: 0; min-height: 0;
    overflow: hidden; }
  body.waves-full #resizer { display: none; }
  /* width: la scrive inline il resizer desktop (localStorage), qui va ignorata */
  #side, #side-left { width: auto !important; flex: 1 1 auto; min-height: 0;
    border-left: none; border-right: none;
    padding-bottom: calc(6px + env(safe-area-inset-bottom)); }
  /* iOS Safari zooma tutta la pagina sul focus di campi sotto i 16px */
  input, select, textarea { font-size: 16px; }

  /* Comandi della carta su telefono: due pulsanti a sola icona, in riga.
     In colonna con le etichette si mangiavano un quarto dell'altezza della
     mappa. La pill dell'ultimo evento sta nell'angolo opposto, di fianco allo
     zoom. */
  .map-tools { flex-direction: row; gap: 5px; }
  .mt-lab { display: none; }
  .mt-btn { padding: 7px 10px; width: auto; }
  .mt-drop { font-size: 13px; min-width: 200px; }
  /* il tocco vuole un bersaglio, non un pixel: righe più alte della versione
     desktop, e le caselle più grandi */
  .mt-row { padding: 9px 8px; }
  .mt-row input { width: 16px; height: 16px; }
  .mt-chip { padding: 5px 10px; font-size: 12px; }
  .mt-sub { padding-left: 20px; }

  /* Modal a schermo pieno: su telefono lo sono TUTTI, non solo la scheda
     evento e i suoi vicini. Una finestra centrata con dieci pixel di margine
     su un lato non regala niente e su un pannello di testo (post-mortem,
     aiuto, consumo, crediti) toglie righe utili. L'id batte le width dei
     singoli box (.pm-box, .cl-box, .snd-box, .help-box, .us-box, .cr-box). */
  #modal .modal-box, #drum .modal-box, #tremor .modal-box,
  #evlist .modal-box, #pm-modal .modal-box, #cl-modal .modal-box,
  #snd-modal .modal-box, #help .modal-box, #rp-modal .modal-box,
  #us-modal .modal-box, #ovm .modal-box, #credits .modal-box,
  #disc .modal-box, .modal-box:has(#tr-canvas) {
    width: 100vw; height: 100dvh; max-height: 100dvh;
    border-radius: 0; border: none;
  }
  .modal-head { flex-wrap: wrap; row-gap: 6px; padding: 8px 10px; }
  /* pannello vulcani: su un telefono la testata è già di due righe, e il
     valore live che spingeva la ✕ a capo ne aggiungeva una terza per niente */
  .tr-head-row { gap: 6px; }
  .tr-head-row #tr-live { min-width: 0; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; }
  /* Testata vulcani in ordine: la prima riga tiene vulcano, live e chiusura;
     i quattro selettori della serie scendono a riga piena, in griglia 2×2,
     invece di spezzarsi a caso a metà riga. */
  .tr-head-row > .tr-field { flex: 1 1 auto; min-width: 0; }
  #tr-volcano { flex: 1 1 auto; min-width: 0; }
  /* "Vulcani" (il titolo del pannello) e "VULCANO" (l'etichetta del campo)
     dicono la stessa cosa a un dito di distanza: sulla riga stretta l'etichetta
     è solo rumore, il selettore si legge da sé */
  .tr-head-row > label:has(#tr-volcano) > span { display: none; }
  #tr-series-ctl { order: 10; flex: 1 1 100%; display: grid;
    grid-template-columns: 1fr 1fr; gap: 6px; }
  #tr-series-ctl.hidden { display: none; }
  /* l'etichetta sopra il selettore: in griglia 2×2 di fianco non ci sta */
  #tr-series-ctl .tr-field { flex-direction: column; align-items: stretch;
    gap: 2px; }
  #tr-series-ctl select,
  /* i due estremi dell'intervallo personalizzato stanno nella stessa griglia
     dei selettori: a larghezza nativa il campo data sfonda la colonna */
  #tr-series-ctl input[type="datetime-local"] {
    width: 100%; max-width: none; min-width: 0; }
  /* i parossismi stanno con i comandi della serie: una casella piccola non
     ha più bisogno della riga piena che serviva al vecchio pulsante, e a
     differenza degli altri campi non ha un'etichetta sopra da impilare */
  #tr-series-ctl #tr-erupt-btn { align-self: center;
    flex-direction: row; align-items: center; }
  /* riga delle viste: le icone da sole non si leggevano a dito, quindi
     l'etichetta torna anche qui sotto i 560px (la regola generale che la
     toglie serve altrove, non a un pannello con solo quattro pulsanti) */
  .tr-views .btn-label, .tr-erupt-chk .btn-label { display: inline; }
  .tr-views button.toggle { padding: 6px 8px; font-size: 12px; }
  /* riga delle viste: prima i pulsanti e gli allarmi, la descrizione del
     sensore scende a riga sua invece di schiacciare tutto il resto */
  .tr-views #tr-sub { flex: 1 1 100%; order: 10; }
  /* i tre indicatori INGV (tremore, intrusione, parossismo) sono un gruppo
     solo e vanno letti insieme: `flex-wrap: wrap` li spezzava su due righe
     senza un bordo a dirlo, che si leggeva come due gruppi diversi. Una riga
     sola, con scroll orizzontale se non entra tutta — non un'ellissi, che su
     un allarme acceso taglierebbe proprio la parola che conta. */
  /* riga tutta loro (dopo #tr-sub, che è ordine 10): spartendo la riga con i
     pulsanti delle viste restavano 170 px scarsi e i tre indicatori si
     leggevano solo scorrendoli uno alla volta */
  .tr-status { flex: 1 1 100%; order: 11;
    flex-wrap: nowrap; overflow-x: auto; max-width: 100%;
    padding-bottom: 2px; -webkit-overflow-scrolling: touch; }
  .tr-status .tr-alert { flex: 0 0 auto; }
  .tr-cam-bar { gap: 8px; }
  .cam-row { gap: 4px 6px; }
  /* lo slider a tutta riga: a dito è l'unico comando che si usa davvero */
  .cam-row-slider #tr-cam-slider { flex: 1 0 100%; }
  /* le etichette dei comandi della cam tornano visibili anche sotto i 560px:
     ◁▷▶ da soli, senza scritta, erano il motivo per cui play e "fotogramma
     successivo" si confondevano (la regola generale che le toglie serve
     altrove, non a una barra con appena sette pulsanti in tre righe) */
  .tr-cam-bar .btn-label { display: inline; }
  .tr-cam-bar button.toggle { padding: 6px 9px; font-size: 12px; }
  .cam-cap { flex-basis: 100%; order: -1; }
  /* la nota in calce parte chiusa: nella vista telecamere è un paragrafo di
     venti righe fra cadenze, licenze e istruzioni, e su un telefono si
     prendeva più spazio del grafico. Il pulsante la riapre. */
  .tr-note-btn { display: block; }
  #tr-note { display: none; }
  /* niente border-top: sopra c'è già quello del pulsante, e due righe
     attaccate si leggono come un bordo storto */
  #tr-note.open { display: block; max-height: 40vh; overflow-y: auto;
    border-top: none; }
  /* scheda evento: colonne impilate, un unico scroll.
     Vale anche per la scheda delle segnalazioni altrui (#ovm), che usa le
     stesse .m-left/.m-right: senza questa riga le due colonne restavano
     affiancate e su un telefono la .m-left (min-width 320 px) da sola riempiva
     lo schermo — i sismogrammi finivano fuori, invisibili e irraggiungibili. */
  #modal .modal-body, #ovm .modal-body {
    flex-direction: column; overflow-y: auto; }
  .m-left { width: auto; min-width: 0; flex: 0 0 auto; overflow: visible;
    border-right: none; border-bottom: 1px solid var(--line); }
  .m-right { flex: 0 0 auto; overflow: visible; }
  /* colonne impilate: non c'è nessuna divisione verticale da spostare, e una
     maniglia alta 7 px in mezzo alla scheda sarebbe solo un intoppo */
  .m-split { display: none; }
  #m-map { height: 38vh; }
  #ovm-map { height: 34vh; }
  /* Storico eventi: una tabella a nove colonne su un telefono è uno scroll
     orizzontale cieco. Sotto gli 820 px il JS disegna le stesse righe come
     schede (#evl-cards) e la tabella sparisce. */
  #evl-table { display: none; }
  #evl-cards { display: flex; }
  .modal-body.evl-body { overflow-x: hidden; }
  /* replay: schermo pieno come gli altri (la width sta nella regola comune
     qui sopra), col form manuale sotto la lista invece che di fianco */
  .modal-body.rp-body { flex-direction: column; overflow-y: auto; }
  /* La barra di revisione su telefono c'era ma era nascosta: il ripick a dito
     su una traccia alta 58 px non era un'operazione reale. Invece di toglierla
     si dà spazio a chi la usa — tracce più alte (drawRecord legge l'altezza
     dal CSS) e comandi a misura di pollice — e la barra resta appiccicata in
     cima alla colonna, così non si perde scorrendo dieci sismogrammi. */
  .mrev-wrap { position: sticky; top: 0; z-index: 4;
    background: var(--panel); padding: 6px 0 4px;
    border-bottom: 1px solid var(--line); margin-bottom: 6px; }
  .mrev { gap: 8px; }
  .mrev button, .mrev .mrev-false { padding: 9px 14px; font-size: 13px; }
  #mr-msg { flex: 1 0 100%; font-size: 12px; }
  .mwave canvas { height: 84px; }
  .mw-sub canvas { height: 64px; }
  .mwave .mrev-use { padding: 4px 0 4px 8px; }
  .mwave .mrev-use input { width: 16px; height: 16px; }
  /* a 10 px il comando è sotto la soglia del polpastrello: stesso trattamento
     già dato alla spunta «usa» */
  .mw-chans-all { padding: 5px 9px; font-size: 11px; }
  /* barra inferiore al posto della fila di icone in header */
  #tabbar { display: flex; }
  header { flex-wrap: nowrap; }
  /* min-width/overflow espliciti: un <button> non si comprime da solo quando
     la riga è piena (il min-width:auto dei form control non collassa come su
     un div). Senza questi il badge della sessione Claude sfondava la testata
     e portava in scroll orizzontale l'intera pagina. Solo qui: su desktop lo
     spazio c'è e il conteggio deve restare per esteso. */
  header #claude-live { min-width: 0; overflow: hidden; }
  /* quando lo spazio manca cede il badge (transitorio, e il titolo completo è
     nel tooltip), non il conteggio stazioni: quello è l'indicatore di salute e
     resta leggibile per intero */
  header #status { flex-shrink: 0; }
  #status-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  header #btn-audio, header #btn-events, header #btn-tremor,
  header #btn-replay, header #settings-box, header #claude-box,
  header #btn-help, header #search-box,
  /* su telefono diretta e canale vivono nel foglio "Altro", come le altre voci
     che qui non hanno un posto in prima fila */
  header #btn-live, header #btn-tg { display: none; }
  .modal-box, .pop { padding-bottom: env(safe-area-inset-bottom); }
  #side { padding-bottom: calc(6px + env(safe-area-inset-bottom)); }
  body:not(.no-tabbar) #layout { padding-bottom: 0; }
}

/* Molto stretti (telefoni in verticale): header compatto, legenda a scomparsa */
@media (max-width: 560px) {
  header { gap: 6px 8px; }
  header h1 { font-size: 15px; }
  /* la riga è già contesa da titolo, copertura e conteggio stazioni: resta
     l'ora, che è il dato, e cadono data e sigla del fuso (stanno nel tooltip) */
  #clock-tz, #clock-date { display: none; }
  .btn-label { display: none; }
  #rb-speed, #rb-bar { display: none; }
  /* eccezione: durante il download sono l'unica cosa che dice che sta
     succedendo qualcosa, e nasconderli lascia un badge fermo e muto */
  #replay-badge.loading #rb-time { display: none; }
  #replay-badge.loading #rb-speed { display: inline; }
  #replay-badge.loading #rb-bar { display: block; width: 70px; }
  /* il tetto fisso al 40vw tagliava "stazioni attive" anche quando in riga
     c'era spazio: ora il conteggio prende quello che avanza e l'ellissi resta
     solo come rete di sicurezza */
  #status { max-width: none; }
  /* Il badge della sessione Claude è largo quanto la frase che contiene
     ("revisione #4293 · 1 min 10 s"): in fila con titolo, copertura e
     conteggio stazioni sfondava la testata e portava in scroll orizzontale
     tutta la pagina. Su telefono va a riga sua, per intero e senza troncature,
     e la riga esiste solo mentre una sessione è in corso. */
  header { flex-wrap: wrap; }
  #claude-live {
    order: 10; flex: 1 0 100%; max-width: none; justify-content: center;
  }
  /* Legenda compatta: righe fitte, controlli più piccoli, e le note
     discorsive si sacrificano — erano quattro righe di testo su dieci di
     legenda. Gli errori di caricamento degli strati invece restano. */
  .map-legend { max-width: min(74vw, 300px); font-size: 10px;
    padding: 4px 7px; line-height: 1.25;
    /* aperta, con vulcani e strati di contesto, la legenda arriva a mezzo
       schermo e il fondo finiva TAGLIATO dal bordo della mappa (l'ultima
       sezione non si leggeva affatto): oltre questa altezza scorre dentro di
       sé, invece di sparire fuori */
    max-height: 46dvh; overflow-y: auto; overscroll-behavior: contain; }
  .map-legend .ml-modes { gap: 3px; margin-bottom: 4px; }
  .map-legend .ml-mode { padding: 1px 6px; font-size: 10px; }
  .map-legend .ml-rows { gap: 2px 8px; }
  .map-legend .ml-sec { margin: 5px 0 2px; }
  .map-legend .ml-note:not(.ml-err) { display: none; }
}

/* Telefono tenuto in verticale: cade anche l'ora, e con lei tutto l'orologio.
   Anche ridotta al solo orario restava il pezzo di troppo — titolo, copertura
   e conteggio stazioni riempiono già la riga, e per pochi pixel la testata
   andava a capo. L'ora vera è a due centimetri, nella barra di sistema del
   telefono; il fuso in cui sono scritti gli eventi resta in Vista. In
   orizzontale c'è spazio e l'orologio torna. */
@media (max-width: 560px) and (orientation: portrait) {
  #clock { display: none; }
}

/* ==================== componenti nuovi ==================== */

/* --- conteggio stazioni come indicatore di salute --- */
.health-btn {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  background: none; border: 1px solid transparent; border-radius: 6px;
  color: inherit; font: inherit; padding: 3px 8px;
}
.health-btn:hover { border-color: var(--line-2); background: var(--panel-2); }
#status-ring {
  width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0;
  background: var(--c-stale);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-stale) 25%, transparent);
}
#status-ring.ok { background: var(--c-green);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-green) 25%, transparent); }
#status-ring.warn { background: var(--c-yellow);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-yellow) 25%, transparent); }
#status-ring.bad { background: var(--c-red);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-red) 25%, transparent); }

/* --- banner di connessione persa --- */
#conn-banner {
  display: flex; align-items: center; gap: 12px; justify-content: center;
  background: color-mix(in srgb, var(--c-red) 18%, var(--panel));
  color: var(--ink); border-bottom: 1px solid var(--c-red);
  padding: 6px 12px; font-size: 13px; flex: 0 0 auto;
}
#conn-banner.hidden { display: none; }
#conn-banner.warn { background: color-mix(in srgb, var(--c-yellow) 16%, var(--panel));
  border-bottom-color: var(--c-yellow); }

/* --- pill dell'ultimo evento registrato: in alto a sinistra della carta, di
       fianco ai pulsanti dello zoom, a ogni larghezza. Su telefono è l'unica
       traccia dell'ultimo evento (la sidebar non c'è); su desktop resta il
       riferimento che non scade mentre la sidebar mostra solo l'ultima ora.
       La posizione la dà Leaflet: sta in un control nell'angolo in alto a
       sinistra (vedi mapNotesCtl), insieme al badge della sessione Claude.
       Il bordo sinistro prende il colore della magnitudo (JS). --- */
/* L'angolo in alto a sinistra ospita due control: lo zoom e le due notifiche.
   Leaflet li impilerebbe in colonna, con la pill sotto al [+]/[−] a mangiarsi
   l'altezza della carta: qui vanno in riga, notifiche a destra dello zoom. */
#map .leaflet-top.leaflet-left { display: flex; align-items: flex-start; }
/* l'id serve: `.leaflet-left .leaflet-control` batte `.map-notes` e rimetterebbe
   i suoi 10px di margine sinistro */
#map .leaflet-top.leaflet-left .map-notes {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 6px; margin: 10px 0 0 4px; }
/* casella della quiete: stessa veste della pill (pannello semitrasparente sopra
   la carta), ma compatta — è un comando, non deve pesare quanto una notifica */
.map-quiet {
  display: flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 4px 8px; border-radius: 8px;
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  border: 1px solid var(--line-2); color: var(--ink);
  font-size: 11px; line-height: 1.2; white-space: nowrap;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .4); backdrop-filter: blur(2px);
}
.map-quiet:hover { background: var(--hover); }
.map-quiet input { margin: 0; cursor: pointer; flex-shrink: 0; }
.map-quiet.hidden { display: none; }
#ev-pill {
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  max-width: min(62vw, 260px); padding: 5px 10px; cursor: pointer;
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  border: 1px solid var(--line-2); border-left: 4px solid var(--c-orange);
  border-radius: 8px; color: var(--ink); font: inherit; text-align: left;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .4); backdrop-filter: blur(2px);
}
#ev-pill.hidden { display: none; }
/* dice che cos'è la pill: senza, una magnitudo sola in un angolo della carta
   si legge come un'allerta in corso */
#ev-pill .ep-head { font-size: 9px; font-weight: 700; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .07em; }
#ev-pill .ep-mag { font-weight: 700; font-size: 13px;
  font-variant-numeric: tabular-nums; }
#ev-pill .ep-place { font-size: 11px; max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
#ev-pill .ep-when { font-size: 10px; color: var(--ink-2);
  font-variant-numeric: tabular-nums; }
#ev-pill .ep-sol { margin-top: 3px; }
/* il voto della soluzione nell'angolo in alto a destra: fuori dal flusso, così
   non sposta di un pixel la magnitudo e il luogo, che sono la sostanza */
#ev-pill { position: relative; }
#ev-pill.has-q { padding-right: 32px; }
#ev-pill > .q-badge { position: absolute; top: 5px; right: 7px; margin-left: 0; }
#ev-pill.is-new { animation: ev-in 1.6s ease-out; }

/* --- popover ancorati all'header (soglie, vista, salute) --- */
.pop {
  position: fixed; top: 52px; right: 10px; z-index: 1300;
  width: min(400px, 94vw); max-height: min(80vh, 720px);
  background: var(--panel); border: 1px solid var(--line-2);
  border-radius: 10px; box-shadow: var(--shadow);
  display: flex; flex-direction: column;
}
.pop.hidden { display: none; }
/* la salute della rete si apre in alto a SINISTRA, sotto il conteggio */
.pop.pop-left { left: 10px; right: auto; }
.pop-head { display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-bottom: 1px solid var(--line); }
.pop-body { padding: 11px 13px; overflow-y: auto; }
.pop-field { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin: 9px 0; color: var(--ink-2); font-size: 13px; }
.pop-field select, .pop-field input {
  background: var(--bg); color: var(--ink); border: 1px solid var(--line-2);
  border-radius: 5px; padding: 4px 6px; font: inherit; font-size: 13px;
  max-width: 62%;
}
.pop-check { display: flex; align-items: center; gap: 8px; margin: 10px 0;
  color: var(--ink-2); font-size: 13px; cursor: pointer; }
#th-reset { margin-left: 2px; }
#th-preview { color: var(--ink); }
.th-bad { color: var(--c-red) !important; }
.legend input.bad { border-color: var(--c-red); }
/* su telefono un popover ancorato all'angolo è irraggiungibile: diventa un
   foglio dal basso, come il resto */
@media (max-width: 820px) {
  .pop { top: auto; bottom: 0; right: 0; left: 0; width: 100vw;
    border-radius: 12px 12px 0 0; max-height: 80dvh;
    padding-bottom: env(safe-area-inset-bottom); }
  /* .pop-left (la salute della rete) ha specificità maggiore di .pop e il
     media query non la cambia: senza questa riga il foglio restava incollato a
     left:10px ed era largo 100vw, cioè sbordava di 10 px a destra */
  .pop.pop-left { left: 0; right: 0; }
}

/* --- salute della rete --- */
.hl-top { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 10px; }
.hl-stat { display: flex; flex-direction: column; }
.hl-stat b { font-size: 19px; font-variant-numeric: tabular-nums; }
.hl-stat span { color: var(--ink-2); font-size: 11px; text-transform: uppercase;
  letter-spacing: .6px; }
.hl-bar { height: 6px; border-radius: 3px; background: var(--line);
  overflow: hidden; margin: 2px 0 12px; }
.hl-bar i { display: block; height: 100%; background: var(--c-green); }
#health-body table { width: 100%; border-collapse: collapse; font-size: 12px; }
#health-body th { text-align: left; color: var(--ink-3); font-size: 10px;
  text-transform: uppercase; letter-spacing: .5px; padding: 4px 6px; }
#health-body td { padding: 3px 6px; border-top: 1px solid var(--line);
  font-variant-numeric: tabular-nums; }
#health-body td:first-child { font-family: ui-monospace, monospace; }

/* --- ricerca stazioni --- */
#search-box { position: relative; }
#search {
  background: var(--panel-2); color: var(--ink); border: 1px solid var(--line-2);
  border-radius: 6px; padding: 5px 10px; font: inherit; font-size: 13px;
  width: 180px; transition: width .15s;
}
#search:focus { width: 240px; outline: 2px solid var(--c-blue); outline-offset: 1px; }
#search-res {
  position: absolute; top: 110%; right: 0; width: 300px; z-index: 1300;
  background: var(--panel); border: 1px solid var(--line-2); border-radius: 8px;
  box-shadow: var(--shadow); max-height: 50vh; overflow-y: auto; padding: 4px;
}
#search-res.hidden { display: none; }
.sr-item { padding: 6px 8px; border-radius: 5px; cursor: pointer;
  display: flex; gap: 8px; align-items: baseline; }
.sr-item:hover, .sr-item.sel { background: var(--hover); }
.sr-item b { font-family: ui-monospace, monospace; font-size: 12px; }
.sr-item span { color: var(--ink-2); font-size: 12px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

/* --- barra inferiore (solo mobile) --- */
#tabbar {
  display: none; flex: 0 0 auto; background: var(--panel);
  border-top: 1px solid var(--line); z-index: 800;
  padding-bottom: env(safe-area-inset-bottom);
}
#tabbar .tab {
  flex: 1 1 0; min-width: 0; background: none; border: none; color: var(--ink-2);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 7px 2px 6px; font-size: 10px; cursor: pointer;
}
/* con Onde e Vulcani accese le voci sono sei: le etichette si restringono
   invece di andare a capo e alzare la barra */
#tabbar .tab > span:last-child {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 560px) {
  #tabbar .tab { font-size: 9.5px; padding: 7px 1px 6px; }
}
#tabbar .tab .ti { font-size: 17px; line-height: 18px; }
#tabbar .tab.on { color: var(--c-blue); }
/* la tab Onde esiste solo con almeno un sismografo aperto (syncWaveSidebar):
   serve il combinato perché la regola base ha specificità maggiore di .hidden */
#tabbar .tab.hidden { display: none; }

/* --- foglio "Altro" --- */
.sheet-box {
  position: relative; width: 100vw; background: var(--panel);
  border-radius: 14px 14px 0 0; border-top: 1px solid var(--line-2);
  padding: 6px 10px calc(10px + env(safe-area-inset-bottom));
  box-shadow: var(--shadow);
}
.sheet-grip { width: 40px; height: 4px; border-radius: 2px;
  background: var(--line-2); margin: 6px auto 10px; }
/* Riga a due colonne: icona in un riquadro a larghezza fissa, etichetta
   allineata con tutte le altre. Con gli emoji dentro al testo ogni voce
   partiva da un'ascissa diversa (⚙ e 🎚 non sono larghi uguale) e l'elenco si
   leggeva come una fila storta. */
.sheet-item {
  display: flex; align-items: center; gap: 12px;
  width: 100%; text-align: left; background: none;
  border: none; border-radius: 8px; color: var(--ink); font: inherit;
  padding: 13px 12px; cursor: pointer;
}
.sheet-item .si {
  flex: 0 0 24px; display: inline-flex; align-items: center;
  justify-content: center; font-size: 17px; line-height: 1;
}
.sheet-item:hover, .sheet-item:active { background: var(--hover); }
/* la regola base sta dopo .hidden e a pari specificità vincerebbe lei */
.sheet-item.hidden { display: none; }
.snd-row.hidden { display: none; }

/* --- schede dell'elenco eventi (mobile) --- */
.evl-cards { display: none; flex-direction: column; gap: 6px; padding: 4px 0 8px; }
.evl-card {
  display: flex; gap: 10px; align-items: flex-start; cursor: pointer;
  border: 1px solid var(--line-2); border-left-width: 3px; border-radius: 8px;
  padding: 8px 10px; background: var(--panel-2);
}
.evl-card .m { font-size: 19px; font-weight: 700; min-width: 72px;
  font-variant-numeric: tabular-nums; }
/* la magnitudo INGV sotto le nostre, nella stessa colonna: è un confronto, e
   un confronto si legge solo se i due numeri stanno vicini. Il colore è quello
   del link INGV (.ingv), non quello della magnitudo: non è una nostra misura.
   `nowrap` perché "INGV ML 1.2 ↗" a capo su due righe non è più una riga. */
.evl-card .m .mi { font-size: 11px; font-weight: 600; margin-top: 2px;
  color: var(--c-green); white-space: nowrap; line-height: 1.3; }
.evl-card .b { min-width: 0; flex: 1; }
.evl-card .b .w { font-variant-numeric: tabular-nums; font-size: 12px;
  color: var(--ink); }
.evl-card .b .p { font-weight: 600; margin: 1px 0; }
.evl-card .b .x { color: var(--ink-2); font-size: 12px; }
.evl-card.is-anthro { opacity: .72; }
.evl-card.is-cat {
  background: color-mix(in srgb, var(--c-green) 6%, var(--panel-2));
}

/* --- scheda evento: titolo, azioni, focale --- */
.m-headline { font-size: 17px; font-weight: 700; margin: 0 0 10px;
  line-height: 1.25; }
/* stato della soluzione in alto a destra della colonna sinistra, sopra la
   carta: fuori dal flusso del titolo, così una località lunga non lo spinge
   a capo */
.m-headline .mh-badge { float: right; margin: 2px 0 4px 10px; }
.m-headline .mh-sub { display: block; font-size: 12px; font-weight: 400;
  color: var(--ink-2); margin-top: 3px; font-variant-numeric: tabular-nums; }
a.toggle { text-decoration: none; display: inline-block; }
/* nella testata sono figli di un flex accanto ai pulsanti: allineati al centro
   come loro, e con il gap che il flex mangia fra icona ed etichetta */
header a.toggle { display: inline-flex; align-items: center; gap: 5px; }
/* a.toggle è (0,1,1) e batterebbe .hidden: serve il combinato, come per
   .mrev e #replay-badge più sopra */
a.toggle.hidden { display: none; }
.m-focal.hidden { display: none; }
/* stacco dalla tabella dei parametri: il titolo appoggiava sull'ultima riga
   ("origine") e le due cose si leggevano come un unico blocco */
.m-focal { margin-top: 12px; }
.m-focal > h3 { margin: 0 0 6px; }
.m-focal-row { display: flex; gap: 12px; align-items: center; }
#m-beach { flex: 0 0 auto; }
/* filmato della diretta: stesso stacco delle altre sezioni della scheda. Il
   video non si scarica finché non lo si chiede (preload="none"): sono
   centinaia di MB, e chi apre la scheda quasi sempre vuole i numeri. */
.m-clip.hidden { display: none; }
.m-clip { margin-top: 12px; border-top: 1px solid var(--line);
  padding-top: 10px; }
.m-clip h3 { margin: 0 0 6px; }
.m-clip video { width: 100%; max-height: 60vh; background: #000;
  border-radius: 4px; }
.m-clip .m-clip-note { margin: 6px 0 0; }
/* storico delle revisioni: chiuso di default, e con una tabella che può
   essere più larga della colonna (7 colonne di numeri) — scorre da sola
   invece di allargare il modal */
.m-hist.hidden { display: none; }
/* stesso filetto di separazione dei parametri secondari: le due barre sono
   sezioni pari, e ognuna si stacca da quello che ha sopra */
.m-hist { margin-top: 12px; border-top: 1px solid var(--line);
  padding-top: 10px; }
.m-hist h3 { margin: 0 0 4px; }
/* la veste della barra sta insieme a quella di `.m-sec > summary`, in cima al
   foglio: qui restano solo le cose che il <button> non eredita comunque */
/* il conteggio ("· 2 soluzioni") sta nella riga del titolo: stesso corpo,
   altrimenti `.small` lo rifà più grande del titolo che accompagna */
.m-hist-tog .small { font-size: inherit; text-transform: none;
  letter-spacing: 0; }
/* firma della revisione in testa allo storico: staccata dalla tabella da un
   filetto proprio, o si legge come una riga di intestazione */
.mh-rev { margin: 0 0 10px; padding: 0 0 8px;
  border-bottom: 1px solid var(--line); }
.mh-rev-head { display: flex; align-items: center; gap: 6px;
  flex-wrap: wrap; }
#m-hist-body { padding-top: 6px; }
#m-hist-body.hidden { display: none; }
.mh-scroll { overflow-x: auto; }
.mh-tab { width: 100%; border-collapse: collapse; font-size: 11px;
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.mh-tab th { text-align: left; font-weight: 600; color: var(--ink-2);
  padding: 2px 6px 4px 0; border-bottom: 1px solid var(--line); }
.mh-tab td { padding: 3px 6px 3px 0; border-bottom: 1px solid var(--line);
  vertical-align: top; }
.mh-tab tr:last-child td { border-bottom: 0; }
.mh-tab .mh-n { color: var(--ink-2); }
/* il punteggio accanto alla lettera: si legge insieme al badge, quindi niente
   a capo fra i due, e in tono minore perché la lettera resta la sintesi */
.mh-tab .mh-q { white-space: nowrap; }
/* il punteggio 0-100: vale anche fuori dallo storico (colonna qualità
   dell'elenco eventi), dove la tabella non impone già le cifre tabulari */
.mh-qs { color: var(--ink-2); font-variant-numeric: tabular-nums; }
/* lo spostamento rispetto alla soluzione precedente: è il numero per cui lo
   storico esiste, non deve confondersi con le coordinate accanto */
.mh-d { color: var(--accent); }
/* La riga scritta dal ricalcolo appena chiesto con "Applica": lo storico si
   apre da sé e ci si scorre sopra, ma in mezzo ad altre otto righe uguali
   quella nuova va indicata. Tre lampeggi e poi si spegne: è un richiamo, non
   uno stato — la riga resta una riga come le altre. */
.mh-tab tr.mh-flash td { animation: mh-flash 1s ease-in-out 3; }
@keyframes mh-flash {
  50% { background: color-mix(in srgb, var(--c-blue) 26%, transparent); }
}
/* meno animazioni: niente lampeggio, ma la riga va comunque distinta — resta
   evidenziata ferma */
@media (prefers-reduced-motion: reduce) {
  .mh-tab tr.mh-flash td { animation: none;
    background: color-mix(in srgb, var(--c-blue) 18%, transparent); }
}

/* revisione manuale: sezione sempre visibile, mai collassabile */
.mrev-wrap { margin: 4px 0 8px; }
.mrev-wrap h3 { margin-bottom: 4px; }
/* …e appiccicata in cima alla colonna dei sismogrammi mentre si scorre: con
   dieci tracce i comandi finivano fuori schermo proprio mentre si riposiziona
   un pick in fondo alla lista. Sotto gli 820 px lo stesso effetto ce l'ha già
   la regola su .mrev-wrap nella media query (là scorre .modal-body, non
   .m-right, e l'aggancio è a top:0). Il top negativo copre il padding
   superiore della colonna, altrimenti si vedrebbe una fetta di traccia
   scorrere sopra la barra. */
@media (min-width: 821px) {
  #m-rev-wrap { position: sticky; top: -12px; z-index: 4;
    background: var(--panel); padding: 6px 0 4px; margin-bottom: 6px;
    border-bottom: 1px solid var(--line); }
}
details.note summary { cursor: pointer; color: var(--ink-2); font-size: 12px; }
details.note { margin-top: 10px; }

/* --- aiuto --- */
.modal-box.help-box { width: min(720px, 94vw); height: auto; max-height: 90vh; }
.modal-body.help-body { display: block; overflow-y: auto; padding: 4px 16px 16px; }
.help-body p { color: var(--ink-2); }
.help-body b { color: var(--ink); }
.help-body .keys { line-height: 2; }
kbd { background: var(--panel-2); border: 1px solid var(--line-2);
  border-bottom-width: 2px; border-radius: 4px; padding: 1px 6px;
  font: 600 11px ui-monospace, monospace; color: var(--ink); }

/* --- crediti e fonti ---
   Un elenco lungo che si legge una volta: righe fitte, la fonte in evidenza e
   il ruolo in grigio, così scorrendo si cerca un nome e non una frase. La
   licenza è una pastiglia perché è l'unico dato che si va a cercare per
   sapere se un pezzo si può rimostrare altrove. */
.modal-box.cr-box { width: min(820px, 94vw); height: auto; max-height: 90vh; }
.modal-body.cr-body {
  display: block; overflow-y: auto; padding: 4px 16px 16px;
  font-size: 13px; line-height: 1.5;
}
.cr-body h3 {
  font-size: 12px; margin: 18px 0 6px; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .8px;
  border-top: 1px solid var(--line); padding-top: 12px;
}
.cr-body h3:first-of-type { border-top: none; }
.cr-body p { color: var(--ink-2); margin: 6px 0; }
.cr-body b { color: var(--ink); }
.cr-lead { border-left: 3px solid var(--c-blue); padding-left: 10px; }
.cr-note { margin-top: 12px !important; }
.cr-items { margin: 6px 0; padding-left: 18px; list-style: none; }
.cr-items li { margin: 8px 0; color: var(--ink-2);
  border-left: 1px solid var(--line-2); padding-left: 10px; margin-left: -18px; }
.cr-lic { background: var(--panel-2); border: 1px solid var(--line-2);
  border-radius: 4px; padding: 0 6px; font-size: 11px; font-weight: 600;
  color: var(--ink); white-space: nowrap; }
.cr-body a { color: var(--c-blue); text-decoration: none; }
.cr-body a:hover { text-decoration: underline; }
.cr-nets-head { margin-bottom: 4px; }
/* le reti sono tante (30 in modalità italia): la tabella scorre da sé invece
   di allargare il modal o spezzare i nomi delle reti a metà parola */
.cr-nets { overflow-x: auto; }
.cr-tab { width: 100%; border-collapse: collapse; font-size: 12px; }
.cr-tab th {
  text-align: left; color: var(--ink-3); font-size: 10px;
  text-transform: uppercase; letter-spacing: .6px; font-weight: 600;
  padding: 5px 8px 3px; border-bottom: 1px solid var(--line);
  position: sticky; top: 0; background: var(--panel);
}
.cr-tab td { padding: 4px 8px; border-bottom: 1px solid var(--line);
  vertical-align: top; color: var(--ink-2); }
.cr-tab td b { font: 600 12px ui-monospace, monospace; }
.cr-tab .cr-n { text-align: right; font-variant-numeric: tabular-nums; }
.cr-doi { white-space: nowrap; font-size: 11px; }

/* --- avvertenze e limiti d'uso (⚠) ---
   Stessa impaginazione dei crediti — è un testo lungo che si legge una volta —
   con una sola differenza voluta: il cappello in rosso, a piena larghezza e col
   corpo del testo normale invece che ridotto. È la frase che deve restare in
   testa a chi la legge di fretta ("questo non è l'INGV"), e in grigio piccolo
   come tutto il resto non la leggerebbe nessuno.
   In gate (prima visita) la ✕ non c'è: l'unica uscita è il pulsante di presa
   visione, e un modale con due uscite di cui una silenziosa non è un gate. */
.modal-box.disc-box { width: min(760px, 94vw); height: auto; max-height: 90vh; }
.modal-body.disc-body {
  display: block; overflow-y: auto; padding: 4px 16px 16px;
  font-size: 13px; line-height: 1.55;
}
.disc-body h3 {
  font-size: 12px; margin: 18px 0 6px; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: .8px;
  border-top: 1px solid var(--line); padding-top: 12px;
}
.disc-body p { color: var(--ink-2); margin: 6px 0; }
.disc-body b { color: var(--ink); }
.disc-hero {
  border: 1px solid color-mix(in srgb, var(--c-red) 45%, var(--line));
  border-left: 4px solid var(--c-red);
  background: color-mix(in srgb, var(--c-red) 9%, var(--panel));
  border-radius: 8px; padding: 12px 14px; margin: 10px 0 4px;
}
.disc-hero p { color: var(--ink); font-size: 14px; }
.disc-hero p:first-child { margin-top: 0; font-size: 15px; }
.disc-hero p:last-child { margin-bottom: 0; }
.disc-links { margin: 6px 0; padding-left: 18px; list-style: none; }
.disc-links li { margin: 6px 0; color: var(--ink-2);
  border-left: 1px solid var(--line-2); padding-left: 10px; margin-left: -18px; }
.disc-body a { color: var(--c-blue); text-decoration: none; }
.disc-body a:hover { text-decoration: underline; }
/* la riga di presa visione resta agganciata in fondo al pannello che scorre:
   su un telefono il pulsante finirebbe sotto due schermate di testo, e "non
   trovo come chiudere" è il modo in cui un gate diventa un guasto */
.disc-ackrow {
  position: sticky; bottom: 0; margin: 14px -16px -16px;
  padding: 12px 16px; background: var(--panel);
  border-top: 1px solid var(--line);
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.disc-ackrow .muted { flex: 1; min-width: 200px; }
/* "⚙ → Avvertenze e limiti" è il percorso da rifare: spezzato a metà si legge
   come due voci diverse */
.disc-ackrow b { white-space: nowrap; }
#disc-ack {
  background: var(--c-blue); color: #fff; border: 0; border-radius: 7px;
  padding: 9px 16px; font: inherit; font-weight: 600; cursor: pointer;
}
.disc-box.gate #disc-close { display: none; }

/* --- l'avvertenza in chiaro sotto l'elenco eventi ---
   Sta nel flusso, non è una pastiglia colorata: deve leggersi come una nota
   della lista che ha sopra, non come un banner da scacciare con lo sguardo.
   Il bordo superiore la separa dai crediti delle fonti, che sono un'altra
   cosa. */
.ev-disc {
  border-top: 1px solid var(--line);
  margin-top: 6px; padding-top: 6px; line-height: 1.45;
}
.ev-disc b { color: var(--ink-2); }
.ev-disc button {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--c-blue); cursor: pointer; text-decoration: underline;
}
/* in vetrina non si clicca: il rimando al pannello diventa testo, e la frase
   deve reggersi da sola */
body.stream .ev-disc button { display: none; }
body.stream .ev-disc { font-size: 12px; opacity: .9; }

/* il richiamo in cima a "Come si legge": stesso rosso del cappello, misura
   ridotta — là il testo per esteso è a un clic */
.help-body p.help-disc {
  color: var(--ink); border-left: 3px solid var(--c-red);
  background: color-mix(in srgb, var(--c-red) 8%, transparent);
  border-radius: 0 6px 6px 0; padding: 9px 12px; margin: 10px 0 4px;
}
.help-disc button, .cr-body button {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--c-blue); cursor: pointer; text-decoration: underline;
}

/* --- evidenza del nuovo evento: senza, una scheda che appare in cima a una
       lista non la vede nessuno --- */
@keyframes ev-in {
  from { background: color-mix(in srgb, var(--c-blue) 40%, var(--panel));
         transform: translateY(-4px); }
}
.event.is-new { animation: ev-in 1.6s ease-out; }

/* --- focus visibile: la navigazione da tastiera esisteva ma era invisibile --- */
:focus-visible { outline: 2px solid var(--c-blue); outline-offset: 2px;
  border-radius: 4px; }

/* --- legenda della mappa richiudibile: su telefono copriva mezza mappa --- */
.map-legend.collapsed .ml-rows, .map-legend.collapsed .ml-sec,
.map-legend.collapsed .ml-note, .map-legend.collapsed .ml-modes,
.map-legend.collapsed .ml-body {
  display: none;
}
.ml-toggle { background: none; border: none; color: var(--ink-2);
  cursor: pointer; font: inherit; font-size: 11px; padding: 0;
  display: flex; align-items: center; gap: 5px; width: 100%; }
.ml-toggle:hover { color: var(--ink); }

/* --- canali come chip (era una fila di sei checkbox per stazione) --- */
.chansel { margin-left: auto; display: flex; flex-wrap: nowrap; gap: 4px;
  justify-content: flex-end; flex-shrink: 0; align-items: center; }
.chip-ch, .chip-more {
  background: var(--panel-2); border: 1px solid var(--line-2); color: var(--ink);
  border-radius: 4px; padding: 1px 6px; font: 10px ui-monospace, monospace;
  cursor: pointer; line-height: 15px;
}
.chip-ch:hover, .chip-more:hover { border-color: var(--c-blue); }
.chip-ch.off { color: var(--ink-3); background: transparent; }
.chip-ch.hide, .chip-more.hide { display: none; }
.chip-more { color: var(--ink-2); border-style: dashed; }

/* il toponimo è la riga che si legge per prima nella scheda evento */
.event .ev-place { font-size: 12px; color: var(--ink); margin-bottom: 1px; }
.event .ev-place b { font-weight: 600; }

/* colonna ordinata dell'elenco eventi */
#evl-table th[data-sort] { cursor: pointer; user-select: none; }
#evl-table th[data-sort]:hover { color: var(--ink-2); }
#evl-table th.sorted { color: var(--c-blue); }
#evl-q {
  background: var(--bg); color: var(--ink); border: 1px solid var(--line-2);
  border-radius: 6px; padding: 4px 9px; font: inherit; font-size: 13px;
  width: 190px;
}

/* flusso fermo: la mappa non deve sembrare viva */
body.stale-feed #map { filter: saturate(.55); }

/* La regola base di #tabbar sta più in basso della media query mobile e a
   pari specificità vinceva lei: la barra va riaffermata qui, in coda. */
@media (max-width: 820px) {
  #tabbar { display: flex; }
  #evl-cards { display: flex; }
  #evl-table { display: none; }
}

/* Intestazione dell'elenco eventi su schermo stretto: titolo, conteggio e
   pulsanti stanno su una riga sola; sotto, una riga per il filtro magnitudo e
   una per la ricerca. Righe intere invece di elementi sparsi a capo a caso. */
@media (max-width: 820px) {
  #evlist .modal-head { flex-wrap: wrap; row-gap: 8px; gap: 6px; }
  #evl-q { order: 10; flex: 1 1 100%; width: auto; }
  #evlist .modal-head .spacer { display: none; }
  /* Il titolo è l'unico elemento elastico e parte da base 0: così non entra
     mai nel conto di chi va a capo, i pulsanti restano tutti sulla prima riga
     e sui telefoni più stretti è il titolo ad accorciarsi con l'ellissi. */
  #evlist .modal-head b {
    flex: 1 1 0; min-width: 0; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; font-size: 14px;
  }
  #evlist .modal-head button.toggle {
    flex: 0 0 auto; padding: 5px 8px; font-size: 12px;
  }
  /* la freccia del CSV è ridondante accanto alla parola: sul telefono lo
     spazio della riga vale più dell'icona */
  #evl-csv .btn-ico { display: none; }
  #evlist .evl-filter { flex: 0 0 auto; white-space: nowrap; }
  #evl-count { flex: 0 0 auto; margin-right: auto; }
  /* riga vuota alta zero: è l'unico modo, in flexbox, di imporre un a capo in
     un punto preciso — tutto quel che ha order maggiore di 4 finisce sotto */
  #evlist .modal-head::before {
    content: ""; order: 4; flex: 0 0 100%; height: 0; margin: -4px 0;
  }
  /* Seconda riga: i due filtri, magnitudo davanti. Con "Nascondi terremoti
     dubbi" in prima riga il titolo restava un moncone; qui i filtri stanno
     insieme, dove lo spazio c'è, e il titolo si legge per intero. */
  #evlist .evl-filter-mag { order: 5; }
  #evlist .evl-filter:not(.evl-filter-mag) { order: 6; }
  /* la ✕ sta in alto a destra, ancorata: nel flusso flex a capo scivolava
     in mezzo alla testata, dove nessuno cerca un pulsante di chiusura */
  #evlist .modal-head { position: relative; padding-right: 52px; }
  #evl-close { position: absolute; top: 8px; right: 10px; }
}

/* --- menù impostazioni (ingranaggio in header) e menù Claude (solo desktop:
       su telefono le stesse voci vivono nel foglio "Altro") --- */
#settings-box, #claude-box { position: relative; }
#claude-drop.hidden { display: none; }
#settings-drop, #claude-drop {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 1350;
  min-width: 150px; background: var(--panel); border: 1px solid var(--line-2);
  border-radius: 8px; box-shadow: var(--shadow); padding: 4px;
  display: flex; flex-direction: column;
}
#settings-drop.hidden { display: none; }
.menu-item {
  display: block; width: 100%; text-align: left; background: none;
  border: none; border-radius: 6px; color: var(--ink); font: inherit;
  font-size: 13px; padding: 8px 10px; cursor: pointer; white-space: nowrap;
}
.menu-item:hover { background: var(--hover); }
.menu-sep { height: 1px; background: var(--line); margin: 4px 6px; }
/* `.menu-item` viene dopo `.hidden` nel foglio e a parità di peso vince lui:
   per spegnere una voce serve un selettore più specifico */
.menu-item.ovf.hidden, .menu-sep.hidden { display: none; }
/* il pulsante uscito dalla fila non si cancella: resta nel DOM (con i suoi
   handler e il suo stato) e vive come voce di menù, vedi syncHeaderOverflow */
header > .in-menu { display: none; }
/* il pallino d'allerta dei vulcani si rifà sull'ingranaggio quando il pulsante
   Vulcani è finito nel menù: un parossismo non può sparire dietro a un menù */
#btn-settings { position: relative; }
/* Ultimo passo prima di cedere e andare a capo, quando tutti i pulsanti
   sacrificabili sono già nel menù: la ricerca si stringe e l'orologio lascia
   la data (che resta nel tooltip). L'ora, il conteggio stazioni e la copertura
   non si toccano: sono i dati, non i comandi. */
header.hdr-tight #search { width: 120px; }
header.hdr-tight #search:focus { width: 180px; }
header.hdr-tight #clock-date { display: none; }

/* --- icona "?" con spiegazione all'hover (scheda evento) --- */
.qi {
  display: inline-block; cursor: help; font-size: 9px; font-weight: 700;
  width: 13px; height: 13px; line-height: 12px; text-align: center;
  border: 1px solid var(--ink-3); border-radius: 50%; color: var(--ink-2);
  margin-left: 4px; vertical-align: 1px;
}
.qi:hover { color: var(--ink); border-color: var(--ink-2); }

/* --- asse temporale sopra il primo sismogramma del modal --- */
.m-axis { margin-bottom: 2px; }
.m-axis canvas { width: 100%; height: 18px; display: block; }
/* il gesto di zoom si scrive solo dove il tooltip non arriva (dito) */
.m-axis-hint { display: none; }
@media (pointer: coarse) {
  .m-axis-hint { display: block; font-size: 11px; margin: 1px 0 3px; }
}

/* --- aggiunta di sismografi alla stima (modal evento) --- */
.m-add { margin: 6px 0 10px; }
.m-add.hidden { display: none; }
#m-add-list {
  margin-top: 6px; border: 1px solid var(--line-2); border-radius: 8px;
  max-height: 220px; overflow-y: auto; background: var(--panel-2); padding: 4px;
}
#m-add-list.hidden { display: none; }
.m-add-it {
  display: flex; gap: 8px; align-items: baseline; width: 100%;
  background: none; border: none; border-radius: 5px; color: var(--ink);
  padding: 5px 7px; cursor: pointer; font: inherit; text-align: left;
}
.m-add-it:hover { background: var(--hover); }
.m-add-it b { font-family: ui-monospace, monospace; font-size: 12px; }
.m-add-it span { color: var(--ink-2); font-size: 12px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.m-add-it .dist { margin-left: auto; flex-shrink: 0;
  font-variant-numeric: tabular-nums; }

/* --- suoni personalizzati per fascia di magnitudo --- */
.snd-cust-row { display: flex; align-items: center; gap: 8px; margin: 6px 0;
  font-size: 13px; color: var(--ink-2); }
.snd-cust-row b { color: var(--ink); font-weight: 600; min-width: 62px;
  font-variant-numeric: tabular-nums; }
.snd-cust-row .fname { flex: 1; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-size: 12px; }
.snd-cust-row input[type="file"] { display: none; }

/* etichette dei pulsanti della testata del modal: su schermi stretti
   restano le sole icone */
@media (max-width: 1000px) {
  #modal .modal-head .btn-label { display: none; }
}

/* scelta fra stazioni sovrapposte (clic in area affollata) */
.pick-pop .leaflet-popup-content-wrapper {
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: 8px;
}
.pick-pop .leaflet-popup-tip { background: var(--panel); }
.pick-pop .leaflet-popup-content { margin: 8px 10px; }
.pick-h { color: var(--ink-2); font-size: 11px; margin-bottom: 5px; }
.pick-it {
  display: flex; gap: 8px; align-items: baseline; width: 100%;
  background: none; border: none; border-radius: 5px; color: var(--ink);
  padding: 5px 7px; cursor: pointer; font: inherit; text-align: left;
}
.pick-it:hover { background: var(--hover); }
.pick-it.on { color: var(--c-green); }
.pick-it b { font-family: ui-monospace, monospace; font-size: 12px; }
.pick-it span { color: var(--ink-2); font-size: 12px; }

/* Testata della scheda evento su telefono: una riga sola, alta il minimo.
   Con `flex-wrap: wrap` i sei comandi (← → 🔗 ⧉ INGV ✕) scendevano a una
   seconda riga e la testata si prendeva ~90 px prima ancora di dire dove è
   stato il terremoto. Qui la riga non va più a capo e a cedere spazio è il
   testo, non i pulsanti: quelli a icona sola sono già il minimo e devono
   restare tutti raggiungibili, mentre titolo e stato si accorciano con
   l'ellissi. Regole in coda al foglio: devono battere .modal-head generica e
   il blocco a 1000 px, che hanno la stessa specificità. */
@media (max-width: 820px) {
  #modal .modal-head { flex-wrap: nowrap; gap: 4px; padding: 7px 7px; }
  #modal .modal-head .btn-label { display: none; }
  /* "Evento #6672 · ML 0.8" -> "#6672 · ML 0.8": la parola costa 62 px in una
     riga che ne ha 395 in tutto, e che questa sia la scheda di un evento lo
     dice già tutto il resto (setModalTitle in app.js) */
  #m-title .tl-word { display: none; }
  #modal .modal-head b, #modal #m-sub {
    flex: 0 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* a stringersi per primo è lo stato ("complete", "caricamento…"): il titolo
     dice DI QUALE evento è questa scheda, ed è l'ultima cosa da perdere */
  #modal #m-sub { flex-shrink: 4; }
  #modal .modal-head .spacer { flex: 1 1 0; min-width: 0; }
  #modal .modal-head button.toggle { flex: 0 0 auto; padding: 5px 7px; }
  /* il rimando al catalogo è testo, non un'icona: un grado in meno di corpo
     gli fa occupare quanto un pulsante e non due */
  #modal .modal-head a.toggle { flex: 0 0 auto; white-space: nowrap;
    font-size: 12px; }
  /* intestazione sotto la testata: 15 px invece di 17 e targhetta più
     stretta, così "ML 0.8 · Abbadia San Salvatore (SI)" sta in una riga */
  .m-headline { font-size: 14px; margin-bottom: 8px; line-height: 1.3; }
  .m-headline .mh-badge { margin: 0 0 2px 8px; }
  .m-headline .mh-sub { margin-top: 2px; }
}

/* --- tutorial guidato (tour.js) ---------------------------------------------
   Riflettore + cartellino. Il buio NON è un pannello nero forato: è l'ombra
   dell'anello (box-shadow con spread grande), così il ritaglio segue il
   bersaglio senza che nessuno debba ricalcolare quattro rettangoli, e resta
   incollato anche mentre la carta si muove. L'ombra non intercetta i clic:
   quello lo fa #tour-back, che è il fondale trasparente sotto — durante il
   giro non si tocca l'app per sbaglio.
   Sopra i modali (1200) e i fogli (1250), sotto gli avvisi dei vulcani (3000):
   un'intrusione magmatica batte un tutorial. */
#tour-back { position: fixed; inset: 0; z-index: 2600; background: transparent; }
#tour-back.dim { background: rgba(0, 0, 0, .66); }
#tour-back.hidden, #tour-ring.hidden, #tour-card.hidden { display: none; }
#tour-ring {
  position: fixed; z-index: 2601; pointer-events: none;
  border: 2px solid var(--c-yellow); border-radius: 8px;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, .66), 0 0 14px rgba(245, 197, 66, .55);
  transition: top .18s ease, left .18s ease, width .18s ease, height .18s ease;
}
#tour-card {
  position: fixed; z-index: 2610; width: min(400px, 94vw);
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: 10px;
  box-shadow: var(--shadow); padding: 12px 14px 10px;
  transition: top .18s ease, left .18s ease;
}
.tour-top { display: flex; align-items: center; gap: 8px; }
#tour-chap { flex: 1 1 auto; min-width: 0; font-size: 11px; font-weight: 600;
  letter-spacing: .6px; text-transform: uppercase; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#tour-x { flex: 0 0 auto; padding: 2px 7px; }
#tour-card h4 { margin: 4px 0 6px; font-size: 16px; }
#tour-text { font-size: 13.5px; line-height: 1.5; color: var(--ink-2);
  max-height: 46vh; overflow-y: auto; }
#tour-text b { color: var(--ink); }
.tour-foot { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--line); }
.tour-sp { flex: 1 1 auto; }
#tour-prog { flex: 0 0 auto; }
#tour-next { background: var(--c-blue); color: #fff; border: 1px solid transparent;
  border-radius: 6px; padding: 6px 14px; font-size: 13px; font-weight: 600;
  cursor: pointer; }
#tour-prev:disabled { opacity: .4; cursor: default; }
#tour-skip.hidden { display: none; }
/* Su telefono il cartellino è largo quanto lo schermo (lo misura tour.js) e la
   riga dei comandi non ci sta in una fila: il progressivo va a capo da solo e
   i pulsanti restano grandi come un dito. */
@media (max-width: 820px) {
  #tour-card { padding: 10px 12px 8px; }
  #tour-card h4 { font-size: 15px; }
  #tour-text { font-size: 13px; max-height: 40vh; }
  #tour-prog { flex: 1 1 100%; order: -1; margin-bottom: 4px; }
  .tour-foot button { padding: 8px 12px; }
}

/* invito della prima visita: un cartellino in basso, non un modale — chi apre
   QuakeMon per guardare una scossa in corso non deve scavalcare un tutorial */
#tour-invite {
  position: fixed; z-index: 1500; right: 12px; bottom: 12px;
  width: min(320px, calc(100vw - 24px));
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line-2); border-left: 3px solid var(--c-blue);
  border-radius: 10px; box-shadow: var(--shadow); padding: 10px 12px;
}
#tour-invite p { margin: 4px 0 8px; font-size: 12.5px; line-height: 1.45;
  color: var(--ink-2); }
.tour-inv-btn { display: flex; gap: 6px; justify-content: flex-end; }
.tour-inv-btn #tour-inv-go { background: var(--c-blue); color: #fff;
  border: 1px solid transparent; border-radius: 6px; padding: 5px 12px;
  font-size: 13px; font-weight: 600; cursor: pointer; }
@media (max-width: 820px) {
  /* sopra la barra inferiore, non sotto */
  #tour-invite { right: 8px; left: 8px; width: auto;
    bottom: calc(62px + env(safe-area-inset-bottom)); }
}

/* il lancio dal pannello "Come si legge": una riga in cima, prima del testo */
.help-tour-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 4px 0 10px; padding: 10px 12px; background: var(--panel-2);
  border: 1px solid var(--line-2); border-radius: 8px; }
.help-tour-row .htr-txt { flex: 1 1 200px; min-width: 0; font-size: 13px;
  color: var(--ink-2); }
#help-tour { background: var(--c-blue); color: #fff; border: 1px solid transparent;
  border-radius: 6px; padding: 7px 14px; font-size: 13px; font-weight: 600;
  cursor: pointer; }

/* ==========================================================================
   RUOLI (body.role-mod, body.role-user) — la stessa GUI con meno comandi
   ==========================================================================
   Le classi le mette app.js leggendo /api/me. Questo blocco è COSMETICA: i
   permessi veri stanno nel middleware di quakemon/server.py, che risponde 403 a
   prescindere da quello che il CSS mostra. Serve a non mettere davanti a
   qualcuno bottoni che gli darebbero un errore — non a impedirgli di premerli.

   Chi tocca questo blocco tenga presente la simmetria con la tabella
   `_ROUTE_CAPS` in server.py: una voce nascosta qui e non negata là è una falla,
   una negata là e non nascosta qui è solo un bottone che non funziona.

   Restano visibili di proposito Suoni e Vista nel menu ⚙: sono preferenze di
   QUESTO browser (localStorage), non taratura del sistema. Toglierle
   significherebbe impedire a chi guarda di scegliersi tema, orario e suoni. */

/* --- configurazione, Claude, post-mortem, replay, copertura --------------- */
body.role-mod #mode-box,
body.role-mod #claude-box,
body.role-mod #claude-live,
body.role-mod #btn-postmortem,
body.role-mod #btn-replay,
body.role-mod #btn-thresholds,
body.role-mod #replay-badge,
body.role-user #mode-box,
body.role-user #claude-box,
body.role-user #claude-live,
body.role-user #btn-postmortem,
body.role-user #btn-replay,
body.role-user #btn-thresholds,
body.role-user #replay-badge { display: none !important; }

/* --- revisione: `user` non ne ha nessuna forma ---------------------------- */
/* #m-rev-wrap tiene dentro tutto il gruppo — pick P/S, Applica, Automatico e il
   verdetto di falso positivo. Le spunte "usa" stanno invece nei sismogrammi e
   vanno tolte a parte, o resterebbe un modo di escludere una stazione
   scavalcando la toolbar. Stesso discorso per #m-add ("+ aggiungi sismografo",
   in coda ai sismogrammi): arruolare una stazione è una revisione — è l'inverso
   dell'esclusione — e senza la toolbar sarebbe anche una traccia in più senza
   modo di piccarla. */
body.role-user #m-rev-wrap,
body.role-user .mrev-use,
body.role-user #m-add,
body.role-user .mrev-note-box { display: none !important; }

/* --- pannello utenti (solo admin) ---------------------------------------- */
/* Il bottone nel menu ⚙ nasce `hidden` e lo scopre initUsersPanel(), che parte
   solo con `users.admin` (che ha il solo admin). Questa riga è la rete: se
   /api/me cade e la GUI resta senza classi di ruolo, o se un domani il bottone
   nascesse visibile, `mod` e `user` continuano a non vederlo. */
body.role-mod #btn-users,
body.role-user #btn-users { display: none !important; }
/* come gli altri pannelli di testo dell'header: largo quanto basta e alto
   quanto il contenuto, non i 820px fissi di .modal-box */
.modal-box.us-box { width: min(640px, 94vw); height: auto; max-height: 92vh; }
/* .modal-body è in flex (scheda evento: due colonne); qui il testo, la tabella
   e la riga di stato sono uno sotto l'altro, e a scorrere è il corpo */
.modal-body.us-body { display: block; overflow-y: auto;
  padding: 14px 16px 16px; }
.us-table { width: 100%; border-collapse: collapse; margin-top: 10px;
  font-size: 13px; }
.us-table th { text-align: left; font-weight: 600; color: var(--ink-2);
  border-bottom: 1px solid var(--line); padding: 6px 8px 6px 0; }
.us-table td { padding: 6px 8px 6px 0; border-bottom: 1px solid var(--line); }
.us-table select { font: inherit; }
/* un'iscrizione in attesa è una cosa da fare: si vede senza cercarla */
.us-table tr.us-pending td:first-child { font-weight: 600; }
.us-table tr.us-pending { background: color-mix(in srgb, var(--c-blue) 8%,
  transparent); }

/* ==========================================================================
   MODALITÀ VETRINA (body.stream) — la GUI vista in una diretta video
   ==========================================================================
   Si accende con `?stream=1` (vedi app.js e scripts/stream-run.sh). Chi guarda
   la diretta non può cliccare niente e spesso guarda su un telefono un
   1920×1080 rimpicciolito: qui dentro si tolgono TUTTI i comandi — che sarebbero
   solo ingombro — e la diagnostica di dettaglio (numero d'evento, sigle dei
   canali, punteggi di qualità), e si ingrandisce quello che resta.

   L'ingrandimento generale dell'interfaccia NON è qui: lo fa Chrome con
   --force-device-scale-factor, così scala anche Leaflet e la corrispondenza fra
   pixel e coordinate resta giusta. Queste regole sono ingrandimenti RELATIVI,
   sopra a quello: servono le poche cose che da lontano si leggono peggio delle
   altre (la magnitudo in carta, il luogo in sidebar, le tracce).

   Regola di condotta per chi tocca questo blocco: niente `display:none` su cose
   che portano informazione unica e nessun taglio ai crediti delle fonti (la
   licenza CC-BY del catalogo INGV li richiede, in diretta come altrove). */

/* --- comandi: nessuno può premerli, quindi non ci sono ------------------- */
body.stream #search-box,
body.stream #btn-audio,
body.stream #btn-events,
body.stream #btn-tremor,
/* in vetrina nessuno può cliccare, e la diretta non linka se stessa */
body.stream #btn-live,
body.stream #btn-tg,
body.stream #claude-box,
body.stream #settings-box,
body.stream #btn-help,
body.stream #claude-live,
body.stream #tabbar,
body.stream #resizer,
body.stream #resizer-left,
body.stream .map-tools,
body.stream .map-quiet,
body.stream .leaflet-control-zoom,
body.stream .map-legend .ml-toggle,
body.stream .map-legend .ml-modes,
body.stream #ev-filters,
body.stream #ev-more,
body.stream #wave-help,
body.stream #wave-mode,
body.stream .watch .head button.x,
body.stream .chansel { display: none !important; }

/* la copertura resta scritta (dice se si guarda l'Italia o il mondo) ma smette
   di sembrare una tendina da aprire */
body.stream #mode-select {
  appearance: none; -webkit-appearance: none; pointer-events: none;
  background: none; border-color: transparent; padding-left: 0;
  color: var(--ink-2); font-weight: 600;
}
body.stream #mode-box { cursor: default; }

/* Le barre di scorrimento sono un comando anche loro: in diretta nessuno le
   trascina, e restano lì come due strisce chiare addosso al bordo della carta e
   della sidebar delle onde. Si nasconde la barra, NON lo scorrimento: le due
   colonne continuano a scorrere da sole (l'elenco eventi quando ne arriva uno
   nuovo, le tracce quando l'automatismo ne apre altre). */
body.stream #events,
body.stream #watches { scrollbar-width: none; }
body.stream #events::-webkit-scrollbar,
body.stream #watches::-webkit-scrollbar { width: 0; height: 0; }

/* --- larghezze: la carta è la protagonista ------------------------------- */
/* …ma non a costo di mandare a capo i toponimi: "5 km E di Gualdo Tadino (PG)"
   su tre righe costa più spazio di quello che la carta guadagna, e in diretta
   una riga spezzata a metà nome è la cosa che si legge peggio di tutte. 400 px
   è la misura in cui i toponimi italiani più lunghi entrano su due righe con i
   corpi ingranditi della vetrina. */
body.stream #side-left { width: 440px !important; }
body.stream #side { width: 330px !important; }

/* --- eventi (sinistra): quando, quanto, dove ----------------------------- */
/* Via la diagnostica: profondità/stazioni/#id e voto di qualità (.ev-tech),
   la targhetta di chi ha rivisto e la minimappa — che ripete, in francobollo,
   quello che la carta grande sta già mostrando. Restano magnitudo, luogo,
   orario e lo STATO della soluzione (PRELIMINARE / dubbia), che è l'unica cosa
   che dice quanto fidarsi del numero. */
body.stream .ev-tech,
body.stream .event .ev-rev,
body.stream .ev-mini,
body.stream .ev-hint { display: none; }

body.stream .event { padding: 7px 9px; margin-bottom: 6px; }
body.stream .event .mag { font-size: 21px; min-width: 66px; }
body.stream .event .det { font-size: 13px; }
body.stream .event .ev-when { font-size: 14px; }
body.stream .event .ev-place { font-size: 16px; line-height: 1.3; }
body.stream .sol-badge { font-size: 12px; line-height: 18px; padding: 1px 8px; }
/* il voto della soluzione resta anche in diretta, e va letto da lontano: la
   lettera è il pezzo che conta, la sua didascalia sta un gradino sotto */
body.stream .event .ev-q { font-size: 12px; }
body.stream .event .ev-q .q-badge { font-size: 13px; }
body.stream .cat-src, body.stream .cat-src-line { font-size: 12px; }
body.stream #ev-panel > h2 { font-size: 15px; }
/* i crediti delle fonti restano, ma non devono competere con gli eventi */
body.stream #ov-foot, body.stream #cat-foot { font-size: 11px; opacity: .75; }

/* --- sismografi live (destra): il nome, la distanza, la traccia ---------- */
/* La sigla completa del canale (FR.OGCE.00.HHZ) ripete la stazione già scritta
   nell'intestazione: da lontano è una riga di rumore su ogni traccia. Resta il
   picco, che è il numero che cambia. */
body.stream .chan .lbl span:first-child { display: none; }
body.stream .chan .lbl { font-size: 12px; line-height: 14px; }
body.stream .watch .head { line-height: 20px; }
body.stream .watch .head b { font-size: 14px; }
body.stream .watch .head .site { font-size: 12px; }
body.stream .watch .head .dist { font-size: 12px; }
/* tracce più alte: è il movimento del suolo, ed è la ragione per cui uno
   guarda una diretta sismica */
body.stream #watches { --chan-base: 52px; }
body.stream #wave-panel > h2 { font-size: 15px; }

/* --- cartografia: meno scritte piccole, più contrasto -------------------- */
body.stream .map-legend { font-size: 13px; max-width: 620px; }
body.stream .map-legend .ml-tri { font-size: 14px; }
body.stream .map-legend .ml-star { font-size: 16px; }
body.stream .map-legend .ml-sec { font-size: 12px; }
/* l'anello cresce col resto, o il triangolo ingrandito ci sborderebbe fuori */
body.stream .map-legend .ml-halo { width: 19px; height: 19px; }
body.stream .map-legend .ml-halo .ml-tri { font-size: 10px; }
/* i codici delle stazioni sotto i triangoli, ad alto zoom, sono decine di
   scritte da 10 px: in diretta diventano una grana grigia sulla carta */
body.stream .station-label { display: none; }

/* magnitudo, "PRELIMINARE" e "da quanto" appesi agli epicentri: più grandi, e
   con i margini rifatti di conseguenza — sono tre righe in colonna, e se
   cresce solo il corpo si sovrappongono (le stesse misure stanno in TXT_ROW,
   lato JS, per il calcolo di chi tace quando due epicentri sono vicini) */
body.stream .eq-star .lbl,
body.stream .eq-ripple .lbl,
body.stream .eq-cross .lbl { font-size: 14px; padding: 2px 5px; }
body.stream .eq-star .age,
body.stream .eq-ripple .age,
body.stream .eq-cross .age { font-size: 12px; }
body.stream .eq-star .tag.prelim,
body.stream .eq-ripple .tag.prelim,
body.stream .eq-cross .tag { font-size: 12px; }

body.stream .eq-star .tag.prelim { margin-top: 21px; }
body.stream .eq-star .age { margin-top: 21px; }
body.stream .eq-star.has-tag .age { margin-top: 37px; }
body.stream .eq-star.no-lbl .tag.prelim,
body.stream .eq-star.no-lbl .age { margin-top: 3px; }
body.stream .eq-star.no-lbl.has-tag .age { margin-top: 19px; }

/* la croce porta SEMPRE la sua scritta, quindi non ha un `has-tag` da cui
   ricavare il salto: il "da quanto" va spostato qui insieme alla scritta,
   con lo stesso passo della preliminare (21 → 37). Senza questa riga la
   scritta cresceva a 12 px e scendeva a 21 px mentre l'età restava al 27 px
   del corpo piccolo, e in diretta "x min fa" finiva stampato sopra
   "Soluzione dubbia · C" — proprio la riga che dice quanto fidarsi. */
body.stream .eq-cross .tag { margin-top: 21px; }
body.stream .eq-cross .age { margin-top: 37px; }
body.stream .eq-cross.no-lbl .tag { margin-top: 3px; }
body.stream .eq-cross.no-lbl .age { margin-top: 19px; }

body.stream .eq-ripple .tag.prelim,
body.stream .eq-ripple .age {
  transform: translate(-50%, calc(var(--core) / 2 + 26px));
}
body.stream .eq-ripple.has-tag .age {
  transform: translate(-50%, calc(var(--core) / 2 + 42px));
}
body.stream .eq-ripple.no-lbl .tag.prelim,
body.stream .eq-ripple.no-lbl .age {
  transform: translate(-50%, calc(var(--core) / 2 + 8px));
}
body.stream .eq-ripple.no-lbl.has-tag .age {
  transform: translate(-50%, calc(var(--core) / 2 + 24px));
}
/* i cerchi concentrici dell'ultimo evento sono il richiamo della diretta:
   anello più spesso e più a lungo visibile, perché è quello che si nota con la
   coda dell'occhio mentre si guarda altro. Il raggio lo dà il JS
   (RIPPLE_SPREAD), che è anche quello che dimensiona il riquadro del marker. */
body.stream .eq-ripple .rg { border-width: 3px; }
/* onde molto più lente: a 25 fps l'anello veloce si legge a scatti, e chi
   guarda non ha modo di fermare l'immagine. Un anello che impiega dieci
   secondi ad aprirsi si nota lo stesso — è il movimento a richiamare l'occhio,
   non la sua velocità — e in una ripresa continua non stanca. Sotto
   `no-preference` per non scavalcare la regola di prefers-reduced-motion, che
   sta più in su. I ritardi restano a un terzo e a due terzi del giro, o i tre
   anelli si accavallerebbero invece di susseguirsi. */
@media (prefers-reduced-motion: no-preference) {
  body.stream .eq-ripple .rg { animation-duration: 9.6s; }
  body.stream .eq-ripple .r2 { animation-delay: 3.2s; }
  body.stream .eq-ripple .r3 { animation-delay: 6.4s; }
  /* e con loro il battito della stella al centro, che è l'altra metà dello
     stesso richiamo: se resta veloce mentre gli anelli rallentano, i due
     movimenti si contraddicono */
  body.stream .eq-ripple .core { animation-duration: 3.6s; }
}

/* ---- la pastiglia dell'ultimo evento ------------------------------------
   È il titolo di testa della diretta — la risposta alla domanda con cui uno
   apre il canale — e prende il posto lasciato libero dai comandi. Un quinto
   più grande: le misure sono quelle di base ×1.2, una per una, perché i corpi
   dentro sono in px fissi e un font-size sul contenitore non li toccherebbe. */
body.stream #ev-pill { padding: 6px 12px; max-width: min(62vw, 312px); }
body.stream #ev-pill .ep-head { font-size: 11px; }
body.stream #ev-pill .ep-mag { font-size: 16px; }
body.stream #ev-pill .ep-place { font-size: 13px; }
body.stream #ev-pill .ep-when { font-size: 12px; }

/* Niente lampo periodico sulla pastiglia: in una ripresa continua un velo
   bianco che si riaccende ogni pochi secondi è un disturbo costante, e il
   richiamo dell'ultimo evento la diretta ce l'ha già dove serve — i cerchi
   concentrici sull'epicentro in carta e l'ingresso `is-new` della scheda. */
