- Server di posta in Impostazioni (password cifrata, email di prova): se compilato prende il posto del .env; non si esporta e l'import XML non lo cancella. MAIL_SCHEME=tls nel .env.example non era valido per Laravel 11. - Proclamatori con email e telefono facoltativi, cifrati, nell'XML e cancellati dall'anonimizzazione; il numero apre WhatsApp. - Dopo l'assegnazione un riquadro in evidenza con il link del PDF, "Invia su WhatsApp" con il messaggio pronto e la scelta "inviato / non ancora", che resta finché non si sceglie. - Conferma dell'email obbligatoria per tutti gli utenti, anche esistenti: email in italiano, link valido senza login, nuova conferma a ogni cambio di indirizzo, "Reinvia conferma" in Utenti. Senza conferma restano raggiungibili profilo, cambio password e, per l'admin, il server di posta; comando utenti:conferma-email per l'emergenza. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1681 lines
50 KiB
CSS
1681 lines
50 KiB
CSS
@import "tailwindcss";
|
|
|
|
/* Carattere servito dall'applicazione, non da un CDN esterno: questo
|
|
gestionale tiene nomi e indirizzi di persone, e una richiesta a un dominio
|
|
di terze parti a ogni pagina caricata sarebbe un dato in uscita che nessuno
|
|
ha chiesto. Il pacchetto e' una dipendenza npm, quindi finisce nel bundle e
|
|
funziona anche senza rete. */
|
|
@import "@fontsource-variable/figtree";
|
|
|
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
TerManager2 — sistema visivo
|
|
|
|
I token stanno qui e in nessun altro posto. Ridefinire le scale `gray` e
|
|
`indigo` di Tailwind e' deliberato: le trentasette viste dell'applicazione
|
|
sono scritte con le utility (`text-gray-600`, `bg-indigo-50`), e cambiare
|
|
la scala le riallinea tutte insieme invece di costringere a riscriverle una
|
|
per una — con il rischio, a ogni vista dimenticata, di lasciare in giro il
|
|
grigio di prima accanto a quello nuovo.
|
|
|
|
Due scelte di fondo:
|
|
|
|
I grigi sono caldi. Il grigio di serie di Tailwind tira al blu e, sommato
|
|
a un accento indaco, dava a tutta l'interfaccia una
|
|
dominante fredda da pannello di controllo. Su carta
|
|
questi elenchi diventano moduli che passano di mano:
|
|
un fondo caldo li avvicina a quello che sono.
|
|
|
|
L'accento e' uno. Prima ce n'erano cinque (indaco, verde, blu, rosso,
|
|
ambra) usati come decorazione. Ora il colore vuol
|
|
dire qualcosa: indaco = azione, verde = fatto,
|
|
ambra = in sospeso, rosso = distrugge dati. Se non
|
|
rientra in queste quattro, e' grigio.
|
|
═══════════════════════════════════════════════════════════════════════════ */
|
|
|
|
@theme {
|
|
--font-sans: "Figtree Variable", ui-sans-serif, system-ui, -apple-system,
|
|
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
|
|
/* Grigi caldi: una sola famiglia, nessuna mescolanza caldo/freddo. */
|
|
--color-gray-50: #f7f6f4;
|
|
--color-gray-100: #efedea;
|
|
--color-gray-200: #e3e0da;
|
|
--color-gray-300: #cfcbc2;
|
|
--color-gray-400: #a8a29a;
|
|
--color-gray-500: #837d74;
|
|
--color-gray-600: #625c54;
|
|
--color-gray-700: #4a453f;
|
|
--color-gray-800: #302c28;
|
|
--color-gray-900: #1c1a17;
|
|
--color-gray-950: #12100e;
|
|
|
|
/* Indaco smorzato: stessa identita' di prima, meno stridente. */
|
|
--color-indigo-50: #eeeef9;
|
|
--color-indigo-100: #dedef3;
|
|
--color-indigo-200: #c3c2e8;
|
|
--color-indigo-300: #a09ed8;
|
|
--color-indigo-400: #7c79c5;
|
|
--color-indigo-500: #605cb2;
|
|
--color-indigo-600: #4a45a8;
|
|
--color-indigo-700: #3b3789;
|
|
--color-indigo-800: #2f2c6c;
|
|
--color-indigo-900: #262350;
|
|
--color-indigo-950: #16142f;
|
|
}
|
|
|
|
:root {
|
|
--tm-fondo: #f7f6f4;
|
|
--tm-superficie: #ffffff;
|
|
--tm-superficie-alt: #faf9f7;
|
|
--tm-bordo: #e3e0da;
|
|
--tm-bordo-forte: #cfcbc2;
|
|
|
|
--tm-testo: #1c1a17;
|
|
--tm-testo-medio: #4a453f;
|
|
--tm-testo-tenue: #837d74;
|
|
|
|
--tm-accento: #4a45a8;
|
|
--tm-accento-scuro: #3b3789;
|
|
--tm-accento-tenue: #eeeef9;
|
|
|
|
--tm-ok: #2f6f4e;
|
|
--tm-ok-tenue: #e8f2ec;
|
|
--tm-attesa: #a06a22;
|
|
--tm-attesa-tenue: #fbf1e2;
|
|
--tm-pericolo: #b3352c;
|
|
--tm-pericolo-tenue: #fbeceb;
|
|
|
|
/* Le ombre portano la tinta del fondo invece del nero puro: su una
|
|
superficie calda un'ombra neutra sporca di grigio. */
|
|
--tm-ombra-1: 0 1px 2px rgba(43, 37, 28, .05);
|
|
--tm-ombra-2: 0 2px 4px rgba(43, 37, 28, .05), 0 6px 16px -8px rgba(43, 37, 28, .10);
|
|
--tm-ombra-3: 0 8px 28px -10px rgba(43, 37, 28, .18);
|
|
|
|
--tm-raggio: 14px;
|
|
--tm-raggio-interno: 10px;
|
|
}
|
|
|
|
html.tm-app,
|
|
body {
|
|
background: var(--tm-fondo);
|
|
color: var(--tm-testo);
|
|
}
|
|
|
|
body {
|
|
font-family: var(--font-sans);
|
|
font-synthesis-weight: none;
|
|
-webkit-font-smoothing: antialiased;
|
|
text-rendering: optimizeLegibility;
|
|
}
|
|
|
|
/* I titoli devono avere peso e stare stretti: alla dimensione di prima, con
|
|
spaziatura di serie, si leggevano come testo qualsiasi in grassetto. */
|
|
h1, h2, h3 {
|
|
letter-spacing: -.018em;
|
|
text-wrap: balance;
|
|
}
|
|
|
|
/* Le cifre incolonnate vogliono larghezza fissa, altrimenti in un elenco di
|
|
date e numeri di territorio le colonne ballano riga per riga. */
|
|
table, .tm-tabella, .tm-cifre {
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
p { text-wrap: pretty; }
|
|
|
|
/* Alpine nasconde con x-cloak solo se il foglio di stile lo dice: la regola non
|
|
arriva col pacchetto. Senza, gli elementi marcati x-cloak (le barre di
|
|
avanzamento, i messaggi d'errore) lampeggiano a ogni caricamento prima che
|
|
Alpine parta. */
|
|
[x-cloak] { display: none !important; }
|
|
|
|
html { scroll-behavior: smooth; }
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
html { scroll-behavior: auto; }
|
|
}
|
|
|
|
/* Chi naviga da tastiera deve poter saltare la barra laterale invece di
|
|
percorrerla a ogni pagina. */
|
|
.tm-salta {
|
|
position: absolute;
|
|
left: -9999px;
|
|
z-index: 100;
|
|
padding: 10px 16px;
|
|
border-radius: var(--tm-raggio-interno);
|
|
background: var(--tm-accento);
|
|
color: #fff;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
}
|
|
.tm-salta:focus {
|
|
left: 12px;
|
|
top: 12px;
|
|
}
|
|
|
|
.tm-solo-lettori {
|
|
position: absolute;
|
|
width: 1px; height: 1px;
|
|
padding: 0; margin: -1px;
|
|
overflow: hidden;
|
|
clip: rect(0 0 0 0);
|
|
white-space: nowrap;
|
|
border: 0;
|
|
}
|
|
|
|
/* ─── Superfici ────────────────────────────────────────────────────────────
|
|
Una card esiste quando l'elevazione dice qualcosa. Il bordo fa il lavoro
|
|
principale, l'ombra e' appena percepibile: bordo + ombra marcata + fondo
|
|
bianco insieme e' il riquadro di serie che si vede ovunque. */
|
|
.card-hover {
|
|
transition: transform .18s ease, box-shadow .18s ease;
|
|
}
|
|
.card-hover:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: var(--tm-ombra-3);
|
|
}
|
|
|
|
/* Bordo d'accento sulle schede di riepilogo: quattro significati, non cinque
|
|
tinte decorative. */
|
|
.card-accent-indigo { border-left: 3px solid var(--tm-accento); }
|
|
.card-accent-green { border-left: 3px solid var(--tm-ok); }
|
|
.card-accent-blue { border-left: 3px solid var(--tm-accento); }
|
|
.card-accent-red { border-left: 3px solid var(--tm-pericolo); }
|
|
.card-accent-amber { border-left: 3px solid var(--tm-attesa); }
|
|
|
|
.table-striped tbody tr:nth-child(even) {
|
|
background-color: var(--tm-superficie-alt);
|
|
}
|
|
|
|
/* La testata era un gradiente indaco a 45 gradi — la firma piu' riconoscibile
|
|
di un'interfaccia generata. Adesso e' una tinta piena e profonda, con un solo
|
|
velo verticale appena accennato per non essere piatta come un francobollo. */
|
|
.header-gradient {
|
|
background: linear-gradient(180deg, #333072 0%, #2b2860 100%);
|
|
}
|
|
|
|
.sidebar-active {
|
|
background: var(--tm-accento-tenue);
|
|
border-right: 3px solid var(--tm-accento);
|
|
color: var(--tm-accento-scuro);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.page-enter {
|
|
animation: fadeSlideIn .28s cubic-bezier(.22, .61, .36, 1);
|
|
}
|
|
@keyframes fadeSlideIn {
|
|
from { opacity: 0; transform: translateY(6px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.empty-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 2.5rem 1rem;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
.empty-state svg {
|
|
width: 44px;
|
|
height: 44px;
|
|
opacity: .35;
|
|
}
|
|
|
|
.badge-pulse {
|
|
animation: pulse 2s infinite;
|
|
}
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: .7; }
|
|
}
|
|
|
|
/* ─── Impaginazione ───────────────────────────────────────────────────────── */
|
|
|
|
.tm-pagina-testata {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: flex-end;
|
|
justify-content: space-between;
|
|
gap: 16px;
|
|
margin-bottom: 28px;
|
|
}
|
|
.tm-pagina-titolo {
|
|
margin: 0;
|
|
font-size: clamp(1.5rem, 1.2rem + 1vw, 1.9rem);
|
|
font-weight: 600;
|
|
line-height: 1.15;
|
|
color: var(--tm-testo);
|
|
}
|
|
.tm-pagina-nota {
|
|
margin: 6px 0 0;
|
|
max-width: 62ch;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
color: var(--tm-testo-medio);
|
|
}
|
|
|
|
.tm-card {
|
|
background: var(--tm-superficie);
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: var(--tm-raggio);
|
|
box-shadow: var(--tm-ombra-1);
|
|
/* Il basso ottico vuole un filo piu' del sopra, altrimenti il blocco
|
|
sembra appoggiato storto. */
|
|
padding: 20px 22px 24px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.tm-card-testata {
|
|
margin-bottom: 18px;
|
|
}
|
|
|
|
/* Scheda che apre la pagina: è il presupposto di tutto quello che segue, e
|
|
deve leggersi come tale prima ancora di aver letto il titolo. Il rilievo
|
|
glielo danno la barra d'accento e il fondo appena caldo, non un colore
|
|
acceso — che a fianco di un avviso vero ne ruberebbe la voce. */
|
|
.tm-card-primaria {
|
|
position: relative;
|
|
padding-left: 26px;
|
|
background: linear-gradient(180deg, var(--tm-superficie-alt) 0%, var(--tm-superficie) 60%);
|
|
box-shadow: var(--tm-ombra-2);
|
|
}
|
|
.tm-card-primaria::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0;
|
|
top: -1px;
|
|
bottom: -1px;
|
|
width: 4px;
|
|
border-radius: var(--tm-raggio) 0 0 var(--tm-raggio);
|
|
background: var(--tm-accento);
|
|
}
|
|
/* Finché il KML manca, l'accento diventa ambra: è un lavoro da fare. */
|
|
.tm-card-manca::before { background: var(--tm-attesa); }
|
|
.tm-card-manca {
|
|
border-color: #e8d3ae;
|
|
background: linear-gradient(180deg, var(--tm-attesa-tenue) 0%, var(--tm-superficie) 65%);
|
|
}
|
|
|
|
.tm-card-testata-icona {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 14px;
|
|
}
|
|
.tm-card-icona {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex: none;
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: var(--tm-raggio-interno);
|
|
border: 1px solid var(--tm-bordo);
|
|
background: var(--tm-superficie);
|
|
color: var(--tm-accento);
|
|
}
|
|
.tm-card-icona svg { width: 21px; height: 21px; }
|
|
.tm-card-manca .tm-card-icona { color: var(--tm-attesa); border-color: #e8d3ae; }
|
|
|
|
/* Chiosa accanto all'etichetta di un campo: spiega dove finisce quel valore
|
|
senza spingere la spiegazione sotto, dove verrebbe letta dopo la scelta. */
|
|
.tm-campo-nota {
|
|
margin-left: 6px;
|
|
font-size: 11.5px;
|
|
font-weight: 500;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
.tm-card-titolo {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin: 0;
|
|
font-size: 1.02rem;
|
|
font-weight: 600;
|
|
color: var(--tm-testo);
|
|
}
|
|
.tm-card-nota {
|
|
margin: 5px 0 0;
|
|
max-width: 68ch;
|
|
font-size: 13px;
|
|
line-height: 1.6;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
.tm-conteggio {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
min-width: 22px;
|
|
height: 22px;
|
|
padding: 0 7px;
|
|
border-radius: 999px;
|
|
background: var(--tm-superficie-alt);
|
|
border: 1px solid var(--tm-bordo);
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: var(--tm-testo-medio);
|
|
}
|
|
|
|
.tm-griglia-2 {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
gap: 22px;
|
|
}
|
|
@media (min-width: 900px) {
|
|
.tm-griglia-2 { grid-template-columns: 1fr 1fr; gap: 28px; }
|
|
}
|
|
|
|
/* ─── Campi ───────────────────────────────────────────────────────────────── */
|
|
|
|
.tm-campo {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 7px;
|
|
}
|
|
.tm-campo > label {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--tm-testo-medio);
|
|
}
|
|
.tm-campo input[type="text"],
|
|
.tm-campo input[type="number"],
|
|
.tm-campo select,
|
|
.tm-elenco-pannello select {
|
|
width: 100%;
|
|
min-height: 40px;
|
|
padding: 8px 12px;
|
|
border: 1px solid var(--tm-bordo-forte);
|
|
border-radius: var(--tm-raggio-interno);
|
|
background: var(--tm-superficie);
|
|
color: var(--tm-testo);
|
|
font-family: inherit;
|
|
font-size: 14px;
|
|
transition: border-color .15s ease, box-shadow .15s ease;
|
|
}
|
|
.tm-campo input:focus,
|
|
.tm-campo select:focus,
|
|
.tm-elenco-pannello select:focus {
|
|
outline: none;
|
|
border-color: var(--tm-accento);
|
|
box-shadow: 0 0 0 3px var(--tm-accento-tenue);
|
|
}
|
|
.tm-campo input::placeholder { color: var(--tm-testo-tenue); }
|
|
|
|
.tm-campo textarea {
|
|
width: 100%;
|
|
min-height: 120px;
|
|
padding: 10px 12px;
|
|
border: 1px solid var(--tm-bordo-forte);
|
|
border-radius: var(--tm-raggio-interno);
|
|
background: var(--tm-superficie);
|
|
color: var(--tm-testo);
|
|
font-family: inherit;
|
|
font-size: 14px;
|
|
line-height: 1.55;
|
|
resize: vertical;
|
|
transition: border-color .15s ease, box-shadow .15s ease;
|
|
}
|
|
.tm-campo textarea:focus {
|
|
outline: none;
|
|
border-color: var(--tm-accento);
|
|
box-shadow: 0 0 0 3px var(--tm-accento-tenue);
|
|
}
|
|
.tm-campo textarea::placeholder { color: var(--tm-testo-tenue); }
|
|
|
|
/* Gruppo di campi: dà un nome a una parte della scheda senza trasformarla in
|
|
una scatola dentro la scatola — solo una riga di separazione e l'etichetta. */
|
|
.tm-gruppo {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
margin: 0;
|
|
padding: 0;
|
|
border: 0;
|
|
}
|
|
.tm-gruppo + .tm-gruppo {
|
|
padding-top: 20px;
|
|
border-top: 1px solid var(--tm-bordo);
|
|
}
|
|
.tm-gruppo > legend {
|
|
padding: 0;
|
|
margin-bottom: 2px;
|
|
font-size: 11.5px;
|
|
font-weight: 600;
|
|
letter-spacing: .05em;
|
|
text-transform: uppercase;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
|
|
/* La riga "crea quello che manca": sta sotto la tendina a cui appartiene, e si
|
|
legge come una sua appendice invece che come un campo a sé. */
|
|
.tm-campo-crea {
|
|
gap: 8px;
|
|
padding-left: 10px;
|
|
border-left: 2px solid var(--tm-bordo);
|
|
}
|
|
.tm-campo-crea input { min-height: 34px; font-size: 13px; }
|
|
|
|
.tm-campo-riga {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.tm-campo-riga input,
|
|
.tm-campo-riga select { flex: 1 1 180px; }
|
|
|
|
.tm-file {
|
|
width: 100%;
|
|
font-size: 14px;
|
|
color: var(--tm-testo-medio);
|
|
}
|
|
.tm-file::file-selector-button {
|
|
margin-right: 12px;
|
|
padding: 8px 14px;
|
|
border: 1px solid var(--tm-bordo-forte);
|
|
border-radius: var(--tm-raggio-interno);
|
|
background: var(--tm-superficie-alt);
|
|
color: var(--tm-testo-medio);
|
|
font-family: inherit;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: background-color .15s ease;
|
|
}
|
|
.tm-file::file-selector-button:hover { background: var(--tm-bordo); }
|
|
|
|
.tm-campo-aiuto {
|
|
margin: 0;
|
|
max-width: 62ch;
|
|
font-size: 12.5px;
|
|
line-height: 1.55;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
.tm-campo-aiuto-attesa { color: var(--tm-attesa); }
|
|
.tm-campo-errore {
|
|
margin: 0;
|
|
font-size: 12.5px;
|
|
font-weight: 500;
|
|
color: var(--tm-pericolo);
|
|
}
|
|
|
|
/* ─── Elenchi ─────────────────────────────────────────────────────────────── */
|
|
|
|
.tm-elenco {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
border-top: 1px solid var(--tm-bordo);
|
|
}
|
|
.tm-elenco-riga {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 10px 16px;
|
|
padding: 14px 2px;
|
|
border-bottom: 1px solid var(--tm-bordo);
|
|
}
|
|
.tm-elenco-intestazione {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 10px;
|
|
flex: 1 1 260px;
|
|
min-width: 0;
|
|
}
|
|
.tm-elenco-nome {
|
|
font-size: 14.5px;
|
|
font-weight: 600;
|
|
color: var(--tm-testo);
|
|
text-decoration: none;
|
|
border-bottom: 1px solid transparent;
|
|
transition: border-color .15s ease, color .15s ease;
|
|
}
|
|
.tm-elenco-nome:hover {
|
|
color: var(--tm-accento);
|
|
border-bottom-color: var(--tm-accento);
|
|
}
|
|
.tm-elenco-stato {
|
|
font-size: 12.5px;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
.tm-elenco-stato-ok { color: var(--tm-ok); }
|
|
.tm-elenco-azioni {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.tm-elenco-pannello {
|
|
flex: 1 1 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
margin-top: 4px;
|
|
padding: 14px;
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: var(--tm-raggio-interno);
|
|
background: var(--tm-superficie-alt);
|
|
}
|
|
|
|
/* ─── Numero di telefono che apre WhatsApp ───────────────────────────────── */
|
|
|
|
.tm-whatsapp {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 4px 10px;
|
|
border: 1px solid #b7e1c6;
|
|
border-radius: 999px;
|
|
background: #eaf7ef;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: #17703f;
|
|
text-decoration: none;
|
|
white-space: nowrap;
|
|
}
|
|
.tm-whatsapp:hover { background: #d8f0e1; }
|
|
.tm-whatsapp:focus-visible { outline: 2px solid #17703f; outline-offset: 2px; }
|
|
.tm-whatsapp svg { width: 16px; height: 16px; flex: none; }
|
|
.tm-whatsapp-compatto { padding: 5px; }
|
|
.tm-whatsapp-grande {
|
|
padding: 10px 18px;
|
|
font-size: 15px;
|
|
}
|
|
.tm-whatsapp-grande svg { width: 20px; height: 20px; }
|
|
.tm-whatsapp-numero { font-weight: 500; opacity: .75; }
|
|
|
|
/* ─── Assegnazione appena fatta: il link da mandare ──────────────────────── */
|
|
|
|
.tm-da-inviare {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 14px;
|
|
max-width: 48rem;
|
|
margin-bottom: 20px;
|
|
padding: 18px 20px;
|
|
border: 2px solid var(--tm-accento);
|
|
border-radius: var(--tm-raggio);
|
|
background: var(--tm-accento-tenue);
|
|
box-shadow: var(--tm-ombra-2);
|
|
}
|
|
.tm-da-inviare-titolo {
|
|
margin: 0;
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
color: var(--tm-accento-scuro);
|
|
}
|
|
.tm-da-inviare-link {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.tm-da-inviare-link code {
|
|
flex: 1 1 260px;
|
|
min-width: 0;
|
|
padding: 8px 12px;
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: var(--tm-raggio-interno);
|
|
background: var(--tm-superficie);
|
|
font-size: 13px;
|
|
color: var(--tm-testo);
|
|
word-break: break-all;
|
|
cursor: text;
|
|
}
|
|
.tm-da-inviare-azioni { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
|
|
.tm-da-inviare-scelta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
padding-top: 12px;
|
|
border-top: 1px solid var(--tm-bordo);
|
|
}
|
|
.tm-da-inviare-scelta p { margin: 0; font-size: 14px; font-weight: 600; color: var(--tm-testo); }
|
|
.tm-da-inviare-scelta > div { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
|
|
/* ─── Ricerca e filtri sopra un elenco ───────────────────────────────────── */
|
|
|
|
.tm-filtri {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 14px;
|
|
}
|
|
.tm-filtri-cerca {
|
|
flex: 1 1 220px;
|
|
min-width: 0;
|
|
padding: 7px 11px;
|
|
border: 1px solid var(--tm-bordo-forte);
|
|
border-radius: var(--tm-raggio-interno);
|
|
background: var(--tm-superficie);
|
|
font-size: 14px;
|
|
color: var(--tm-testo);
|
|
}
|
|
.tm-filtri-cerca:focus {
|
|
outline: 2px solid var(--tm-accento);
|
|
outline-offset: 1px;
|
|
}
|
|
.tm-filtri-schede {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
}
|
|
.tm-filtri-scheda {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 5px 10px;
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: 999px;
|
|
background: var(--tm-superficie);
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: var(--tm-testo-medio);
|
|
cursor: pointer;
|
|
}
|
|
.tm-filtri-scheda:hover { border-color: var(--tm-bordo-forte); }
|
|
.tm-filtri-scheda:focus-visible {
|
|
outline: 2px solid var(--tm-accento);
|
|
outline-offset: 2px;
|
|
}
|
|
.tm-filtri-scheda-attiva {
|
|
border-color: var(--tm-accento);
|
|
background: var(--tm-accento-tenue);
|
|
color: var(--tm-accento-scuro);
|
|
}
|
|
.tm-filtri-scheda .tm-conteggio {
|
|
min-width: 18px;
|
|
height: 18px;
|
|
padding: 0 5px;
|
|
font-size: 11px;
|
|
}
|
|
|
|
/* ─── Etichette di stato ──────────────────────────────────────────────────── */
|
|
|
|
.tm-tag {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 2px 8px;
|
|
border-radius: 5px;
|
|
border: 1px solid var(--tm-bordo-forte);
|
|
background: var(--tm-superficie-alt);
|
|
font-size: 11.5px;
|
|
font-weight: 600;
|
|
letter-spacing: .01em;
|
|
color: var(--tm-testo-medio);
|
|
}
|
|
.tm-tag-attesa {
|
|
border-color: #e8d3ae;
|
|
background: var(--tm-attesa-tenue);
|
|
color: var(--tm-attesa);
|
|
}
|
|
.tm-tag-ok {
|
|
border-color: #bcd9c9;
|
|
background: var(--tm-ok-tenue);
|
|
color: var(--tm-ok);
|
|
}
|
|
|
|
.tm-nota {
|
|
padding: 12px 14px;
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: var(--tm-raggio-interno);
|
|
background: var(--tm-superficie-alt);
|
|
font-size: 13px;
|
|
color: var(--tm-testo-medio);
|
|
}
|
|
.tm-nota p { margin: 0 0 8px; }
|
|
.tm-nota-attesa {
|
|
border-color: #e8d3ae;
|
|
background: var(--tm-attesa-tenue);
|
|
color: var(--tm-attesa);
|
|
}
|
|
|
|
.tm-collegamento {
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: var(--tm-accento);
|
|
text-decoration: none;
|
|
border-bottom: 1px solid color-mix(in srgb, var(--tm-accento) 35%, transparent);
|
|
transition: border-color .15s ease;
|
|
}
|
|
.tm-collegamento:hover { border-bottom-color: var(--tm-accento); }
|
|
|
|
/* ─── Stato vuoto ─────────────────────────────────────────────────────────── */
|
|
|
|
.tm-vuoto {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 40px 20px 44px;
|
|
text-align: center;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
.tm-vuoto svg {
|
|
width: 40px;
|
|
height: 40px;
|
|
opacity: .3;
|
|
}
|
|
.tm-vuoto p {
|
|
margin: 0;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* ─── Tabelle ─────────────────────────────────────────────────────────────── */
|
|
|
|
.tm-tabella-scorre { overflow-x: auto; }
|
|
|
|
.tm-tabella {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 14px;
|
|
}
|
|
.tm-tabella thead th {
|
|
padding: 0 12px 10px 0;
|
|
border-bottom: 1px solid var(--tm-bordo);
|
|
text-align: left;
|
|
font-size: 11.5px;
|
|
font-weight: 600;
|
|
letter-spacing: .04em;
|
|
text-transform: uppercase;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
.tm-tabella tbody td {
|
|
padding: 11px 12px 11px 0;
|
|
border-bottom: 1px solid var(--tm-bordo);
|
|
color: var(--tm-testo-medio);
|
|
vertical-align: middle;
|
|
}
|
|
.tm-tabella tbody tr:last-child td { border-bottom: 0; }
|
|
.tm-tabella-forte { font-weight: 600; color: var(--tm-testo) !important; }
|
|
.tm-tabella .cell-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
padding-right: 0;
|
|
}
|
|
|
|
/* ─── Pulsanti ─────────────────────────────────────────────────────────────
|
|
Un solo sistema per tutta l'applicazione, definito qui e in nessun altro
|
|
posto. Si usa dal componente <x-btn>, mai scrivendo queste classi a mano.
|
|
|
|
Da dove veniamo: quattro vocabolari in circolazione contemporaneamente —
|
|
style= inline con i colori scritti a mano (piu' onmouseover per l'hover),
|
|
catene di utility Tailwind ripetute a memoria, .btn-action* in sei tinte, e
|
|
.btn-primary-* in due. La stessa azione cambiava aspetto da una pagina
|
|
all'altra, un "Rientra" era rosso quanto un "Elimina", e nessuno di questi
|
|
pulsanti aveva uno stato di focus: chi naviga da tastiera non vedeva dove si
|
|
trovava.
|
|
|
|
Due assi, ortogonali:
|
|
|
|
variante primary l'azione principale. Una sola per schermata o zona.
|
|
secondary tutto il resto (dettaglio, modifica, scarica, filtra)
|
|
danger SOLO dove si cancellano o si sovrascrivono dati
|
|
ghost annulla, chiudi, pulisci
|
|
|
|
tono solido il pieno. Per le azioni isolate.
|
|
tenue la tinta chiara. Per le azioni dentro le righe di un
|
|
elenco, dove cinquanta pulsanti pieni urlerebbero.
|
|
|
|
La dimensione `sm` e' quella delle righe di elenco; `icona` toglie il testo e
|
|
lascia il quadrato.
|
|
|
|
Le regole stanno fuori da @layer, quindi vincono sulle utility di Tailwind:
|
|
un pulsante non puo' essere ritoccato per meta' con le classi. */
|
|
.tm-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 6px;
|
|
min-height: 40px;
|
|
padding: 10px 16px;
|
|
border-radius: var(--tm-raggio-interno);
|
|
border: 1px solid transparent;
|
|
font-family: inherit;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
line-height: 1.2;
|
|
white-space: nowrap;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
transition: background-color .15s ease, border-color .15s ease,
|
|
color .15s ease, transform .1s ease;
|
|
}
|
|
.tm-btn svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
flex: none;
|
|
}
|
|
/* Un premuto che si sente: senza, il pulsante resta un rettangolo che non
|
|
risponde finche' la pagina non torna dal server. */
|
|
.tm-btn:active:not(:disabled) { transform: translateY(1px); }
|
|
|
|
/* Un anello unico per ogni variante: il colore del focus non deve dipendere
|
|
da quello del pulsante, altrimenti su rosso o su bianco cambia visibilita'
|
|
proprio dove serve di piu'. Il doppio contorno (chiaro + scuro) lo tiene
|
|
leggibile sia sulle card bianche sia sui riquadri colorati. */
|
|
.tm-btn:focus-visible,
|
|
.tm-console-btn:focus-visible,
|
|
.tm-nav-zona:focus-visible,
|
|
.tm-editor-torna:focus-visible,
|
|
.tm-collegamento:focus-visible,
|
|
.tm-elenco-nome:focus-visible {
|
|
outline: 3px solid var(--tm-accento);
|
|
outline-offset: 2px;
|
|
box-shadow: 0 0 0 5px #fff;
|
|
}
|
|
|
|
.tm-btn:disabled,
|
|
.tm-btn[aria-disabled="true"] {
|
|
opacity: .45;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.tm-btn-primary { background: var(--tm-accento); border-color: var(--tm-accento-scuro); color: #fff; }
|
|
.tm-btn-primary:hover:not(:disabled) { background: var(--tm-accento-scuro); }
|
|
|
|
.tm-btn-danger { background: var(--tm-pericolo); border-color: #8e2a23; color: #fff; }
|
|
.tm-btn-danger:hover:not(:disabled) { background: #8e2a23; }
|
|
|
|
.tm-btn-secondary { background: var(--tm-superficie); border-color: var(--tm-bordo-forte); color: var(--tm-testo-medio); }
|
|
.tm-btn-secondary:hover:not(:disabled) { background: var(--tm-superficie-alt); border-color: var(--tm-testo-tenue); color: var(--tm-testo); }
|
|
|
|
.tm-btn-ghost {
|
|
background: transparent;
|
|
border-color: transparent;
|
|
color: var(--tm-testo-medio);
|
|
min-height: 32px;
|
|
padding: 4px 8px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
text-decoration: underline;
|
|
text-underline-offset: 3px;
|
|
}
|
|
.tm-btn-ghost:hover:not(:disabled) { color: var(--tm-testo); background: #efedea; }
|
|
|
|
/* Tono tenue: stessa gerarchia, voce piu' bassa. Nelle righe di un elenco i
|
|
pulsanti sono tanti quanti i record, e il pieno li trasforma in rumore —
|
|
soprattutto il rosso, che smette di significare "attenzione" quando compare
|
|
cinquanta volte di fila. */
|
|
.tm-btn-tenue { border-color: transparent; font-weight: 600; }
|
|
.tm-btn-tenue.tm-btn-primary { background: var(--tm-accento-tenue); color: var(--tm-accento-scuro); }
|
|
.tm-btn-tenue.tm-btn-primary:hover:not(:disabled) { background: #dedef3; }
|
|
.tm-btn-tenue.tm-btn-danger { background: var(--tm-pericolo-tenue); color: var(--tm-pericolo); }
|
|
.tm-btn-tenue.tm-btn-danger:hover:not(:disabled) { background: #f6dcda; }
|
|
.tm-btn-tenue.tm-btn-secondary { background: #efedea; color: var(--tm-testo-medio); }
|
|
.tm-btn-tenue.tm-btn-secondary:hover:not(:disabled) { background: #e3e0da; color: var(--tm-testo); }
|
|
|
|
/* Tono velato: per i comandi che stanno SOPRA una superficie colorata — oggi
|
|
solo la testata. Un pulsante bianco o indaco lì sopra sarebbe una toppa; qui
|
|
il colore lo dà lo sfondo e il pulsante si limita a velarlo. Il focus diventa
|
|
bianco, perche' l'anello indaco su indaco non si distingue. */
|
|
.tm-btn-velato {
|
|
background: rgba(255, 255, 255, .12);
|
|
border-color: transparent;
|
|
color: rgba(255, 255, 255, .92);
|
|
}
|
|
.tm-btn-velato:hover:not(:disabled) { background: rgba(255, 255, 255, .22); color: #fff; }
|
|
.tm-btn-velato:focus-visible {
|
|
outline: 3px solid #fff;
|
|
outline-offset: 2px;
|
|
box-shadow: 0 0 0 5px rgba(0, 0, 0, .35);
|
|
}
|
|
|
|
.tm-btn-sm { min-height: 32px; padding: 6px 12px; font-size: 13px; gap: 5px; }
|
|
.tm-btn-sm svg { width: 14px; height: 14px; }
|
|
|
|
/* Solo icona: quadrato, senza testo. Serve sempre un aria-label, altrimenti chi
|
|
usa un lettore di schermo trova un pulsante senza nome. */
|
|
.tm-btn-icona { padding-left: 8px; padding-right: 8px; }
|
|
.tm-btn-sm.tm-btn-icona { padding-left: 7px; padding-right: 7px; }
|
|
|
|
/* ─── Zone della pagina ────────────────────────────────────────────────────
|
|
Ogni sezione e' un bersaglio di navigazione: premendo un pulsante o una voce
|
|
dell'indice la pagina ci si porta sopra e la zona lampeggia, cosi' e' chiaro
|
|
*dove* e' finita l'attenzione invece di dover indovinare cosa e' cambiato.
|
|
|
|
scroll-margin-top tiene la testata della zona sotto la barra dell'indice,
|
|
che e' appiccicata in alto: senza, l'ancora finirebbe coperta. */
|
|
.tm-zona {
|
|
scroll-margin-top: 84px;
|
|
}
|
|
|
|
/* Lampeggio d'arrivo. Non usa `outline` perche' si somma male ai bordi delle
|
|
card; box-shadow non occupa spazio e non fa saltare il layout. */
|
|
.tm-zona-evidenziata {
|
|
animation: tmEvidenzia 1.6s ease-out;
|
|
}
|
|
@keyframes tmEvidenzia {
|
|
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--tm-accento) 60%, transparent); }
|
|
12% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--tm-accento) 32%, transparent); }
|
|
100% { box-shadow: 0 0 0 0 transparent; }
|
|
}
|
|
|
|
/* Zona con un'operazione in corso: resta marcata finche' dura, cosi' anche
|
|
tornando sulla pagina dopo un ricaricamento si vede subito chi sta lavorando. */
|
|
.tm-zona-attiva {
|
|
box-shadow: 0 0 0 2px var(--tm-accento);
|
|
}
|
|
|
|
/* Chi ha chiesto meno animazioni riceve comunque il segnale, fermo. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.tm-zona-evidenziata { animation: none; box-shadow: 0 0 0 3px var(--tm-accento); }
|
|
.tm-btn, .card-hover, .page-enter { transition: none; animation: none; }
|
|
}
|
|
|
|
/* Indice delle zone, appiccicato in alto. */
|
|
.tm-nav-zone {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 20;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
padding: 10px 12px;
|
|
margin: -4px 0 0;
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: var(--tm-raggio);
|
|
background: rgba(255, 255, 255, .9);
|
|
backdrop-filter: blur(8px);
|
|
}
|
|
.tm-nav-zona {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 5px 10px;
|
|
border-radius: 999px;
|
|
border: 1px solid var(--tm-bordo);
|
|
background: var(--tm-superficie);
|
|
color: var(--tm-testo-medio);
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
transition: background-color .15s ease, border-color .15s ease, color .15s ease;
|
|
}
|
|
.tm-nav-zona:hover { background: var(--tm-accento-tenue); border-color: var(--tm-accento); color: var(--tm-accento-scuro); }
|
|
.tm-nav-zona-pericolo { color: var(--tm-pericolo); border-color: #edc9c6; }
|
|
.tm-nav-zona-pericolo:hover { background: var(--tm-pericolo-tenue); border-color: var(--tm-pericolo); color: #8e2a23; }
|
|
|
|
/* ─── Console delle operazioni ─────────────────────────────────────────────
|
|
Una sola finestra per tutte le operazioni lunghe, agganciata in fondo alla
|
|
finestra del browser: quello che sta succedendo non deve uscire dallo schermo
|
|
mentre si scorre la pagina. Chiusa e' una striscia alta 44px. */
|
|
.tm-console {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 45;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: #17161f;
|
|
border-top: 1px solid #2c2a38;
|
|
box-shadow: 0 -8px 28px -14px rgba(20, 16, 12, .6);
|
|
color: #e6e3dd;
|
|
}
|
|
.tm-console-testata {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 8px 12px;
|
|
min-height: 44px;
|
|
}
|
|
.tm-console-titolo {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
background: none;
|
|
border: 0;
|
|
padding: 0;
|
|
color: #e6e3dd;
|
|
font-family: inherit;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
.tm-console-btn {
|
|
background: transparent;
|
|
border: 1px solid #3d3a4a;
|
|
border-radius: 8px;
|
|
padding: 4px 10px;
|
|
color: #cfcbc2;
|
|
font-family: inherit;
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
transition: background-color .15s ease, color .15s ease;
|
|
}
|
|
.tm-console-btn:hover { background: #2c2a38; color: #fff; }
|
|
|
|
/* Il corpo scorre; l'altezza e' in vh perche' su un portatile basso una
|
|
finestra fissa in pixel si mangerebbe mezza pagina. */
|
|
.tm-console-corpo {
|
|
max-height: min(38vh, 340px);
|
|
overflow-y: auto;
|
|
padding: 4px 12px 12px;
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
font-size: 12px;
|
|
line-height: 1.55;
|
|
}
|
|
.tm-console-riga { display: flex; gap: 8px; white-space: pre-wrap; word-break: break-word; }
|
|
.tm-console-ora { color: #837d74; flex: none; }
|
|
.tm-console-sorgente { color: #a09ed8; flex: none; }
|
|
.tm-console-riga-errore .tm-console-testo { color: #e8a09a; font-weight: 600; }
|
|
.tm-console-riga-ok .tm-console-testo { color: #8fd0ac; }
|
|
.tm-console-riga-attesa .tm-console-testo { color: #e2bd7e; }
|
|
|
|
.tm-console-barra {
|
|
flex: 1;
|
|
height: 6px;
|
|
min-width: 60px;
|
|
border-radius: 999px;
|
|
background: #2c2a38;
|
|
overflow: hidden;
|
|
}
|
|
.tm-console-barra > div {
|
|
height: 100%;
|
|
border-radius: 999px;
|
|
background: #7c79c5;
|
|
transition: width .3s ease;
|
|
}
|
|
.tm-console-barra-ok > div { background: #3f8b64; }
|
|
.tm-console-barra-errore > div { background: #c9564c; }
|
|
|
|
.tm-console-torna {
|
|
align-self: center;
|
|
margin: 0 12px 8px;
|
|
background: #2f2c6c;
|
|
border: 1px solid var(--tm-accento);
|
|
border-radius: 999px;
|
|
padding: 3px 12px;
|
|
color: #dedef3;
|
|
font-family: inherit;
|
|
font-size: 11px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.tm-console-corpo { max-height: 45vh; font-size: 11px; }
|
|
.tm-console-sorgente { display: none; }
|
|
}
|
|
|
|
/* ─── Editor delle piantine, a schermo intero ──────────────────────────────
|
|
La mappa prende tutta la finestra e i comandi stanno in una striscia sottile
|
|
in alto. Tagliare un confine su una cartografia grande un terzo di schermo
|
|
vuol dire sbagliarlo, e questo disegno finisce stampato su un modulo che poi
|
|
gira di mano in mano. */
|
|
.tm-schermo {
|
|
height: 100dvh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.tm-editor {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
min-height: 0;
|
|
}
|
|
|
|
.tm-editor-barra {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
flex: none;
|
|
padding: 8px 14px;
|
|
background: var(--tm-superficie);
|
|
border-bottom: 1px solid var(--tm-bordo);
|
|
box-shadow: var(--tm-ombra-1);
|
|
z-index: 10;
|
|
}
|
|
.tm-editor-torna {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
flex: none;
|
|
padding: 5px 8px 5px 4px;
|
|
border-radius: 8px;
|
|
color: var(--tm-testo-medio);
|
|
font-size: 13.5px;
|
|
font-weight: 600;
|
|
text-decoration: none;
|
|
transition: background-color .15s ease, color .15s ease;
|
|
}
|
|
.tm-editor-torna:hover { background: var(--tm-superficie-alt); color: var(--tm-testo); }
|
|
.tm-editor-torna svg { width: 17px; height: 17px; }
|
|
|
|
.tm-editor-sep {
|
|
width: 1px;
|
|
height: 22px;
|
|
flex: none;
|
|
background: var(--tm-bordo);
|
|
}
|
|
|
|
.tm-editor-titolo {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 9px;
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
}
|
|
.tm-editor-titolo h1 {
|
|
margin: 0;
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: var(--tm-testo);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.tm-editor-salvato {
|
|
font-size: 12px;
|
|
color: var(--tm-testo-tenue);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.tm-editor-azioni {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
flex: none;
|
|
}
|
|
|
|
/* Le tre zone: elenco, mappa, pannello. Elenco e pannello galleggiano sopra
|
|
la mappa invece di restringerla: se togliessero spazio, aprirli per cambiare
|
|
un nome sposterebbe il disegno sotto il puntatore. */
|
|
.tm-editor-corpo {
|
|
position: relative;
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
/* Lo spazio che elenco e pannello aperti tolgono ai lati: barra degli
|
|
strumenti e controlli della mappa stanno nella parte libera, invece di
|
|
finire sotto (o sopra) un pannello. */
|
|
--libero-sx: 12px;
|
|
--libero-dx: 12px;
|
|
}
|
|
.tm-editor-corpo.con-elenco { --libero-sx: calc(min(270px, 100vw - 24px) + 24px); }
|
|
.tm-editor-corpo.con-pannello { --libero-dx: calc(min(370px, 100vw - 24px) + 24px); }
|
|
.tm-mappa .leaflet-left { left: calc(var(--libero-sx) - 12px); }
|
|
.tm-mappa .leaflet-right { right: calc(var(--libero-dx) - 12px); }
|
|
.tm-editor-mappa {
|
|
position: absolute;
|
|
inset: 0;
|
|
}
|
|
.tm-mappa {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: #efedea;
|
|
}
|
|
|
|
.tm-editor-nota {
|
|
position: absolute;
|
|
left: 50%;
|
|
bottom: 18px;
|
|
transform: translateX(-50%);
|
|
z-index: 500;
|
|
max-width: min(92vw, 460px);
|
|
margin: 0;
|
|
padding: 9px 14px;
|
|
border: 1px solid #e8d3ae;
|
|
border-radius: 999px;
|
|
background: var(--tm-attesa-tenue);
|
|
box-shadow: var(--tm-ombra-2);
|
|
font-size: 12.5px;
|
|
text-align: center;
|
|
color: var(--tm-attesa);
|
|
}
|
|
.tm-editor-nota a { color: inherit; font-weight: 600; }
|
|
|
|
/* ── Barra degli strumenti di disegno ── */
|
|
.tm-strumenti {
|
|
position: absolute;
|
|
top: 12px;
|
|
left: var(--libero-sx);
|
|
right: var(--libero-dx);
|
|
z-index: 650;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
width: fit-content;
|
|
margin-inline: auto;
|
|
padding: 5px;
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: 12px;
|
|
background: var(--tm-superficie);
|
|
box-shadow: var(--tm-ombra-3);
|
|
overflow-x: auto;
|
|
}
|
|
.tm-strumenti:empty { display: none; }
|
|
.tm-strumento {
|
|
flex: none;
|
|
min-height: 34px;
|
|
padding: 6px 12px;
|
|
border: 1px solid transparent;
|
|
border-radius: 8px;
|
|
background: none;
|
|
font: inherit;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--tm-testo-medio);
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
transition: background-color .15s ease, color .15s ease;
|
|
}
|
|
.tm-strumento:hover { background: var(--tm-superficie-alt); color: var(--tm-testo); }
|
|
.tm-strumento:focus-visible { outline: 2px solid var(--tm-accento); outline-offset: 1px; }
|
|
.tm-strumento-principale { background: var(--tm-accento); color: #fff; }
|
|
.tm-strumento-principale:hover { background: var(--tm-accento-scuro); color: #fff; }
|
|
.tm-strumenti-modo {
|
|
flex: none;
|
|
padding: 0 8px 0 6px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
letter-spacing: .04em;
|
|
text-transform: uppercase;
|
|
color: var(--tm-pericolo);
|
|
}
|
|
|
|
/* ── Elenco ── */
|
|
.tm-editor-elenco {
|
|
position: absolute;
|
|
top: 12px;
|
|
left: 12px;
|
|
bottom: 12px;
|
|
z-index: 600;
|
|
width: min(270px, calc(100vw - 24px));
|
|
display: flex;
|
|
flex-direction: column;
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: var(--tm-raggio);
|
|
background: var(--tm-superficie);
|
|
box-shadow: var(--tm-ombra-3);
|
|
overflow: hidden;
|
|
}
|
|
.tm-editor-elenco-cerca {
|
|
flex: none;
|
|
padding: 10px;
|
|
border-bottom: 1px solid var(--tm-bordo);
|
|
}
|
|
.tm-editor-elenco-cerca input {
|
|
width: 100%;
|
|
padding: 8px 10px;
|
|
border: 1px solid var(--tm-bordo-forte);
|
|
border-radius: 8px;
|
|
font: inherit;
|
|
font-size: 13.5px;
|
|
}
|
|
.tm-editor-elenco-righe {
|
|
flex: 1 1 auto;
|
|
margin: 0;
|
|
padding: 6px;
|
|
list-style: none;
|
|
overflow-y: auto;
|
|
}
|
|
.tm-editor-elenco-voce {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
width: 100%;
|
|
min-height: 38px;
|
|
padding: 7px 10px;
|
|
border: 0;
|
|
border-radius: 8px;
|
|
background: none;
|
|
font: inherit;
|
|
font-size: 13.5px;
|
|
text-align: left;
|
|
color: var(--tm-testo);
|
|
cursor: pointer;
|
|
}
|
|
.tm-editor-elenco-voce:hover { background: var(--tm-superficie-alt); }
|
|
.tm-editor-elenco-voce.is-selezionata { background: var(--tm-accento-tenue); color: var(--tm-accento-scuro); font-weight: 600; }
|
|
.tm-editor-elenco-stato {
|
|
flex: none;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
.tm-stato-bozza, .tm-stato-modificata { color: var(--tm-attesa); }
|
|
.tm-stato-pubblicata { color: var(--tm-ok); }
|
|
.tm-editor-elenco-vuoto {
|
|
padding: 14px 10px;
|
|
font-size: 13px;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
|
|
/* ── Pannello della piantina ── */
|
|
.tm-editor-pannello {
|
|
position: absolute;
|
|
top: 12px;
|
|
right: 12px;
|
|
bottom: 12px;
|
|
z-index: 600;
|
|
width: min(370px, calc(100vw - 24px));
|
|
display: flex;
|
|
flex-direction: column;
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: var(--tm-raggio);
|
|
background: var(--tm-superficie);
|
|
box-shadow: var(--tm-ombra-3);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.tm-editor-pannello-testata {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
flex: none;
|
|
padding: 12px 16px;
|
|
border-bottom: 1px solid var(--tm-bordo);
|
|
}
|
|
.tm-editor-pannello-testata h2 {
|
|
margin: 0;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
}
|
|
.tm-editor-chiudi {
|
|
background: none;
|
|
border: 0;
|
|
padding: 2px 6px;
|
|
border-radius: 7px;
|
|
color: var(--tm-testo-tenue);
|
|
font-size: 20px;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
transition: background-color .15s ease, color .15s ease;
|
|
}
|
|
.tm-editor-chiudi:hover { background: var(--tm-superficie-alt); color: var(--tm-testo); }
|
|
|
|
.tm-editor-pannello-corpo {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 20px;
|
|
padding: 16px;
|
|
overflow-y: auto;
|
|
}
|
|
.tm-editor-pannello-piede {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: 10px;
|
|
padding-top: 16px;
|
|
border-top: 1px solid var(--tm-bordo);
|
|
}
|
|
.tm-editor-aiuto {
|
|
margin: 0;
|
|
font-size: 12.5px;
|
|
line-height: 1.55;
|
|
color: var(--tm-testo-tenue);
|
|
}
|
|
.tm-stato-piantina p { margin: 0; font-size: 13px; line-height: 1.5; color: var(--tm-testo-medio); }
|
|
.tm-azioni-riga {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
.tm-azioni-riga:empty { display: none; }
|
|
.tm-passi {
|
|
margin: 0;
|
|
padding-left: 20px;
|
|
font-size: 13.5px;
|
|
line-height: 1.6;
|
|
color: var(--tm-testo-medio);
|
|
}
|
|
.tm-collegamento-piccolo {
|
|
align-self: flex-start;
|
|
padding: 0;
|
|
border-top: 0;
|
|
border-left: 0;
|
|
border-right: 0;
|
|
background: none;
|
|
font: inherit;
|
|
font-size: 12.5px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.tm-solo-stretto { display: none; }
|
|
|
|
/* Tablet: l'elenco si apre a richiesta e copre la mappa per il tempo della
|
|
scelta. */
|
|
@media (max-width: 1099px) {
|
|
.tm-editor-elenco { width: min(300px, calc(100vw - 24px)); }
|
|
.tm-editor-corpo.con-elenco { --libero-sx: calc(min(300px, 100vw - 24px) + 24px); }
|
|
}
|
|
|
|
/* Telefono e tablet in verticale: il pannello sale dal basso. */
|
|
@media (max-width: 720px) {
|
|
.tm-editor-barra { flex-wrap: wrap; }
|
|
.tm-editor-titolo { order: 3; flex-basis: 100%; }
|
|
.tm-editor-azioni .tm-btn span { display: none; }
|
|
.tm-solo-stretto { display: inline-flex; }
|
|
.tm-editor-pannello {
|
|
top: auto;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
width: auto;
|
|
height: min(62dvh, 520px);
|
|
border-radius: var(--tm-raggio) var(--tm-raggio) 0 0;
|
|
}
|
|
/* Elenco e pannello qui coprono la mappa per intero o dal basso: la
|
|
barra resta in alto, sopra il foglio del pannello. */
|
|
.tm-editor-corpo,
|
|
.tm-editor-corpo.con-elenco,
|
|
.tm-editor-corpo.con-pannello { --libero-sx: 12px; --libero-dx: 12px; }
|
|
/* A destra dei pulsanti dello zoom, e su più righe invece di uscire
|
|
dallo schermo. */
|
|
.tm-strumenti { left: 58px; flex-wrap: wrap; }
|
|
.tm-mappa .leaflet-left { left: 0; }
|
|
/* Il foglio copre crediti e scala della mappa finché è aperto, invece di
|
|
averli stampati sopra i campi. */
|
|
.tm-editor-pannello { z-index: 1001; }
|
|
}
|
|
|
|
/* Dita, non puntatore: vertici e maniglie di leaflet-draw più grandi, così
|
|
si prendono senza zoomare al massimo. */
|
|
@media (pointer: coarse) {
|
|
.leaflet-editing-icon,
|
|
.leaflet-touch-icon {
|
|
width: 22px !important;
|
|
height: 22px !important;
|
|
margin-left: -11px !important;
|
|
margin-top: -11px !important;
|
|
}
|
|
.tm-strumento { min-height: 42px; padding: 8px 14px; }
|
|
}
|
|
|
|
/* ─── Finestra di pubblicazione ───────────────────────────────────────────── */
|
|
|
|
.tm-modale-sfondo {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 800;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 16px;
|
|
background: rgba(28, 26, 23, .45);
|
|
}
|
|
.tm-modale {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 100%;
|
|
max-width: 460px;
|
|
max-height: calc(100dvh - 32px);
|
|
border-radius: var(--tm-raggio);
|
|
background: var(--tm-superficie);
|
|
box-shadow: var(--tm-ombra-3);
|
|
overflow: hidden;
|
|
}
|
|
.tm-modale-larga { max-width: 820px; }
|
|
.tm-modale-testata {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
padding: 14px 18px;
|
|
border-bottom: 1px solid var(--tm-bordo);
|
|
}
|
|
.tm-modale-testata h2 { margin: 0; font-size: 15px; font-weight: 600; }
|
|
.tm-modale-corpo {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
padding: 18px;
|
|
overflow-y: auto;
|
|
}
|
|
.tm-modale-piede {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
padding: 12px 18px;
|
|
border-top: 1px solid var(--tm-bordo);
|
|
}
|
|
.tm-anteprima-pdf {
|
|
width: 100%;
|
|
height: min(62dvh, 640px);
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: var(--tm-raggio-interno);
|
|
background: var(--tm-superficie-alt);
|
|
}
|
|
.tm-attesa {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 14px;
|
|
padding: 18px 4px;
|
|
}
|
|
.tm-attesa-titolo { margin: 0 0 4px; font-size: 14px; font-weight: 600; }
|
|
.tm-attesa-giro {
|
|
flex: none;
|
|
width: 22px;
|
|
height: 22px;
|
|
border: 3px solid var(--tm-accento-tenue);
|
|
border-top-color: var(--tm-accento);
|
|
border-radius: 50%;
|
|
animation: tm-giro .9s linear infinite;
|
|
}
|
|
@keyframes tm-giro { to { transform: rotate(360deg); } }
|
|
@media (prefers-reduced-motion: reduce) { .tm-attesa-giro { animation-duration: 3s; } }
|
|
.tm-nota-errore {
|
|
border-color: #edc9c6;
|
|
background: var(--tm-pericolo-tenue);
|
|
color: var(--tm-pericolo);
|
|
}
|
|
|
|
/* ─── Avvisi galleggianti (componente <x-avvisi>) ───────────────────── */
|
|
|
|
.tm-avvisi {
|
|
position: fixed;
|
|
top: 14px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
z-index: 900;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
width: min(92vw, 520px);
|
|
pointer-events: none;
|
|
}
|
|
.tm-avviso {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 11px 14px;
|
|
border-radius: var(--tm-raggio-interno);
|
|
border: 1px solid var(--tm-bordo);
|
|
background: var(--tm-superficie);
|
|
box-shadow: var(--tm-ombra-3);
|
|
font-size: 13.5px;
|
|
pointer-events: auto;
|
|
}
|
|
.tm-avviso svg { width: 18px; height: 18px; flex: none; }
|
|
.tm-avviso-ok { border-color: #bcd9c9; background: var(--tm-ok-tenue); color: var(--tm-ok); }
|
|
.tm-avviso-errore { border-color: #edc9c6; background: var(--tm-pericolo-tenue); color: var(--tm-pericolo); }
|
|
.tm-avviso-chiudi {
|
|
margin-left: auto;
|
|
background: none;
|
|
border: 0;
|
|
padding: 0 4px;
|
|
color: inherit;
|
|
font-size: 19px;
|
|
line-height: 1;
|
|
opacity: .6;
|
|
cursor: pointer;
|
|
}
|
|
.tm-avviso-chiudi:hover { opacity: 1; }
|
|
|
|
/* ─── Responsive list tables ───────────────────────────────────────────────
|
|
Under 640px (a phone held in portrait; rotate to landscape and the width
|
|
grows past the breakpoint, restoring the normal table) every table with the
|
|
`table-cards` class reflows: the header is hidden and each row becomes a
|
|
card, with each cell shown as "LABEL … value" (the label comes from the
|
|
cell's data-label attribute). Cells with no data-label (the actions column,
|
|
marked `.cell-actions`) span the row full width. */
|
|
@media (max-width: 640px) {
|
|
table.table-cards,
|
|
table.table-cards tbody {
|
|
display: block;
|
|
width: 100%;
|
|
}
|
|
table.table-cards thead {
|
|
display: none;
|
|
}
|
|
table.table-cards tr {
|
|
display: block;
|
|
border: 1px solid var(--tm-bordo);
|
|
border-radius: var(--tm-raggio-interno);
|
|
margin-bottom: 12px;
|
|
padding: 4px 2px;
|
|
background: var(--tm-superficie);
|
|
}
|
|
/* zebra striping would fight the card borders */
|
|
table.table-cards.table-striped tbody tr:nth-child(even) {
|
|
background: var(--tm-superficie);
|
|
}
|
|
table.table-cards td {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
width: 100%;
|
|
text-align: right;
|
|
padding: 6px 12px;
|
|
border: none !important;
|
|
}
|
|
table.table-cards td[data-label]::before {
|
|
content: attr(data-label);
|
|
flex: 0 0 auto;
|
|
margin-right: auto;
|
|
text-align: left;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: var(--tm-testo-tenue);
|
|
text-transform: uppercase;
|
|
letter-spacing: .04em;
|
|
}
|
|
/* Actions row: full width, buttons wrapped to the right */
|
|
table.table-cards td.cell-actions {
|
|
justify-content: flex-end;
|
|
flex-wrap: wrap;
|
|
margin-top: 2px;
|
|
padding-top: 10px;
|
|
border-top: 1px solid var(--tm-bordo) !important;
|
|
}
|
|
/* Cells spanning all columns (empty state, inline edit form) stay full
|
|
width and block, so their inner layout isn't squeezed by the flex row. */
|
|
table.table-cards td[colspan] {
|
|
display: block;
|
|
text-align: center;
|
|
}
|
|
}
|