/* ===========================================================================
   Piccione. Foglio di stile della dashboard.

   La lingua visiva e' quella di Guild Keeper, portata qui dal suo `tokens.css`:
   fondo quasi nero, oro caldo come unico accento, inchiostro crema, angoli vivi
   e moltissimi filetti sottili. I titoli sono in graziato (Georgia), il testo
   dell'interfaccia in system-ui, i valori macchina in Consolas: tre ruoli, come
   la'. Niente font da scaricare: sono tutti gia' nel sistema.

   Le superfici:
     carta     il fondo della pagina        (--bg di Guild Keeper)
     rilievo   cio' che galleggia sopra     (--card)
     incavo    il fondo dei campi           (--well-1)
     tenue     blocchi minori e passaggio   (--surface-2)

   I colori sono misurati. Quelli di Guild Keeper passano quasi tutti cosi'
   come sono: l'unico ritocco e' `--text-muted`, che sulle superfici sollevate
   si fermava a 3,79:1. Il tema chiaro non esiste la', ed e' derivato qui nello
   stesso spirito: carta calda, oro scuro.
   =========================================================================== */

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

/* --- Aggancio ai token di Guild Keeper ------------------------------------- */
/* I colori non stanno piu' qui: arrivano da `tokens.css`, copiato identico.
   Questi sono solo i nomi con cui Piccione li chiama, cosi' le sue regole non
   vanno riscritte una per una. Cambiare la palette la' cambia questo pannello.

   `--ok`, `--danger` e `--warn` non vengono rinominati: sono gia' i suoi. */
:root {
  --carta: var(--bg);
  --rilievo: var(--card);
  --incavo: var(--well-1);
  --tenue: var(--surface-2);

  --text-primary: var(--ink);
  --text-secondary: var(--text-2);
  /* Unico valore non suo: il suo --muted (#8a869c) sulle superfici sollevate
     scende a 3,79:1, sotto la soglia. Misurato, non supposto. */
  --text-muted: #9894aa;

  /* ── L'ACCENTO, IN DUE GRADINI DELLA STESSA FAMIGLIA ──────────────────
     hsl(205, 43%) tutti e due: cambia la LUCE, non la tinta e non la
     saturazione. Non sono due azzurri, e' un azzurro con due mestieri —
     e i due mestieri hanno vincoli OPPOSTI:

       - quello CHIARO (L 73%) e' quello che si SCRIVE e si DISEGNA: il
         testo d'accento al 70%, gli anelli del fuoco, le frecce, il
         chevron del profilo. Deve reggere 4,5:1 contro ogni tinta che il
         cielo mette sotto, e al 70% e' un vincolo stretto;
       - quello SCURO (L 59%) e' quello con cui si RIEMPIE: il pulsante
         principale, la riga scelta, la casella accesa del tema. Sopra ci
         va una scritta scura, quindi piu' scuro e' PIU' contrastato.

     Prima erano lo stesso valore, e quel valore doveva soddisfare il
     vincolo del testo: un fondo se lo portava addosso senza averne
     bisogno. E' la stessa distinzione che il tema chiaro fa gia' da
     sempre poco piu' sotto (`--selezione`), dove riusare il colore del
     testo come fondo aveva fatto una riga scelta NERA in una pagina
     chiara.

     ⚠️  E il gradino chiaro non si puo' abbassare. Prima era #6fc5e7:
     stessa luce, ma 71% di saturazione, ed era troppo acceso. A poter
     scendere e' la SATURAZIONE, non la luce — misurato contro la rete
     del cielo, come testo al 70%: #6b9fc4 fa 3,34:1 e #85b3d3 fa 4,07:1,
     tutti e due sotto soglia; questo fa 4,59:1, cioe' il filo di margine
     che aveva anche quello di prima (4,56:1). */
  --gold: #9bbfd8;
  --gold2: #b6d0e2;
  --gold-rgb: 155, 191, 216;
  --gold2-rgb: 182, 208, 226;

  /* L'accento AL 70%, come chiesto. Si mescola col fondo su cui sta, quindi
     il contrasto va misurato dopo la mescolanza: e' quello che si vede. */
  --accento: rgba(var(--gold-rgb), 0.7);
  --accento-forte: rgba(var(--gold2-rgb), 0.7);
  --accento-rgb: var(--gold-rgb);

  /* Opaco, e ci sta sopra del testo: e' il gradino SCURO. Un riempimento al
     70% si schiara col fondo e il testo sopra scende sotto la soglia
     (bianco sull'accento chiaro al 70% misurava 4,02:1). */
  --accento-pieno: #6b9fc4;
  /* ⚠️  E quello che si DISEGNA non e' quello che si riempie. Anelli del
     fuoco, frecce, chevron: nessuno di loro porta del testo addosso,
     quindi nessuno ha bisogno di essere scuro — e scurendoli si perde
     proprio la cosa per cui esistono, farsi vedere. Prima pescavano da
     `--accento-pieno` perche' i due valori COINCIDEVANO: una coincidenza,
     non una scelta, ed e' bastato separare i gradini per accorgersene. */
  --accento-segno: var(--gold);
  /* Il nero della pagina, non uno suo: il testo sul pieno e' la carta. */
  --su-accento: #0d0d0f;

  /* La riga scelta ha i suoi colori, separati dall'accento.
     Nello scuro il riempimento E' l'accento pieno: su una pagina scura una
     riga azzurra accesa si vede da lontano e non serve altro. */
  --selezione: var(--accento-pieno);
  --su-selezione: #0d0d0f;
  --su-selezione-tenue: #0d0d0f;

  --pericolo: var(--danger);
  --pericolo-rgb: var(--danger-rgb);
  --avviso: var(--warn);

  --filo: var(--line);
  --filo-forte: rgba(223, 228, 242, 0.30);

  --fondo-pulsante: rgba(0, 0, 0, 0.5);

  --discord: #5865f2;
  --discord-hover: #4752c4;

  /* ⚠️  Nello scuro un'ombra nera sotto un pannello quasi nero non si vede:
     il fondo non puo' diventare piu' scuro di cosi'. Quello che stacca e'
     il filo di luce in ALTO, dove il pannello prende la luce dell'ambiente.
     Non e' un bordo: e' un'ombra interna su un lato solo, e non chiude il
     riquadro. */
  --ombra-pannello: 0 18px 44px -24px rgba(0, 0, 0, 0.9),
                    inset 0 1px 0 rgba(255, 255, 255, 0.055);
  --barra: #2b2b30;
  --barra-viva: #3d3d44;
  --alone: #191a2e;
  --opacita-spiegazione: 0.5;
  /* Da qui parte la dissolvenza del testo di spiegazione. Dimezzando
     l'inchiostro crema si arrivava a 4,11:1, sotto la soglia; partendo dal
     bianco pieno la stessa opacita' del 50% misura 5,16:1. L'opacita' chiesta
     resta quella: cambia solo il colore da cui si parte. */
  --ink-spiegazione: #ffffff;

  --bg-wallpaper-pc: url('/wallpaper/piccione-pc-dark.webp');
  --bg-wallpaper-mobile: url('/wallpaper/piccione-smartphone-dark.webp');

  /* Il ritratto di Zio Piccio, e il suffisso nomina il MODO in cui si usa,
     non com'e' fatta l'immagine — stessa convenzione degli sfondi qui sopra.
     Le due versioni sono INVERTITE apposta: `-dark` ha il fondo CHIARO e
     `-light` il fondo NERO. Sembra un errore e non lo e': il ritratto e' un
     disco di 38-64px dentro una barra, e dentro una barra scura un disco
     scuro sparisce. Il fondo del ritratto fa da contrasto al pannello, non
     da tinta in armonia con lui. */
  --ritratto-piccio: url('/wallpaper/zio-piccio-dark.webp');

  /* Una faccia sola, variabile. I ruoli non sono piu' caratteri diversi ma
     posizioni diverse sugli assi della stessa faccia. */
  /* Due nomi, e l'ordine conta. Google Sans Flex viene prima e vince su tutto
     il latino; per una lettera cirillica non ha il glifo — quel sottoinsieme
     non esiste proprio — e si passa a Onest, che per `unicode-range` esiste
     solo lì. Prima ci ricadeva `system-ui`, cioè una faccia diversa da quella
     di tutto il resto: si vedeva nelle righe che mescolano le due scritture.
     Il perché e i file stanno in `public/font/caratteri.css`. */
  --faccia: "Google Sans Flex", "Onest", system-ui, "Segoe UI", Roboto, sans-serif;
  --ui: var(--faccia);
  --display: var(--faccia);
  --mono: var(--faccia);

  /* La scala in pixel di Guild Keeper, rialzata: `tokens.css` resta intatto,
     si sovrascrive qui perche' e' caricato prima. */
  --fs-display: 34px;
  --fs-xl: 28px;
  /* Di questa vecchia famiglia di misure ne restava viva una sola: le altre
     cinque non le usava nessuno. E `--fs-ui` non serve piu' a noi, serve al
     foglio copiato: `.btn` e `.field`, la' dentro, la leggono. Il loro valore
     e' 13px, ma style.css si carica dopo tokens.css, quindi vince questo — e
     tutti i pulsanti della pagina scrivono a 15px per causa sua, senza che
     fosse mai stata una decisione. Adesso lo e', e il numero lo prende dalla
     scala invece di essere scritto una seconda volta qui. */
  --fs-ui: var(--t4);

  --nav-height: 72px;
  /* Il rientro laterale della pagina, in un posto solo. Barra, contenuto e pie'
     devono cominciare sulla stessa verticale: quando ognuno se lo dichiarava per
     conto suo hanno smesso di farlo senza che se ne accorgesse nessuno. */
  --margine-pagina: clamp(14px, 2.4vw, 44px);

  /* Era 0, ed e' il motivo per cui le pillole ADMIN/OWNER, i campi, la testata
     del pannello profilo e le voci della barra avevano gli angoli vivi mentre
     tutto il resto era arrotondato: nessuno lo aveva scelto, era rimasto li'.
     Contenitori e pulsanti non lo usano (hanno i loro), quindi questo tocca
     solo le cose piccole, che e' esattamente dove serviva. */
  --radius: var(--raggio-voce);
  /* La molla delle animazioni. Era `cubic-bezier(0.34, 1.56, 0.64, 1)`, che
     passa oltre il valore finale del 9,8%: un rimbalzo da giocattolo in
     mezzo a una pagina che altrove si muove piano. Questa passa oltre del
     2,0% — misurato, non stimato — e sta in un token solo perche' il
     rimbalzo dev'essere lo stesso dappertutto. */
  --molla: cubic-bezier(0.25, 1.25, 0.4, 1);
  /* --- LA SCALA DEGLI SPAZI --------------------------------------------
     Trentaquattro riempimenti diversi e quattordici distanze, contati sulla
     pagina vera: 11px 14px, 7px 11px, 9px 11px, 13px 14px... numeri a caso,
     uno per componente.

     Passo da 8, mezzo passo da 4. E' quello che raccomanda designsystems.com,
     che sconsiglia proprio il 4 come passo principale: «ti apre a troppe
     variabili». Il 2 c'e' solo per i distacchi minimi fra due icone.

     Chi sta piu' di 3px lontano da un gradino resta com'e': se una misura e'
     cosi' fuori scala di solito ha una ragione, e forzarla sposterebbe le
     cose invece di ordinarle. */
  --s0: 2px;
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 20px;
  --s6: 24px;
  --s7: 32px;
  --s8: 40px;
  --s9: 48px;
  /* --- LA SCALA DEL TESTO ---------------------------------------------
     Quarantotto misure diverse dichiarate, in DUE unita': 34 in `rem` e 14
     in `px`. E la radice sta a 19px, non a 16, quindi ogni `rem` cadeva su
     un pixel rotto: 0,78rem faceva 14,82px e 0,85rem 16,15px. Misure che
     nessuno aveva scelto, a meta' pixel, che il browser deve comunque
     arrotondare per disegnarle.

     Nove gradini, in pixel interi. In cima sono piu' fitti apposta: lo
     stesso titolo cambia misura fra telefono e schermo largo, e due gradini
     troppo distanti gli appiattirebbero la rampa. C'e' un controllo che lo
     verifica: con sette gradini `.section-title` passava da 22,8/26,6/33,2 a
     20/28/28, cioe' due misure su tre uguali.

     Le misure del testo non devono stare sulla griglia da 4: quella vale per
     le altezze di riga e per gli spazi. */
  --t1: 11px;
  --t2: 12px;
  --t3: 13px;
  --t4: 15px;
  --t5: 17px;
  --t6: 20px;
  --t7: 24px;
  --t8: 28px;
  --t9: 32px;
  /* ─── LA SCALA DEI PULSANTI ───────────────────────────────────────────
     Undici coppie di colori diverse, contate sulla pagina vera: la stessa
     idea di «pulsante tinto» era scritta cinque volte con cinque ricette
     (14% sul pieno, 12% sul trasparente, 22%, 10%, 30%) e su tre basi
     diverse. Nessuno lo aveva deciso: si erano accumulate.

     Adesso ci sono TRE livelli, che e' quello che dicono di fare i sistemi
     di progetto seri (Carbon, Material, Cieden): pieno per l'azione
     principale, tinto per tutto il resto, piano per quello che non deve
     nemmeno sembrare un pulsante. Una ricetta per livello, non una per
     pulsante. Il rosso non e' un quarto livello: e' il livello tinto con
     un'altra tinta, con le stesse identiche percentuali.

     Il pieno va usato UNA volta per schermata. E il colore non lavora mai
     da solo: ELIMINA ha il cestino, «Sostienici» ha il cuore, e il tema ha
     tre simboli — perche' chi non distingue i colori deve poterli comunque
     distinguere. */
  --pieno: var(--accento-pieno);
  --su-pieno: var(--su-accento);
  /* ⚠️  Il livello tinto si mescola sopra il RILIEVO, non sopra il
     trasparente: viene fuori un blocco OPACO, che non lascia passare la
     pagina e quindi non si accende sopra al cielo. Con la velatura, fondo
     e scritta erano la stessa tinta a due intensita' — e la scritta era
     la piu' accesa delle due, per farsi vedere. Adesso il fondo e' un
     grigio-ardesia e la scritta e' quasi bianca: due famiglie, non una.
     Le percentuali salgono (20/32 -> 30/44) perche' mescolando con una
     base opaca invece che col nulla il colore rende meno.

     La ricetta resta UNA, con due tinte: il rosso non e' un quarto
     livello, e' questo livello con l'altra tinta e le stesse identiche
     percentuali. C'e' una prova che confronta i due numeri. */
  --tinto: color-mix(in srgb, var(--accento-pieno) 30%, var(--rilievo));
  --tinto-sopra: color-mix(in srgb, var(--accento-pieno) 44%, var(--rilievo));
  --su-tinto: #e4ebf2;
  --tinto-rosso: color-mix(in srgb, var(--pericolo) 30%, var(--rilievo));
  --tinto-rosso-sopra: color-mix(in srgb, var(--pericolo) 44%, var(--rilievo));
  /* ⚠️  La scritta del livello rosso è quella di TUTTI gli altri pulsanti,
     non una versione chiara del rosso. Un fondo rosso con sopra una scritta
     rossa è la stessa forma che il livello tinto ha appena smesso di avere
     con l'azzurro: due intensità della stessa tinta, con la più accesa delle
     due nella scritta per farsi vedere. Il rosso lo portano il FONDO e la
     forma del CESTINO — che è anche il solo segnale che arriva a chi i colori
     non li distingue. Vale nei due temi: qui è il quasi-bianco freddo, nel
     chiaro il blu scuro, sempre lo stesso di «Tutorial» e «Modifica». */
  --su-tinto-rosso: var(--su-tinto);
  /* I contenitori: velati, sfocati, e con un'ombra sotto.
     Col fondo che cambia colore tutto il giorno non bastava piu' il solo
     colore a staccarli — al tramonto carta e cielo si somigliano. L'ombra
     e' quella che li stacca sempre, perche' non dipende dal colore di
     sotto ma dalla luce. La sfocatura fa il resto: dietro al pannello il
     cielo e la pioggia si vedono, ma sfocati, e questo dice «vetro».
     L'opacita' e' larga: misurata, i testi reggono anche a pannello del
     tutto trasparente (4,93:1 nel chiaro), quindi 86% e' comodissimo. */
  /* Piu' leggera di prima: sotto adesso c'e' il cielo sgranato, e con la
     carta all'86% dei blocchi non si vedeva quasi niente. La misura dice
     che i testi reggono anche a pannello del tutto trasparente (4,93:1),
     quindi il 62% e' ancora molto largo. */
  --pannello-alfa: 62%;
  --sfocatura-pannello: blur(16px) saturate(1.1);
  /* Il colore dei comandi della barra: la stessa famiglia di «Entra nella
     dashboard» (#0b3241) ma piu' chiara, perche' li' e' un pulsante grande
     e isolato mentre questi stanno in fila. Prima erano azzurro chiaro su
     una pagina azzurra e non si vedevano proprio. Bianco sopra: 8,39:1. */
  --pulsante-barra: #175468;
  --su-pulsante-barra: #ffffff;
  /* Massima rotondita': i pulsanti sono pastiglie piene. Il valore enorme si
     taglia da solo a meta' dell'altezza, quindi la curva e' sempre la piu'
     grande possibile su qualunque altezza.
     ⚠️  Qui dentro e' finito anche `--radius-full`, che era questo stesso 9999px
         con un altro nome: l'ultimo dei quindici alias del «ponte» verso i
         componenti arrivati da main. Gli altri quattordici non li leggeva piu'
         nessuno da b96e753, che ha portato via i loro consumatori. Due nomi per
         una misura sola e' il modo in cui le due meta' divergono: le caselle del
         tema ne usavano uno e il pulsante del profilo l'altro, nella stessa
         barra. */
  --raggio-pulsante: 9999px;
  /* Fisso: su una scatola alta il raggio dei pulsanti diventerebbe un ovale. */
  --raggio-contenitore: 12px;
  --raggio-voce: 6px;
  --raggio-riga: 10px;
  /* I filetti che dividono sono barre vere, non bordi: un bordo non si puo'
     arrotondare da solo. Vedi `.step-header::after`.
     ⚠️  Due pesi, e non sono intercambiabili. Il grosso divide un TITOLO dal
         suo contenuto, il sottile divide due pezzi di contenuto fra loro. Li
         avevo scambiati nel registro — intestazione sottile e righe grosse —
         e la tabella si leggeva come sette blocchi separati sotto a un
         cappello attaccato al primo. Il grosso e' anche piu' contrastato: se
         no due pesi cosi' vicini non si distinguono.
     ⚠️  E `--barra-titolo` stava scritto DUE VOLTE, qui e otto righe piu' giu',
         con lo stesso valore: il commento sui due pesi e' arrivato dopo, si e'
         portato dietro la sua coppia, e la dichiarazione di prima e' rimasta
         sopra. Non si vedeva, e non si sarebbe visto finche' qualcuno non ne
         cambiava una sola. */
  --barra-riga: 2px;
  --barra-titolo: 2.5px;
  --passo: 0.1s ease;

  /* Di questa famiglia restano due nomi soli, e restano perche' sono gli unici
     spazi che cambiano con lo schermo. Gli altri erano un secondo nome per
     numeri che la scala aveva gia': --sp-1..--sp-6 del foglio copiato valgono
     4, 8, 12, 16, 24, 32, cioe' sei gradini di --s. Due vocabolari per gli
     stessi pixel, e chi scriveva una regola nuova ne pescava uno a caso. */
  --sp-largo: var(--s7);
  /* Il respiro fra sezioni sta sopra la scala apposta: e' l'unico stacco che
     deve dire "qui finisce una cosa e ne comincia un'altra", e il gradino piu'
     largo, 48, non si distingueva dal respiro dentro un pannello. Resta sulla
     griglia da 8: 56 sono sette passi. */
  --sp-sezione: 56px;
}

/* --- Tema chiaro ----------------------------------------------------------- */
/* Guild Keeper e' solo scuro. Il chiaro e' un'aggiunta di Piccione, e per
   restare una cosa sola sovrascrive le SUE variabili, non le nostre: cosi'
   anche le regole copiate in `componenti-gk.css` seguono il tema.
   Ogni valore misurato sul peggiore dei quattro fondi. */
[data-theme="light"] {
  --bg: #f2efe6;
  --card: #fbf9f3;
  --card2: #eae6da;
  --well-1: #e6e2d5;
  --well-2: #e2ded0;
  --well-3: #ddd8c9;
  --surface-2: #e3dfd2;
  --surface-3: #dcd7c8;

  /* Non nero. #2a2620 era un quasi-nero caldo e di giorno pesava troppo su un
     fondo azzurro: adesso e' un grigio scuro della stessa famiglia violacea
     degli altri due, cosi' i tre passi si leggono come una scala sola.
     Misurato sul peggiore dei fondi chiari (l'incavo): 9,05:1, che e' il
     doppio abbondante della soglia. Non e' l'inchiostro a limitare il
     cielo — quello lo limitano il nero della spiegazione e l'accento. */
  --ink: #3a3644;
  --text-2: #4a4550;
  --text-muted: #5b5665;

  /* Sul chiaro l'azzurro deve scurire molto: al 70% sopra una carta quasi
     bianca, questo e' il piu' chiaro della stessa tonalita' che regge 4,5:1
     su tutte le superfici. */
  --gold: #0b3241;
  /* Nel tema scuro `--gold2` e' la variante PIU CHIARA: e' il passaggio del
     mouse, e su fondo scuro schiarire vuol dire "acceso". Nel chiaro il verso
     si ribalta, e la prima versione se l'era portato dietro sbagliato: un
     accento piu' chiaro del gia' chiaro. `.brand-name`, che lo usa al 70%,
     misurava 4,07:1 sulla carta. Qui e' piu' scuro di `--gold`: 5,07:1. */
  --gold2: #092834;
  --gold-rgb: 11, 50, 65;
  --gold2-rgb: 9, 40, 52;
  --su-accento: #ffffff;

  /* Nel chiaro no. Qui il riempimento era `--accento-pieno`, cioe' #0b3241, un
     blu quasi nero: ma quel valore era stato scelto per reggere 4,5:1 come
     TESTO al 70% di opacita', non per fare da fondo. Un fondo non ha quel
     vincolo, e riusarlo faceva una riga scelta nera dentro una pagina chiara.
     Qui e' una velatura d'azzurro con l'inchiostro di sempre sopra:
     12,31:1 il testo, 5,80:1 le scritte piccole. */
  --selezione: #d6ecf5;
  --su-selezione: #2a2620;
  --su-selezione-tenue: #5b5665;

  /* ⚠️  `--muted` arriva dal foglio di Guild Keeper e non era mai stato
         riscritto per il chiaro: restava il grigio del tema scuro, e siccome
         vince per specificità sui placeholder di tre campi il testo lì dentro
         faceva 2,68:1. Stesso valore del suo gemello `--text-muted`, che il
         chiaro ce l'aveva: sono due nomi per la stessa idea. */
  --muted: #5b5665;
  /* Il riempimento tinto dei pulsanti e' l'accento al 20% (`--tinto`) su quello
     che ha dietro, e non piu' una mescola fissa: dietro c'e' il pannello, che e'
     `--rilievo` al 62% sul cielo. Misurata sulla pagina quella superficie fa
     #bcccd8, e il verde ci sta sopra a 5,15:1 — su 720 momenti (sei luoghi,
     cinque giornate, ogni ora) non scende mai sotto 4,85:1.
     Era `--gold` al 14% sopra `--well-2`, cioe' #c4c6bc, e li' il verde di prima
     si fermava a 4,28:1: e' per questo che e' sceso di un tono.
     Il bianco sopra la spia verde ne guadagna, da 7,41:1 a 8,47:1. */
  --ok: #2b5719;
  /* Sul riempimento tinto del pulsante Elimina — la stessa ricetta con l'altra
     tinta — il rosso precedente si fermava a 4,44:1, appena sotto: e' per questo
     che e' sceso di un soffio. Oggi quella superficie misura #d7cbd4 e questo
     rosso ci sta a 5,00:1, 4,79:1 nel peggiore dei cieli. (Il 4,44 era misurato
     su #d7c6b9, quando il tinto era al 14% sopra `--well-2`.) */
  --danger: #91312a;
  --danger-rgb: 145, 49, 42;
  --warn: #7d4500;

  --line: rgba(42, 38, 32, 0.16);
  --line-soft: rgba(42, 38, 32, 0.10);
  --line-gold: rgba(11, 50, 65, 0.45);
  --line-gold-soft: rgba(11, 50, 65, 0.26);
  --filo-forte: rgba(42, 38, 32, 0.32);

  --fondo-pulsante: rgba(255, 255, 255, 0.55);
  --alone: #e6e2f2;
  --ink-spiegazione: #000000;
  /* Nel chiaro il 50% non puo' passare: sopra una carta quasi bianca il nero
     pieno dimezzato arriva a 3,94:1 ed e' il MASSIMO possibile, perche' non
     esiste un colore piu' scuro del nero da cui partire. 55% era il primo
     valore che reggeva sul PEGGIORE dei due fondi chiari: sulla carta della
     pagina (4,60:1) e non solo su quella delle schede, dove il 54% sarebbe
     bastato. Nello scuro resta il 50% chiesto, che li' misura 5,16:1.

     Adesso e' 66%, per due ragioni misurate. La prima e' che il 55% non
     reggeva davvero dappertutto: su `--card2` (#eae6da) misurava 4,4999:1,
     cioe' sotto soglia per un capello, e la rete di sicurezza del cielo ci
     sbatteva contro a ogni tramonto senza poterci fare niente — il colore
     era gia' quello di partenza. Al 66% quella stessa misura fa 7,15:1.
     La seconda e' il cielo: era questo il testo che teneva il fondo inchiodato
     al bianco, perche' un nero al 55% smette di reggere appena il foglio si
     scurisce, e senza scurire il foglio l'azzurro non entra nel gamut. */
  --opacita-spiegazione: 0.66;

  /* Erano beige, che veniva dalla palette calda di prima: dentro una pagina
     azzurra stonavano. Stessa famiglia violacea dei testi. */
  /* Nel chiaro l'ombra fa tutto il lavoro. */
  --ombra-pannello: 0 14px 34px -22px rgba(12, 22, 40, 0.55),
                    0 3px 10px -8px rgba(12, 22, 40, 0.26);
  --barra: #b6c1d4;
  --barra-viva: #8e9cb8;

  --bg-wallpaper-pc: url('/wallpaper/piccione-pc.webp');
  --bg-wallpaper-mobile: url('/wallpaper/piccione-smartphone.webp');
  --ritratto-piccio: url('/wallpaper/zio-piccio-light.webp');

  /* ⚠️  Nello scuro i pulsanti sono passati alla tavolozza opaca, e il
     gradino che li riempie e' scritto per esteso invece di derivare da
     `--gold`: qui vanno ridichiarati, se no il giorno si porterebbe
     addosso i blocchi della notte. Sono le ricette di sempre, non valori
     nuovi — il tema chiaro non e' cambiato di un pixel. Il giorno che si
     rifa' anche lui, e' qui che si tocca.
     `--accento-segno` non serve: e' `var(--gold)`, che qui sopra e' gia'
     quello del chiaro. */
  --accento-pieno: var(--gold);
  --tinto: color-mix(in srgb, var(--accento-pieno) 20%, transparent);
  --tinto-sopra: color-mix(in srgb, var(--accento-pieno) 32%, transparent);
  --su-tinto: var(--accento-pieno);
  --tinto-rosso: color-mix(in srgb, var(--pericolo) 20%, transparent);
  --tinto-rosso-sopra: color-mix(in srgb, var(--pericolo) 32%, transparent);
  --su-tinto-rosso: var(--su-tinto);
}

/* --- Quello che a Guild Keeper non serve ----------------------------------- */
/* Da qui in giu' e' roba di Piccione, tenuta separata da cio' che e' copiato.

   Il suo `.btn` e' alto ~28px. Qui il minimo resta 44px: e' il bersaglio da
   pollice che questo pannello si e' gia' dato, e che i test difendono. */
.btn,
.btn-rule-action,
.btn-refresh,
.btn-logout-popover {
  min-height: 44px;
  justify-content: center;
}

/* `.btn` suo nasce con `align-self: flex-start`: dentro le barre di Piccione,
   che sono flex in riga, schiaccerebbe i pulsanti in alto. */
.btn {
  align-self: auto;
}



/* Il suo `.btn` ha il fondo scritto a mano (`rgba(0,0,0,.5)`), non preso da una
   variabile: essendo lui solo scuro non serviva. Nel chiaro diventava una
   scatola quasi nera. Si sovrascrive qui, senza toccare il file copiato. */
/* ⚠️  Qui c'erano `.stato-bot` e una PRIMA dichiarazione di
   `.status-indicator-dot` (piu' la sua variante `.online`), e non facevano
   niente. `.stato-bot` non esiste nel markup ne' in `app.js`: vestiva un nome
   che non c'e' piu'. Le altre due erano scavalcate in OGNI dichiarazione da una
   seconda copia 600 righe piu' in basso — stessa specificita', quindi vince
   l'ultima — e la differenza non si vedeva nemmeno sul colore, perche'
   `--avviso` e `--warn` sono lo stesso token (vedi sopra): e' per questo che
   nessuno se n'era accorto. Chi passava di qui per arrotondare la spia toccava
   questa e non cambiava un pixel: e' la trappola gia' raccontata per
   `.theme-icon-slot`. La spia viva sta piu' sotto, insieme a
   `.pigeon-avatar-wrap .status-indicator-dot`, che e' quella che le da' il
   cerchio e l'anello. */



/* --- Base ----------------------------------------------------------------- */
/* Il rem parte da qui: alzandolo cresce tutto cio' che e' scritto in rem, che
   e' quasi tutto. I token in pixel di Guild Keeper non seguono il rem, quindi
   sono rialzati piu' sotto uno per uno. */
/* ⚠️  Senza questa riga, Chrome per Android disegna il SUO lampo a ogni tocco:
   misurato, `rgba(51, 181, 229, 0.4)`, un ciano che nel foglio non compare da
   nessuna parte. Si vede su ogni pulsante, ogni collegamento e ogni `<label>` —
   quindi anche sui tre modi del tema — ed e' l'unico colore della dashboard che
   non ha scelto nessuno. Trasparente: il ritorno al tocco lo danno gia'
   `:active` e le transizioni.
   E `user-select: none` sui comandi: tenendo premuto un pulsante, Android
   seleziona la scritta e apre la lente invece di premere. Non si mette sul
   testo, che si deve poter copiare — solo su cio' che si preme. */
html {
  -webkit-tap-highlight-color: transparent;
}

button,
.btn,
.nav-btn-server,
.theme-icon-slot,
.tendina-voce {
  -webkit-user-select: none;
  user-select: none;
}

html {
  font-size: var(--t6);
  scroll-behavior: smooth;
}

#server-select-section,
#editor-section {
  scroll-margin-top: calc(var(--nav-height) + var(--s5));
}

/* Unico contorno del foglio. Senza, la pagina non si usa da tastiera. */
:focus-visible {
  outline: 2px solid var(--accento-segno);
  outline-offset: 2px;
}

[tabindex="-1"]:focus {
  outline: none;
}

/* Colonna alta almeno quanto lo schermo: il contenuto cresce, il pie' di
   pagina resta in fondo. Con poche news la pagina non lasciava piu' una
   fascia vuota sotto al pie', ne' lo faceva galleggiare a meta' schermo.
   Gli elementi in `position: fixed` (sfondo, modale, barra di salvataggio)
   sono fuori dal flusso e non entrano in questa colonna. */
body {
  font-family: var(--ui);
  /* La sua pagina non e' nero piatto: c'e' un alone in alto al centro.
     E' il suo primo `body`, prima che venisse ridotto a tinta unita.
     Qui l'alone non sta piu' fermo al centro: `--alone-x` lo porta dove sta il
     Sole davvero, da sinistra a destra lungo la giornata. Il 50% resta il
     ripiego per quando `cielo.js` non c'e'. Il colore di carta e alone lo
     scrive lo stesso script, in linea sull'elemento radice.
     La dissolvenza serve perche' il cielo si ridipinge ogni minuto e il salto
     si vedrebbe. Non e' piu' lunga di cosi' perche' l'alone e' un gradiente,
     e un gradiente non si puo' dissolvere: il suo colore cambia di scatto
     mentre il fondo sfuma, e piu' dura la sfumatura piu' i due si vedono
     sfasati. Con "riduci movimento" attivo la regola piu' in basso la annulla,
     come per tutto il resto. */
  /* Tre strati, dal piu' vicino al piu' lontano: la LUCE del Sole (un cerchio
     caldo che scende verso il bordo quando il Sole scende), l'alone del cielo,
     e la carta. Il primo e' quello che mancava: senza, il fondo virava di
     colore ma non c'era niente che somigliasse a una luce. */
  background:
    radial-gradient(circle at var(--sole-x, 50%) var(--sole-y, -16%),
                    var(--sole-velo, transparent) 0, transparent 46%),
    radial-gradient(ellipse at var(--alone-x, 50%) -8%, var(--alone) 0, transparent 60%),
    var(--carta);
  background-attachment: fixed;
  transition: background-color 1.2s linear;
  color: var(--text-primary);
  font-size: var(--t6);
  line-height: 1.5;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

#app-main {
  flex: 1 0 auto;
}

#app-footer {
  margin-top: auto;
}

/* Le righe di testo discorsivo non seguono la larghezza della finestra: oltre
   una certa misura l'occhio perde il capo della riga successiva. Larghezza al
   contenuto che ne guadagna (tabelle, elenchi), non alla prosa. */
.section-subtitle,
.rules-sub,
.form-help,
.popover-aiuto,
.empty-rules-box p,
.restore-box p,
.tut-step p {
  max-width: 76ch;
}

/* I valori che arrivano dalla macchina sono composti come codici. */
.codice,
.server-card-meta,
.rule-flow-val,
.brand-badge,
.rules-count,
.footer-copy {
  font-family: var(--mono);
  font-variant-ligatures: none;
}

/* I titoli vanno in graziato: e' il segno piu' riconoscibile di Guild Keeper.
   Ma restano color INCHIOSTRO, come il suo `.pcard h3`: da lui l'accento sta
   sui pulsanti e sui segni piccoli, non sui titoli. Dandolo a tutto, il
   pannello si tingeva per intero, cosa che il suo non fa. */
.login-title,
.section-title,
.banner-title,
.step-title,
.rules-toggle-label,
.rules-title,
.modal-title,
.riepilogo-numero,
.footer-logo {
  font-family: var(--display);
  letter-spacing: .01em;
  color: var(--text-primary);
}

/* L'unico marchio dorato, come il suo `.brand b`. */
.brand-name {
  font-family: var(--display);
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--accento-forte);
}

a {
  color: var(--accento);
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

img {
  max-width: 100%;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* --- Barra di scorrimento -------------------------------------------------- */
/* Quella di sistema era l'ultimo pezzo con un aspetto suo. Sottile, squadrata,
   senza frecce: si vede quando serve e non disegna una riga sul bordo. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--barra) transparent;
}

*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background: var(--barra);
  border-radius: var(--radius);
}

*::-webkit-scrollbar-thumb:hover,
*::-webkit-scrollbar-thumb:active {
  background: var(--barra-viva);
}

*::-webkit-scrollbar-corner {
  background: transparent;
}

/* Sul telefono la barra e' un velo che appare mentre si scorre: piu' magra. */
@media (pointer: coarse) {
  *::-webkit-scrollbar {
    width: 6px;
    height: 6px;
  }
}

/* --- Divisori --------------------------------------------------------------- */
/* I filetti separano le SEZIONI, le intestazioni e le RIGHE. Il fondo alterno
   c'era prima ed e' stato tolto: su un elenco corto la riga piu' chiara si
   legge come "questa e' scelta", che non e' quello che vuole dire. Un filo per
   riga divide senza affermare niente. Vedi `.registro tbody td`. */
.divisorio {
  height: 1px;
  background: var(--filo);
}

/* --- Superficie dei pannelli ---------------------------------------------- */
/* La scheda di Guild Keeper: piano proprio e un filo sottile. Sono le sue due
   dichiarazioni, prese dal suo `.pcard` (che nasce con un doppio filo da 4px e
   piu' avanti nel suo foglio passa a 1px pieno, perche' "ora che il
   riempimento la definisce, quei 4px sono peso ripetuto 53 volte"). */
.pannello {
  background: var(--card);
  border-radius: var(--radius);
  padding: var(--s6);
}

/* La barra in alto e il pie' non sono schede: nessun filo attorno, e nemmeno il
   riempimento da scheda. Il `padding: 0` che c'era qui pero' azzerava anche il
   loro rientro laterale: il marchio partiva dal bordo dello schermo mentre il
   titolo della pagina cominciava 35 px piu' in la'. Il rientro va rimesso, ed e'
   lo stesso del contenuto. */
.navbar.pannello,
.app-footer.pannello {
  border: none;
  padding: var(--s2) var(--margine-pagina);
}

/* --- Sfondo fotografico. Solo dietro all'accesso. ------------------------- */
.app-background,
.background-overlay {
  display: none;
}

html.login-active .app-background {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 0;
  background-image: var(--bg-wallpaper-pc);
  background-size: cover;
  background-position: center;
}

/* --- La fotografia fuori dall'accesso ------------------------------------- */
/* Quando l'impostazione lo chiede, la fotografia esce da dietro l'accesso e sta
   dietro a tutta la pagina. Il fondo del corpo diventa trasparente, cosi' si
   vede: negli spazi fra i pannelli, attorno alle schede, sotto al pie'.

   NON si mette un velo sopra per proteggere il testo. E' stato calcolato: per
   reggere 4,5:1 sopra un pixel QUALUNQUE della foto il velo dovrebbe stare a
   0,89 nel tema scuro e a 0,97 nel chiaro, cioe' lascerebbe passare l'11% e il
   3% della fotografia. Un velo cosi' non protegge una foto, la cancella.

   La via giusta e' un'altra: quasi tutto il testo di questa pagina sta gia' su
   una superficie opaca sua (pannelli, barra, pie', schede). Misurando, l'unico
   blocco che poggia sul fondo nudo e' l'intestazione di sezione. Quello riceve
   una superficie, e il contrasto regge dappertutto senza toccare la foto. */
html.con-foto body {
  background: transparent;
}

/* ⚠️  «Una superficie opaca sua» qui sopra non era piu' vero, ed e' questa
       riga che lo rimette in piedi. I pannelli sono diventati vetro DOPO —
       `--pannello-alfa: 62%` — e quella trasparenza e' stata misurata contro
       IL CIELO, che `cielo.js` garantisce leggibile prima di dipingerlo.
       Dietro a una fotografia non c'e' nessuna garanzia: misurato sul nostro
       sfondo, col pannello al 62%, il testo normale scende a 4,27:1 e quello
       smorzato a 1,97:1 nel tema scuro (3,27:1 nel chiaro). Per rientrare
       servirebbe il 90%, cioe' un vetro che non si vede piu' che sia vetro:
       tanto vale l'opacita' piena, che e' anche il presupposto su cui e'
       scritta tutta questa sezione.

       Vale per ogni modo in cui la foto si vede, `entrambi` compreso: li' il
       colore dell'ora tinge il pannello lo stesso, perche' arriva da
       `--rilievo`. Quello che si perde e' la sgranatura dentro al pannello,
       non il colore. */
html.con-foto {
  --pannello-alfa: 100%;
}

.con-foto .app-background {
  display: block;
  position: fixed;
  inset: 0;
  z-index: -2;
  background-image: var(--bg-wallpaper-pc);
  background-size: cover;
  background-position: center;
}

/* La fotografia sta sfocata, sempre: sia dietro alla pagina sia dietro
   all'accesso. Sfocata smette di competere col testo e diventa una superficie.

   `inset: -40px` non e' un vezzo: la sfocatura prende i pixel oltre il bordo
   del riquadro, e oltre il bordo non c'e' niente. Lasciandolo a zero resta una
   cornice chiara larga quanto il raggio, tutt'attorno allo schermo. Il riquadro
   si allarga oltre lo schermo di piu' del raggio, cosi' la parte sfumata cade
   fuori e non si vede. */
.con-foto .app-background,
html.login-active .app-background {
  inset: -40px;
  filter: blur(16px);
}

/* La tela del tempo sta sopra allo sfondo e sotto al contenuto. Nessun tocco
   la raggiunge: e' un disegno, non un comando. */
.meteo-tela {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

/* Con `entrambi` il colore del cielo torna, ma come velo sopra la fotografia:
   la tinge dell'ora che e' invece di coprirla. */
html[data-sfondo="entrambi"] .background-overlay {
  display: block;
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--carta);
  opacity: 0.5;
}

/* I blocchi di testo che stanno sul fondo nudo. Sopra la fotografia hanno
   bisogno di un piano loro, o non si leggono piu'. Sono due, e li ha trovati la
   verifica, non l'occhio: l'intestazione di sezione e la striscia del
   riepilogo.

   Il pannello che stava in questa lista portava la classe `glass-panel`, che in questo foglio NON ESISTE:
   e' rimasta da un sistema di stili precedente. Finora non si notava, perche'
   sul fondo pieno il testo si appoggiava comunque al colore della pagina. Sopra
   una fotografia si nota subito.

   ⚠️  Il riepilogo NON sta piu' in questa lista, e toglierlo e' stata una
       correzione, non una semplificazione: da quando i quattro numeri stanno
       dentro l'intestazione, la carta ce l'hanno gia' dal pannello che li
       contiene. Lasciandocelo si dipingeva una seconda carta OPACA sopra al
       vetro, cioe' una fascia nera larga quanto il pannello e alta quanto i
       numeri. Misurato: il testo sopra stava su fondo trasparente e quello
       sotto su `rgb(13, 13, 15)`, dentro la stessa scatola. */
.con-foto .section-header,
/* Il meteo e' lo stesso problema della fotografia: dietro a questo testo
   passano pioggia, nuvole e lampi, e il contrasto crollava a 2,7:1 col
   temporale sul tema chiaro. Anche qui l'ha detto la verifica, non l'occhio.
   Con la carta sotto, il testo non dipende piu' da che tempo fa. */
.con-meteo .section-header {
  background: var(--carta);
  border-radius: var(--raggio-contenitore);
  padding: var(--s4);
}

.con-foto .section-header,
.con-meteo .section-header {
  margin-bottom: var(--s1);
}

html.login-active .background-overlay {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 1;
  background: var(--carta);
  opacity: 0.6;
}

/* --- Accesso -------------------------------------------------------------- */
/* ⚠️  L'accesso e' la PAGINA, non un riquadro sopra la pagina (Stefano,
       29/9/2026), e a deciderlo e' una classe sola: `login-active` sulla radice.
       La scrive gia' il SERVER quando chi chiede la pagina non ha fatto l'accesso
       (`conAccessoDaFare`, src/webServer.js), e poi la tiene app.js
       (`setLoginScreenVisibility`). Per questo le regole di questa schermata
       guardano `html.login-active` e non `body`: il corpo la classe la prende solo
       da app.js, e fino ad allora sotto l'accesso si sarebbe vista la dashboard
       vuota — cioe' il lampo che la pagina ha smesso di fare. Una pagina servita
       da un altro server statico non ha la classe, e fa come prima: la decide
       app.js dopo /api/auth/me. */
.login-screen {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--s5);
  background: color-mix(in srgb, var(--carta) 84%, transparent);
  overflow: hidden;
  touch-action: none;
  overscroll-behavior: none;
}

html.login-active .login-screen {
  display: flex;
}

/* Dietro all'accesso non c'e' niente: niente barra, niente elenco dei server,
   niente piede. Prima c'erano, velati, e dentro l'elenco due «Accedi con
   Discord» ripetevano quello della carta. Si toglie anche il salto al
   contenuto: porterebbe a un contenuto che non c'e'.
   Sono le stesse quattro regioni che `regioniDiFondo()` (app.js) spegne dietro
   a un dialogo: se un giorno se ne aggiunge una, va aggiunta in tutti e due i
   posti, e la sezione #108 li confronta. */
html.login-active #app-header,
html.login-active #app-main,
html.login-active #app-footer,
html.login-active .salta-al-contenuto {
  display: none;
}

html.login-active,
body.login-active {
  overflow: hidden;
  height: 100vh;
  height: 100dvh;
  position: fixed;
  inset: 0;
  width: 100%;
  touch-action: none;
  overscroll-behavior: none;
}

.login-card {
  max-width: 380px;
  width: 100%;
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s4);
  padding: var(--s7) var(--s7);
  text-align: center;
  background: var(--rilievo);
  border-radius: var(--radius);
  touch-action: pan-y;
}

.login-logo-wrap {
  position: relative;
  width: 64px;
  height: 64px;
}

/* ⚠️  Non e' un `<img>`, ed e' per questo: il ritratto cambia col tema, e la
   sorgente di un'immagine non la si puo' far dipendere da `data-theme` senza
   passare da JavaScript — che vorrebbe dire caricare prima quello sbagliato e
   poi scambiarlo, cioe' un lampo. Da qui il tema lo sceglie il CSS, come gia'
   fa per lo sfondo della pagina. Il nome non si perde: due righe sotto c'e'
   l'`<h1>PICCIONE BOT</h1>`, e un `alt` lo faceva leggere due volte. */
.login-logo {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background-image: var(--ritratto-piccio);
  background-size: cover;
  background-position: center;
}

/* ⚠️  Le spie di questa pagina sono DISCHI, e questa era l'unica quadrata
   insieme a quella del piede: non perche' qualcuno avesse scritto un raggio
   sbagliato, ma perche' non ne aveva nessuno. Tonda lo diventa qui, non
   copiando `.status-indicator-dot` — quella, in tutte e due le sue
   dichiarazioni, il raggio non ce l'ha: alla spia della barra lo da'
   `.pigeon-avatar-wrap .status-indicator-dot`, che ha due classi.
   L'anello e' la stessa cosa che fa lassu' e serve allo stesso scopo: staccare
   il disco dal ritratto che ha sotto. Ma il colore e' `--rilievo`, che e' il
   fondo della carta d'accesso (`.login-card`): copiando `--carta` alla lettera
   si dipinge un alone del colore di un'altra superficie. */
.login-status-dot {
  position: absolute;
  bottom: 2px;
  right: 2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--avviso);
  box-shadow: 0 0 0 2.5px var(--rilievo);
}

.login-status-dot.online {
  background: var(--ok);
}

.login-title {
  font-size: var(--t7);
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.1;
}

.login-subtitle {
  font-size: var(--t5);
  color: var(--text-secondary);
}

.login-texts {
  display: grid;
  gap: var(--s1);
}

/* La frase che dice cos'e' Piccione. Col colore del testo e non con quello
   smorzato del sottotitolo: e' la cosa da leggere, non una didascalia. */
.login-presenta {
  margin: 0;
  font-size: var(--t5);
  line-height: 1.45;
  color: var(--ink);
  text-wrap: pretty;
}

.btn-discord-login {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  width: 100%;
  min-height: 44px;
  padding: var(--s3) var(--s5);
  background: var(--discord);
  color: #fff;
  font-weight: 600;
  font-size: var(--t5);
  text-decoration: none;
  border-radius: var(--radius);
  transition: background var(--passo);
}

.btn-discord-login:hover {
  background: var(--discord-hover);
}

.login-footer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  width: 100%;
  margin-top: var(--s1);
  padding: var(--s2) var(--s3);
  background: var(--incavo);
  border-radius: var(--radius);
  font-size: var(--t4);
  color: var(--text-secondary);
}

/* Disco anche qui, per la stessa ragione della spia sul ritratto. Senza anello,
   pero': l'anello lassu' serve a staccare il disco dalla FOTO che ha sotto, e
   qui sotto c'e' solo l'incavo del piede, piatto e di un colore solo. */
.login-footer-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--avviso);
}

.login-footer-dot.online {
  background: var(--ok);
}

/* --- Testata -------------------------------------------------------------- */
/* ⚠️  Il fondo di questo contenitore e' quello che si vede ATTRAVERSO la barra:
       `.navbar` e' un pannello di vetro al 62%, quindi il colore di qui passa
       per tutta la sua superficie, non solo dove sporge. Ed e' anche il motivo
       per cui il raggio deve essere lo STESSO: la barra e' arrotondata, questo
       era squadrato, e negli angoli restava scoperto il fondo pieno — misurato,
       `rgb(4,4,11)` contro `rgb(12,13,18)` del centro, cioe' 8 livelli su 255.
       Otto livelli non si nominano, ma si vedono: il disegno leggeva come un
       rettangolo opaco dietro a una pastiglia tonda.
       Il fondo pieno serve e resta: la barra e' appiccicata in cima e sotto le
       passa il contenuto della pagina, che al 62% si leggerebbe attraverso le
       scritte. La stessa ragione per cui i pannelli sopra una fotografia sono
       opachi — il vetro e' stato misurato contro il cielo, che `cielo.js`
       garantisce leggibile, e dietro a una pagina che scorre non c'e' nessuna
       garanzia.
       Il raggio arriva dallo stesso token della barra: due numeri scritti a
       mano divergono, e a divergere sarebbe proprio l'angolo. */
.navbar-wrapper {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--carta);
  border-radius: var(--raggio-contenitore);
}

.navbar {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: var(--nav-height);
  padding: var(--s2) var(--margine-pagina);
  background: var(--carta);
  border-radius: 0;
  flex-wrap: wrap;
}

/* Marchio e stato del bot. Si vedono solo nella schermata di scelta dei
   server: dentro la configurazione il posto serve alla pillola dei server.
   Lo mostra e lo nasconde `aggiornaMarchioBarra()` in app.js. */
.nav-brand {
  display: flex;
  align-items: center;
  gap: var(--s2);
  color: inherit;
}

/* La foto e' tonda come su Discord, e porta la spia dello stato attaccata al
   bordo: e' quella la convenzione, non una scritta di stato che galleggia. */
/* Stessa ragione di `.login-logo`. */
.brand-avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background-image: var(--ritratto-piccio);
  background-size: cover;
  background-position: center;
  display: block;
}

.pigeon-avatar-wrap {
  position: relative;
  display: block;
  flex: none;
}

/* La spia sta sul bordo della foto, con un anello del colore della barra che la
   stacca dalla foto sotto. */
.pigeon-avatar-wrap .status-indicator-dot {
  position: absolute;
  right: -1px;
  bottom: -1px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  box-shadow: 0 0 0 2.5px var(--carta);
}

/* La misura della foto sta scritta una volta sola, qui sopra. Ce n'era una
   seconda copia proprio in questo punto, con 28px invece di 38: veniva dopo e
   vinceva, e la foto restava piccola qualunque cosa dicesse la regola sopra. */
.status-indicator-dot {
  position: absolute;
  bottom: -1px;
  right: -1px;
  width: 9px;
  height: 9px;
  background: var(--avviso);
}

.status-indicator-dot.online {
  background: var(--ok);
}

.brand-text {
  display: grid;
  gap: var(--s0);
  line-height: 1.15;
  min-width: 0;
}

.brand-name {
  font-weight: 700;
  font-size: var(--t5);
  color: var(--text-primary);
  white-space: nowrap;
}

/* Lo stato: minuscolo e smorzato. E' un dettaglio, non un titolo, e cambia da
   solo ogni cinque secondi: se pesasse quanto il nome la barra sembrerebbe
   muoversi in continuazione. */
/* ⚠️  La scritta «Online» non si vede piu', a nessuna larghezza. Lo stato del
   bot lo dice il pallino accanto al ritratto, e ripeterlo a parole costava 47px
   della barra — su un telefono, la differenza fra una riga e due.

   Ma l'elemento RESTA, e resta nell'albero accessibile: e' l'unica cosa che ha
   `aria-live`, cioe' l'unica che annuncia il cambio di stato a chi non vede, e
   il pallino verde non ha un nome. Queste sette righe sono quelle di `.sr-only`
   e stanno qui e non nella classe perche' `.brand-badge` e' dichiarata piu'
   avanti nel foglio: a parita' di specificita' vince l'ultima, e messa altrove
   la scritta sarebbe tornata a vedersi senza che nulla lo segnalasse. */
.brand-badge {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* ⚠️  Non si veste da solo: fondo, sollevamento e rotondita' gli arrivano dalle
       stesse liste degli altri pulsanti, piu' in basso nel foglio. Qui restano
       le sole cose sue, cioe' come si legge la scritta.

       A 11px stava dentro una pillola e faceva da rifinitura. Adesso e' il
       comando principale della barra — l'unico modo di tornare all'elenco —
       e a quella misura si leggeva come una didascalia.

       Il `:hover` che stava qui e' sparito: prometteva fondo trasparente e
       bordo d'accento, cioe' il contrario di quello che fa la scala trenta
       regole piu' giu', e non vinceva mai. Era rimasto a raccontare una cosa
       falsa a chi legge il foglio. */
.nav-btn-server {
  font-family: var(--ui);
  font-size: var(--t2);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* ⚠️  La scatola e' `flex` ma non diceva dove mettere il contenuto, quindi la
         scritta si appoggiava in alto a sinistra: misurato, 3px sopra e 27
         sotto dentro un tasto alto 44. Non si vedeva finche' il rientro
         verticale valeva 8 e mascherava meta' dello scarto — poi il rientro e'
         sceso e la scritta e' finita appiccicata al bordo di sopra.
         Il centramento non si fa col rientro: quello riempie, non allinea, e a
         ogni misura nuova va ritarato. Queste due righe lo fanno una volta.
         `.nav-btn-server` non sta nella lista dei `.btn` che ricevono
         `justify-content: center` poco sopra: e' una classe di Piccione, e
         quella lista non la nomina. */
  align-items: center;
  justify-content: center;
  /* ⚠️  Misurato in un browser vero: senza questa riga il tasto veniva alto
         38,5px, sotto il bersaglio da pollice — e su un telefono è l'unico
         modo di uscire da una configurazione. Il maiuscoletto a `--t2` da solo
         non ci arriva. */
  min-height: 44px;
  padding: var(--s2) var(--s3);
}

.nav-links {
  display: flex;
  gap: var(--s0);
}

.nav-link {
  display: inline-flex;
  align-items: center;
  padding: var(--s2) var(--s2);
  font-size: var(--t4);
  font-weight: 500;
  color: var(--text-secondary);
  text-decoration: none;
  border-radius: var(--radius);
  transition: color var(--passo), background var(--passo);
}

.nav-link:hover {
  color: var(--text-primary);
  background: var(--incavo);
}

/* Tutto cio' che riguarda il sistema sta a destra, spinto li' dal margine
   automatico: la pillola dei server resta la prima cosa a sinistra. */
.nav-actions {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin-left: auto;
}

.nav-patreon-heart-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  flex: none;
  min-height: 44px;
  padding: 0 var(--s3);
  /* Il testo segue il livello tinto come tutti gli altri. A distinguerlo resta
     il CUORE, che e' un segnale di forma e non di colore — e la forma la vede
     anche chi i colori non li distingue. Il cuore, lui, resta rosso. */
  color: var(--su-tinto);
  text-decoration: none;
  border-radius: var(--raggio-pulsante);
  background: var(--tinto);
  font-family: var(--ui);
  font-size: var(--t3);
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: color var(--passo), background var(--passo);
}

.nav-patreon-heart-btn .patreon-heart-svg {
  flex: none;
}

/* ⚠️  Qui sopra c'era un SECONDO `:hover` per questo pulsante, otto righe piu'
       in alto, con `background: var(--tinto-sopra)`. Non dipingeva niente:
       stesso selettore, stessa specificita', e a vincere era questo che viene
       dopo. Ci era finito per sbaglio — `e2469a1` ha riscritto un blocco di
       cinque regole con uno di quattro andando a caselle, e nella casella
       avanzata (che prima vestiva il CUORE in hover) e' finito un secondo hover
       del PULSANTE. Poi `edbb213`, la revisione dei colori, ha modificato quella
       copia credendola viva: il suo messaggio dichiara un cambiamento che sullo
       schermo non e' mai avvenuto.

       Fra i due si e' scelto di tenere QUESTO, cioe' quello che si vedeva. Vuol
       dire che questo pulsante, al passaggio del mouse, si comporta diversamente
       dagli altri tinti (loro vanno a `--tinto-sopra` e tengono il testo pieno;
       lui va all'incavo e smorza la scritta): e' una scelta, non un residuo, e
       non va «raddrizzata» per uniformita' senza chiederlo. */
.nav-patreon-heart-btn:hover {
  color: var(--accento);
  background: var(--incavo);
}

.patreon-heart-svg {
  flex: none;
}

/* --- Profilo e preferenze ------------------------------------------------- */
.user-profile-widget {
  position: relative;
}

.btn-profile {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s1) var(--s2);
  min-height: 44px;
  color: var(--text-secondary);
  background: var(--incavo);
  border-radius: var(--radius);
  transition: color var(--passo);
}

/* L'avatar e l'iniziale che ne prende il posto quando non arriva
   (`ripiegaAvatarUtente`) sono lo stesso disco: la misura sta in un posto solo,
   se no divergono. */
.user-avatar-img,
.user-avatar-iniziale {
  width: 22px;
  height: 22px;
  border-radius: 50%;
}

.user-avatar-img {
  object-fit: cover;
}

/* Piena d'accento come la casella del tema scelta, che le sta accanto: e'
   l'unica cosa piena dentro una pastiglia velata, e a 22px si legge come un
   avatar e non come una lettera caduta nel chip. */
.user-avatar-iniziale {
  flex: none;
  display: grid;
  place-items: center;
  background: var(--accento-pieno);
  color: var(--su-accento);
  font-size: var(--t1);
  font-weight: 700;
  line-height: 1;
}

/* ⚠️  `display: grid` qui sopra scavalca l'attributo `hidden`, che e' solo una
       regola del browser: senza questa riga il disco resterebbe accanto
       all'avatar anche quando l'avatar arriva. */
.user-avatar-iniziale[hidden] {
  display: none;
}

.user-name-text {
  font-size: var(--t4);
  font-weight: 500;
  max-width: 12ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.profile-chevron {
  flex: none;
  color: var(--text-muted);
}

.profile-popover {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 150;
  width: min(320px, calc(100vw - 32px));
  padding: var(--s4);
  background: var(--rilievo);
  border-radius: var(--radius);
  display: grid;
  gap: var(--s3);
}

.profile-popover[hidden] {
  display: none;
}

.popover-header {
  padding: var(--s2) var(--s2);
  background: var(--incavo);
  border-radius: var(--radius);
}

.popover-title {
  font-size: var(--t3);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.popover-campo {
  display: grid;
  gap: var(--s1);
}

/* Era un filetto. Senza linee, lo spazio fa lo stesso lavoro. */
.popover-separatore {
  display: none;
}

.btn-logout-popover {
  width: 100%;
  min-height: 44px;
  font-family: var(--ui);
  font-size: var(--t1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--su-tinto-rosso);
  background: var(--fondo-pulsante);
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 rgba(var(--pericolo-rgb), 0.26);
  transition: transform 0.08s, box-shadow 0.08s, border-color 0.08s;
}

.btn-logout-popover:hover {
  border-color: var(--pericolo);
  box-shadow: 3px 3px 0 rgba(var(--pericolo-rgb), 0.45);
}

/* --- Salto al contenuto --------------------------------------------------- */
/* Sta fuori dallo schermo e rientra solo quando prende il fuoco. Non e' nascosto
   con `display: none`, che lo toglierebbe anche dal giro della tabulazione ed e'
   proprio quello che non deve succedere. */
.salta-al-contenuto {
  position: fixed;
  top: var(--s1);
  left: var(--s1);
  z-index: 300;
  padding: var(--s2) var(--s4);
  border-radius: var(--raggio-pulsante);
  background: var(--rilievo);
  color: var(--text-primary);
  font-size: var(--t5);
  font-weight: 600;
  transform: translateY(calc(-100% - var(--s1) * 2));
  transition: transform var(--passo);
}

/* `:focus` e non `:focus-visible`: qui non serve distinguere il fuoco da
   tastiera da quello col mouse, perche' questo collegamento lo raggiunge solo
   la tastiera. Con `:focus-visible` restava fuori schermo in qualche caso. */
.salta-al-contenuto:focus {
  transform: translateY(0);
}

/* Il contenuto principale riceve il fuoco dal salto, ma non deve disegnarsi
   attorno un anello: non e' un comando, e' solo il punto d'arrivo. */
.main-container:focus {
  outline: none;
}

/* --- Interruttore del tema ------------------------------------------------ */
/* Tre caselle affiancate, senza la slitta che scorreva sotto: quella faceva
   sembrare le tre posizioni i gradini di una stessa scala, mentre sono tre
   scelte pari fra loro. Adesso la scelta si accende e le altre due stanno
   spente, che e' come si legge un gruppo di scelte. */
.theme-toggle-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--s0);
  flex: none;
  padding: var(--s0);
  border-radius: var(--raggio-pulsante);
  background: var(--incavo);
}

/* ⚠️  Qui NON si scrive la misura: la dichiara la regola dell'interruttore del
   tema, piu' avanti nel foglio, ed e' quella che vince a parita' di
   specificita'. Scriverla in tutti e due i posti vuol dire che uno dei due
   mente, e sara' quello che qualcuno ha appena cambiato. */
.theme-icon-slot {
  display: grid;
  place-items: center;
  cursor: pointer;
  flex: none;
  border-radius: var(--raggio-voce);
  color: var(--text-muted);
  transition: color var(--passo), background var(--passo);
}

/* Il radio e' fuori vista, quindi l'anello del fuoco va disegnato sulla casella
   che gli sta accanto: altrimenti chi naviga da tastiera non vede dov'e'.
   Questa regola e' viva: dichiara `outline-offset`, che piu' sotto nessuno
   riscrive. Le due che stavano qui accanto — il passaggio del mouse e la
   casella scelta — non lo erano, ed erano gia' rifatte in coda al foglio. */
input[name="tema-modo"]:focus-visible + .theme-icon-slot {
  outline: 2px solid var(--accento-segno);
  outline-offset: -2px;
}

.theme-icon {
  display: block;
}

/* --- Impaginato ----------------------------------------------------------- */
.main-container {
  width: 100%;
  padding: var(--s6) var(--margine-pagina) var(--s9);
}

/* Il respiro fra sezioni e' il gradino piu' largo della scala: senza riquadri
   e' l'unica cosa che dice "qui finisce una cosa e ne comincia un'altra". */
.section-container {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sezione);
}

/* Titolo e descrizione a sinistra, "Aggiorna" spinto tutto a destra, e nessun
   filetto sotto: il titolo e' gia' abbastanza grosso da separarsi da solo. */
.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  padding: var(--s1) 0 var(--s4);
}

/* La colonna di sinistra. `min-width: 0` per la solita ragione: senza, una voce
   di flex non scende sotto il proprio contenuto e la descrizione lunga terrebbe
   aperta la riga invece di andare a capo. */
.section-testi {
  display: grid;
  gap: var(--s1);
  min-width: 0;
}

.section-badge {
  justify-self: start;
  font-size: var(--t2);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.section-title {
  font-size: var(--t8);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.section-subtitle {
  font-size: var(--t5);
  color: var(--text-secondary);
}

/* ⚠️  I due pulsanti stanno in un contenitore loro e non come figli diretti
   dell'intestazione. `.section-header` e' `space-between` con DUE voci: coi
   pulsanti sciolti le voci diventano tre, e `space-between` li stacca l'uno
   dall'altro spingendoli agli estremi della riga invece di tenerli vicini.
   Con il contenitore le voci restano due — i testi a sinistra, i comandi a
   destra — e la spaziatura fra i pulsanti la decide il `gap` di qui. */
.section-azioni {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex: none;
}

.btn-refresh {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 44px;
  padding: var(--s2) var(--s3);
  font-family: var(--ui);
  font-size: var(--t1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accento);
  background: var(--fondo-pulsante);
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 rgba(var(--accento-rgb), 0.26);
  transition: transform 0.08s, box-shadow 0.08s, color 0.08s, border-color 0.08s;
}

.btn-refresh:hover:not(:disabled) {
  color: var(--accento-forte);
  border-color: var(--accento);
  box-shadow: 3px 3px 0 rgba(var(--accento-rgb), 0.45);
}

.btn-refresh:active:not(:disabled) {
  transform: translate(3px, 3px);
  box-shadow: none;
}

.btn-refresh:disabled {
  opacity: 0.6;
  cursor: default;
}

.btn-refresh.in-corso .refresh-icon {
  animation: giraFerma 0.8s linear infinite;
}

@keyframes giraFerma {
  to { transform: rotate(360deg); }
}

/* --- Scelta dei server ---------------------------------------------------- */
/* Le due colonne stanno larghe fra loro quanto due sezioni: e' cio' che le
   tiene separate adesso che non c'e' piu' un riquadro attorno a ciascuna. */
.wizard-steps-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-sezione);
}

.wizard-step-box {
  display: grid;
  align-content: start;
  gap: var(--s4);
  /* `1fr` non vuol dire "una frazione e basta": vuol dire `minmax(auto, 1fr)`,
     e quell'`auto` come minimo significa "non piu' stretto del contenuto".
     Il nome del server non va a capo, quindi il contenuto non si stringe, e la
     colonna cresceva oltre la griglia che la contiene: su un telefono da 375 px
     la pagina sbordava di 118 e i distintivi ADMIN finivano fuori schermo.
     Con `min-width: 0` la casella puo' scendere sotto il proprio contenuto, e
     l'`overflow: hidden` che il nome aveva gia' comincia finalmente a servire.
     Vale anche per `.server-card-testi` piu' sotto: stessa trappola, un piano
     piu' in giu'. */
  min-width: 0;
}

.step-header {
  position: relative;
  display: flex;
  gap: var(--s3);
  padding-bottom: var(--s4);
}

/* La barra che separa il titolo dall'elenco. E' un elemento, non un bordo:
   i bordi non hanno un raggio proprio. Larga quanto il contenuto del
   pannello, cosi' le righe qui sotto possono allinearcisi esattamente. */
.step-header::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--barra-titolo);
  background: var(--filo-forte);
  border-radius: 9999px;
}

.step-number {
  flex: none;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  background: var(--accento-pieno);
  color: var(--su-accento);
  font-size: var(--t4);
  font-weight: 700;
  border-radius: 50%;
}

.step-titles {
  display: grid;
  gap: var(--s0);
}

.step-title {
  font-size: var(--t6);
  font-weight: 700;
}

.servers-cards-grid {
  display: flex;
  flex-direction: column;
  /* Stessa trappola della casella che la contiene: e' una voce di griglia, e
     una voce di griglia non scende sotto il proprio contenuto se non glielo si
     dice. Vedi `.wizard-step-box`. */
  min-width: 0;
}

.loading-spinner-wrap {
  padding: var(--s4) var(--s1);
  text-align: center;
  font-size: var(--t4);
  color: var(--text-muted);
}

/* Righe alterne, come nel registro: e' l'unica convenzione del sito per
   distinguere una riga dall'altra. Un riempimento uguale su tutte le righe le
   trasformava in tre riquadri impilati. */
.server-select-card {
  display: flex;
  align-items: center;
  gap: var(--s3);
  position: relative;
  padding: var(--s2) var(--s3);
  margin: 0 -12px;
  /* Il riempimento del passaggio del mouse e della selezione prende questo
     angolo: prima era un rettangolo vivo in mezzo a tutto il resto tondo. */
  border-radius: var(--raggio-riga);
  cursor: pointer;
  /* E l'ultima della catena. Sopra a questa nessuno forzava piu' la larghezza:
     erano tre livelli con lo stesso difetto, uno dentro l'altro. */
  min-width: 0;
  transition: background var(--passo);
}

/* La riga che divide due server. Sta rientrata di 12px per compensare il
   margine negativo qui sopra: cosi' comincia e finisce esattamente dove
   comincia e finisce la barra sotto al titolo, invece di sporgere di 24px. */
.server-select-card::after {
  content: '';
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 0;
  height: var(--barra-riga);
  background: var(--filo);
  border-radius: 9999px;
}

/* L'ultima non ha riga sotto: chiuderebbe l'elenco con un filo sospeso. */
.server-select-card:last-child::after {
  display: none;
}

/* ⚠️  Era `--incavo` pieno, cioe' lo stesso peso della selezione: passandoci
       sopra col mouse la riga sembrava gia' scelta. Il passaggio deve dire
       «sono qui», non «sono selezionato»: qui e' un quarto di quel colore. */
.server-select-card:hover {
  background: color-mix(in srgb, var(--incavo) 38%, transparent);
}

.server-select-card.selected:hover {
  background: var(--selezione);
}

/* Selezione senza linee: fondo velato d'accento. Il colore non lavora da solo,
   perche' la card porta anche aria-checked e il nome passa in grassetto. */
.server-select-card.selected {
  background: var(--selezione);
}

.server-select-card.selected .server-card-name {
  color: var(--su-selezione);
}

.server-select-card.selected .server-card-meta {
  color: var(--su-selezione-tenue);
}

/* ⚠️  Erano `background: transparent`, e su una riga scelta la pillola spariva:
       il suo fondo era lo stesso della riga, quindi restava solo la parola in
       mezzo al nulla mentre su tutte le altre righe era una pillola. Adesso il
       fondo e' un velo scuro sopra la selezione: si vede su tutte e due, e non
       ha bisogno di un colore suo per ogni tema. */
.server-select-card.selected .server-owner-badge,
.server-select-card.selected .server-admin-badge {
  color: var(--su-selezione-tenue);
  background: color-mix(in srgb, var(--su-selezione) 12%, transparent);
}

.server-select-card.selected .server-card-icon {
  background: var(--su-selezione);
  color: var(--selezione);
}

.server-card-info {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex: 1 1 auto;
  min-width: 0;
}

/* La colonna col nome dentro. Senza `min-width: 0` non si stringe: una voce di
   flex, di suo, si rifiuta di scendere sotto la larghezza del proprio
   contenuto, e allora il nome lungo teneva aperta tutta la catena. Su un
   telefono da 375 px la pagina sbordava di 118, e i distintivi ADMIN finivano
   fuori dallo schermo. `overflow: hidden` sul nome c'era gia' e non poteva
   servire a niente, perche' non gli veniva mai chiesto di stare piu' stretto.
   Non si vedeva con i nomi corti dell'anteprima di prima. */
.server-card-testi {
  min-width: 0;
}

.server-card-icon {
  flex: none;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--incavo);
  /* Le foto dei server sono tonde, come su Discord. */
  border-radius: 50%;
  font-family: var(--mono);
  font-size: var(--t3);
  font-weight: 600;
  color: var(--text-secondary);
}

.server-card-icon img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.server-card-name {
  font-size: var(--t6);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.server-card-meta {
  font-size: var(--t1);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.server-badges-col {
  display: flex;
  align-items: center;
  gap: var(--s1);
  flex: none;
}

/* "Owner" e "Admin/Mod" non sono valori macchina ma etichette da leggere:
   vanno nella faccia del testo, non in quella dei codici. E non sono stati di
   salute, quindi niente verde e ambra, che qui dentro vogliono dire "va bene"
   e "attenzione": sono targhette come le altre della pagina. */
.server-owner-badge,
.server-admin-badge {
  font-size: var(--t3);
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  padding: var(--s0) var(--s2);
  border-radius: var(--radius);
  background: var(--incavo);
  color: var(--text-secondary);
}

/* L'unica distinzione che serve davvero: proprietario o no. */
.server-owner-badge {
  color: var(--accento);
}

.server-invite-btn {
  /* ⚠️  ~25px: e' il bersaglio che si tocca quando il bot NON c'e', cioe'
     nell'unico momento in cui quella scheda serve a qualcosa. Nasce da
     `app.js` e nessun test lo guardava. */
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  font-size: var(--t3);
  font-weight: 600;
  white-space: nowrap;
  padding: var(--s1) var(--s2);
  color: var(--su-accento);
  background: var(--accento);
  border-radius: var(--radius);
  text-decoration: none;
}

/* --- Barra di conferma ---------------------------------------------------- */
.server-confirm-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
  /* Il riempimento della scheda, come tutte le altre. Prima era azzerato ai
     lati: il testo toccava il bordo a sinistra e il pulsante quello a destra,
     ed e' questo che la faceva sembrare storta. */
  padding: var(--s4) var(--s6);
}

.confirm-info {
  display: flex;
  /* Allineati in BASSO, non al centro: le due voci sono su due righe, e
     centrandole la freccia finiva a mezz'aria fra l'etichetta e il valore
     invece che sulla riga dei valori, che e' cio' che collega. */
  align-items: flex-end;
  gap: var(--s6);
  flex-wrap: wrap;
}

.confirm-item {
  display: grid;
  gap: var(--s0);
  min-width: 0;
}

.confirm-label {
  font-size: var(--t2);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.confirm-value {
  font-size: var(--t5);
  font-weight: 600;
  line-height: 1.25;
}

.confirm-value.empty {
  font-weight: 400;
  color: var(--text-muted);
}

/* Sulla riga dei valori: e' fra quei due che indica il verso. */
.confirm-arrow {
  display: inline-flex;
  align-items: center;
  color: var(--accento);
  padding-bottom: var(--s0);
}

/* --- Segnaposto di apertura ----------------------------------------------- */
.restore-box {
  display: grid;
  justify-items: center;
  gap: var(--s2);
  padding: var(--s8) 0;
  text-align: center;
}

.restore-box h4 {
  font-size: var(--t6);
  font-weight: 600;
}

.restore-box p {
  font-size: var(--t4);
  color: var(--text-muted);
}

.restore-spinner {
  width: 18px;
  height: 18px;
  background: var(--accento-pieno);
  animation: giraFerma 0.9s linear infinite;
}

/* --- Intestazione della configurazione ------------------------------------ */
/* Un pannello solo: sopra di chi si parla, sotto quanto ci sta dentro. Prima
   erano due scatole staccate, e il riquadro dei numeri non aveva niente che
   lo legasse al server a cui si riferivano.

   Il riempimento e' quello del pannello, non zero: senza, il testo partiva
   dal filo del vetro mentre il resto della pagina rientrava, e il pannello
   sembrava un fondale messo dietro a caso invece che la scatola di quel
   contenuto. */
.server-banner {
  display: flex;
  flex-direction: column;
  gap: var(--s5);
  padding: var(--s5);
}

.banner-top {
  display: flex;
  align-items: center;
  gap: var(--s4);
  flex-wrap: wrap;
}

/* ⚠️  Quadrata con gli angoli smussati, e grande quanto il blocco di testo che
       le sta accanto: e' l'immagine del server, cioe' la sola cosa in tutta la
       pagina che dica a colpo d'occhio DOVE si sta lavorando. A 40px e tonda
       si leggeva come un'icona di sistema, e spariva. La misura non e' scelta
       a occhio: cerchio + titolo + sottotitolo fanno 68px, e 72 e' il gradino
       che li pareggia senza far crescere la riga. */
.banner-avatar-box {
  flex: none;
  width: 72px;
  height: 72px;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--incavo);
  border: 1px solid var(--filo);
  border-radius: var(--raggio-contenitore);
  color: var(--text-secondary);
  font-family: var(--faccia);
  font-size: var(--t7);
  font-weight: 700;
  letter-spacing: -0.02em;
}

.banner-avatar-box img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Il blocco di testo si prende lo spazio che avanza: cosi' i due pulsanti
   restano appoggiati al bordo destro senza `margin-left: auto` sparso in
   giro, e su schermo stretto scendono sotto invece di stringere il nome. */
.banner-texts {
  display: grid;
  gap: var(--s0);
  flex: 1 1 260px;
  min-width: 0;
}

.banner-badge {
  justify-self: start;
  font-size: var(--t1);
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.banner-title {
  font-size: var(--t6);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.15;
}

.banner-sub {
  font-size: var(--t4);
  color: var(--text-secondary);
}

.banner-actions {
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
}

/* --- La spia del salvataggio ---------------------------------------------- */
/* Al posto del pulsante «Salva». Non e' un pulsante: le modifiche partono da
   sole. Ma un salvataggio automatico che fallisce in silenzio perde il lavoro
   senza dirlo, quindi lo stato si vede sempre, e quando fallisce si preme.

   Ogni stato ha un SEGNO diverso, non solo un colore diverso: chi non
   distingue il verde dal rosso deve comunque distinguere «fatto» da «non
   fatto». */
.stato-salvataggio {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  border-radius: var(--raggio-pulsante);
  font-size: var(--t2);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text-muted);
  transition: color var(--passo), background var(--passo);
}

.stato-salvataggio svg {
  width: 15px;
  height: 15px;
  flex: none;
}

.stato-salvataggio.salvato { color: var(--text-muted); }
.stato-salvataggio.in-attesa { color: var(--text-secondary); }
.stato-salvataggio.in-corso { color: var(--text-secondary); }

/* Solo il guasto si veste: e' l'unico stato che chiede qualcosa a chi guarda. */
.stato-salvataggio.fallito {
  background: var(--tinto-rosso);
  color: var(--su-tinto-rosso);
  cursor: pointer;
}

.stato-salvataggio.fallito:hover {
  background: var(--tinto-rosso-sopra);
}

/* La spunta si disegna quando arriva, invece di comparire e basta: e' il solo
   momento in cui la spia ha qualcosa da dire. */
.stato-salvataggio.salvato .ico-spunta {
  stroke-dasharray: 24;
  animation: spunta-si-disegna 0.45s ease forwards;
}

.stato-salvataggio .ico-gira {
  animation: giraFerma 0.9s linear infinite;
  transform-box: fill-box;
  transform-origin: center;
}

/* La lancetta dell'orologio fa un giro corto quando c'e' qualcosa in attesa. */
.stato-salvataggio.in-attesa .ico-lancetta {
  transform-box: fill-box;
  transform-origin: 12% 88%;
  animation: lancetta-gira 2.4s ease-in-out infinite;
}

@keyframes lancetta-gira {
  0%, 70%, 100% { transform: rotate(0deg); }
  35% { transform: rotate(90deg); }
}

/* La freccia del riprova gira su se stessa al passaggio del mouse. */
.stato-salvataggio.fallito:hover .ico-ritorno {
  transform-box: fill-box;
  transform-origin: center;
  animation: giraFerma 0.7s ease;
}

/* --- I quattro numeri, dentro l'intestazione ------------------------------ */
/* Il filetto sta in cima a QUESTO blocco, non in fondo a quello sopra: cosi'
   quando non c'e' nessuna news e il riepilogo sparisce, non resta una riga
   orizzontale appesa a meta' pannello. */
.riepilogo {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  padding-top: var(--s5);
  border-top: 1px solid var(--filo);
}

.riepilogo-voce {
  display: grid;
  gap: var(--s0);
  flex: 1 1 140px;
  min-width: 0;
  padding: 0 var(--s5);
  border-left: 1px solid var(--filo);
}

/* Il primo non ha il filo a sinistra: allinea al bordo del pannello come il
   cerchio e il titolo di sopra. */
.riepilogo-voce:first-child {
  padding-left: 0;
  border-left: none;
}

/* ⚠️  Il numero e' testo normale, non colorato d'accento. L'accento in questa
       pagina vuol dire «si puo' toccare»: speso su quattro cifre che non fanno
       niente diceva il falso, e per giunta su vetro scuro quel celeste veniva
       piu' fioco dell'etichetta che doveva sovrastare. Il peso e la misura
       bastano a dire qual e' la cifra e qual e' la didascalia. */
.riepilogo-numero {
  font-family: var(--mono);
  font-size: var(--t8);
  font-weight: 600;
  line-height: 1.05;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.riepilogo-etichetta {
  font-size: var(--t2);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* --- Il tutorial ---------------------------------------------------------- */
/* Un dialogo, non piu' una fisarmonica dentro la pagina. Dentro, due cose:
   lo schema del percorso e i tre passi da fare. */
.tutorial-dialogo {
  max-width: 760px;
  width: 100%;
}

.tutorial-corpo {
  display: flex;
  flex-direction: column;
  gap: var(--s7);
}

/* Lo schema: tre caselle e due frecce, in riga. */
.flusso {
  display: flex;
  align-items: stretch;
  gap: var(--s2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.flusso-tappa {
  flex: 1 1 0;
  min-width: 0;
  display: grid;
  justify-items: center;
  gap: var(--s1);
  padding: var(--s4) var(--s3);
  text-align: center;
  background: var(--incavo);
  border: 1px solid var(--filo);
  border-radius: var(--raggio-contenitore);
}

.flusso-icona {
  display: inline-flex;
  color: var(--text-muted);
}

.flusso-icona svg { width: 22px; height: 22px; }

/* L'ultima tappa e' quella che si vede su Discord: si segna, cosi' lo schema
   ha un verso invece di essere tre scatole pari. */
.flusso-tappa:last-child .flusso-icona { color: var(--accento); }

.flusso-dove {
  font-size: var(--t4);
  font-weight: 700;
  line-height: 1.2;
}

.flusso-cosa {
  font-size: var(--t2);
  color: var(--text-secondary);
}

/* La freccia porta scritto CHI fa il passo: senza, lo schema diceva che le
   news si spostano da sole. */
.flusso-freccia {
  flex: none;
  display: grid;
  justify-items: center;
  align-content: center;
  gap: var(--s0);
  padding: 0 var(--s1);
  color: var(--text-muted);
}

.flusso-freccia svg { width: 20px; height: 20px; }

.flusso-chi {
  font-size: var(--t1);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  white-space: nowrap;
}

.flusso-chi-bot { color: var(--accento); }

/* I tre passi: numerati dal browser, non da un div che finge di essere un
   numero. Cosi' un lettore di schermo dice davvero «uno di tre». */
.tut-passi {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s5);
  margin: 0;
  padding: 0;
  counter-reset: passo;
  list-style: none;
}

.tut-passi li {
  counter-increment: passo;
  display: grid;
  gap: var(--s1);
  align-content: start;
}

.tut-passi li::before {
  content: counter(passo);
  font-family: var(--mono);
  font-size: var(--t2);
  font-weight: 700;
  color: var(--accento);
}

.tut-passi h4 {
  margin: 0;
  font-size: var(--t4);
  font-weight: 700;
}

.tut-passi p {
  margin: 0;
  font-size: var(--t3);
  line-height: 1.5;
  color: var(--text-secondary);
}

/* Sul telefono lo schema e i passi si impilano: in riga, tre caselle in 375px
   fanno tre colonne da un centimetro. */
@media (max-width: 700px) {
  .flusso { flex-direction: column; }
  .flusso-freccia .ico-freccia { transform: rotate(90deg); }
  .tut-passi { grid-template-columns: 1fr; }
}


.tut-step h4 {
  font-size: var(--t5);
  font-weight: 600;
}

.tut-step p {
  font-size: var(--t4);
  color: var(--text-secondary);
}

/* --- Registro degli inoltri ----------------------------------------------- */
/* ⚠️  Una colonna sola, ma dichiarata. Senza, e' quella implicita, `auto`, e
       una colonna `auto` ha per minimo il contenuto: la tabella la allargava
       fino alla propria larghezza minima (848px coi dati dell'anteprima), cosi'
       `.registro-wrap` non era mai piu' stretto di lei e lo scorrimento laterale
       della fascia 769-1100 non scattava MAI. Misurato il 23/9/2026: sotto i
       ~950px la tabella usciva dal pannello (174px a 769), e con lei la spia
       del salvataggio, che sta nella stessa colonna — a 769px da x 784 a 890 in
       una finestra larga 759, cioe' fuori dallo schermo. E' la regola che
       CLAUDE.md scrive per le griglie: `minmax(0, 1fr)`, non `1fr`. */
.rules-container {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s4);
}

.rules-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
}

.rules-header-titles {
  display: grid;
  gap: var(--s0);
  cursor: pointer;
  min-width: 0;
}

.rules-title {
  font-size: var(--t6);
  font-weight: 600;
}

.rules-toggle {
  display: flex;
  align-items: center;
  gap: var(--s2);
  color: inherit;
  font-weight: 600;
}

.rules-toggle-label {
  font-size: var(--t6);
}

.rules-count {
  font-size: var(--t3);
  font-weight: 500;
  color: var(--text-secondary);
  background: var(--incavo);
  border-radius: var(--radius);
  padding: var(--s0) var(--s2);
}

.rules-count[hidden] {
  display: none;
}

.rules-chevron {
  color: var(--text-muted);
  transition: transform var(--passo);
}

.rules-container.collapsed .rules-chevron {
  transform: rotate(-90deg);
}

.rules-sub {
  font-size: var(--t4);
  color: var(--text-muted);
}

.rules-container.collapsed #rules-collapsible {
  display: none;
}

/* Niente `overflow` qui sopra i 1100px: un contenitore che scorre diventa lui
   il riferimento di `position: sticky`, e l'intestazione della tabella non si
   aggancerebbe piu' allo scorrimento della pagina. Lo scorrimento orizzontale
   serve solo nella fascia in cui le colonne si stringono troppo; sotto i 768px
   la tabella si impila e non serve affatto. */
.registro-wrap {
  overflow: visible;
}

@media (min-width: 769px) and (max-width: 1100px) {
  .registro-wrap {
    overflow-x: auto;
  }

  /* ⚠️  E qui dentro l'intestazione non si misura piu' dalla pagina. Un
         contenitore che scorre in un asse scorre in tutti e due — con
         `overflow-x: auto` anche `overflow-y` si calcola `auto` — e `position:
         sticky` prende lui come riferimento: `top: var(--nav-height)` voleva
         dire 72px dalla cima del contenitore, cioe' l'intestazione disegnata
         72px sotto il suo `thead`, sopra la prima riga. Opaca com'e', di quella
         riga copriva sorgente, destinazione e ruolo. Misurato il 23/9/2026 a
         769, 900, 1024 e 1100px, nei due temi: `th` = `thead` + 72, sempre.
         Con `top: 0` resta sulla sua riga, e quando la pagina scorre se ne va
         con la tabella: in questa fascia appiccicarsi sotto la barra non si
         puo'.
         Due classi e non una: la regola base sta piu' giu' nel foglio, e a
         parita' di specificita' vincerebbe lei. E non `position: static`: la
         barretta del titolo (`.registro th::after`) e' assoluta e si misura dal
         `th`; con un `th` statico si misura dal primo schermo della pagina, e
         il titolo resta senza. C'e' un test che risolve la cascata a ogni
         larghezza dove una media query cambia qualcosa. */
  .registro-wrap .registro th {
    top: 0;
  }
}

.registro {
  width: 100%;
  border-collapse: collapse;
}

/* Le intestazioni si staccano con una fascia di fondo, non con un filetto.

   ⚠️  Opaca deve restarlo: e' `sticky`, e da trasparente le righe le
       passerebbero sotto restando leggibili. Ma non basta prendere il TOKEN
       del pannello, ed e' l'errore che ho fatto la volta scorsa: il pannello
       non dipinge `--rilievo`, dipinge `--rilievo` al 62% sopra al cielo, piu'
       il quadro sgranato. Un `--rilievo` pieno e' un colore che nel pannello
       non compare da nessuna parte, e la fascia restava diversa.

       Quindi si ridipinge la STESSA pila del pannello. Il quadro e' `fixed`,
       cioe' ancorato allo schermo e non all'elemento: due elementi con questa
       stessa dichiarazione mostrano gli stessi pixel nello stesso punto, e la
       fascia sparisce dentro al pannello invece di somigliargli. Ed e' opaca
       lo stesso, perche' il quadro sotto lo e'. */
.registro th {
  position: sticky;
  top: var(--nav-height);
  z-index: 2;
  background-color: var(--carta);
  background-image:
    linear-gradient(color-mix(in srgb, var(--rilievo) var(--pannello-alfa), transparent),
                    color-mix(in srgb, var(--rilievo) var(--pannello-alfa), transparent)),
    var(--vetro, none);
  background-size: auto, 100vw 100vh;
  background-position: 0 0, 0 0;
  background-repeat: repeat, no-repeat;
  background-attachment: scroll, fixed;
  image-rendering: pixelated;
  /* ⚠️  Una BARRETTA e non un bordo: Chrome arrotonda `border-width` al pixel
         intero, quindi 2,5px diventavano 2 e il filetto da titolo finiva
         identico a quello delle righe. L'altezza di uno pseudo-elemento non
         viene arrotondata, e i due pesi restano due. */
  border-bottom: 0;
  font-family: var(--mono);
  font-size: var(--t1);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-align: left;
  padding: 0 var(--s3) var(--s2);
  white-space: nowrap;
}

.registro th::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--barra-titolo);
  background: var(--filo-forte);
  border-radius: 9999px;
}

.registro td {
  padding: var(--s3) var(--s3);
  vertical-align: middle;
  font-size: var(--t5);
}

/* Righe alterne: si segue una riga senza bisogno di righelli. */
.registro th:first-child,
.registro td:first-child {
  padding-left: 0;
}

.registro th:last-child,
.registro td:last-child {
  padding-right: 0;
}

/* Un filo per riga. Il fondo qui dentro resta libero di voler dire qualcosa:
   lo usa solo il passaggio del mouse. */
/* ⚠️  Gli stessi filetti dell'elenco dei server, che e' la prima lista che si
       vede entrando: un bordo da 1px qui e una barretta tonda da 2px la' erano
       due liste con due grammature, nella stessa pagina.

       Barretta e non bordo: un bordo va da un capo all'altro della cella e si
       ferma agli angoli vivi, questa e' rientrata e tonda come la la'. Il
       rientro compensa il riempimento della prima e dell'ultima colonna, cosi'
       comincia e finisce dove comincia e finisce il testo. */
.registro tbody tr { position: relative; }

.registro tbody td { border-bottom: 0; }

.registro tbody td::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--barra-riga);
  background: var(--filo);
  border-radius: 9999px;
  pointer-events: none;
}

/* ⚠️  E RIENTRANO di 12px ai due capi, come nell'elenco dei server: la
       barretta di una riga sta DENTRO alla barra del titolo, non alla pari.
       Facendole larghe uguali le sette news si leggevano come sette blocchi
       affiancati al cappello invece che come un elenco sotto di lui. In mezzo
       le barrette si toccano, o l'elenco si spezza in colonne. */
.registro tbody td:first-child::after { left: 12px; }
.registro tbody td:last-child::after { right: 12px; }

/* L'ultima non ha filetto: chiuderebbe l'elenco con un filo sospeso. */
.registro tbody tr:last-child td::after { display: none; }

.registro tbody tr:hover td:first-child {
  border-top-left-radius: var(--raggio-riga);
  border-bottom-left-radius: var(--raggio-riga);
}

.registro tbody tr:hover td:last-child {
  border-top-right-radius: var(--raggio-riga);
  border-bottom-right-radius: var(--raggio-riga);
}

/* ⚠️  Era `--incavo` pieno, cioe' lo stesso peso che nell'elenco dei server
       vuol dire «questa e' scelta». Qui non si sceglie niente, si passa: e
       come la', il passaggio e' un quarto di quel colore. */
.registro tbody tr:hover td {
  background: color-mix(in srgb, var(--incavo) 38%, transparent);
}

.rule-card-name {
  font-weight: 600;
}

.rule-meta-row {
  display: flex;
  gap: var(--s1);
  flex-wrap: wrap;
  margin-top: var(--s0);
}

.rule-badge-pill {
  font-family: var(--mono);
  font-size: var(--t1);
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  white-space: nowrap;
}

.rule-badge-pill.filter-tag { color: var(--text-muted); }
.rule-badge-pill.lang-tag   { color: var(--text-secondary); }
.rule-badge-pill.expire-tag { color: var(--accento); }

/* ─── LA RIGA DI SALUTE ──────────────────────────────────────────────────────
   Una riga discreta: quando tutto va bene sparisce nello sfondo, e si fa notare
   solo quando qualcosa non torna.

   ⚠️  Il colore non è l'unico segnale — il testo dice sempre cosa sta
       succedendo — ma per un po' è stato l'UNICO a mancare: passando alla
       tavolozza di Guild Keeper queste regole sono sparite e non è tornata
       nessuna, così i quattro stati rendevano identici e «Consegna non
       riuscita» aveva lo stesso peso e lo stesso colore di «Ultima news: 3 ore
       fa». Il pallino, per giunta, era uno `<span>` vuoto largo zero. */
.rule-health {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s2);
  font-size: var(--t2);
  color: var(--text-muted);
}

.health-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
  /* Un fondo ce l'ha SEMPRE, anche nello stato «mai»: un pallino che compare e
     scompare si legge come un difetto di caricamento, non come uno stato. */
  background: var(--text-muted);
}

.rule-health--attiva .health-dot     { background: var(--ok); }
.rule-health--silenziosa .health-dot { background: var(--warn); }
.rule-health--errore .health-dot     { background: var(--danger); }

.rule-health--silenziosa { color: var(--warn); }
.rule-health--errore     { color: var(--danger); font-weight: 600; }

.health-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ⚠️  Quando un canale, un thread o un ruolo non si risolve — cancellato, o
       semplicemente invisibile al bot — al suo posto finisce lo snowflake:
       venti cifre che nessuno legge e che non vanno a capo. Misurato: le due
       celle si prendevano 442px sui 938 di larghezza minima della tabella,
       cioe' quasi meta'. Da li' il registro non si lasciava stringere, e dove
       divide la riga con altri due pannelli sbordava passando sotto al vicino.

       Il limite qui sotto e' un tetto, non un taglio di routine: con le due
       colonne di adesso il registro ha spazio e i valori si leggono interi,
       inclusi gli snowflake. Serve al caso limite — un nome di thread molto
       lungo alla larghezza minima — e quando morde il valore intero resta nel
       `title`. Non si puo' invece far
       scorrere `.registro-wrap`: un antenato che taglia annulla la `position:
       sticky` dell'intestazione della tabella. E' la ragione per cui lo
       scorrimento laterale, piu' su, vale solo in una fascia stretta.

       Quando i nomi si risolvono — cioe' quasi sempre — qui non si taglia
       niente: `#annunci` e `@Membri` stanno comodi ben dentro il limite. */
.rule-flow-val {
  font-size: var(--t4);
  color: var(--text-secondary);
  display: inline-block;
  max-width: 28ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

.rule-flow-label {
  display: none;
  font-family: var(--mono);
  font-size: var(--t1);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* La stessa freccia del tutorial, disegnata e non il carattere →: quella era
   di un'altra famiglia e cambiava spessore da un sistema all'altro. */
.rule-freccia {
  color: var(--text-muted);
  text-align: center;
  width: 1px;
}

.rule-freccia svg {
  width: 17px;
  height: 17px;
  vertical-align: middle;
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--passo), color var(--passo);
}

/* Scivola in avanti quando si passa sulla riga: dice il verso della news. */
.registro tbody tr:hover .rule-freccia { color: var(--accento); }
.registro tbody tr:hover .rule-freccia svg { transform: translateX(2.5px); }

.rule-role-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  font-size: var(--t4);
  color: var(--text-secondary);
  white-space: nowrap;
  /* Stessa ragione di `.rule-flow-val`: `@ID: 100000000000000001700` da solo
     misurava 239px. Il pallino del colore non si stringe (`flex: none`), a
     tagliarsi e' il nome. */
  max-width: 24ch;
  overflow: hidden;
}

/* Tondo, e un filo piu' grande: a 8px quadrati sembravano un artefatto di
   rendering piu' che il colore di un ruolo. */
.role-color-dot {
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 50%;
}

.rule-card-actions {
  display: flex;
  gap: var(--s1);
  justify-content: flex-end;
}

.btn-rule-action {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  min-height: 44px;
  padding: var(--s2) var(--s3);
  font-family: var(--ui);
  font-size: var(--t1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accento);
  background: var(--fondo-pulsante);
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 rgba(var(--accento-rgb), 0.26);
  transition: transform 0.08s, box-shadow 0.08s, color 0.08s, border-color 0.08s;
}

.btn-rule-action:active {
  transform: translate(3px, 3px);
  box-shadow: none;
}

.btn-rule-edit:hover {
  color: var(--accento-forte);
  border-color: var(--accento);
  box-shadow: 3px 3px 0 rgba(var(--accento-rgb), 0.45);
}

/* La variante rossa di Guild Keeper: stesso disegno, altro colore. */
.btn-rule-delete {
  color: var(--su-tinto-rosso);
  border-color: rgba(var(--pericolo-rgb), 0.26);
  box-shadow: 3px 3px 0 rgba(var(--pericolo-rgb), 0.26);
}

.btn-rule-delete:hover {
  color: var(--su-tinto-rosso);
  border-color: var(--pericolo);
  box-shadow: 3px 3px 0 rgba(var(--pericolo-rgb), 0.45);
}

/* ⚠️  I comandi di riga compaiono al passaggio del mouse. Con sette news erano
       quattordici pulsanti accesi tutti insieme, che si prendevano l'occhio
       prima dei nomi: e' la raccomandazione di pencilandpaper.io sulle tabelle
       dense, «hover state discoverability».

       Due precauzioni, e nessuna e' facoltativa:
       - solo dove il mouse c'e' davvero (`hover: hover`). Su un telefono non
         esiste passaggio del mouse: nascondendoli li', le news non si
         potrebbero piu' modificare ne' cancellare.
       - `:focus-within` tiene la riga aperta quando ci si arriva col tasto
         Tab, se no il fuoco finisce su un pulsante invisibile. */
@media (hover: hover) and (pointer: fine) {
  .rule-card-actions {
    opacity: 0;
    transition: opacity var(--passo);
  }

  .registro tbody tr:hover .rule-card-actions,
  .registro tbody tr:focus-within .rule-card-actions {
    opacity: 1;
  }
}

/* La punta della matita scrive, il coperchio del cestino si solleva. Sono i
   due pezzi che dicono cosa fa il comando: muovere tutto il simbolo non
   direbbe niente. */
.ico-punta,
.ico-coperchio {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--passo);
}

.btn-rule-edit:hover .ico-punta,
.btn-rule-edit:focus-visible .ico-punta {
  animation: punta-scrive 0.5s ease;
}

@keyframes punta-scrive {
  0%, 100% { transform: translate(0, 0); }
  30% { transform: translate(-1.5px, 1.5px); }
  65% { transform: translate(1px, -1px); }
}

.btn-rule-delete:hover .ico-coperchio,
.btn-rule-delete:focus-visible .ico-coperchio {
  transform: translateY(-2.5px) rotate(-9deg);
}

/* --- Stati vuoti ---------------------------------------------------------- */
.empty-rules-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s2);
  padding: var(--s8) var(--s5);
  text-align: center;
}

.empty-icon,
.empty-rules-box svg {
  color: var(--text-muted);
}

.empty-rules-box h4 {
  font-size: var(--t5);
  font-weight: 600;
}

.empty-rules-box p {
  font-size: var(--t4);
  color: var(--text-muted);
}

/* --- Impostazioni avanzate ------------------------------------------------ */
/* ⚠️  Il riempimento laterale era ZERO dentro un pannello: i campi arrivavano
       al filo del vetro mentre tutto il resto della pagina rientrava, ed e' lo
       stesso difetto che aveva l'intestazione della configurazione. Il filo in
       cima nemmeno serviva: e' un pannello, ce l'ha gia' il suo bordo. */
.advanced-settings-card {
  padding: var(--s4);
}

/* ⚠️  Era maiuscoletto muto da 13px, cioe' esattamente il vestito delle
       ETICHETTE dei campi che gli stanno sotto: titolo e didascalie si
       leggevano come la stessa cosa scritta quattro volte, e la scheda non
       aveva piu' una cima. Adesso e' un titolo come quello dell'automazione. */
.adv-title {
  margin: 0 0 var(--s4);
  font-size: var(--t5);
  color: var(--text-primary);
}

.adv-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--s5);
}

/* Quando i due gruppi si impilano, li stacca la stessa barretta delle liste.
   Affiancati non serve: a dividerli c'e' gia' lo spazio fra le colonne. */
.adv-grid .form-group + .form-group {
  position: relative;
  padding-top: var(--s5);
}

.adv-grid .form-group + .form-group::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: var(--s2);
  height: var(--barra-riga);
  background: var(--filo);
  border-radius: 9999px;
}

@media (min-width: 560px) {
  .adv-grid .form-group + .form-group { padding-top: 0; }
  .adv-grid .form-group + .form-group::before { display: none; }
}

/* --- Campi ---------------------------------------------------------------- */
/* Etichetta e campo si toccano quasi: e' il legame piu' stretto della pagina
   e deve battere qualunque altra vicinanza. */
.form-group {
  display: grid;
  gap: var(--s1);
}

/* ⚠️  L'etichetta di un campo e' una didascalia, non un titolo: a 15px e in
       grassetto pesava quanto il valore che descriveva, e la scheda si leggeva
       come una pila di frasi tutte uguali. Adesso e' il maiuscoletto che la
       pagina usa gia' per le didascalie — i quattro numeri, l'intestazione del
       registro, il cappello del banner — cosi' il valore e' la cosa grossa e
       l'etichetta gli sta sopra piccola. */
.form-group label {
  font-size: var(--t2);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Come i campi di Guild Keeper: incavo scuro e un contorno sottile.

   ⚠️  Ma NON in monospazio. Il monospazio in questa pagina vuol dire «questo
       viene dalla macchina»: gli snowflake, i conteggi, i nomi dei canali nel
       registro. Qui dentro si scrive a mano il nome di una news, e vederlo
       uscire come codice faceva sembrare la scheda un file di configurazione
       invece di un modulo. */
.form-input,
.form-select {
  width: 100%;
  min-height: 44px;
  padding: var(--s2) var(--s3);
  font-family: var(--ui);
  font-size: var(--t4);
  color: var(--text-primary);
  background: var(--incavo);
  border-radius: var(--radius);
  transition: border-color var(--passo);
}

/* ⚠️  Il selettore e' qualificato dall'ELEMENTO, ed e' la stessa ragione — e lo
       stesso rimedio — dei 16px di iOS e del riempimento dei campi.
       `componenti-gk.css` dichiara `input.field:focus { outline: none }`, che
       vale (0,2,1); scritta `.form-input:focus` questa ne vale (0,2,0) e
       perdeva comunque venisse dopo, perche' la specificita' viene prima
       dell'ordine. Risultato misurato in un browser vero, con un clic e il fuoco
       vero: sui tredici campi `outline: none`, `border: 0px`, `box-shadow: none`
       — cioe' NESSUN segno di quale campo ha il fuoco. E la finestra «Nuova
       News» si apre gia' col fuoco dentro il primo campo.
       Pareggiando il peso decide l'ordine, e `style.css` si carica per ultimo.
       `textarea.form-input` sta nella lista per simmetria con quella dei 16px:
       oggi non c'e' un textarea con questa classe, ma il giorno che arriva
       deve trovare la regola gia' fatta. */
input.form-input:focus,
textarea.form-input:focus,
select.form-select:focus {
  outline: 2px solid var(--accento-segno);
  outline-offset: 1px;
}

.form-input::placeholder {
  color: var(--text-muted);
}

.form-help {
  font-size: var(--t3);
  color: var(--text-muted);
}

/* I gruppi della scheda si staccano con la stessa barretta delle liste, non
   con un bordo: e' lo stesso segno che divide due server e due news. */
.rule-form .form-group + .form-row-2,
.rule-form .form-row-2 + .form-group,
.rule-form .modal-avanzate {
  position: relative;
  margin-top: var(--s3);
  padding-top: var(--s5);
}

.rule-form .form-group + .form-row-2::before,
.rule-form .form-row-2 + .form-group::before,
.rule-form .modal-avanzate::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: var(--barra-riga);
  background: var(--filo);
  border-radius: 9999px;
}

.form-row-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s3);
}

/* ⚠️  `hidden` da solo qui NON nasconde niente: `.form-group` dichiara
       `display: grid`, e una dichiarazione del foglio batte lo stile
       predefinito del browser (`[hidden] { display: none }`). Il campo
       resterebbe visibile e, peggio, `required` continuerebbe a impedire
       il salvataggio senza dire perché. È la stessa regola già scritta per
       i toast: si usa `display: none`, mai la sola opacità o un attributo
       che qualcun altro sta già scavalcando. */
.rule-form .form-group[hidden] { display: none; }

/* Rimasta una colonna sola, si prende la riga: due tracce con una vuota
   lascerebbero il menu stretto a metà per niente. */
.form-row-2.senza-post { grid-template-columns: 1fr; }

/* --- Pulsanti ------------------------------------------------------------- */
/* Il pulsante di Guild Keeper: maiuscoletto spaziato, contorno sottile
   dell'accento, e un'ombra dura di 3px senza sfocatura. Premendolo si sposta
   proprio dentro la sua ombra, che sparisce: sembra affondare.

   L'altezza minima resta 44px, che la' non c'e': e' il bersaglio da pollice
   che questo pannello si e' gia' dato, e che i test difendono. */
/* La variante piena, per l'azione principale della vista. */
.btn-discord {
  background: var(--discord);
  color: #fff;
}

.btn-discord:hover {
  background: var(--discord-hover);
}

/* Il pulsante principale resta un pulsante, non un blocco: il corpo e' quello
   degli altri, cambia solo il respiro ai lati. */
.btn-large {
  padding: var(--s3) var(--s5);
}

/* --- Modale --------------------------------------------------------------- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s5);
  background: color-mix(in srgb, var(--carta) 82%, transparent);
}

body.modal-open {
  overflow: hidden;
}

html.modal-open {
  overflow: hidden;
}

.modal-dialog {
  width: 100%;
  max-width: 600px;
  max-height: calc(100dvh - 40px);
  display: flex;
  flex-direction: column;
  background: var(--rilievo);
  border-radius: var(--radius);
}

/* ⚠️  Aveva un fondo suo e un bordo sotto: dentro un dialogo si leggeva come
       due pannelli impilati, uno chiaro col titolo e uno scuro col contenuto.
       Il titolo si stacca come si staccano tutti i titoli della pagina, con la
       barra da titolo, e il dialogo torna una superficie sola. */
.modal-header {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s5) var(--s5) var(--s4);
}

.modal-header::after {
  content: '';
  position: absolute;
  left: var(--s5);
  right: var(--s5);
  bottom: 0;
  height: var(--barra-titolo);
  background: var(--filo-forte);
  border-radius: 9999px;
}

.modal-title {
  font-size: var(--t6);
  font-weight: 700;
}

.modal-body {
  padding: var(--s4);
  overflow-y: auto;
  /* ⚠️  Senza questo, arrivati in fondo al modulo il dito continua a scorrere
     e trascina la PAGINA sotto al velo: su Chrome per Android, se la pagina e'
     gia' in cima, quel gesto in piu' e' il «tira per aggiornare» — cioe' una
     ricarica, col modulo pieno e niente salvato. Qui lo scorrimento si ferma
     dove finisce il dialogo. */
  overscroll-behavior: contain;
}

.rule-form {
  display: grid;
  gap: var(--s3);
}

/* ⚠️  Il piede sta DENTRO l'area che scorre, e su un telefono questo vuol dire
   che il comando principale e' fuori dallo schermo: misurato a 375x812, «Salva
   News» stava a 143px sotto il fondo visibile, cioe' si salvava solo dopo aver
   scorso un'area che scorre dentro una pagina che scorre. Appiccicato in fondo
   resta sempre raggiungibile.
   `position: sticky` e non `fixed`: fixed, quando si apre la tastiera del
   telefono, salta in mezzo allo schermo — la tastiera cambia il viewport, non
   la finestra. Sticky resta ancorato al suo contenitore, che e' il dialogo.
   Il fondo e' opaco apposta: sotto ci scorre il modulo. */
.modal-actions {
  position: sticky;
  bottom: calc(var(--s4) * -1);
  margin: 0 calc(var(--s4) * -1) calc(var(--s4) * -1);
  padding: var(--s3) var(--s4);
  background: var(--rilievo);
  border-top: 1px solid var(--filo);
}

.modal-avanzate {
  padding-top: var(--s1);
}

/* ⚠️  Era un «+ Opzioni avanzate» scritto come un collegamento: nessun fondo,
       nessun bersaglio, e un `+` che sembrava dire «aggiungi qualcosa». Adesso
       e' un comando come gli altri, con la freccia che gira aprendo. */
.avanzate-summary {
  font-size: var(--t2);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text-secondary);
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 44px;
  padding: 0 var(--s3) 0 var(--s2);
  border-radius: var(--raggio-pulsante);
  background: var(--tinto);
  transition: background var(--passo), color var(--passo);
}

.avanzate-summary:hover {
  background: var(--tinto-sopra);
  color: var(--su-tinto);
}

/* La freccia la disegna il foglio: nel markup c'e' solo il testo, e un
   `<summary>` non porta figli suoi. */
.avanzate-summary::before {
  content: '';
  width: 9px;
  height: 9px;
  flex: none;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg) translate(-1px, -1px);
  transition: transform 0.24s var(--molla);
}

.modal-avanzate[open] .avanzate-summary::before {
  transform: rotate(45deg) translate(-2px, -2px);
}

.avanzate-summary::-webkit-details-marker {
  display: none;
}

/* ⚠️  Qui c'erano altre due `::before`, con `content: '+'` e `content: '-'`:
       il «+ Opzioni avanzate» di prima, che il commento qui sopra dichiara
       sostituito dalla freccia. Non era stato tolto, ed erano DUE pezzi dello
       stesso `::before` — il glifo da una regola, i due bordi e la rotazione
       dall'altra — quindi non vinceva l'una o l'altra: si disegnavano insieme,
       un `+` ruotato di -45 gradi dentro una freccia, che sullo schermo legge
       come una X. Aprendo diventava un `-` ruotato, cioe' una barra storta.
       E' la stessa famiglia dei rifacimenti scritti accanto al vecchio invece
       che al suo posto, con una differenza che la rende piu' difficile da
       vedere: nessuna delle due regole e' MORTA, ognuna vince su una proprieta'
       diversa, quindi il contatore delle regole morte non le trova. */

.modal-avanzate .form-group {
  margin-top: var(--s3);
}

/* ⚠️  Le opzioni avanzate sono il premium (PREMIUM.md), e chi apre la sezione
       deve capire cosa ci guadagnerebbe: stanno divise per quello che decidono,
       ognuna col suo titolo. I gruppi si staccano con la stessa barretta che
       divide i blocchi della scheda, non con un bordo. */
.avanzate-gruppo {
  position: relative;
  margin-top: var(--s5);
  padding-top: var(--s4);
}

.avanzate-gruppo::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: var(--barra-riga);
  background: var(--filo);
  border-radius: 9999px;
}

.avanzate-titolo {
  margin: 0;
  font-size: var(--t4);
  font-weight: 700;
  color: var(--text-secondary);
}

/* La didascalia di un gruppo di campi che non ha un campo solo da nominare
   (i ruoli per parola, le due ore del silenzio): deve leggersi come le
   etichette che le stanno accanto, quindi ne ricopia la voce. */
.avanzate-etichetta {
  font-size: var(--t2);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text-muted);
}

.ruoli-parola {
  display: grid;
  gap: var(--s2);
}

/* Senza righe il contenitore non occupa niente: un buco sopra «Aggiungi»
   sembrerebbe una riga rimasta vuota. */
.ruoli-parola:empty { display: none; }

/* ⚠️  `minmax(0, 1fr)`, non `1fr`: una traccia `1fr` ha per minimo il suo
       contenuto, e un menu con un nome di ruolo lungo spingerebbe la crocetta
       fuori dal modale sul telefono. È la stessa ragione di `.invito-voce`. */
.ruolo-parola-riga {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: var(--s2);
  align-items: center;
}

/* ⚠️  I campi di Guild Keeper portano un margine SOTTO (`componenti-gk.css`):
       in colonna stacca l'aiuto dal campo, in riga alzava la riga a 58px e
       lasciava la crocetta a metà altezza, sette pixel sotto i due campi. */
.ruolo-parola-riga > .field {
  margin: 0;
}

/* Sul telefono tre colonne non ci stanno: misurato a 375px, parola e ruolo
   restavano larghi 98px l'uno, cioè «manutenzi» e «@Meml». La parola prende
   la riga intera, e ruolo e crocetta le stanno sotto. */
@media (max-width: 480px) {
  /* Due righe per coppia: lo spazio FRA le coppie deve essere più largo di
     quello dentro, se no quattro campi uguali in colonna non dicono quale
     ruolo va con quale parola. */
  .ruoli-parola {
    gap: var(--s5);
  }

  .ruolo-parola-riga {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .ruolo-parola-riga > input {
    grid-column: 1 / -1;
  }
}

/* La crocetta di riga è la crocetta dei dialoghi (`.btn-modal-close`), con la
   sua misura da pollice: qui si aggiusta solo dove sta. */
.btn-togli-riga {
  align-self: center;
}

.btn-aggiungi-riga {
  justify-self: start;
  min-height: 44px;
}

/* Un titolo con una battuta accanto (25/9/2026): il titolo resta in maiuscoletto,
   la battuta si scrive com'è, e se non ci sta va a capo lei. */
.etichetta-riga {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--s2);
}

.etichetta-battuta {
  font-size: var(--t2);
  color: var(--text-muted);
}

/* ─── Le tessere e le tavolozze (25/9/2026) ────────────────────────────────── */
/* Le reazioni di una news e i ruoli del captcha: quello che si è scelto sta in
   tessere con la ✕, e si sceglie da una tavolozza che si apre sotto il suo
   pulsante. Le tessere sono alte quanto un pollice, perché la ✕ si tocca col
   dito come ogni altro comando. */
.tessere-riga {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
}

.tessere {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  min-width: 0;
}

.tessere:empty {
  display: none;
}

/* Il campo in cui si scrive prende quello che le tessere lasciano. */
.tessere-campo {
  flex: 1 1 12rem;
  min-width: 0;
}

.tessera {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  max-width: 100%;
  min-height: 44px;
  padding-left: var(--s3);
  border-radius: var(--raggio-pulsante);
  background: var(--incavo);
  color: var(--text-primary);
  font-size: var(--t3);
}

.tessera-emoji {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 24px;
  height: 24px;
  font-size: var(--t6);
  line-height: 1;
}

img.tessera-emoji {
  object-fit: contain;
}

/* Il nome di un ruolo è quello che gli ha dato il server: si scrive com'è, e se è
   lungo si taglia lui, non la ✕. */
.tessera-nome {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tessera-togli {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: var(--raggio-pulsante);
  background: transparent;
  color: var(--text-muted);
  font-size: var(--t3);
  cursor: pointer;
}

.tessera-togli:hover:not(:disabled) {
  color: var(--text-primary);
  background: var(--tinto);
}

.tessera-togli:disabled {
  opacity: 0.4;
  cursor: default;
}

.tessere-nessuna {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--text-muted);
  font-size: var(--t3);
}

.tavolozza {
  display: grid;
  gap: var(--s3);
  padding: var(--s3);
  border: 1px solid var(--filo);
  border-radius: var(--raggio-contenitore);
  background: var(--rilievo);
}

/* ⚠️  `hidden` da solo qui non nasconde niente: `display: grid` qui sopra batte lo
       stile del browser, come per il campo del post. */
.tavolozza[hidden] {
  display: none;
}

/* Tante emoji scorrono per conto loro, dentro una finestra che scorre: senza
   `contain`, in fondo alla lista il dito trascinerebbe la finestra sotto. */
.tavolozza-voci {
  display: grid;
  gap: var(--s4);
  max-height: 16rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.tavolozza-sezione {
  display: grid;
  gap: var(--s2);
}

/* Il titolo di una sezione è il nome del server: niente maiuscoletto, si scrive
   com'è (vedi `label.mod-ruolo`). */
.tavolozza-titolo {
  font-size: var(--t2);
  font-weight: 650;
  color: var(--text-muted);
}

.tavolozza-griglia {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1);
}

.tavolozza-voce {
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: var(--raggio-voce);
  background: transparent;
  cursor: pointer;
}

.tavolozza-voce img {
  width: 32px;
  height: 32px;
  object-fit: contain;
}

/* Il passaggio del mouse non spegne il premuto: senza il secondo `:not`, questa
   regola pesa più di quella qui sotto e una voce scelta perdeva il colore. */
.tavolozza-voce:hover:not(:disabled):not([aria-pressed="true"]) {
  background: var(--incavo);
}

/* La scelta si dice col fondo E con un anello: nel chiaro il fondo della
   selezione sta vicino a quello delle voci, e il colore da solo non arriva a chi
   i colori non li distingue. */
.tavolozza-voce[aria-pressed="true"] {
  background: var(--selezione);
  color: var(--su-selezione);
  box-shadow: inset 0 0 0 2px var(--accento-segno);
}

.tavolozza-voce:disabled {
  opacity: 0.35;
  cursor: default;
}

/* I ruoli: una pastiglia col pallino del colore e il nome, larga quanto il nome. */
.tavolozza-ruoli {
  display: flex;
  flex-wrap: wrap;
  align-content: start;
  gap: var(--s2);
}

.tavolozza-ruolo {
  display: inline-flex;
  gap: var(--s2);
  width: auto;
  max-width: 100%;
  height: auto;
  min-height: 44px;
  padding: 0 var(--s3);
  border-radius: var(--raggio-pulsante);
  background: var(--incavo);
  color: var(--text-primary);
  font-size: var(--t3);
}

.tavolozza-ruolo:hover:not(:disabled):not([aria-pressed="true"]) {
  background: var(--tinto);
}

.tavolozza-vuota {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--t3);
}

/* ─── La moderazione ───────────────────────────────────────────────────────── */
/* Si apre anche SOPRA la finestra della news («Modera questa destinazione»):
   un velo suo, più in alto di quello. */
.moderazione-backdrop {
  z-index: 210;
}

/* ⚠️  `hidden` cede a qualunque `display` scritto — `.form-group` dichiara `grid`,
       `.btn` `inline-flex` — ed è la lezione del campo del post: qui dentro si
       nascondono gruppi, righe d'aiuto e avvisi, e un id vale più di tutte le loro
       classi. Oggi i gruppi li copre anche `.rule-form .form-group[hidden]`; questa
       copre il resto, e il giorno che a nascondersi sarà un pulsante. */
#moderazione-modal [hidden] {
  display: none;
}

/* Lo stacco fra i campi di un gruppo, lo stesso di `.modal-avanzate .form-group`.
   Senza, la riga d'aiuto di un campo stava attaccata all'etichetta di quello
   dopo, e si leggeva come sua: «Scrive solo lo staff.» sopra «Solo questi ruoli». */
#moderazione-modal .avanzate-gruppo .form-group {
  margin-top: var(--s3);
}

/* La finestra ha due parti (25/9/2026, Stefano): in cima le difese, gratis e
   per tutto il server — la trappola, il captcha, lo staff — e sotto le regole
   spazio per spazio, che sono il premium e un giorno avranno un velo sopra.
   La prima sezione apre la finestra: la barretta che separa un gruppo da quello
   prima lì non separerebbe niente. */
#moderazione-form > .avanzate-gruppo:first-child {
  margin-top: 0;
  padding-top: 0;
}

#moderazione-form > .avanzate-gruppo:first-child::before {
  display: none;
}

/* Le regole stanno in un blocco solo, per il velo, e dentro tengono la griglia
   del modulo: senza, fra il menu dello spazio e le sezioni sotto spariva il
   passo che c'era quando erano figli diretti del modulo. Il menu dello spazio
   non prende anche il margine dei campi di un gruppo: il passo glielo dà già la
   griglia, come alla prima riga di ogni altra sezione. */
.mod-regole-spazi {
  display: grid;
  gap: var(--s3);
}

#moderazione-modal .mod-regole-spazi > .form-group {
  margin-top: 0;
}

/* Due campi affiancati con etichette di lunghezza diversa: quella che va a capo
   («Dopo quante violazioni in un'ora») spingeva giù il suo campo, e i due non
   stavano più sulla stessa riga. Allineati in fondo, i campi combaciano in ogni
   lingua; qui dentro una cella ha solo etichetta e campo. */
#moderazione-modal .form-row-2 {
  align-items: end;
}

/* I ruoli a caselle: colonne che si stringono, e una sola sul telefono. Una lista
   lunga scorre per conto suo, dentro una finestra che scorre: senza `contain`, in
   fondo alla lista il dito trascinerebbe la pagina sotto. */
.mod-ruoli {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 12rem), 1fr));
  gap: 0 var(--s3);
  max-height: 14rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.mod-ruoli:empty {
  display: none;
}

/* Tutta la riga è il bersaglio, alta quanto un pollice. */
.mod-ruolo {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 44px;
  min-width: 0;
  overflow-wrap: anywhere;
  cursor: pointer;
}

/* ⚠️  Il nome di un ruolo non è una didascalia nostra: è il nome che gli ha dato
       il server. `.form-group label` veste ogni etichetta di un gruppo col
       maiuscoletto delle didascalie, e «Membri» usciva «MEMBRI». Si scrive
       com'è, come il nome di una fonte (`.origine-nome`). Qualificato
       dall'elemento: `.mod-ruolo` da solo vale meno di `.form-group label`, e
       così la specificità pareggia e decide l'ordine. */
label.mod-ruolo {
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--t3);
}

.mod-trappole {
  display: grid;
  gap: var(--s4);
}

.mod-trappole:empty {
  display: none;
}

/* Una trappola è una scheda: canale, cosa fa, cartello, e il modo di toglierla. */
.mod-trappola {
  display: grid;
  gap: var(--s2);
  padding: var(--s3);
  border-radius: var(--raggio-voce);
  background: var(--incavo);
}

/* Come nelle righe dei ruoli per parola: il margine sotto dei campi di Guild
   Keeper, dentro una scheda fitta, apriva buchi fra un campo e il suo nome. */
.mod-trappola > .field {
  margin: 0;
}

/* La frase che dice cosa fa una trappola è quella che si deve leggere prima di
   sceglierne una: prende il colore degli avvisi. */
.mod-trappole-avviso {
  color: var(--avviso);
}

/* La foto della news e i due pulsanti che la scelgono e la tolgono: in riga, e
   a capo sul telefono quando non ci stanno. */
.identita-foto {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3);
}

/* Un disco, com'è sotto la news su Discord: quadrata avrebbe promesso uno
   spigolo che nessuno vedrà. Il fondo si vede solo finché l'immagine arriva. */
.identita-anteprima {
  flex: none;
  width: var(--s9);
  height: var(--s9);
  border-radius: 50%;
  object-fit: cover;
  background: var(--incavo);
}

/* ⚠️  `hidden` da solo cede a qualunque `display` scritto, ed è la stessa lezione
       del campo del post: `.btn` dichiara `display: inline-flex`
       (`componenti-gk.css`), quindi «Togli la foto» restava lì anche senza una
       foto da togliere. Due classi battono la sua una. */
.identita-foto > [hidden] {
  display: none;
}

.silenzio-riga {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--s3);
}

.silenzio-campo {
  display: grid;
  gap: var(--s1);
}

.origini {
  display: grid;
  gap: var(--s1);
}

.origini-nota {
  font-size: var(--t3);
  color: var(--text-muted);
}

/* Il nome della fonte prende lo spazio, e quando è passata l'ultima volta sta
   in fondo alla riga: è un'informazione in più, non l'etichetta. */
/* ⚠️  Il nome di una fonte NON è un'etichetta nostra: è il nome che lo studio
       ha dato al suo canale («Play Warframe #announcements»). Col maiuscoletto
       delle etichette diventava «#ANNOUNCEMENTS», un nome di canale gridato che
       su Discord non esiste così. Si scrive com'è. */
.origine .origine-nome {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--t3);
}

.origine-quando {
  flex: none;
  font-size: var(--t2);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-muted);
}

/* L'avviso di un permesso che al bot manca: lo stesso colore della riga di
   salute di una fonte silenziosa, che è l'altro «funziona, ma non del tutto»
   della pagina. Il link è sottolineato perché è l'unica cosa da fare. */
.avviso-permesso {
  font-size: var(--t3);
  color: var(--avviso);
}

.avviso-permesso a {
  color: inherit;
  font-weight: 700;
  text-decoration: underline;
}

/* Il nome del fuso lo dà il browser, non l'utente: è «roba della macchina», e
   in questa pagina quella si scrive in monospazio. */
.silenzio-fuso {
  font-family: var(--mono);
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--s2);
  padding-top: var(--s2);
  flex-wrap: wrap;
}

/* --- Avviso temporaneo ----------------------------------------------------- */
/* L'aspetto sta qui e non piu' inline in app.js: scritto la' seguiva una
   grafica sua ed e' rimasto una pillola arrotondata con l'ombra quando tutto
   il resto era gia' piatto. In app.js restano posizione e stato, che sono
   comportamento e servono ai test.

   Il fondo e' pieno e il testo e' il colore della carta: un avviso che
   galleggia sopra al contenuto deve staccarsi da qualunque cosa ci sia sotto. */
.app-toast {
  padding: var(--s3) var(--s5);
  border-radius: var(--radius);
  font-size: var(--t5);
  font-weight: 600;
  text-align: center;
  background: var(--text-primary);
  color: var(--carta);
  transition: opacity 0.25s ease;
}

.app-toast.successo {
  background: var(--ok);
  color: var(--su-accento);
}

.app-toast.errore {
  background: var(--pericolo);
  color: var(--su-accento);
}

/* --- Menu del tasto destro ------------------------------------------------- */
/* Stesso vocabolario di tutto il resto: fondo pieno, niente contorni, niente
   angoli tondi. Sta sopra al modale perche' si puo' aprire anche li' sopra. */
.menu-contestuale {
  position: fixed;
  z-index: 400;
  min-width: 180px;
  padding: var(--s1);
  background: var(--rilievo);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
}

.menu-contestuale[hidden] {
  display: none;
}

/* Due colonne: cosa fa a sinistra, come farlo senza menu a destra. */
.menu-voce {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s6);
  width: 100%;
  text-align: left;
  padding: var(--s2) var(--s3);
  font-size: var(--t4);
  font-weight: 500;
  color: var(--text-secondary);
  border-radius: var(--radius);
  white-space: nowrap;
  transition: background var(--passo), color var(--passo);
}

button.menu-voce:hover,
button.menu-voce:focus-visible {
  background: var(--incavo);
  color: var(--text-primary);
}

.menu-tasti {
  font-family: var(--mono);
  font-size: var(--t3);
  color: var(--text-muted);
}

/* L'ultima riga e' un promemoria, non un comando: non si accende al passaggio
   e non prende il fuoco, perche' premerla non farebbe niente. */
.menu-nota {
  cursor: default;
  color: var(--text-muted);
}

/* Separatore fra gruppi: una fascia di fondo alta due pixel, non un filetto. */
.menu-divisore {
  height: 1px;
  margin: 4px 6px;
  background: var(--filo);
}

/* --- Pie' di pagina ------------------------------------------------------- */
/* ⚠️  Alto 17px: era il bersaglio piu' piccolo di tutta la pagina, ed e' un
   collegamento che porta fuori dal sito. Non e' un `<button>`, quindi il test
   dei 44px non lo vedeva. Diventa alto 44 senza spostare la riga: `inline-flex`
   con l'allineamento al centro tiene la scritta dov'era. */
.footer-patreon {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.app-footer {
  background: var(--carta);
  border-top: 1px solid var(--filo);
  border-radius: 0;
}

.footer-content {
  padding: var(--s4) var(--margine-pagina);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s5);
  flex-wrap: wrap;
}

.footer-logo {
  font-weight: 700;
  font-size: var(--t2);
}

/* ⚠️  Qui vestiva `.footer-desc`, la riga «Gestione multi-server isolata e
       inoltro automatico notizie su Discord» sotto al nome. Tolta su richiesta:
       il piè di pagina tiene il nome, la firma e il sostegno, e basta.
       Era anche l'ultima scritta della pagina senza `data-i18n` — italiano
       fisso in mezzo a una dashboard che parla nove lingue — quindi con lei se
       n'è andato un difetto che nessun test vedeva: i due controlli sulle
       chiavi guardano le chiavi, e una scritta che non ne ha non esiste per
       loro. */
.footer-right {
  display: grid;
  gap: var(--s1);
  justify-items: flex-end;
}

.footer-copy {
  font-size: var(--t3);
  color: var(--text-muted);
}

/* === Schermo largo: due colonne ========================================== */
/* Il registro e' l'unica cosa che guadagna davvero dalla larghezza. Le
   impostazioni, che sono due campi, salgono di fianco invece di restare in
   fondo: si riduce lo scorrimento e si occupa la fascia destra che
   altrimenti resterebbe vuota.

   Si lavora in flex e non in grid di proposito: `display` su questa sezione
   e' scritto inline da app.js (`style.display = 'flex'`), e uno stile inline
   batte il foglio. Cambiare direzione e avvolgimento funziona lo stesso. */
/* ⚠️  Tre pannelli in riga a questa larghezza NON ci stanno, e per anni la
       regola qui dentro ne conosceva solo due: registro e impostazioni. Un
       pannello nato dopo la regola e mai aggiunto qui dentro si infila come
       terza colonna alla larghezza che vuole lui. Misurato allora a
       1900px: al registro restavano 898px, ma la sua tabella sotto i ~940 non
       si stringe, e sbordava di 64px passando SOTTO al pannello accanto. Le
       scatole non si accavallavano: a uscire era il contenuto.

       Ed e' la seconda volta: il commento del riepilogo, prima che il
       riepilogo si trasferisse nell'intestazione, raccontava lo stesso
       difetto con lo stesso rimedio dimenticato.

       Adesso: registro e impostazioni dividono la prima riga, l'automazione si
       prende tutta la seconda. Tre figli, tre regole, nessuno che si infila
       alla larghezza che gli pare. */
@media (min-width: 1500px) {
  #editor-section {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
  }

  /* Intestazione e tutorial tengono la riga intera. Un elemento nascosto non
     occupa spazio, quindi il tutorial chiuso non lascia righe vuote. */
  .server-banner,
  #tutorial-accordion {
    flex: 1 0 100%;
  }

  /* `min-width: 0` o la tabella non si lascia stringere. */
  #rules-container {
    flex: 1 1 0;
    min-width: 0;
  }

  .advanced-settings-card {
    flex: 0 0 380px;
  }

  .adv-grid {
    grid-template-columns: 1fr;
  }

  .wizard-steps-grid {
    gap: var(--s5);
  }
}

/* === Schermi medi ========================================================= */
@media (max-width: 980px) {
  .wizard-steps-grid {
    grid-template-columns: 1fr;
  }
}

/* === Telefono ============================================================= */
@media (max-width: 768px) {
  .main-container {
    /* Il rientro laterale resta quello di tutti: scritto a mano era 12 mentre
       la barra ne usava 14, e sul telefono marchio e titolo partivano da due
       verticali diverse per due pixel. */
    padding: var(--s5) var(--margine-pagina) var(--s8);
  }

  /* La scala si accorcia, ma i rapporti restano: e' la differenza fra i
     gradini a raggruppare, non la loro misura assoluta. */
  :root {
    /* Sul telefono scendono tutti e due di un gradino: 22 e 34 erano scritti a
       mano e non stavano su nessuna griglia. */
    --sp-largo: var(--s6);
    --sp-sezione: var(--s7);
  }

  /* ⚠️  UNA RIGA SOLA, e a cedere sono i blocchi. Misurato a 375px: i tre
     gruppi chiedono 439,3px e ce ne sono 347 — 92 di troppo. Andando a capo la
     barra restava alta 116px appiccicati, cioe' il 14% dello schermo tolto per
     sempre al contenuto; stringendosi resta a 60.

     Lo spazio che manca se lo dividono le cose che possono darlo, e l'ordine in
     cui cedono e' scelto:
       1. la scritta «Online» (47px), che il pallino verde sull'avatar dice gia';
       2. gli spazi fra i gruppi e dentro il gruppo dei temi (~20px);
       3. i tre modi del tema e il cuore, che scendono da 44px verso 32 SOLO se
          serve — a 375px si fermano a ~41, a 360 a ~37.
     Il tasto dei server NON cede: e' l'unico modo di uscire da una
     configurazione su un telefono, e una scritta tagliata non e' un comando.
     A restringersi e' la larghezza; l'altezza resta 44 per tutti. */
  .navbar {
    padding: var(--s2) var(--margine-pagina);
    gap: var(--s1);
    flex-wrap: nowrap;
  }


  /* Chi puo' stringersi lo dichiara: senza `min-width: 0` un contenitore flex
     non scende sotto il suo contenuto e la riga sborda invece di comprimersi. */
  .nav-brand,
  .nav-actions {
    min-width: 0;
  }

  /* ⚠️  Due classi e non una, e non e' pignoleria: `.theme-toggle-btn` e'
     dichiarata una SECONDA volta 800 righe piu' in basso, e a parita' di
     specificita' vince l'ultima. Scritto `.theme-toggle-btn` e basta, qui non
     succedeva niente — misurato: il gruppo restava largo 140px e a stringersi
     era il chip del profilo, cioe' l'esatto contrario dell'ordine voluto.
     Con due classi vince questa ovunque stia, e il blocco puo' restare qui
     invece di andare in fondo al file — dove la nota sul fumetto del profilo
     dice che deve restare LUI l'ultimo `@media (max-width: 768px)`.

     L'ordine in cui si cede e' questo: prima il rientro del gruppo, poi i
     bersagli, tutti insieme e in proporzione, fino al pavimento di 32px. */
  .nav-actions .theme-toggle-btn {
    flex-shrink: 1;
    min-width: 0;
    padding: 0;
    gap: 0;
  }


  /* ⚠️  I bersagli sono DISCHI, e restano dischi: `flex: none` piu' una
     larghezza pari all'altezza. Prima erano `flex: 0 1 44px` con un pavimento
     di 32, cioe' cedevano in larghezza mentre l'altezza restava 44 — e misurato
     a 375px diventavano 39,9x44, cioe' cinque ellissi in fila dentro una barra
     fatta di pastiglie tonde. Lo spazio che serviva a tenerli tondi si e'
     trovato dove c'era davvero: la scritta del tasto dei server e il chevron
     del profilo, qui sotto. */
  .theme-icon-slot,
  .nav-patreon-heart-btn {
    flex: none;
    width: 44px;
  }

  /* ⚠️  Il chevron se ne va. Costa 14px piu' il rientro che lo stacca dal
     ritratto, e non dice niente che il ritratto non dica gia': il pannello si
     apre toccando la faccia, ed e' l'unico modo di aprirlo. Senza di lui il
     chip smette di essere una pillola schiacciata e torna un disco da 44 come
     gli altri comandi. La freccia resta sul desktop, dove lo spazio c'e'.
     Due classi e non una: `.btn-profile` e' ridichiarata piu' avanti nel
     foglio, e a parita' di peso vince l'ultima — la stessa ragione per cui
     `.nav-actions .theme-toggle-btn` qui sopra ne porta due. */
  .navbar .profile-chevron {
    display: none;
  }

  .navbar .btn-profile {
    width: 44px;
    padding: 0;
    gap: 0;
    justify-content: center;
  }

  #user-profile-widget {
    flex: none;
  }

  /* ⚠️  Il tasto dei server si fa piu' piccolo, ed e' lui a pagare per i dischi:
     la scritta scende alla misura degli altri comandi della pagina (`--t1`, la
     stessa di «Aggiorna») invece dei 15px che gli arrivano dal blocco dei
     pulsanti. Non cede la SCRITTA — resta leggibile per intero, ed e' la sola
     strada per uscire da una configurazione su un telefono.
     Anche qui due classi: quel blocco che gli da' `--t4` sta PIU' AVANTI nel
     foglio, quindi una regola con una classe sola perderebbe comunque, media
     query o no. E' gia' successo col gruppo dei temi. */
  .navbar .nav-btn-server {
    font-size: var(--t1);
    padding: var(--s2);
  }

  /* ⚠️  Qui spariva tutto il gruppo, e finche' il ritorno all'elenco stava
         nella pillola accanto al marchio non si perdeva niente. Tolta la
         pillola, nascondere anche questo lasciava il telefono SENZA nessun
         modo di uscire da una configurazione: dentro ci si entrava e basta.
         Resta il tasto dei server; se ne va la voce dell'editor, che e' la
         sola che qui non ci sta e che comunque non porta da nessuna parte. */
  #nav-link-editor {
    display: none !important;
  }

  /* Sul telefono il sostegno torna a essere il solo cuore: la scritta non ci
     sta, e il nome dell'azione resta comunque nell'etichetta accessibile. */
  .nav-patreon-testo {
    display: none;
  }

  /* ⚠️  36px, e il minimo da pollice vale sui DUE lati. Era alto 44 e largo 36:
     il test dei bersagli non lo vedeva perche' e' un `<a>` e lui parte dai
     `<button>`. Adesso e' quadrato. */
  .nav-patreon-heart-btn {
    width: 44px;
    padding: 0;
  }

  /* ⚠️  Il nome del bot se ne va, l'immagine e lo stato restano. Sono 144px di
     scritta che mandavano la barra su due righe: il marchio resta riconoscibile
     dal ritratto, e il nome sta scritto grande nella schermata d'accesso e nel
     titolo della scheda. E' la terza scritta che si toglie qui per lo stesso
     motivo, dopo quella di Patreon e il nome dell'utente. */
  .brand-name {
    display: none;
  }

  /* `margin-left: auto` resta: adesso la riga e' di nuovo UNA, ed e' il suo
     mestiere — spingere le azioni a destra. Era lui a lasciare il buco di 173px
     quando le righe erano due. */

  /* ⚠️  L'unico che NON cede. E' la sola strada per uscire da una
     configurazione su un telefono, e un comando con la scritta tagliata a meta'
     non e' un comando: preferisce prendersi lo spazio e lasciare che a
     stringersi siano i tre modi del tema. */
  .nav-links {
    flex: none;
  }

  /* Il nome del server si accorcia invece di mandare la barra su due righe. */
  .server-pill-text {
    max-width: 9ch;
  }

  /* ⚠️  `space-between` su una riga da 347px mette il testo di presentazione e il
     tasto «Aggiorna» a contendersi la larghezza, e vince il tasto perche' non si
     stringe: misurato a 320px in tedesco, al testo restavano 101px — una colonna
     di due parole alta 306px. E' la PRIMA schermata che un cliente nuovo vede, e
     l'unica che deve attraversare per forza. Impilati, il testo si prende tutta
     la riga e il tasto va sotto, dove per giunta e' piu' facile da toccare. */
  .section-header {
    flex-direction: column;
    align-items: stretch;
    gap: var(--s3);
  }

  /* Per la stessa ragione della riga qui sopra: «Aggiorna elenco server» e
     «Invita Bot» sono due scritte, non due icone, e affiancate su 347px si
     spartiscono una larghezza in cui nessuna delle due ci sta per intero.
     Impilati si prendono tutta la riga e restano leggibili, che e' la stessa
     scelta gia' fatta per il pulsante quando era solo. */
  .section-azioni {
    flex-direction: column;
    align-items: stretch;
  }

  .section-title {
    font-size: var(--t7);
  }

  /* ⚠️  16px NETTI, fuori dalla scala apposta: sotto questa misura iOS
         ingrandisce la pagina appena si tocca un campo. Non e' una scelta di
         stile, e' l'unico numero che quel browser accetta — `--t4` vale 15 e
         basterebbe quel pixel per far saltare la pagina.

         E il selettore e' qualificato dall'ELEMENTO apposta. Questa regola era
         scritta `.form-input, .form-select`, che vale (0,1,0), e perdeva contro
         `input.field` di `componenti-gk.css`, che vale (0,1,1): da quando i
         campi portano tutte e due le classi, tredici di loro scrivevano a 15px
         e su iPhone la pagina si ingrandiva a ogni tocco senza tornare
         indietro. L'ordine di caricamento non c'entra e non basta — la
         specificita' viene prima — quindi qui si pareggia, e a quel punto e'
         l'ordine a decidere: `style.css` si carica dopo. */
  input.form-input,
  textarea.form-input,
  select.form-select {
    font-size: 16px;
  }

  .form-row-2 {
    grid-template-columns: 1fr;
  }

  .rules-toggle {
    min-height: 44px;
  }


  .con-foto .app-background,
  html.login-active .app-background {
    background-image: var(--bg-wallpaper-mobile);
  }

  .riepilogo-voce {
    flex: 1 1 40%;
    padding: var(--s2) 0 var(--s2) var(--s4);
  }

  .riepilogo-voce:nth-child(odd) {
    padding-left: 0;
    border-left: none;
  }

  .server-banner,
  .rules-header {
    flex-direction: column;
    align-items: stretch;
  }

  .banner-actions .btn {
    flex: 1 1 auto;
  }

  /* La tabella si impila. Le intestazioni tornano accanto ai valori. */
  .registro thead {
    display: none;
  }

  .registro,
  .registro tbody,
  .registro tr,
  .registro td {
    display: block;
    width: auto;
  }

  /* Impilate, le celle non devono portarsi dietro un filo per ognuna: il
     filo passa alla riga intera, che qui e' la scheda. */
  .registro tr {
    padding: var(--s3) var(--s3);
    border-bottom: 1px solid var(--filo);
  }

  .registro tbody tr:last-child {
    border-bottom: none;
  }

  .registro tbody td,
  .registro tbody tr:last-child td {
    border-bottom: none;
  }

  .registro tbody tr:hover td {
    background: transparent;
  }

  .registro td {
    padding: var(--s0) 0;
  }

  /* Qualificata con `td`: la regola generica qui sopra ha specificita'
     maggiore di una classe sola e lascerebbe la freccia su una riga vuota. */
  .registro td.rule-freccia {
    display: none;
  }

  .rule-flow-label {
    display: inline;
    margin-right: 6px;
  }

  .rule-card-actions {
    justify-content: flex-start;
    margin-top: var(--s2);
  }

  .btn-rule-action {
    flex: 1 1 auto;
    justify-content: center;
  }

  .footer-right {
    justify-items: flex-start;
  }

  .flow-diagram {
    flex-direction: column;
  }

  .flow-connector {
    padding: var(--s1) 0;
  }

  .flow-connector .flow-arrow,
  .flow-connector.has-bot-tag .flow-arrow-1,
  .flow-connector.has-bot-tag .flow-arrow-2 {
    transform: rotate(90deg);
  }
}


/* === Gli schermi piu' stretti: a cedere sono i rientri ==================== */
/* ⚠️  Misurato: cinque dischi da 44, il tasto dei server e il ritratto del
       marchio stanno in una riga sola fino a **356px**, e sotto l'ultima
       casella del tema usciva dalla barra — di 28,8px a 320. Non se ne
       accorgeva nessuno: le scatole non si accavallano, a uscire e' il
       CONTENUTO, e la pagina non prende nemmeno lo scorrimento laterale.
       Quello che cede e' il RITRATTO DEL MARCHIO, e i bersagli restano 44 fino
       a 320 — lo schermo piu' stretto che questa pagina promette di reggere.
       Il marchio qui e' decorazione pura: il nome del bot se n'e' gia' andato
       a 768, e il ritratto non porta da nessuna parte. Tutto il resto della
       barra e' invece un COMANDO, e un comando mezzo fuori schermo non esiste.

       ⚠️  Il primo tentativo era recuperare lo spazio dai rientri e tenere il
       marchio: non basta, e per una ragione che si vede solo misurando. Il
       marchio ha `min-width: 0`, quindi non e' fisso: si riprende lo spazio che
       gli si libera davanti — passava da 37,4 a 42,8 — e l'ultima casella
       restava fuori lo stesso, di 12,1px. Chi si stringe da solo non regala
       niente a nessuno: va tolto, non stretto.

       Due classi (`.navbar-wrapper ...`) e non una: le regole di questi
       elementi stanno dentro il blocco dei 768px, che a parita' di peso
       vincerebbe per ordine se questo blocco gli stesse prima. Alzare la
       specificita' invece di spostare il blocco e' la stessa scelta gia' fatta
       per il gruppo dei temi, e lascia in fondo al file il fumetto del profilo,
       che ci deve restare. */
/* ⚠️  Qui il tasto dei server perdeva anche il rientro — `var(--s0)`, cioe' 2px
       per lato — e la scritta finiva appiccicata ai bordi della pastiglia.
       Non serviva: misurato a 375, 360 e 320 col rientro pieno (`var(--s2)`,
       8px), l'ultima casella del tema resta dentro la barra di 14, 4,5 e 12,6px,
       e il ritratto del marchio non viene tagliato. Il calcolo che aveva fatto
       togliere quei pixel dava il marchio per largo 46 fisso: non lo e', si
       stringe fino ai 38 del ritratto senza tagliarlo, e quello spazio bastava
       gia'. La regola se n'e' andata invece di essere ritarata: la misura la
       da' il blocco dei 768px, che e' l'unico posto dove sta scritta. */

/* ⚠️  E sotto i 360 non basta piu' nemmeno quello: col marchio al suo posto il
       margine che resta all'ultima casella e' 14px a 375 e a 360, scende a 2 a
       345 e a -18,8 a 320 (misurato, un capo all'altro). 359 e' il primo
       gradino sotto la larghezza piu' stretta che regge — 360px, che e' anche
       la piu' comune fra gli Android. */
@media (max-width: 359px) {
  .navbar-wrapper .nav-brand {
    display: none;
  }
}


/* === Il fumetto del profilo sul telefono ================================== */
/* Ancorato al bottone finiva fuori schermo. Si aggancia ai due margini della
   barra. Questo blocco resta l'ULTIMO `@media (max-width: 768px)` del file. */
@media (max-width: 768px) {
  .user-profile-widget {
    position: static;
  }

  .profile-popover {
    left: 12px;
    right: 12px;
    width: auto;
    top: calc(100% + 8px);
  }

  .profile-popover::before {
    display: none;
  }

  .user-name-text {
    display: none;
  }
}

/* === Movimento ridotto ==================================================== */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* === I PULSANTI, IN CODA ================================================== */
/* I pulsanti che si vedono nel suo pannello sono quelli dentro le schede,
   `#featGrid .pcard .btn`: NON hanno contorno e NON hanno l'ombra dura. Sono
   un riempimento tinto d'accento al 14%, che al passaggio del mouse sale al
   30%. Qui non esiste `#featGrid`, quindi quelle sue dichiarazioni sarebbero
   inerti: di suo e' rimasta la FORMA, riportata sui pulsanti di Piccione.
   ⚠️  Le percentuali no, e non vanno riallineate alle sue: le sue erano una
       delle cinque ricette che «La scala dei pulsanti» ha unificato in tre
       livelli. Qui si legge `--tinto` e `--tinto-sopra` — di notte 30% e
       44% sopra il rilievo, opachi; di giorno 20% e 32% velati — e il
       numero sta in un token solo perche' non torni a essercene uno per
       pulsante. */
.btn,
.btn-rule-action,
.btn-refresh,
.btn-logout-popover,
.nav-btn-server {
  border-color: transparent;
  background: var(--tinto);
  color: var(--su-tinto);
  box-shadow: none;
  /* I suoi numeri finali, non quelli del primo blocco: glieli danno regole a
     selettore multiplo piu' avanti nel foglio, che valgono per `.btn` ma non
     per le classi di Piccione. La misura la prende dalla scala: e' lo stesso
     15px che `--fs-ui` da' al foglio copiato, scritto pero' dove si legge. */
  font-size: var(--t4);
  font-weight: var(--w-strong);
  letter-spacing: var(--tr-caps);
}

/* Guild Keeper non ha uno stato disabilitato per `.btn`: prendendo le sue
   regole, Piccione aveva perso il suo, e "Entra nella Dashboard" restava oro
   pieno con il cursore a manina anche quando non si poteva premere. Un
   pulsante che sembra vivo e non fa niente e' peggio di uno spento. */
.btn:disabled,
.btn[disabled],
.btn-rule-action:disabled,
.btn-refresh:disabled {
  opacity: 0.4;
  cursor: default;
  box-shadow: none;
}

/* Nessun pulsante porta l'ombra dura: nel suo pannello non se ne vede una. */
.btn.gold,
.btn.gold:hover:not(:disabled),
.btn:active,
.btn-rule-action:active {
  box-shadow: none;
  transform: none;
}

/* === COME SI MUOVONO I PULSANTI =========================================== */
/* Il primo tentativo era un lampo di luce che attraversava il pulsante al
   passaggio del mouse. E' stato scartato MISURANDO, non per gusto: un velo di
   un colore solo sotto la scritta funziona solo se i pulsanti hanno tutti lo
   stesso verso, e qui non ce l'hanno. Ce ne sono con testo chiaro su fondo
   scuro (la pillola, il cuore, l'elimina nel tema scuro) e con testo scuro su
   fondo chiaro (gli stessi nel tema chiaro). Un velo bianco aiuta i secondi e
   affoga i primi, uno nero il contrario: l'opacita' massima che regge 4,5:1 su
   TUTTI e otto i casi misurati e' 0,11 per il bianco e 0,03 per il nero. Cioe'
   invisibile. Un lampo che non si vede non e' un lampo.

   Quindi il movimento sta dove il testo non c'e': il pulsante si solleva, si
   accende un alone FUORI dai suoi bordi, e alla pressione si schiaccia. Nessuno
   dei tre tocca il contrasto, perche' nessuno dei tre passa sopra la scritta.

   L'alone e' `currentColor`: ogni pulsante si illumina del proprio colore, e
   quello rosso resta rosso senza doverglielo dire. */
.btn,
.btn-rule-action,
.btn-refresh,
.btn-logout-popover,
.nav-btn-server,
.server-invite-btn,
.nav-patreon-heart-btn,
.js-riprova-guilds {
  transition:
    transform 0.16s var(--molla),
    box-shadow 0.2s ease,
    background var(--passo),
    color var(--passo);
}

.btn:hover:not(:disabled),
/* Il pulsante principale ha una regola sua che spegne ombra e trasformazione,
   e con `.btn.gold:hover:not(:disabled)` pesa un grado piu' di questa: senza
   ripeterlo qui restava l'unico a non muoversi. La specificita' non si supera
   mettendosi piu' in basso. */
.btn.gold:hover:not(:disabled),
.btn-rule-action:hover:not(:disabled),
.btn-refresh:hover:not(:disabled),
.btn-logout-popover:hover:not(:disabled),
.nav-btn-server:hover:not(:disabled),
.server-invite-btn:hover,
.nav-patreon-heart-btn:hover {
  transform: translateY(-1.5px);
  box-shadow: 0 6px 16px -8px currentColor;
}

/* Il principale e' l'eccezione all'alone di `currentColor`: il suo colore di
   testo e' quello di CONTRASTO, bianco sul chiaro e quasi nero sullo scuro, e
   un alone bianco su una pagina chiara non si vede. Si illumina del pieno
   d'accento, cioe' del colore di cui e' fatto. */
.btn.gold:hover:not(:disabled) {
  box-shadow: 0 6px 18px -8px var(--accento-pieno);
}

/* Alla pressione si schiaccia e torna su con lo stesso rimbalzo della riga
   scelta: mezzo pixel di sfondamento, non un molleggio da giocattolo.
   `:active` vince su `:hover`, quindi va dopo. */
.btn:active:not(:disabled),
.btn-rule-action:active:not(:disabled),
.btn-refresh:active:not(:disabled),
.btn-logout-popover:active:not(:disabled),
.nav-btn-server:active:not(:disabled),
.server-invite-btn:active,
.nav-patreon-heart-btn:active {
  transform: translateY(0) scale(0.955);
  box-shadow: none;
  transition-duration: 0.07s;
}

/* Il cuore fa il suo mestiere: batte, ma una volta sola e piano. */
@keyframes battito {
  0%, 100% { transform: scale(1); }
  28%      { transform: scale(1.22); }
  52%      { transform: scale(0.96); }
  76%      { transform: scale(1.09); }
}

.nav-patreon-heart-btn:hover .patreon-heart-svg {
  animation: battito 0.62s ease-in-out;
}

/* La freccia dell'aggiorna accenna il giro che sta per fare. */
.btn-refresh:hover:not(:disabled) .refresh-icon {
  transform: rotate(-38deg);
}

.refresh-icon {
  transition: transform 0.24s var(--molla);
}

.btn:hover:not(:disabled),
.btn:focus-visible,
.btn-rule-action:hover,
.btn-refresh:hover:not(:disabled) {
  border-color: transparent;
  background: var(--tinto-sopra);
  color: var(--su-tinto);
  box-shadow: none;
}

/* Il rosso segue la stessa forma, con la sua tinta. */
.btn-rule-delete,
.btn-logout-popover {
  background: var(--tinto-rosso);
  color: var(--su-tinto-rosso);
  border-color: transparent;
  box-shadow: none;
}

.btn-rule-delete:hover,
.btn-logout-popover:hover {
  background: var(--tinto-rosso-sopra);
  color: var(--su-tinto-rosso);
  border-color: transparent;
  box-shadow: none;
}

/* La classe si chiama ancora `.gold` perche' e' la sua, ma il colore qui e'
   l'azzurro: e' la variante piena, per l'azione principale. Usa `--gold`, cioe'
   l'accento OPACO, non quello al 70%: sopra ci va del testo. */
.btn.gold {
  background-color: var(--pieno);
  color: var(--su-pieno);
}

.btn.gold:hover:not(:disabled) {
  background-color: var(--gold2);
  color: #14140f;
}

/* Nel tema chiaro il pieno d'accento e' scuro: il testo sopra deve schiarirsi,
   o resterebbe inchiostro su inchiostro (1,5:1). */
[data-theme="light"] .btn.gold,
[data-theme="light"] .btn.gold:hover:not(:disabled) {
  color: #ffffff;
}

/* === ANGOLI DEI PULSANTI ================================================== */
/* Tutto cio' che si preme, comprese le icone quadrate della barra e i due
   pulsanti di chiusura, che altrimenti resterebbero gli unici spigoli vivi
   in mezzo a comandi arrotondati. */
.btn,
.btn-rule-action,
.btn-refresh,
.btn-logout-popover,
.nav-btn-server,
.btn-discord-login,
.btn-close-tut,
.btn-modal-close,
.btn-profile,
.nav-patreon-heart-btn,
.theme-toggle-btn,
.server-invite-btn,
.js-riprova-guilds,
.rules-toggle,
.menu-voce {
  border-radius: var(--raggio-pulsante);
}

/* La slitta dell'interruttore del tema segue la sua scatola, o sporgerebbe
   dagli angoli ora smussati. */
.theme-toggle-btn {
  overflow: hidden;
}

/* === I RUOLI DEL TESTO ==================================================== */
/* Google Sans Flex e' variabile: la rotondita' e' l'asse `ROND` (0-100) e la
   larghezza e' `wdth` (25-151), che in CSS si guida con `font-stretch`. Ogni
   ruolo e' una posizione su quegli assi, non un carattere diverso.

   Si usa `font-stretch` e non `font-variation-settings: 'wdth'` perche' quando
   esiste la proprieta' alta e' quella a valere: mescolarle le fa litigare. */

/* Titoli: rotondita' massima, grassi, piu' grandi.

   ⚠️  Un titolo che non sta in questa lista NON prende la faccia del sito:
       ricade su `--ui`, che e' un'altra famiglia con un'altra rotondita'. Non
       si vede come un errore, si vede come «quel pezzo sembra di un altro
       sito» — ed e' esattamente quello che era successo a tutto il blocco
       dell'automazione, che ne aveva quattro fuori. Aggiungendo un titolo
       nuovo da qualche parte, va aggiunto anche qui.

       `.tutorial-title-row h3` non c'e' piu': quel markup e' sparito quando il
       tutorial e' diventato un dialogo. Un selettore morto in questa lista non
       fa danni, ma racconta che ci sia un titolo dove non c'e'. */
.login-title,
.section-title,
.banner-title,
.modal-title,
.rules-toggle-label,
.rules-title,
.adv-title,
.flusso-dove,
.tut-passi h4 {
  font-family: var(--faccia);
  font-variation-settings: 'ROND' 100;
  font-stretch: 100%;
  font-weight: 700;
}

.section-title { font-size: var(--t9); }
.banner-title  { font-size: var(--t8); }
.login-title   { font-size: var(--t8); }
.modal-title,
.rules-toggle-label,
.rules-title   { font-size: var(--t7); }


.adv-spunta {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 44px;
  cursor: pointer;
  /* Niente colore e niente corpo scritti qui: la regola su `label` del gruppo
     vince comunque per specificita', e questa etichetta DEVE restare identica
     a quelle che le stanno accanto. Scrivere un colore che non si applica
     sarebbe peggio che non scriverlo: sembrerebbe deciso. */
}

.adv-spunta input {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  accent-color: var(--pieno);
  cursor: pointer;
}

.adv-nota {
  display: block;
  margin-top: var(--s1);
  font-size: var(--t2);
  color: var(--text-muted);
}

/* Gli easter egg uno per uno: colonne che si stringono, e una sola quando la
   scheda è stretta (sopra i 1500px sta in una colonna da 380px). */
.egg-griglia {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr));
  gap: 0 var(--s3);
}

/* ⚠️  Una voce da spuntare in un elenco è una frase, non una didascalia:
       `.form-group label` le mette addosso il maiuscoletto delle etichette, e
       nove righe in maiuscoletto si leggono come nove titoli. La didascalia del
       gruppo resta in maiuscoletto sopra di loro. Qualificato dall'elemento per
       pareggiare la specificità di `.form-group label`: decide l'ordine. */
label.spunta-voce {
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--t3);
  min-width: 0;
  overflow-wrap: anywhere;
}

.form-group-larga { grid-column: 1 / -1; }

/* Sottotitoli: grassi, poco rotondi, leggermente piu' larghi. */
.step-title,

.banner-badge,
.popover-title,
.footer-logo,
.empty-rules-box h4,
.restore-box h4,
.confirm-label,
.registro th,
.riepilogo-etichetta {
  font-variation-settings: 'ROND' 30;
  font-stretch: 108%;
  font-weight: 700;
}

/* Testo corrente. */
body,
.confirm-value,
.server-card-name,
.rule-card-name,
.rule-flow-val,
.brand-badge,
.form-input,
.form-select,
.menu-voce {
  font-variation-settings: 'ROND' 40;
  font-stretch: 100%;
}

/* --- Il grassetto della selezione, per gradi ------------------------------- */
/* Deve stare DOPO il blocco dei ruoli qui sopra, che riscrive
   `font-variation-settings` e cancellerebbe quello che si dichiara qui.
   `'ROND'` sparisce e non cambia niente: quell'asse in questo carattere NON
   ESISTE, verificato misurando la stessa scritta a ROND 0 e a ROND 100 (stessa
   larghezza al centesimo di pixel).

   IL PESO NON SI ANIMA. Animarlo funzionava sulla carta: i valori calcolati
   passavano da 500 a 800 a passi regolari, e il carattere sa disegnare ogni
   peso intermedio (misurato: tredici larghezze diverse a passi di 5 punti).
   Ma ogni fotogramma cambiava la LARGHEZZA della scritta, e cambiare larghezza
   vuol dire rifare il calcolo dell'impaginato, ricostruire un'istanza del
   carattere variabile e riformare il testo. Sessanta volte al secondo non ci
   sta, e il browser se la cavava disegnando tre o quattro scalini: peggio che
   se fosse stato istantaneo.

   Qui invece cresce un'OMBRA, che e' pura pittura: non tocca le metriche
   (misurato: 308,828 px identici su tutti i fotogrammi), quindi non rifluisce
   niente e un fotogramma costa quanto un cambio di colore. Il peso resta 600
   sempre, e la riga scelta non si allarga piu' di quella accanto.

   Quattro ombre TRASPARENTI a distanza zero, che diventano del colore del testo
   e si spingono di tre decimi di pixel nelle quattro direzioni: e' il vecchio
   trucco del finto neretto, e a questo corpo rende come un 800 vero
   (confrontati a occhio, uno sotto l'altro). Devono essere quattro in tutti e
   due gli stati, o il browser non sa appaiarle e non interpola.

   Trasparenti e non `currentColor`: a distanza zero sembrano invisibili, ma
   quattro copie opache esattamente sotto le lettere impilano i bordi sfumati e
   ingrassano il testo. Erano venuti in neretto TUTTI i nomi, anche quelli non
   scelti.

   Non `-webkit-text-stroke`, che sarebbe stato piu' pulito: Chrome lo accetta
   nella riga `transition` e ne cambia il valore, ma NON lo interpola
   (`getAnimations()` resta vuoto e il tratto compare di colpo). Provato e
   scartato. */
.server-card-name {
  text-shadow: 0 0 0 transparent, 0 0 0 transparent,
               0 0 0 transparent, 0 0 0 transparent;
  letter-spacing: 0;
  /* Stessa curva dell'entrata: uscendo, la scritta si stringe un pelo sotto il
     normale e poi si posa. E' lo stesso rimbalzo al contrario. */
  transition: text-shadow 0.15s var(--molla),
              letter-spacing 0.15s var(--molla);
}

/* Un filo piu' larga, oltre che piu' nera. Qui la larghezza cambia davvero, ma
   la spaziatura fra le lettere e' un'altra cosa dal peso: le lettere restano
   quelle, gia' disegnate, e cambia solo di quanto avanza il cursore fra l'una e
   l'altra. Niente istanza nuova del carattere variabile, niente riformattazione
   del testo, niente ridisegno dei contorni: era quello a non stare nei
   sedici millesimi di secondo del fotogramma.
   Tre decimi e mezzo di pixel per lettera fanno una decina di pixel su un nome
   lungo: si vede che cresce, senza spingere via il distintivo del ruolo. */
.server-select-card.selected .server-card-name {
  /* Due decimi e non tre: a tre l'ombra chiudeva le contro-forme delle lettere
     e la riga scelta si leggeva come una macchia scura invece che come una
     scritta in neretto. */
  text-shadow: 0.2px 0 0 currentColor, -0.2px 0 0 currentColor,
               0 0.2px 0 currentColor, 0 -0.2px 0 currentColor;
  letter-spacing: 0.35px;
  /* Il rimbalzo, ed e' microscopico per davvero: questa curva sfora il traguardo
     del 9,8% (calcolato, non a occhio), e su dieci pixel di crescita fanno un
     pixel scarso oltre il segno prima di posarsi. Vale solo entrando: la regola
     sta qui, e uscendo torna a valere quella lisca di sopra. */
  transition: text-shadow 0.15s var(--molla),
              letter-spacing 0.15s var(--molla);
}

/* Testo di spiegazione: molto largo, per niente rotondo, meta' opacita'.
   Parte dall'inchiostro pieno e non da quello gia' smorzato: dimezzando un
   colore gia' smorzato il testo scendeva sotto la soglia di leggibilita'. */
.section-subtitle,
.rules-sub,
.form-help,
.popover-aiuto,
.node-sub,
.empty-rules-box p,
.restore-box p,
.menu-tasti {
  font-variation-settings: 'ROND' 0;
  font-stretch: 110%;
  font-weight: 400;
  color: var(--ink-spiegazione);
  opacity: var(--opacita-spiegazione);
}

/* Il ruolo sul server: piu' largo della spiegazione, grasso, per niente
   rotondo. Non e' piu' un'etichetta sola per tutti: dice Owner, Admin o Mod
   a seconda dei permessi veri, letti da `src/auth.js`. */
.server-owner-badge,
.server-admin-badge {
  font-variation-settings: 'ROND' 0;
  font-stretch: 118%;
  font-weight: 700;
  font-size: var(--t2);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* I valori che vengono dalla macchina non hanno piu' un carattere a larghezza
   fissa: le cifre pero' devono restare incolonnate, o gli id ballano. */
.codice,
.server-card-meta,
.rule-flow-val,
.brand-badge,
.rules-count,
.footer-copy,
.riepilogo-numero,
.menu-tasti,
.form-input,
.form-select {
  font-variant-numeric: tabular-nums;
}

/* === ANGOLI DEI CONTENITORI =============================================== */
/* I contenitori sono arrotondati, ma NON con il valore dei pulsanti. Il raggio
   dei pulsanti e' volutamente enorme (9999px) e il browser lo taglia a meta'
   del lato corto: su un pulsante alto 44px fa una pastiglia, su un pannello
   alto 300px fa un ovale che si mangia il contenuto agli angoli. Provato: la
   pagina diventava illeggibile.

   Qui il raggio e' fisso e generoso: legge come "tondo quanto i pulsanti" a
   colpo d'occhio, senza deformare le scatole. */
/* ⚠️  Queste sono DUE regole, ed erano diventate una sola. `7066d01` ha tolto
   `.mobile-save-bar` — l'ultimo selettore di questa lista — e con lei se ne sono
   andati il `{ border-radius }` e la graffa: la lista e' rimasta appesa e ha
   preso il corpo della regola dopo. Nessun errore, nessun test rosso, due difetti
   in silenzio per settimane:
     1. i contenitori sono tornati a `--raggio-voce` (6px), cioe' al valore delle
        cose piccole, mentre tutto il commento qui sopra spiega perche' ne vogliono
        uno loro (12px). Una sezione che descrive una decisione e non la prende.
     2. `overflow: visible` si e' allargato a `.pannello`, `.login-card` e
        `.app-toast`, che non erano nella sua lista. Sulla carta dell'accesso ha
        cancellato l'`overflow-y: auto` dichiarato 3000 righe sopra — stessa
        specificita', piu' avanti nel foglio, vince l'ultimo — e con lui il
        solo modo di scorrere: misurato a 740x360, 349px di contenuto in un tetto
        di 328 che USCIVANO dalla carta invece di scorrere, con la riga di stato
        del bot a penzoloni fuori dal riquadro.
   Il segnale che erano fuse si vede a occhio: `.menu-contestuale`,
   `.modal-dialog` e `.profile-popover` comparivano DUE VOLTE nella stessa lista.
   C'e' un test che adesso cerca proprio quello, su tutto il foglio. */
.pannello,
.menu-contestuale,
.modal-dialog,
.profile-popover,
.login-card,
.app-toast {
  border-radius: var(--raggio-contenitore);
}

/* Solo le scatole che galleggiano tagliano ai bordi: dentro non hanno niente
   che debba sporgere. Il `.pannello` NO: contiene l'intestazione appiccicata
   della tabella, e un antenato che taglia annulla `position: sticky`. E la
   `.login-card` nemmeno: lei deve SCORRERE quando il telefono e' in orizzontale. */
/* ⚠️  Niente `overflow: hidden` qui: serviva a tagliare la testata azzurra a
   tutta larghezza, che non c'e' piu', e adesso taglierebbe la tendina aperta
   sul bordo del pannello — cioe' proprio la cosa che deve uscirne. */
.menu-contestuale,
.modal-dialog,
.profile-popover {
  overflow: visible;
}

/* Le voci del menu non sono pastiglie: in un elenco una pastiglia sembra un
   pulsante staccato, ed e' quello che faceva sembrare rotto il menu. */
.menu-voce {
  border-radius: var(--raggio-voce);
}

/* === NIENTE CONTORNI ====================================================== */
/* Le regole copiate da Guild Keeper disegnano un contorno attorno a pulsanti,
   campi e schede. Si spengono qui e non nel file copiato, che deve restare
   confrontabile con l'originale riga per riga.

   I filetti su UN LATO restano: quelli non sono contorni, sono i divisori. */
.btn,
.field,
.pcard,
.metric,
.kpi,
.module-head,
.toast,
.pod,
.tile,
.form-input,
.form-select,
.btn-rule-action,
.btn-refresh,
.btn-logout-popover,
.nav-btn-server,
.btn-profile,
.theme-toggle-btn,
.menu-voce,
.server-invite-btn,
/* Con lo stesso peso della sua: `input.field` vale (0,1,1) e batteva `.field`
   comunque venisse dopo. La specificita' non si supera con l'ordine. */
input.field,
textarea.field,
select.field {
  border: none;
}

/* ⚠️  Qui i campi riprendevano il filo, e non l'hanno MAI ripreso: sono tre
       regole che non hanno disegnato un pixel dal giorno in cui sono nate.
       Chi le scrisse (`c515b7d`) aveva diagnosticato un problema d'ORDINE — «ha
       la stessa specificita' e sta piu' in basso, quindi vinceva lei» — ma il
       blocco qui sopra ha DUE meta': `.form-input`/`.form-select` a (0,1,0) e
       `input.field`/`select.field` a (0,1,1). Il rimedio rispondeva alla prima
       meta'; a spegnere i campi era la seconda. Funzionava davvero sui quattro
       campi del pannello premium, che `app.js` costruiva senza la classe
       `field` — e quelli sono spariti con `b96e753`.
       Oggi TUTTI i campi portano tutte e due le classi, e il «niente contorni»
       e' quello che si vede e quello che si vuole: la decisione e' stata presa
       guardando le due schermate a confronto. Le regole se ne vanno con lei,
       perche' un filo dichiarato e mai disegnato dice il falso a chi legge.
       Quello che invece era un difetto vero — nessun segno di quale campo ha il
       fuoco, dalla STESSA collisione — e' riparato dove i campi sono definiti. */

/* ─── Il permesso per la posizione ─────────────────────────────────────────── */
/* Dialogo corto: due pulsanti e una frase. Piu' stretto degli altri perche'
   una riga lunga il doppio non si legge meglio, si legge peggio. */
.meteo-dialogo { max-width: 460px; }
.meteo-modal-testo {
  margin: 0;
  font-size: var(--t3);
  line-height: 1.6;
  color: var(--text-secondary);
}
.meteo-modal-footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  padding: var(--s3) var(--s4) var(--s4);
}
/* `min-width: 0` perche' `flex: 1 1 0` da solo non restringe sotto al testo
   del pulsante: su un telefono stretto uscivano dal riquadro. */
.meteo-modal-footer .btn { flex: 1 1 0; min-width: 0; min-height: 44px; }

/* ⚠️  La croce di chiusura era il CARATTERE ✕ dentro un pulsante trasparente:
       niente fondo, niente sollevamento, niente animazione, e un disegno
       diverso da un sistema all'altro. Adesso e' un simbolo come tutti gli
       altri, dentro un pulsante che si veste come tutti gli altri. */
.btn-modal-close {
  /* ⚠️  Misurata 34×34: sotto il bersaglio da pollice, e chiudere un dialogo
         col dito e' la cosa che si fa piu' spesso su un telefono. 44 e' il
         minimo, ed e' una delle tre misure che stanno fuori dalla scala
         apposta. Il disegno dentro resta di 15px: a crescere e' il bersaglio,
         non la crocetta. */
  width: 44px;
  height: 44px;
  min-height: 44px;
  display: grid;
  place-items: center;
  flex: none;
  padding: 0;
  color: var(--text-secondary);
  background: var(--tinto);
  border: 0;
  cursor: pointer;
  transition: background var(--passo), color var(--passo), transform var(--passo);
}

.btn-modal-close svg { width: 15px; height: 15px; }

.btn-modal-close:hover {
  background: var(--tinto-sopra);
  color: var(--su-tinto);
  transform: translateY(-1.5px);
}

.btn-modal-close:active { transform: translateY(0) scale(0.94); }

/* Le due stanghette si aprono e si richiudono: una croce che ruota di 90 gradi
   torna identica a se stessa, quindi non si vedrebbe girare. */
.btn-modal-close .ico-croce-a,
.btn-modal-close .ico-croce-b {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.3s var(--molla);
}

.btn-modal-close:hover .ico-croce-a { transform: rotate(-14deg) scale(1.1); }
.btn-modal-close:hover .ico-croce-b { transform: rotate(14deg) scale(1.1); }

/* ─── I simboli si animano, non si spostano ────────────────────────────────── */
/* Un'icona che scivola di due pixel al passaggio del mouse e' il pulsante che si
   muove, non il simbolo che fa qualcosa. Qui si muove il PEZZO che ha un senso:
   la freccia entra nella porta e la porta resta ferma, le pagine del libro si
   sfogliano attorno al dorso, i raggi del sole girano attorno al suo cuore.

   `transform-box: fill-box` non e' facoltativo: senza, un `transform` dentro un
   SVG prende come origine l'angolo del sistema di coordinate del disegno e non
   il centro del pezzo, e una rotazione «sul posto» parte per la tangente. */
.btn svg [class^="ico-"],
.btn svg[class^="ico-"],
.theme-icon [class^="ico-"],
.rules-toggle svg[class^="ico-"] {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.28s var(--molla),
              opacity 0.28s ease;
}

/* La freccia entra nella porta, e torna indietro quando si lascia. */
.btn:hover:not(:disabled) .ico-freccia,
.btn:focus-visible .ico-freccia { transform: translateX(2.5px); }
.btn:active:not(:disabled) .ico-freccia { transform: translateX(4px); }

/* ⚠️  Il libro si sfoglia. `rotateY` da solo NON si vede: senza prospettiva il
       browser lo appiattisce in uno schiacciamento orizzontale di due pixel,
       che a 16px non se ne accorge nessuno. Serve la prospettiva sul simbolo,
       e allora la rotazione diventa una pagina che si alza davvero. */
svg:has(.ico-pagina-sx) { perspective: 60px; }
.ico-pagina-sx { transform-origin: right center; }
.ico-pagina-dx { transform-origin: left center; }
.btn:hover:not(:disabled) .ico-pagina-sx,
.btn:focus-visible .ico-pagina-sx { transform: rotateY(-42deg); }
.btn:hover:not(:disabled) .ico-pagina-dx,
.btn:focus-visible .ico-pagina-dx { transform: rotateY(42deg); }

/* ⚠️  Il piu' gira di un quarto e le due stanghette si allungano. Ingrandirle
       e basta, com'era prima, voleva dire tre pixel su una linea spessa due:
       l'animazione c'era e non si vedeva, che e' come non averla. */
.btn:hover:not(:disabled) .ico-piu-v,
.btn:focus-visible .ico-piu-v { transform: rotate(90deg) scaleX(1.3); }
.btn:hover:not(:disabled) .ico-piu-o,
.btn:focus-visible .ico-piu-o { transform: rotate(90deg) scaleY(1.3); transition-delay: 0.04s; }

/* Lo scudo della moderazione si alza, come chi para un colpo. */
.btn:hover:not(:disabled) .ico-scudo,
.btn:focus-visible .ico-scudo { transform: translateY(-1.5px) scale(1.12); }

/* La spunta si ridisegna da capo invece di comparire e basta. */
.ico-spunta { stroke-dasharray: 24; stroke-dashoffset: 0; }
.btn:hover:not(:disabled) .ico-spunta,
.btn:focus-visible .ico-spunta { animation: spunta-si-disegna 0.5s ease forwards; }
@keyframes spunta-si-disegna {
  from { stroke-dashoffset: 24; }
  to   { stroke-dashoffset: 0; }
}

/* Il dischetto: lo sportello si apre e l'etichetta scende. */
/* Lo sportello e l'etichetta del dischetto se ne sono andati insieme al
   pulsante «Salva»: adesso si salva da solo e al suo posto c'e' una spia. */

/* ─── L'interruttore del tema ──────────────────────────────────────────────── */
/* Tre bottoni tondi dentro una pastiglia, invece di tre rettangolini smussati:
   sono tre scelte alternative e la forma tonda lo dice meglio di un quadrato. */
.theme-toggle-btn { padding: var(--s0); gap: var(--s0); }

/* ⚠️  44px, il minimo da pollice, e la misura sta QUI perche' e' questa la
   regola che vince: c'e' un altro `.theme-icon-slot` 2800 righe piu' su, stessa
   specificita', e a parita' vince l'ultimo. Scriverla la' non si vedeva — e non
   faceva fallire niente, perche' il test dei bersagli parte dai `<button>` e
   questi sono `<label>`. Misurati a 32x30 su un telefono, coi centri a 34px:
   un polpastrello ne copriva due, e sbagliare qui cambia il tema.
   A crescere e' solo l'AREA: il simbolo dentro resta di 17px. */
.theme-icon-slot {
  width: 44px;
  height: 44px;
  border-radius: var(--raggio-pulsante);
}


/* Il sole gira: i raggi attorno al cuore, che resta fermo e respira. */
.theme-icon-sun:hover .ico-raggi,
input[name="tema-modo"]:checked + .theme-icon-sun .ico-raggi {
  animation: raggi-girano 9s linear infinite;
}
.theme-icon-sun:hover .ico-sole-cuore,
input[name="tema-modo"]:checked + .theme-icon-sun .ico-sole-cuore {
  animation: cuore-respira 2.6s ease-in-out infinite;
}
@keyframes raggi-girano { to { transform: rotate(360deg); } }
@keyframes cuore-respira { 50% { transform: scale(1.14); } }

/* La luna sta ferma, la scintilla brilla: e' quello che fanno le stelle. */
.theme-icon-moon:hover .ico-scintilla,
input[name="tema-modo"]:checked + .theme-icon-moon .ico-scintilla {
  animation: scintilla 2.2s ease-in-out infinite;
}
@keyframes scintilla {
  0%, 100% { transform: scale(1) rotate(0deg); opacity: 1; }
  45%      { transform: scale(0.45) rotate(35deg); opacity: 0.45; }
}

/* L'automatico e' un sole che sorge: sorge davvero, e i raggi lo seguono. */
.theme-icon-auto:hover .ico-sole-alba,
input[name="tema-modo"]:checked + .theme-icon-auto .ico-sole-alba {
  animation: sole-sorge 3.2s ease-in-out infinite;
}
.theme-icon-auto:hover .ico-raggi-alba,
input[name="tema-modo"]:checked + .theme-icon-auto .ico-raggi-alba {
  animation: raggi-alba 3.2s ease-in-out infinite;
}
@keyframes sole-sorge { 50% { transform: translateY(-1.6px); } }
@keyframes raggi-alba { 50% { transform: translateY(-1.1px); opacity: 0.55; } }

/* ⚠️  Chi ha chiesto meno movimento non vuole un'icona che gira all'infinito
       nella barra in alto. Le transizioni al passaggio restano: sono brevi e
       rispondono a un gesto, non partono da sole. */
@media (prefers-reduced-motion: reduce) {
  .theme-icon-sun .ico-raggi,
  .theme-icon-sun .ico-sole-cuore,
  .theme-icon-moon .ico-scintilla,
  .theme-icon-auto .ico-sole-alba,
  .theme-icon-auto .ico-raggi-alba,
  .btn .ico-spunta {
    animation: none !important;
  }
}

/* ─── Il pannello delle preferenze ─────────────────────────────────────────── */
/* Era una pila di riquadri azzurri pieni sopra una carta quasi bianca: la
   testata azzurra, i campi azzurri, il pulsante di uscita azzurrino. Tutto
   dello stesso peso, e nessuna gerarchia. Adesso il colore lo porta solo quello
   che si tocca; il resto sono parole. */
.popover-header {
  padding: var(--s0) var(--s0) 0;
  background: none;
}

.popover-title {
  font-size: var(--t2);
  letter-spacing: 0.16em;
  color: var(--text-muted);
}

.profile-popover { gap: var(--s4); }

.popover-campo { gap: var(--s1); }

.popover-label {
  font-size: var(--t4);
  font-weight: 650;
  color: var(--text-primary);
}

/* ⚠️  `opacity: 1` non e' di troppo. Questa regola da' un colore proprio, ma
       l'aiuto e' anche nell'elenco dei «testi di spiegazione», che porta
       `opacity: var(--opacita-spiegazione)`: senza spegnerla, il colore nuovo
       veniva sbiadito dal 66% ereditato e il contrasto scendeva a 3,06:1 nel
       chiaro e 2,48:1 nello scuro. Il colore lo mette questa regola, quindi la
       trasparenza qui non serve piu'. */
.popover-aiuto {
  font-size: var(--t3);
  line-height: 1.45;
  color: var(--text-muted);
  opacity: 1;
}

/* L'uscita non e' una preferenza: sta staccata, e non porta il colore
   dell'accento addosso perche' non e' l'azione che ci si aspetta da qui. */
.btn-logout-popover {
  margin-top: var(--s0);
  min-height: 44px;
  font-size: var(--t3);
  border-radius: var(--raggio-pulsante);
  color: var(--su-tinto-rosso);
  background: var(--tinto-rosso);
  box-shadow: none;
  transition: background var(--passo), transform 0.07s;
}
.btn-logout-popover:hover { background: var(--tinto-rosso-sopra); box-shadow: none; }
.btn-logout-popover:active { transform: scale(0.975); box-shadow: none; }

/* ─── Le tendine ───────────────────────────────────────────────────────────── */
/* Il menu del browser non si veste: si apre come vuole il sistema, con la sua
   riga blu. Questo si veste. Il `<select>` vero resta sotto, nascosto, e tiene
   il valore: qui c'e' solo l'aspetto. */
.tendina-nativa {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.tendina {
  position: relative;
}

.tendina-bottone {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  width: 100%;
  min-height: 44px;
  padding: var(--s2) var(--s3);
  font-family: var(--ui);
  font-size: var(--t4);
  font-variation-settings: 'ROND' 40;
  text-align: left;
  cursor: pointer;
  color: var(--text-primary);
  background: var(--incavo);
  border-radius: var(--raggio-riga);
  transition: background var(--passo), color var(--passo);
}

.tendina-bottone:hover:not(:disabled) { background: var(--surface-2); }

.tendina-bottone:disabled {
  cursor: default;
  opacity: 0.55;
}

.tendina-bottone:focus-visible {
  outline: 2px solid var(--accento-segno);
  outline-offset: 2px;
}

.tendina-valore {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* La freccia gira quando la tendina e' aperta: e' il simbolo che si anima,
   non il pulsante che si sposta. */
.tendina-freccia {
  flex: none;
  color: var(--text-muted);
  transform-origin: center;
  transition: transform 0.22s var(--molla), color var(--passo);
}
.tendina-bottone[aria-expanded="true"] .tendina-freccia {
  transform: rotate(180deg);
  color: var(--text-primary);
}

.tendina-lista {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 20;
  max-height: 248px;
  overflow-y: auto;
  /* Stessa ragione di `.modal-body`: la lista delle nove lingue scorre, e
     arrivati in fondo il dito non deve passare alla pagina sotto. */
  overscroll-behavior: contain;
  padding: var(--s1);
  display: grid;
  gap: var(--s0);
  /* ⚠️  Qui c'era `background: var(--carta-tendina, var(--rilievo))`, e non
         dipingeva niente due volte: `--carta-tendina` non e' dichiarata da
         nessuna parte, e la regola del vetro piu' in basso (stessa specificita,
         piu' avanti nel foglio) riscrive comunque colore e immagine. Restava
         li' a far credere che esistesse un colore della tendina da toccare.
         Il fondo della tendina lo decide `.tendina-lista` dentro l'elenco dei
         contenitori di vetro, e basta. */
  border-radius: var(--raggio-contenitore);
  box-shadow: 0 10px 28px -12px rgba(0, 0, 0, 0.45),
              0 2px 6px -3px rgba(0, 0, 0, 0.25);
  animation: tendina-scende 0.16s ease;
}
.tendina-lista[hidden] { display: none; }

@keyframes tendina-scende {
  from { opacity: 0; transform: translateY(-5px); }
}

.tendina-voce {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  width: 100%;
  /* 44px, il bersaglio minimo da pollice: queste voci si toccano col dito come
     tutto il resto, e 38px non stanno ne' nella scala degli spazi ne' sotto
     quella regola. Misurate erano 38,5px. */
  min-height: 44px;
  padding: var(--s2) var(--s2);
  font-family: var(--ui);
  font-size: var(--t4);
  font-variation-settings: 'ROND' 40;
  text-align: left;
  cursor: pointer;
  color: var(--text-primary);
  background: none;
  border-radius: var(--raggio-voce);
  transition: background var(--passo), color var(--passo);
}

/* `.attiva` e' la voce sotto la freccia della tastiera: deve vedersi come il
   passaggio del mouse, se no chi naviga da tastiera non sa dov'e'. */
.tendina-voce:hover,
.tendina-voce.attiva { background: var(--incavo); }

.tendina-voce.scelta {
  color: var(--su-selezione);
  background: var(--selezione);
  font-weight: 650;
}

.tendina-voce-testo {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* La spunta occupa il suo posto anche quando non si vede, se no le voci
   ballano di due pixel quando si cambia scelta. */
.tendina-voce-spunta {
  flex: none;
  font-size: var(--t4);
  opacity: 0;
}
.tendina-voce.scelta .tendina-voce-spunta { opacity: 1; }

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

/* Quando sotto non c'e' spazio la tendina si apre in su: e' l'ultimo campo del
   pannello, e in fondo allo schermo la lista finirebbe fuori dalla finestra. */
.tendina-lista.verso-alto {
  top: auto;
  bottom: calc(100% + 6px);
  animation: tendina-sale 0.16s ease;
}
@keyframes tendina-sale {
  from { opacity: 0; transform: translateY(5px); }
}
@media (prefers-reduced-motion: reduce) {
  .tendina-lista.verso-alto { animation: none; }
}

/* ─── Anche questi sono pulsanti ───────────────────────────────────────────── */
/* Il profilo e il gruppo del tema stavano fermi mentre tutto il resto si
   sollevava: sembravano due riquadri appoggiati sulla barra, non due cose da
   premere. Stessa curva, stesso sollevamento, stessa spinta degli altri. */
.btn-profile,
.theme-toggle-btn {
  border-radius: var(--raggio-pulsante);
  /* ⚠️  `--passo` vale gia' `0.1s ease`: scrivendogli accanto un altro `ease`
         la dichiarazione diventava `0.1s ease ease`, cioe' NON VALIDA — e una
         transizione non valida non si limita a ignorare quel pezzo, azzera
         tutta la proprieta'. Risultato: `all 0s`, cioe' nessuna animazione da
         nessuna parte su questi pulsanti, in silenzio. (Il token si chiamava
         `--transition-fast`, che era un alias di questo ed e' sparito con il
         resto del ponte: la trappola e' la stessa, il nome no.) */
  transition: transform 0.16s var(--molla),
              box-shadow 0.2s ease,
              background var(--passo), color var(--passo);
}

/* Il gruppo non si schiaccia tutto: si schiaccia la casella che si preme, se
   no premendo «chiaro» si muove anche «scuro», che non c'entra niente. */
.theme-icon-slot:active { transform: scale(0.9); }
.theme-icon-slot { transition: color var(--passo), background var(--passo), transform 0.07s ease; }

/* La freccia del profilo gira quando il pannello e' aperto: e' il simbolo che
   dice lo stato, non un secondo indicatore da qualche altra parte. */
.profile-chevron {
  transform-origin: center;
  transition: transform 0.24s var(--molla), color var(--passo);
}
.btn-profile[aria-expanded="true"] .profile-chevron {
  transform: rotate(180deg);
  color: var(--text-primary);
}

/* ─── La tendina entra e esce ──────────────────────────────────────────────── */
/* Entra scendendo con un rimbalzo, esce risalendo. L'uscita ha bisogno che
   l'elemento resti visibile mentre finisce: e' `tendinaApri()` che tiene
   `hidden` per un attimo in piu' e poi lo rimette. */
.tendina-lista {
  animation: tendina-entra 0.24s var(--molla);
  transform-origin: top center;
}

@keyframes tendina-entra {
  from { opacity: 0; transform: translateY(-8px) scale(0.98); }
  60%  { opacity: 1; transform: translateY(1px) scale(1.002); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.tendina-lista.in-uscita {
  animation: tendina-esce 0.13s ease forwards;
  pointer-events: none;
}

@keyframes tendina-esce {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to   { opacity: 0; transform: translateY(-7px) scale(0.985); }
}

/* Aprendosi verso l'alto il verso si ribalta: scende sarebbe sbagliato. */
.tendina-lista.verso-alto {
  transform-origin: bottom center;
  animation: tendina-entra-su 0.24s var(--molla);
}
@keyframes tendina-entra-su {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  60%  { opacity: 1; transform: translateY(-1px) scale(1.002); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.tendina-lista.verso-alto.in-uscita { animation: tendina-esce-su 0.13s ease forwards; }
@keyframes tendina-esce-su {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to   { opacity: 0; transform: translateY(7px) scale(0.985); }
}

/* ─── La barra di scorrimento della tendina ────────────────────────────────── */
/* Quella di sistema dentro un riquadro tondo di 250px e' larga e squadrata:
   qui e' sottile, tonda e staccata dal bordo, e si accende solo quando serve. */
.tendina-lista {
  scrollbar-width: thin;
  scrollbar-color: var(--barra) transparent;
}
.tendina-lista::-webkit-scrollbar { width: 8px; }
.tendina-lista::-webkit-scrollbar-track {
  background: transparent;
  margin: 6px 0;
}
.tendina-lista::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--barra) 70%, transparent);
  border: 2px solid transparent;
  background-clip: content-box;
  border-radius: 9999px;
}
.tendina-lista::-webkit-scrollbar-thumb:hover {
  background: var(--barra-viva);
  background-clip: content-box;
}

@media (prefers-reduced-motion: reduce) {
  .tendina-lista,
  .tendina-lista.verso-alto,
  .tendina-lista.in-uscita { animation: none; }
}

/* ⚠️  Qui c'era un secondo blocco `.btn-aspetto`, rimasto da quando il
       pulsante stava nella barra: maiuscolo, 11px, fondo da campo e uno
       stato aperto riempito d'accento. Continuava ad applicarsi insieme a
       quello vero piu' sotto, e lo stato aperto era l'unico pulsante della
       pagina fuori dalla scala. L'ha trovato il test della scala. */

/* Le gocce di colore si accendono a una a una, come chi prova i colori sulla
   tavolozza prima di dipingere. La tavolozza sta ferma. */
.aspetto-icona { flex: none; }
.ico-goccia {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0.55;
}
.btn-aspetto:hover .ico-goccia,
.btn-aspetto[aria-expanded="true"] .ico-goccia {
  animation: goccia-si-accende 1.8s ease-in-out infinite;
}
.btn-aspetto:hover .ico-goccia-2,
.btn-aspetto[aria-expanded="true"] .ico-goccia-2 { animation-delay: 0.12s; }
.btn-aspetto:hover .ico-goccia-3,
.btn-aspetto[aria-expanded="true"] .ico-goccia-3 { animation-delay: 0.24s; }
.btn-aspetto:hover .ico-goccia-4,
.btn-aspetto[aria-expanded="true"] .ico-goccia-4 { animation-delay: 0.36s; }

@keyframes goccia-si-accende {
  0%, 100% { opacity: 0.5; transform: scale(1); }
  40%      { opacity: 1;   transform: scale(1.32); }
}

/* ⚠️  Qui c'era anche una media query che nascondeva `.nav-aspetto-testo` e
       stringeva il pulsante su telefono: serviva quando stava nella barra.
       `.nav-aspetto-testo` non esiste piu' nel markup da quando il pulsante e'
       sceso nel pannello, quindi quella regola nascondeva una classe che non
       c'e' e restringeva una voce che ha tutta la larghezza del pannello. */

/* ─── Il dialogo dell'aspetto ──────────────────────────────────────────────── */
.aspetto-dialogo { max-width: 460px; }

.aspetto-corpo {
  display: grid;
  gap: var(--s4);
  /* ⚠️  `overflow: visible` perche' qui dentro ci sono due tendine, e il corpo
         del dialogo scorre di suo: senza, la lista aperta veniva tagliata sul
         bordo come succedeva nel pannello del profilo. */
  overflow: visible;
}

@media (prefers-reduced-motion: reduce) {
  .ico-goccia { animation: none !important; }
  .btn-aspetto:hover { transform: none; }
}

/* ─── Il dialogo dell'invito ───────────────────────────────────────────────── */
/* Le voci riusano il vocabolario delle schede del wizard — `.server-card-info`,
   `.server-card-icon`, `.server-card-name`, `.server-card-meta` e il pulsante
   `.server-invite-btn` — invece di inventarne uno nuovo: e' lo stesso oggetto
   («un server, e cosa ci puoi fare»), disegnato nello stesso modo. E' anche la
   ragione per cui `.server-invite-btn` non e' diventata una classe morta quando
   le schede senza bot sono sparite dall'elenco: si e' spostata qui. */
.invito-dialogo { max-width: 460px; }

.invito-lista {
  display: grid;
  gap: var(--s2);
  margin-top: var(--s3);
}

.invito-voce {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s2) var(--s3);
  background: var(--incavo);
  border-radius: var(--radius);
  /* ⚠️  E' una voce di griglia, e una traccia ha per minimo il proprio
     CONTENUTO: senza questa riga la voce non si stringeva sotto la larghezza
     del nome piu' lungo. Misurato a 375px: la riga usciva di 79px dal corpo del
     dialogo e il tasto d'invito finiva 83px oltre il bordo destro — cioe' fuori
     dallo schermo, e il tasto d'invito e' l'unica cosa per cui questo dialogo
     esiste. E' la stessa ragione per cui altrove si scrive `minmax(0, 1fr)` e
     non `1fr`. Da qui in giu' il resto si stringe da solo: `.server-card-info`
     ha gia' `min-width: 0` e il nome ha gia' i puntini. */
  min-width: 0;
}

.invito-nota {
  margin-top: var(--s4);
  font-size: var(--t3);
  color: var(--text-muted);
}

.invito-vuoto {
  padding: var(--s5) var(--s3);
  text-align: center;
  font-size: var(--t4);
  color: var(--text-secondary);
}

/* ⚠️  Qui c'era «I comandi della barra si vedono», una stesura INTERA del
       profilo e del gruppo del tema costruita sui token `--su-pulsante-barra` e
       `--pulsante-barra`. Non dipingeva piu' niente: `85d666d` l'ha rifatta da
       capo piu' in basso («i due della barra presi come "Sostienici": una
       velatura del proprio colore INVECE DI un riempimento pieno») e ha scritto
       la versione nuova accanto alla vecchia invece che al suo posto. Tutte e
       dieci le sue regole erano scavalcate una per una, a parita' di
       specificita', dal blocco che vince — misurato togliendole dal foglio vivo
       e confrontando lo stile calcolato di 580 elementi nei due temi: zero
       differenze, con un controllo a vuoto che dava pure zero.

       Se ne e' andato con lei anche il commento su `[aria-expanded="true"]`, che
       spiegava di doverlo ripetere per battere una regola scritta piu' in alto:
       quella regola non c'e' piu' (stessa pulizia), quindi la ragione non vale
       piu' e un commento che descrive una cascata inesistente e' peggio di
       nessun commento. Lo stato aperto un colore suo ce l'ha lo stesso, ma
       perche' e' una scelta, non per scavalcare qualcuno: sta nel blocco vivo. */

/* ─── L'aspetto, come voce del pannello ────────────────────────────────────── */
/* Non sta piu' nella barra: e' una preferenza come le altre, e la barra non e'
   il posto delle preferenze. */
.btn-aspetto {
  display: flex;
  align-items: center;
  gap: var(--s2);
  width: 100%;
  min-height: 44px;
  padding: var(--s2) var(--s3);
  font-family: var(--ui);
  font-size: var(--t4);
  font-variation-settings: 'ROND' 40;
  text-align: left;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  cursor: pointer;
  color: var(--su-tinto);
  background: var(--tinto);
  border-radius: var(--raggio-riga);
  transition: background var(--passo), color var(--passo), transform 0.07s ease;
}
.btn-aspetto:hover,
.btn-aspetto[aria-expanded="true"] { background: var(--tinto-sopra); color: var(--su-tinto); }
.btn-aspetto:active { transform: scale(0.985); }
.btn-aspetto:focus-visible { outline: 2px solid var(--accento-segno); outline-offset: 2px; }

/* ─── Il pannello del profilo entra come le tendine ────────────────────────── */
/* Ogni cosa che si apre in questa pagina si apre allo stesso modo: scende,
   sfuma, e rimbalza appena. Prima il pannello compariva di colpo mentre le
   tendine dentro di lui erano animate, che era la cosa piu' strana di tutte. */
.profile-popover {
  animation: tendina-entra 0.22s var(--molla);
  transform-origin: top right;
}
.profile-popover.in-uscita {
  animation: tendina-esce 0.13s ease forwards;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .profile-popover,
  .profile-popover.in-uscita { animation: none; }
}

/* ─── I contenitori si staccano dal cielo ──────────────────────────────────── */
/* Col fondo che cambia colore tutto il giorno, il solo colore non basta piu' a
   distinguere un pannello da quello che ci sta dietro: al tramonto la carta e
   il cielo si somigliano. Tre cose insieme lo risolvono, e nessuna e' un bordo:

   1. l'OMBRA, che non dipende dal colore di sotto ma dalla luce, quindi stacca
      il pannello a qualunque ora;
   2. la SFOCATURA di quello che passa dietro, che e' il segnale che leggiamo
      come «vetro» e che nessun colore piatto puo' dare;
   3. la TRASPARENZA, che fa vedere il cielo e la pioggia attraverso il
      pannello e lo lega alla pagina invece di posarcelo sopra.

   Quanta trasparenza si puo' dare l'ha detto la misura, non il gusto: i testi
   sulle schede reggono 4,93:1 anche con il pannello COMPLETAMENTE trasparente,
   su tutto il giro dell'anno e in tutti e due i temi. L'86% e' larghissimo. */
.pannello,
.profile-popover,
.modal-dialog,
.tendina-lista,
.menu-contestuale {
  /* Tre strati, dal davanti al dietro: la carta velata, il cielo SGRANATO, e
     quello che passa davvero dietro al pannello, sfocato. Il quadro del cielo
     e' agganciato alla finestra, quindi ogni pannello mostra da solo il pezzo
     che ha dietro senza calcoli di posizione.

     ⚠️  Scritto per esteso e non con la scorciatoia `background:`. In un elenco
         di strati SOLO L'ULTIMO puo' portare un colore, e mettendo la carta
         velata come primo strato l'intera dichiarazione diventava non valida:
         il pannello restava senza nessuno sfondo, e il quadro del cielo non si
         vedeva per niente. La carta qui e' un gradiente di un colore solo,
         cioe' un'immagine, e sta davanti come deve. */
  background-color: transparent;
  background-image:
    linear-gradient(color-mix(in srgb, var(--rilievo) var(--pannello-alfa), transparent),
                    color-mix(in srgb, var(--rilievo) var(--pannello-alfa), transparent)),
    var(--vetro, none);
  background-size: auto, 100vw 100vh;
  background-position: 0 0, 0 0;
  background-repeat: repeat, no-repeat;
  background-attachment: scroll, fixed;
  /* ⚠️  `pixelated` e' tutto il punto: il quadro e' piccolo e viene ingrandito,
         e senza questo il browser lo interpola e torna una sfumatura liscia. */
  image-rendering: pixelated;
  box-shadow: var(--ombra-pannello);
  -webkit-backdrop-filter: var(--sfocatura-pannello);
  backdrop-filter: var(--sfocatura-pannello);
}

/* Il testo dentro i pannelli non c'entra niente con la sgranatura: `pixelated`
   si eredita alle immagini, e le icone dei server sono immagini. */
.pannello img,
.profile-popover img,
.modal-dialog img {
  image-rendering: auto;
}

/* La tendina sta gia' sopra un pannello: la sua ombra e' piu' corta, se no
   sembra staccata dalla pagina invece che dal campo che l'ha aperta. */
.tendina-lista {
  box-shadow: 0 12px 30px -16px rgba(12, 22, 40, 0.5),
              0 2px 8px -6px rgba(12, 22, 40, 0.3);
}

/* ⚠️  Il velo del dialogo NON si sfoca: sfocando il fondo di una finestra che
       ne contiene un'altra gia' sfocata, Chrome le compone una dentro l'altra e
       il dialogo si vela di grigio. Qui basta il velo. */
.modal-backdrop {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* ─── I comandi della barra, come «Sostienici» ─────────────────────────────── */
/* Stessa idea del pulsante di Patreon: una velatura del proprio colore invece
   di un riempimento pieno, e il colore anche nel testo. Erano pieni di blu e
   pesavano come due mattoni accanto a una pastiglia leggera. */
.btn-profile,
.theme-toggle-btn {
  color: var(--su-tinto);
  background: var(--tinto);
  box-shadow: none;
}

.btn-profile:hover,
.theme-toggle-btn:hover {
  color: var(--su-tinto);
  background: var(--tinto-sopra);
  transform: translateY(-1.5px);
  box-shadow: 0 6px 16px -8px currentColor;
}

.btn-profile:active { transform: translateY(0) scale(0.955); transition-duration: 0.07s; }

.btn-profile[aria-expanded="true"] { color: var(--accento-segno); }
.profile-chevron { color: color-mix(in srgb, var(--accento-segno) 70%, transparent); }
.btn-profile[aria-expanded="true"] .profile-chevron { color: var(--accento-segno); }

/* Dentro la pastiglia velata la casella scelta e' l'unica cosa piena: e' il
   solo modo di dire «questa» senza aggiungere un contorno. E si guida dallo
   stato del radio, non da una classe messa a mano dal codice: cosi' la casella
   accesa e quella che il browser considera scelta non possono divergere. */
/* 78% e non 58%: le caselle non scelte erano a 3,08:1 sul chiaro e 3,39 sullo
   scuro. Per un simbolo la soglia e' 3, quindi passavano — ma senza un filo
   di margine e spente da vedere. A 78% fanno 6,31 e 4,89. */
.theme-icon-slot { color: color-mix(in srgb, var(--accento-segno) 78%, transparent); }
.theme-icon-slot:hover {
  color: var(--su-tinto);
  background: var(--tinto-sopra);
}
input[name="tema-modo"]:checked + .theme-icon-slot {
  color: var(--su-accento);
  background: var(--accento-pieno);
}
input[name="tema-modo"]:focus-visible + .theme-icon-slot {
  outline: 2px solid var(--accento-segno);
}

@media (prefers-reduced-motion: reduce) {
  .btn-profile:hover,
  .theme-toggle-btn:hover { transform: none; }
}

/* Il cuore di «Sostienici» resta rosso: e' l'unica cosa che lo distingue dagli
   altri pulsanti tinti, ed e' una forma prima che un colore. */
.nav-patreon-heart-btn .patreon-heart-svg { color: var(--pericolo); }

/* ─── Gli ultimi due fuori scala ───────────────────────────────────────────── */
/* `componenti-gk.css` e' una copia identica di Guild Keeper e non si tocca: si
   sovrascrive qui, come gia' si fa per il fondo dei suoi pulsanti. Erano gli
   unici due riempimenti rimasti fuori dalla scala su tutta la pagina — `.btn` a
   7px 13px e `.field` a 9px 11px, numeri suoi che con la nostra griglia da 8
   non c'entrano niente. */
/* ⚠️  I selettori vanno ripetuti con l'elemento davanti: nel file copiato la
       regola e' `input.field, textarea.field, select.field` (0,1,1), che batte
       un `.field` semplice (0,1,0) anche stando piu' sopra. Con il solo
       `.field` i due campi delle impostazioni restavano a 9px 11px. */
.btn,
.field,
input.field,
textarea.field,
select.field {
  padding: var(--s2) var(--s3);
}

/* ⚠️  Il posto per la freccia, e va DOPO la regola qui sopra: `select.field` e
       `select.form-select` valgono tutte e due (0,1,1), quindi a decidere è
       l'ordine. Messa più in alto veniva riportata a 12px e il testo di
       un'opzione lunga passava sotto alla freccia — misurato in un browser
       vero, 12px contro i 23px che la freccia occupa. */
select.form-select,
select.field {
  padding-right: var(--s7);
}

/* E la loro distanza interna, che li' vale 6: fra i due gradini della scala,
   quindi fuori da tutti e due. */
.btn,
.field {
  gap: var(--s2);
}
