/* corax — design system "Digital Workshop" (Refined Brutalist).
 *
 * Tradotto a mano dai token di docs/design/DESIGN.md. Niente Tailwind e niente CDN:
 * l'app è interna e deve avere lo stesso aspetto anche senza rete verso terzi.
 *
 * **Elevazione.** L'intenzione era «strati tonali e bordi, non ombre». Misurata il 24
 * agosto 2026, non era mai stata consegnata: `--outline-variant` su bianco dava **1,70:1**
 * di contrasto, sotto il 3:1 che serve perché un confine si veda. Il bordo c'era nel CSS e
 * non sullo schermo — ed è il motivo per cui l'interfaccia si leggeva «piatta».
 * Adesso l'elevazione la fanno tre cose insieme: un bordo che si vede (3,1:1), un'ombra
 * corta, e lo stacco fra il fondo della pagina e la superficie di lavoro.
 *
 * **Gerarchia della tipografia.** Il mono maiuscolo era su tutto: nav, pulsanti, etichette
 * dei campi, badge. Quando tutto urla, niente si distingue — le etichette dei form
 * risultavano più forti dei titoli. Ora il mono maiuscolo è **solo** dove è un dato o una
 * targhetta (badge, intestazioni di tabella, `.tech`, piè di pagina); pulsanti ed
 * etichette usano il font dell'interfaccia in tondo, che si legge come un comando.
 */

@import url("/static/fonts/fonts.css");

:root {
  /* ---- superfici: tre livelli, e si distinguono ------------------------- */
  --bg-main: #eef1f7;          /* il fondo: più staccato, o la card non emerge */
  --bg-surface: #ffffff;       /* la superficie di lavoro */
  --bg-inset: #f1f5f9;         /* l'incasso tecnico: log, codice, diff */
  --bg-subtle: #f8fafc;        /* zone di servizio dentro una superficie */
  --surface: #ffffff;
  --surface-container: #eaedff;
  --surface-container-high: #e2e7ff;

  /* ---- testo ------------------------------------------------------------ */
  --on-surface: #131b2e;
  --on-surface-variant: #434655;
  --text-muted: #5a6577;       /* era #64748b: 4,76:1. Ora 5,74:1 su bianco */
  --secondary: #515f74;

  /* ---- bordi: due, con due lavori diversi ------------------------------- */
  /* Il bordo **strutturale** delimita un componente e deve stare a 3:1 (WCAG 1.4.11).
     Quello **interno** separa righe dentro un componente già delimitato, e lì un tratto
     leggero è giusto: sono due esigenze diverse che prima avevano lo stesso colore. */
  --border: #a8aebf;           /* 3,08:1 su bianco — si vede */
  --border-subtle: #e2e6ee;    /* divisori interni */
  --outline: #737686;
  --outline-variant: #a8aebf;  /* retrocompatibile: puntava a #c3c6d7, 1,70:1 */

  /* ---- azione ----------------------------------------------------------- */
  --primary: #1d4ed8;
  --primary-hover: #1a43b8;
  --primary-active: #163a9e;
  --primary-container: #2563eb;
  --on-primary: #ffffff;
  --primary-fixed: #dbe1ff;
  --primary-soft: #eef2ff;     /* fondo tonale: nav attiva e pulsante secondario */
  --primary-soft-hover: #e0e7ff;
  --primary-border: #b9c6f5;   /* bordo del secondario: stessa famiglia del primario */
  --on-primary-soft: #1a3ea8;  /* 7,1:1 su #eef2ff */

  /* ---- stato: fondo tonale + testo scuro dello stesso tono -------------- */
  /* Prima erano tinte piene con testo bianco, e quattro su cinque non passavano il
     contrasto: ambra 2,15:1, verde 2,54:1, blu 3,68:1, rosso 3,76:1 (serve 4,5:1).
     Un badge illeggibile è una decorazione con l'aria di un'informazione. */
  --waiting-bg: #fef3c7;   --waiting-fg: #78350f;   --waiting-br: #fcd34d;
  --running-bg: #dbeafe;   --running-fg: #1e3a8a;   --running-br: #93c5fd;
  --success-bg: #d1fae5;   --success-fg: #065f46;   --success-br: #6ee7b7;
  --danger-bg:  #fee2e2;   --danger-fg:  #991b1b;   --danger-br:  #fca5a5;
  /* La linguetta delle segnalazioni: rossa piena, sempre. Non è un badge di stato — è
     l'unico posto della piattaforma da cui si dice che qualcosa non va, e deve trovarsi
     senza cercarlo. Misurati: bianco su questo rosso 6,47:1, e il rosso contro il fondo
     della pagina 5,72:1 (per il bordo di un comando ne servono 3). */
  --segnala-bg: #b91c1c;   --segnala-fg: #ffffff;   --segnala-hover: #991b1b;
  /* La striscia «il sistema è stato aggiornato»: **viola pieno**, che in questa tavolozza
     non è usato da niente altro — l'ambra è «aspetta», il blu è «in corso», il rosso è
     «segnala». Un avviso che riusasse uno di quei tre si leggerebbe come uno stato del
     lavoro, e questo non parla del lavoro: parla della pagina che si ha davanti.
     Misurati: bianco su #6d28d9 fa 7,04:1; il testo del pulsante bianco, #5b21b6 su
     bianco, 8,99:1. */
  --aggiorna-bg: #6d28d9;  --aggiorna-fg: #ffffff;  --aggiorna-br: #5b21b6;
  --aggiorna-azione-fg: #5b21b6;
  --neutral-bg: #e8ebf1;   --neutral-fg: #3f4a5c;   --neutral-br: #cbd2de;
  /* i vecchi nomi restano: sono usati anche fuori dai badge (punti, bordi, testi) */
  --status-waiting: #b45309;
  --status-running: #1d4ed8;
  --status-success: #047857;
  --status-error: #b91c1c;
  --error: #b91c1c;
  --error-container: #fee2e2;
  --on-error-container: #991b1b;
  --tertiary: #943700;
  --tertiary-container: #bc4800;
  --on-tertiary-container: #ffede6;

  /* ---- elevazione ------------------------------------------------------- */
  /* Corta e a bassa opacità: deve dire «questo sta sopra», non simulare un rilievo. */
  --shadow-1: 0 1px 2px rgba(19, 27, 46, 0.06), 0 1px 3px rgba(19, 27, 46, 0.04);
  --shadow-2: 0 2px 4px rgba(19, 27, 46, 0.07), 0 4px 10px rgba(19, 27, 46, 0.05);
  --shadow-3: 0 8px 24px rgba(19, 27, 46, 0.12);
  --shadow-primary: 0 1px 2px rgba(29, 78, 216, 0.30);

  /* ---- il fuoco si deve vedere ------------------------------------------ */
  /* Non c'era su nessun pulsante: chi naviga da tastiera non sapeva dov'era. */
  --focus: #1d4ed8;
  --focus-ring: 0 0 0 3px rgba(29, 78, 216, 0.35);

  /* ritmo 4px/8px */
  --xs: 0.25rem;
  --sm: 0.5rem;
  --md: 1rem;
  --lg: 1.5rem;
  --xl: 2.5rem;

  /* forme "soft-square" */
  --radius-sm: 0.3125rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --radius-pill: 9999px;

  --font-ui: "Hanken Grotesk", system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Un campo di testo largo mille pixel non si legge: la riga ottimale sta sotto i
     75 caratteri, e un nome di progetto non ne ha bisogno di più. */
  --field-max: 34rem;

}

/* ---------------------------------------------------------------- tema scuro */
/* **Solo i token, e nient'altro.** Tutto il foglio legge da qui, quindi il tema scuro è
 * una seconda dichiarazione delle stesse variabili: nessuna regola di componente sa che
 * esiste. È la ragione per cui valeva la pena mettere i colori in un posto solo.
 *
 * Segue il sistema (`prefers-color-scheme`), senza interruttore: un interruttore vuole
 * un posto dove ricordare la scelta, e questa è un'app interna dove il sistema operativo
 * la scelta l'ha già fatta.
 *
 * **Ogni coppia è stata calcolata, non scelta a occhio** — e il primo tentativo aveva lo
 * stesso difetto del tema chiaro: il bordo strutturale a 2,23:1, sotto il 3:1. Adesso
 * 3,27:1. Le altre undici coppie stanno fra 6,5:1 e 14:1.
 *
 * Nel tema scuro il primario si **inverte**: il pieno diventa chiaro e il testo scuro.
 * Un blu scuro con testo bianco su un fondo già scuro non si stacca da niente.
 */
@media (prefers-color-scheme: dark) {
  :root {
    --bg-main: #0f131a;
    --bg-surface: #171c26;
    --bg-inset: #1e242f;
    --bg-subtle: #1b212b;
    --surface: #171c26;
    --surface-container: #1e242f;
    --surface-container-high: #262d3a;

    --on-surface: #e6e9f0;          /* 14,04:1 */
    --on-surface-variant: #b6bccb;  /*  8,98:1 */
    --text-muted: #98a1b3;          /*  6,57:1 */
    --secondary: #b6bccb;

    --border: #626d80;              /*  3,27:1 — il minimo che si vede */
    --border-subtle: #2a3140;
    --outline: #7c8698;
    --outline-variant: #626d80;

    /* il pieno è chiaro e il testo scuro: 8,23:1 */
    --primary: #93b4ff;
    --primary-hover: #a8c2ff;
    --primary-active: #bcd0ff;
    --on-primary: #0d1b3d;
    --primary-soft: #1c2740;
    --primary-soft-hover: #24325a;
    --primary-border: #3a4b78;
    --on-primary-soft: #b9ccff;     /*  9,30:1 */
    --primary-fixed: #1c2740;

    --waiting-bg: #3a2c10; --waiting-fg: #fcd9a0; --waiting-br: #6b5220;
    --running-bg: #152340; --running-fg: #bcd3ff; --running-br: #2f4a80;
    --success-bg: #0f2c20; --success-fg: #a7e8c8; --success-br: #1f6047;
    --danger-bg:  #3a1518; --danger-fg:  #ffc0c0; --danger-br:  #7a2b30;
    /* Il rosso chiaro qui non va: #8c2020 contro il fondo scuro fa **1,97:1**, sotto il
       3:1 che serve al bordo di un comando — è il difetto che questo foglio ha già
       pagato due volte. #c0392b: testo 5,44:1, bordo 3,42:1. */
    --segnala-bg: #c0392b; --segnala-fg: #ffffff; --segnala-hover: #c94a3f;
    /* Al buio il viola pieno acceca: si scurisce il fondo e si schiarisce il testo.
       #ede9fe su #4c1d95 fa 8,4:1. */
    --aggiorna-bg: #4c1d95; --aggiorna-fg: #ede9fe; --aggiorna-br: #6d28d9;
    --aggiorna-azione-fg: #4c1d95;
    --neutral-bg: #242b36; --neutral-fg: #c9d0dd; --neutral-br: #3a4353;

    --status-waiting: #f0b45e;
    --status-running: #93b4ff;
    --status-success: #6ee7b7;
    --status-error: #ff9b9b;
    --error: #ff9b9b;
    --error-container: #3a1518;
    --on-error-container: #ffc0c0;
    --tertiary: #ffb596;
    --tertiary-container: #5a2400;
    --on-tertiary-container: #ffdbcd;

    /* Su fondo scuro un'ombra nera non si vede: l'elevazione la fanno il bordo e lo
       stacco fra i livelli di superficie. Le ombre restano, appena accennate, per il
       distacco della testata e dei pulsanti. */
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.40);
    --shadow-2: 0 2px 6px rgba(0, 0, 0, 0.45);
    --shadow-3: 0 8px 24px rgba(0, 0, 0, 0.55);
    --shadow-primary: 0 1px 2px rgba(0, 0, 0, 0.45);

    --focus: #93b4ff;
    --focus-ring: 0 0 0 3px rgba(147, 180, 255, 0.35);
  }
  /* Il diff: le due tinte del tema chiaro su fondo scuro diventano illeggibili. */
  .diff .add { background: rgba(110, 231, 183, 0.14); color: #a7e8c8; }
  .diff .del { background: rgba(255, 155, 155, 0.14); color: #ffc0c0; }
}

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

/* `hidden` deve vincere su qualunque `display` di questo foglio. Senza questa riga
   un elemento nascosto dal JS restava visibile se la sua classe dichiarava un
   display — è successo alla pastiglia «vicina all'handoff», accesa al 39%. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-main);
  color: var(--on-surface);
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.5;
}

/* ---------------------------------------------------------------- tipografia */
h1, h2, h3, h4 { font-family: var(--font-ui); margin: 0 0 var(--sm); }
/* Il testo dentro una card respirava troppo: due paragrafi di spiegazione facevano
   scorrere la pagina più della tabella che spiegavano. */
.card p { margin: 0 0 0.6rem; }
.card p:last-child { margin-bottom: 0; }
h1 { font-size: 24px; font-weight: 700; line-height: 1.25; letter-spacing: -0.015em; }
h2 { font-size: 17px; font-weight: 700; line-height: 1.35; letter-spacing: -0.005em; }
h3, h4 { font-size: 16px; font-weight: 600; }
p { margin: 0 0 var(--sm); }
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }

.mono, code, pre, time { font-family: var(--font-mono); }
code { font-size: 13px; }

/* etichetta tecnica in maiuscoletto: "voce del sistema" */
/* **Un'etichetta non è una targhetta.** In mono maiuscolo con la spaziatura larga
   pesava più dei titoli: nella stanza «CHI RISPONDE QUANDO QUALCUNO CHIEDE ALL'AI, E CON
   QUANTO SFORZO» era la riga più forte della pagina, sopra al titolo della stanza.
   Il mono maiuscolo resta dove è **un dato**: badge, intestazioni di tabella, `.tech`. */
.label {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--on-surface-variant);
}
/* Nella testata «admin» accanto all'icona della persona: là è un dato, non un'etichetta. */
.topbar .right .label { font-family: var(--font-mono); font-size: 12px; }
.muted { color: var(--text-muted); font-size: 13px; }
.tech { font-family: var(--font-mono); font-size: 12.5px; }
/* Un nome di variabile o un percorso dentro una frase: si distingue dal discorso senza
   diventare un blocco. Prima cambiava solo il font, e in mezzo a una riga si perdeva. */
p .tech, li .tech, td .tech, .muted .tech, summary .tech {
  background: var(--bg-inset);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  padding: 0.05em 0.35em;
}
.badge .tech, .btn .tech { background: none; border: none; padding: 0; }

/* ------------------------------------------------------------------- header */
.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--md);
  flex-wrap: wrap;
  padding: 0.6rem var(--lg);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-subtle);
  /* La testata è appiccicata in alto e passa sopra il contenuto che scorre: senza
     un'ombra il testo le passava sotto e per un istante si leggevano sovrapposti. */
  box-shadow: var(--shadow-1);
}
/* **Il marchio: il corvo e il nome.** Era un testo in grassetto blu, cioè niente: un
   nome che poteva essere di qualunque cosa. Il corvo mette da parte quello che trova e si
   ricorda dove — il mestiere di questa piattaforma, detto in una figura invece che in un
   paragrafo. Il disegno sta in `icons.html` (`marchio()`); qui ci sono solo i colori,
   perché il marchio deve girare col tema e non contro. */
.brand {
  font-size: 18px;
  font-weight: 700;
  color: var(--primary);
  letter-spacing: -0.01em;
  display: inline-flex;
  align-items: baseline;
}
.brand:hover { text-decoration: none; }
/* Il quadratino: è il pezzo che si riconosce nella colonna stretta e in una scheda del
   browser piccola, dove il nome per intero non si legge. `flex: none` perché in un
   contenitore flessibile un SVG si lascia schiacciare senza dirlo; `align-self: center`
   perché il resto della riga è allineato alla base, e una figura non ha una base. */
.brand .marchio {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  align-self: center;
  margin-right: 0.32em;
}
.brand .marchio .campo { fill: var(--primary); }
/* Il corvo e l'occhio sono lo stesso colore del fondo del quadratino: l'occhio è un buco,
   non un punto disegnato — così resta nitido anche a 16px, dove un punto chiaro su scuro
   si chiuderebbe. */
.brand .marchio .corvo { fill: var(--on-primary); }
.brand .marchio .occhio { fill: var(--primary); }
.brand .nome { font-family: var(--font-ui); font-weight: 600; color: var(--on-surface); }
/* Il numero di versione accanto al nome: si legge se lo si cerca, non compete col nome.
   Monospaziato perché è un dato, come il commit nel piè di pagina. */
.brand .rilascio {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: 0;
  margin-left: 0.4em;
}
.topbar nav { display: flex; gap: var(--md); flex-wrap: wrap; align-items: center; }
.topbar nav a {
  color: var(--secondary);
  font-weight: 500;
  padding: 0.3rem 0.55rem;
  border-radius: var(--radius-sm);
}
.topbar nav a:hover { color: var(--on-surface); background: var(--bg-inset); text-decoration: none; }
.topbar nav a:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
/* «Dove sono» con il fondo, non con una sottolineatura di 2px: la sottolineatura si
   perdeva fra otto voci, e su schermo stretto finiva su due righe scollegata dal testo. */
.topbar nav a[aria-current="page"] {
  color: var(--primary);
  background: var(--primary-soft);
  font-weight: 700;
}
.topbar .right { display: flex; align-items: center; gap: var(--sm); }
/* **Il pallino delle notifiche, come sull'icona di un'applicazione.** Era una pastiglia
   azzurra: azzurro in questa tavolozza vuol dire «in corso», ed è lo stesso colore che
   segna un run che sta lavorando — un numero da leggere si confondeva con uno stato del
   lavoro. Rosso, tondo, e alto quanto è largo: è la forma che chiunque ha già imparato
   sul telefono, e non ha bisogno di essere spiegata.

   Misurato: bianco su #b91c1c fa 6,47:1. */
.badge-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 0.3rem;
  border-radius: 9999px;
  background: var(--segnala-bg);
  color: var(--segnala-fg);
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  /* Un numero non va a capo e non si stringe: è la cosa più corta della riga. */
  flex: 0 0 auto;
  white-space: nowrap;
}

/* ricerca in testata */
.search { position: relative; display: flex; align-items: center; }
.search input { padding-right: 2rem; width: 14rem; max-width: 40vw; }
.search .icon { position: absolute; right: var(--sm); color: var(--secondary); }

/* -------------------------------------------------------------------- layout */
/* **Il guscio: menu a sinistra, contenuto a destra, contesto in fondo a destra.**
 *
 * Prima era una colonna sola larga 1100px, centrata, con tutto impilato: su uno schermo
 * da 1440 avanzavano 340px per lato e ogni pagina chiedeva di scorrere per arrivare alla
 * cosa che serviva. Le sezioni non sono tutte uguali — alcune si **fanno** e altre si
 * **consultano** — e impilate diventavano indistinguibili.
 */
.shell { display: flex; align-items: stretch; min-height: 100vh; }
.colonna { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.sidebar {
  width: 15rem;
  flex: 0 0 15rem;
  display: flex;
  flex-direction: column;
  gap: var(--md);
  padding: var(--md);
  background: var(--bg-surface);
  border-right: 1px solid var(--border-subtle);
  /* Fissa: una pagina lunga non deve far perdere il menu. */
  position: sticky;
  top: 0;
  align-self: flex-start;
  height: 100vh;
  overflow-y: auto;
}
.sidebar .brand { font-size: 20px; padding: 0 var(--xs); }
.sidebar .search { width: 100%; }
.sidebar .search input { width: 100%; max-width: none; }
/* La testa della colonna: il marchio e il pulsante che la stringe, sulla stessa riga. */
.testa-colonna { display: flex; align-items: center; justify-content: space-between; gap: var(--sm); }
/* La ricerca ridotta a icona: c'è **solo** nella colonna stretta, dove la casella non
   entra. Larga sarebbe un secondo modo di fare la stessa cosa a due centimetri dal primo. */
.cerca-stretta { display: none; }
.stringi-colonna {
  display: none;                     /* sotto i 900px la colonna è già un pannello */
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
}
.stringi-colonna:hover { background: var(--bg-inset); color: var(--on-surface); }
.stringi-colonna:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
@media (min-width: 900px) { .con-js .stringi-colonna { display: inline-flex; } }

.sidebar nav { display: flex; flex-direction: column; gap: 2px; }
.sidebar nav a .icon { flex: 0 0 auto; color: var(--text-muted); }
.sidebar nav a[aria-current="page"] .icon { color: var(--primary); }
.sidebar nav a .voce { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar nav a {
  display: flex;
  align-items: center;
  gap: var(--sm);
  padding: 0.45rem 0.6rem;
  border-radius: var(--radius-sm);
  color: var(--on-surface-variant);
  font-weight: 500;
}
.sidebar nav a:hover { background: var(--bg-inset); color: var(--on-surface); text-decoration: none; }
.sidebar nav a:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
/* «Sei qui» con una barra piena: in colonna c'è lo spazio per dirlo senza gridarlo. */
.sidebar nav a[aria-current="page"] {
  background: var(--primary-soft);
  color: var(--primary);
  font-weight: 700;
  box-shadow: inset 3px 0 0 var(--primary);
}
.sidebar-fondo {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: var(--sm);
  padding-top: var(--md);
  border-top: 1px solid var(--border-subtle);
}
.chi-sono { display: flex; align-items: center; gap: var(--xs); color: var(--secondary); min-width: 0; }
.chi-sono:hover { color: var(--primary); text-decoration: none; }
.chi-sono .label { font-family: var(--font-mono); font-size: 12px; overflow: hidden; text-overflow: ellipsis; }
.sidebar-fondo .btn { margin-left: auto; }

.page { flex: 1; padding: 1.25rem var(--lg); max-width: 1280px; width: 100%; margin: 0 auto; }
.page.wide { max-width: none; }

/* Le due colonne del contenuto: ciò che si fa, e ciò che si consulta. Il rail non è una
   discarica — è largo 20rem, quindi ci sta un riquadro di stato e non una tabella. */
.con-rail { display: grid; grid-template-columns: minmax(0, 1fr) 20rem; gap: var(--lg); align-items: start; }
.principale { min-width: 0; }
.rail { min-width: 0; display: flex; flex-direction: column; }
.rail .card { margin-bottom: var(--md); }
.rail .card:last-child { margin-bottom: 0; }
/* Nel rail i titoli sono più piccoli: è materiale di consultazione, non deve competere
   con i titoli della colonna principale. */
.rail h2 { font-size: 15px; }
.rail .card-title { margin-bottom: var(--sm); }
.rail .muted { font-size: 12.5px; }

@media (max-width: 1180px) {
  /* Sotto questa larghezza il rail non ha più i suoi 20rem senza rubarli al contenuto:
     scende sotto, dove resta leggibile invece di diventare una colonna di due parole. */
  .con-rail { grid-template-columns: minmax(0, 1fr); }
}

/* Layout a piena altezza: la conversazione scorre **dentro** la sua zona e la barra di
   composizione resta ancorata in basso. Solo le pagine che lo chiedono (la sessione):
   le altre scorrono normalmente. */
/* **Una chat ha un solo scorrimento, ed è il suo.**
 *
 * Se scorre la pagina, il campo per scrivere se ne va in fondo al documento: per
 * rispondere si scorre, e mentre si scorre arriva un messaggio e ti sposta il punto in
 * cui stavi leggendo. Quindi il filo scorre dentro la propria zona, e la barra di
 * composizione resta ancorata: `body.fixed` blocca la finestra e **un solo elemento** —
 * `.thread` — ha `overflow-y: auto`.
 *
 * Da quando esiste il guscio (menu a sinistra, contesto a destra) la catena degli
 * antenati è più lunga, e ogni anello vuole `min-height: 0`: senza, un figlio flessibile
 * non può rimpicciolirsi sotto il proprio contenuto e lo scorrimento torna alla finestra
 * — silenziosamente, che è il modo in cui questo difetto si ripresenta. */
body.fixed { height: 100vh; overflow: hidden; }
body.fixed .shell { height: 100vh; }
body.fixed .colonna { min-height: 0; }
body.fixed .page { flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* `align-items: stretch` e non l'`start` del caso normale: in una griglia con `start` le
   colonne non si allungano, quindi il filo non si stringeva e la barra di composizione
   finiva **sotto** il bordo della finestra, clippata da `overflow: hidden` — invisibile e
   irraggiungibile. Non l'ho visto leggendo: l'ho misurato chiedendo alla pagina se il
   composer stesse dentro la finestra. */
body.fixed .con-rail { flex: 1; min-height: 0; align-items: stretch; }
body.fixed .principale { min-height: 0; display: flex; flex-direction: column; }
/* Il contesto a destra scorre per conto suo: è il secondo scroller, e non è la chat. */
body.fixed .rail { min-height: 0; overflow-y: auto; }
body.fixed .thread { min-height: 0; }
/* A piena altezza il contenuto tocca i bordi: il respiro lo dà la card, non la pagina —
   e un padding attorno a una chat che occupa lo schermo è spazio tolto al filo. */
body.fixed .page { padding: 0; max-width: none; }
body.fixed .con-rail { gap: 0; }
/* Il pannello degli strumenti della sessione è denso — selettori, form, cronologia — e
   in 20rem diventerebbe una colonna di etichette a capo. Là il rail è più largo. */
body.fixed .rail {
  width: 25rem;
  padding: var(--md);
  background: var(--bg-surface);
  border-left: 1px solid var(--border-subtle);
}
body.fixed .con-rail { grid-template-columns: minmax(0, 1fr) 25rem; }

/* due zone: conversazione fluida + strumenti a larghezza minima leggibile */
/* `.workshop`, `.zone-a` e `.zone-b` sono state togliere: erano la grammatica che la
   sessione si era scritta da sola prima che il guscio esistesse, e adesso dice la stessa
   cosa con `{% block rail %}` come tutte le altre pagine. Due modi di dire «contenuto a
   sinistra, contesto a destra» divergono al primo ritocco. */
@media (max-width: 900px) {
  /* Su schermo stretto le due zone si impilano, come previsto dal design. La barra di
     composizione resta però appiccicata in fondo alla finestra: dover scorrere una
     conversazione lunga per poter scrivere sarebbe una trappola. */
  body.fixed { height: auto; overflow: visible; }
  body.fixed .con-rail { grid-template-columns: minmax(0, 1fr); }
  body.fixed .rail { width: auto; border-left: none; border-top: 1px solid var(--border-subtle); }
  .composer { position: sticky; bottom: 0; z-index: 20; }
  .topbar { padding: var(--sm) var(--md); }
  .search input { width: 100%; max-width: none; }
  h1 { font-size: 20px; }   /* i titoli scalano del 15% */
  h2 { font-size: 16px; }
}

.footer {
  display: flex;
  justify-content: space-between;
  gap: var(--md);
  flex-wrap: wrap;
  padding: var(--sm) var(--lg);
  border-top: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
}

/* --------------------------------------------------------------- contenitori */
.card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  padding: 1rem 1.25rem;
  margin-bottom: 0.875rem;
  /* Il bordo da solo non bastava a staccarla dal fondo (1,70:1): serve l'ombra, e
     serve che il fondo della pagina sia più scuro della superficie. Le tre cose
     insieme sono l'elevazione che il design dichiarava e non consegnava. */
  box-shadow: var(--shadow-1);
}
.card > :last-child { margin-bottom: 0; }
/* **La riga di testo ha una lunghezza leggibile, e non è la larghezza della finestra.**
   Su uno schermo da 1600 un paragrafo dentro una card attraversava 1360px: oltre i ~75
   caratteri l'occhio perde il ritorno a capo e rilegge la riga sbagliata. Vale per la
   prosa, non per tabelle, log e diff — quelli hanno bisogno di tutto lo spazio. */
.card p, .card li, .card > .muted, .rail p, .rail li { max-width: 76ch; }
.card .inset, .card .table-wrap, .card table, .card pre { max-width: none; }
/* Il titolo di una card era un h2 sospeso nel bianco, alla stessa distanza dal contenuto
   suo e da quello della card precedente: tre riquadri di seguito diventavano un muro.
   Una riga sotto il titolo chiude la testata e dice dove comincia il corpo. */
.card-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sm);
  flex-wrap: wrap;
  margin: 0 0 0.7rem;
  padding-bottom: 0.45rem;
  border-bottom: 1px solid var(--border-subtle);
}
.card-title > h2, .card-title > h3 { margin: 0; }
/* Una card che è solo un contenitore di azioni non ha bisogno del vestito completo. */
.card-flush { padding: var(--md); box-shadow: none; background: var(--bg-subtle); }

/* incasso tecnico: log, codice, diff — "carved into the surface" */
.inset {
  background: var(--bg-inset);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-sm);
  padding: var(--md);
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-x: auto;
  margin: 0 0 var(--sm);
}
.inset.scroll { max-height: 28rem; overflow-y: auto; }

/* ---------------------------------------------------------------- messaggi */
.notice, .alert {
  border-radius: var(--radius-sm);
  padding: var(--sm) var(--md);
  margin-bottom: var(--md);
  border: 1px solid;
}
/* Un avviso deve dire di che tipo è **prima** di essere letto: la barra a sinistra si
   riconosce con la coda dell'occhio, il colore da solo no (e non a tutti). */
.notice, .alert {
  display: flex;
  align-items: flex-start;
  gap: var(--sm);
  border-left-width: 4px;
  box-shadow: var(--shadow-1);
  font-weight: 500;
}
.notice {
  background: var(--success-bg);
  border-color: var(--success-br);
  border-left-color: var(--success-fg);
  color: var(--success-fg);
}
.alert {
  background: var(--danger-bg);
  border-color: var(--danger-br);
  border-left-color: var(--danger-fg);
  color: var(--danger-fg);
}

/* ------------------------------------------------------------------ pulsanti */
/* **Quattro livelli, e si distinguono a occhio.**
 *
 * Il problema misurato: su cento pulsanti nei template, **58 erano `class="btn"` nudo** —
 * la stessa faccia per navigare, per inviare un form e per spendere un turno di modello.
 * Con una faccia sola non c'è gerarchia: c'è un elenco di rettangoli.
 *
 *   .btn            secondaria — bianca, bordo che si vede, testo scuro
 *   .btn-primary    l'azione della pagina — piena, con un'ombra
 *   .btn-danger     ciò che non si disfa — rossa, e diventa piena passandoci sopra
 *   .btn-icon       solo icona, senza peso
 *
 * E la sotto-navigazione dell'amministratore **non è un pulsante**: nella pagina di
 * ingest le cinque voci di nav erano identiche all'unica azione vera. Si distingue
 * per contesto (`.subnav a`), non con una classe in più — così i template non cambiano.
 *
 * Il testo è nel font dell'interfaccia, in tondo: il mono maiuscolo faceva sembrare i
 * pulsanti delle targhette invece dei comandi. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--xs);
  font-family: var(--font-ui);
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  min-height: 2.25rem;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-sm);
  /* **Tonale, non bianca.** Bianca con un bordo grigio si leggeva come un campo di
     input, e la gerarchia era due tinte sole: bianco o blu pieno. Con un fondo dello
     stesso colore del primario, in tono chiaro, i due si riconoscono come parenti —
     è la continuità che mancava — e restano distinti per peso. */
  border: 1px solid var(--primary-border);
  background: var(--primary-soft);
  color: var(--on-primary-soft);
  cursor: pointer;
  text-decoration: none;
  box-shadow: var(--shadow-1);
  transition: background-color .12s ease, box-shadow .12s ease,
              transform .12s ease, border-color .12s ease;
}
.btn:hover {
  background: var(--primary-soft-hover);
  border-color: var(--primary);
  box-shadow: var(--shadow-2);
  text-decoration: none;
}
/* Un pulsante deve **cedere** sotto il dito: senza questo non si distingue da un'etichetta
   che per caso ha un bordo. */
.btn:active { transform: translateY(1px); box-shadow: none; }
/* Non c'era alcuno stile di fuoco: chi naviga da tastiera non sapeva dove fosse.
   `:focus-visible` e non `:focus`, o l'anello comparirebbe anche al clic del mouse. */
.btn:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  box-shadow: var(--focus-ring);
}
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
  background: var(--bg-inset);
}
.btn:disabled:hover { transform: none; background: var(--bg-inset); }

/* Il primario dice «è questa l'azione», non «occupa tutta la riga»: la larghezza la
   decide il posto in cui sta, con .btn-block. Prima era 100% qui dentro, e ogni uso in
   linea doveva disdirlo con uno style="width:auto" — quindici volte. */
.btn-primary {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
  box-shadow: var(--shadow-primary);
}
.btn-primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
  box-shadow: var(--shadow-2);
}
.btn-primary:active { background: var(--primary-active); }

/* Rossa da ferma, piena al passaggio: si riconosce senza leggerla, e chiede un gesto in
   più. La domanda vera la fa `data-conferma` — questo è il segnale visivo che la precede. */
.btn-danger {
  color: var(--danger-fg);
  border-color: var(--danger-br);
  background: var(--bg-surface);
}
.btn-danger:hover {
  background: var(--danger-fg);
  border-color: var(--danger-fg);
  color: #fff;
}
.btn-danger:focus-visible {
  outline-color: var(--danger-fg);
  box-shadow: 0 0 0 3px rgba(153, 27, 27, 0.30);
}

/* **Le azioni che spendono un turno di modello.**
 * Nella stanza «Chiedi all'AI» e «Cerca e condividi» erano identiche, e non fanno la
 * stessa cosa: la prima passa dalla coda corax e consuma il modello, la seconda è solo
 * recupero. Su una piattaforma costruita attorno a una coda con capienza finita, quella
 * è la distinzione che conta di più — e non si vedeva.
 * Un punto prima del testo, del colore della coda: non urla e si impara in un giorno.
 */
.btn-modello::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--status-running);
  flex: none;
}
.btn-modello.btn-primary::before { background: rgba(255, 255, 255, 0.85); }

/* **Il terzo livello: neutro.** Se ogni pulsante fosse tonale, una tabella di venti
 * righe con due azioni per riga sarebbe quaranta macchie blu, e il primario non si
 * troverebbe più. Le azioni di riga sono di poco peso per costruzione, quindi `.btn-sm`
 * dentro una tabella diventa neutro **per contesto** — nessuna classe da aggiungere nei
 * template. `.btn-neutro` è la stessa cosa dichiarata a mano, dove serve. */
.btn-neutro, table .btn-sm, .table-wrap .btn-sm {
  background: var(--bg-surface);
  border-color: var(--border);
  color: var(--on-surface-variant);
}
.btn-neutro:hover, table .btn-sm:hover, .table-wrap .btn-sm:hover {
  background: var(--bg-inset);
  border-color: var(--outline);
  color: var(--on-surface);
}
/* Il rosso resta rosso anche in tabella: è semantica, non peso. */
table .btn-sm.btn-danger, .table-wrap .btn-sm.btn-danger {
  background: var(--bg-surface);
  border-color: var(--danger-br);
  color: var(--danger-fg);
}
table .btn-sm.btn-danger:hover, .table-wrap .btn-sm.btn-danger:hover {
  background: var(--danger-fg); border-color: var(--danger-fg); color: #fff;
}

.btn-block { width: 100%; }
/* Dentro una riga di tabella: alto quanto il testo che gli sta accanto, così
   una colonna di pulsanti non raddoppia l'altezza di ogni riga. */
.btn-sm { min-height: 1.75rem; padding: 0.15rem 0.5rem; font-size: 12.5px; }
.btn-icon {
  min-height: 2rem;
  padding: var(--xs);
  border-color: transparent;
  background: none;
  color: var(--secondary);
  box-shadow: none;
}
.btn-icon:hover {
  background: var(--primary-soft);
  border-color: var(--primary-border);
  box-shadow: none;
  color: var(--primary);
}

/* --------------------------------------------------------------------- form */
input[type="text"], input[type="password"], input[type="search"], input:not([type]),
select, textarea {
  font-family: var(--font-ui);
  font-size: 14px;
  color: var(--on-surface);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.65rem;
  min-height: 2.25rem;
  width: 100%;
  max-width: 100%;
  transition: border-color .12s ease, box-shadow .12s ease;
}
textarea { resize: vertical; min-height: 4.5rem; }
/* Il fuoco con l'anello e non col bordo raddoppiato. Prima il bordo passava da 1px a 2px
   e il padding veniva ridotto di 1px per compensare: due regole che devono restare
   d'accordo per non far saltare il testo, e una compensazione che si dimentica al primo
   campo con un padding diverso. Un `box-shadow` non occupa spazio. */
input:focus-visible, select:focus-visible, textarea:focus-visible,
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--focus);
  box-shadow: var(--focus-ring);
}
input::placeholder, textarea::placeholder { color: #8b93a3; }
label { display: block; }
.field { margin-bottom: var(--md); }
/* **Le etichette non devono urlare.** Erano mono maiuscolo con la spaziatura larga, cioè
   più forti dei titoli della pagina: chi guardava vedeva prima «NOME DEL PROGETTO» che
   «Progetto da una repo GitHub». Qui tornano a essere etichette. */
.field > .label, .field > label {
  display: block;
  margin-bottom: var(--xs);
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--on-surface-variant);
}
/* Un campo di testo su una riga non deve essere largo come la finestra: oltre i ~75
   caratteri l'occhio perde la riga, e un nome di progetto non ne ha bisogno. Le aree di
   testo e i campi dentro una `.row` restano come sono: là la larghezza è del layout. */
.field > input[type="text"], .field > input[type="password"], .field > input:not([type]) {
  max-width: var(--field-max);
}
/* **E anche fuori da `.field`.** Il tetto era solo lì dentro, quindi un modulo scritto
   senza quella classe tornava a occupare tutta la card: nella pagina degli utenti il
   campo «username» era largo 1190px. La larghezza di un campo dovrebbe suggerire quanto
   ci si scrive dentro; larga come la finestra non suggerisce niente. */
.card input[type="text"], .card input[type="password"],
.card input[type="search"], .card input:not([type]) { max-width: var(--field-max); }
/* Dove la larghezza la decide il layout — una riga con un pulsante accanto, la barra di
   composizione, la ricerca nel menu — il tetto non si applica. */
.row input, .composer input, .sidebar input, .inline-form input { max-width: none; }
.inline-form { display: inline-flex; gap: var(--xs); align-items: center; }
.row { display: flex; gap: var(--sm); flex-wrap: wrap; align-items: center; }
/* Una riga di azioni in fondo a una pagina, fuori da ogni contenitore, sembrava caduta
   lì: le si dà una barra sua, che la lega al contenuto invece di lasciarla galleggiare. */
.actions {
  display: flex;
  gap: var(--sm);
  flex-wrap: wrap;
  align-items: center;
  padding: 0.7rem var(--lg);
  margin-bottom: var(--md);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-1);
}
.actions .spinta { margin-left: auto; }

/* ------------------------------------------------------------------- tabelle */
table { width: 100%; border-collapse: collapse; font-size: 14px; }
thead th {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--secondary);
  text-align: left;
  padding: var(--sm);
  border-bottom: 1px solid var(--outline-variant);
  white-space: nowrap;
}
tbody td {
  padding: 0.55rem var(--sm);
  border-bottom: 1px solid var(--border-subtle);
  vertical-align: middle;
}
/* Le righe alternate non sono decorazione: su una tabella larga sono ciò che tiene
   l'occhio sulla riga giusta mentre si attraversano otto colonne. */
tbody tr:nth-child(even) { background: var(--bg-subtle); }
tbody tr:hover { background: var(--primary-soft); }
tbody tr:last-child td { border-bottom: none; }
/* L'intestazione resta visibile mentre si scorre: la coda dei run è lunga, e a metà
   elenco non si sa più quale colonna sia quale. */
thead th { position: sticky; top: 0; background: var(--bg-surface); z-index: 1; }
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
}
.card .table-wrap { border: none; }

/* -------------------------------------------------------------------- stato */
/* **Un badge illeggibile è una decorazione con l'aria di un'informazione.**
 * Erano tinte piene con testo bianco, misurate il 24 agosto 2026: ambra 2,15:1, verde
 * 2,54:1, blu 3,68:1, rosso 3,76:1 — quattro su cinque sotto il 4,5:1 richiesto. E
 * visivamente pesavano più del contenuto: cinque adesivi accesi in cima a una card
 * attiravano l'occhio prima del titolo.
 * Fondo tonale, testo scuro dello stesso tono, bordo: si leggono, e stanno al loro posto.
 */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--xs);
  font-family: var(--font-mono);
  /* 12px e non 11: un badge porta uno stato, cioè un'informazione, e sotto i 12 in
     maiuscolo monospaziato si legge male anche con il contrasto giusto. */
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  background: var(--neutral-bg);
  color: var(--neutral-fg);
  border: 1px solid var(--neutral-br);
  white-space: nowrap;
}
.badge-queued, .badge-waiting, .badge-requested,
.badge-in_pausa, .badge-attesa_conferma, .badge-da_fare, .badge-handoff,
/* le segnalazioni: «aperta» aspetta l'amministratore, «chiarimenti» aspetta
   chi ha segnalato. Aspettano tutt'e due, e il colore è quello dell'attesa. */
.badge-aperta, .badge-chiarimenti {
  background: var(--waiting-bg); color: var(--waiting-fg); border-color: var(--waiting-br);
}
.badge-running, .badge-active, .badge-open, .badge-in_corso, .badge-bozza {
  background: var(--running-bg); color: var(--running-fg); border-color: var(--running-br);
}
.badge-done, .badge-sent, .badge-approved, .badge-success,
.badge-concluso, .badge-fatto, .badge-accettata, .badge-fatta {
  background: var(--success-bg); color: var(--success-fg); border-color: var(--success-br);
}
.badge-failed, .badge-timeout, .badge-rejected, .badge-error, .badge-fallito,
.badge-rifiutata {
  background: var(--danger-bg); color: var(--danger-fg); border-color: var(--danger-br);
}
.badge-closed, .badge-cancelled, .badge-revoked, .badge-annullato, .badge-saltato {
  background: var(--neutral-bg); color: var(--neutral-fg); border-color: var(--neutral-br);
}
.badge-outline {
  background: transparent;
  color: var(--secondary);
  border: 1px solid var(--outline-variant);
}

/* La memoria di progetto vive nella colonna della conversazione: è un documento, non
   un pannello di strumenti. Nessun limite di altezza — è tutta da leggere — e il
   riassunto fa da intestazione cliccabile. */
/* Sezioni di **consultazione**: ci sono, e non stanno aperte. La pagina di un progetto
   è cresciuta di un riquadro per fase e ormai chiede di scorrere per trovare la cosa che
   serve; quello che si guarda una volta al giorno — i file caricati, i documenti del
   know-how — sta chiuso, e quello che si guarda adesso resta aperto. È lo stesso gesto
   già usato per la memoria del progetto, con la stessa classe di stile. */
details.consulta { padding: var(--md); }
/* **La maniglia è la riga, non il triangolino.** Il `▸` era largo otto pixel e stava a
   sinistra di un titolo: per aprire un riquadro bisognava colpire quello o il testo, e a
   occhio non si capiva nemmeno che fosse un comando. Adesso si preme tutta la riga — con
   un fondo che risponde al passaggio del mouse — e il segno sta a **destra**, dove sta in
   ogni fisarmonica che la gente ha già usato altrove. */
details.consulta > summary {
  cursor: pointer;
  font-weight: 600;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--sm);
  margin: calc(var(--md) * -1);
  padding: var(--md);
  border-radius: var(--radius-md);
}
details.consulta > summary::-webkit-details-marker { display: none; }
details.consulta > summary:hover { background: var(--bg-inset); }
details.consulta > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
/* Il segno a destra, e lo spazio che lo separa dal titolo se lo prende lui: così i badge
   restano attaccati al nome invece di galleggiare a metà riga. */
details.consulta > summary::after {
  content: "";
  margin-left: auto;
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid var(--outline);
  border-bottom: 2px solid var(--outline);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 120ms ease;
}
details.consulta[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
details.consulta[open] > summary { margin-bottom: 0; }

.memoria { padding: var(--md); }
.memoria > summary {
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--sm);
  flex-wrap: wrap;
}
.memoria > summary::-webkit-details-marker { display: none; }
.memoria[open] > summary { margin-bottom: var(--sm); }
.memoria > .rendered { max-width: 72ch; }

/* pallino di stato per gli elenchi densi */
.dot { width: 8px; height: 8px; border-radius: 9999px; display: inline-block; flex: none; }
.dot-done, .dot-success, .dot-sent { background: var(--status-success); }
.dot-failed, .dot-timeout, .dot-error { background: var(--status-error); }
.dot-running { background: var(--status-running); }
.dot-queued, .dot-waiting, .dot-pending { background: var(--status-waiting); }
.dot-closed, .dot-cancelled { background: var(--secondary); }
.dot-handoff { background: var(--status-waiting); }

/* -------------------------------------------- indicatore di contesto (memoria) */
.contextbar {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--md);
  flex-wrap: wrap;
  padding: var(--xs) var(--md);
  background: var(--surface);
  border-bottom: 1px solid var(--outline-variant);
}
.meter {
  flex: 1;
  min-width: 6rem;
  max-width: 22rem;
  height: 8px;
  background: var(--bg-inset);
  border: 1px solid var(--outline-variant);
  border-radius: 9999px;
  overflow: hidden;
}
/* riempimento a spigolo vivo: dà l'aria "misurata" di uno strumento */
.meter > span { display: block; height: 100%; background: var(--status-running); }
.meter.near-full > span { background: var(--status-waiting); }

/* ------------------------------------------------------------ conversazione */
.thread { flex: 1; overflow-y: auto; padding: var(--md); display: flex; flex-direction: column; gap: var(--lg); }
/* La stanza usa la stessa grammatica della dev chat: una card che si stringe e un filo
   che scorre dentro. Prima era una card che cresceva e la pagina che scorreva sotto. */
.conversazione { display: flex; flex-direction: column; min-height: 0; padding: 0; overflow: hidden; }
body.fixed .conversazione { flex: 1; }
.conversazione > .thread { gap: var(--md); }
.turn { display: flex; flex-direction: column; }
.turn.from-user { align-items: flex-end; }
.turn.from-model { align-items: flex-start; }
.turn .who { display: flex; align-items: center; gap: var(--xs); margin-bottom: var(--xs); }
.bubble {
  background: var(--bg-surface);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-lg);
  padding: var(--md);
  max-width: 85%;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin: 0;
  font-family: var(--font-ui);
  font-size: 14px;
}
.turn.from-user .bubble { border-top-right-radius: 0; }
.turn.from-model .bubble { border-top-left-radius: 0; max-width: 95%; }
.bubble.tech-body { font-family: var(--font-mono); font-size: 13px; }

/* cassetto del ragionamento */
details.thinking {
  background: var(--bg-inset);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-sm);
  margin-bottom: var(--xs);
  max-width: 95%;
}
details.thinking > summary {
  display: flex;
  align-items: center;
  gap: var(--sm);
  padding: var(--sm);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--secondary);
}
details.thinking > summary:hover { background: var(--surface-container-high); }
details.thinking > div {
  padding: var(--md);
  border-top: 1px solid var(--outline-variant);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
  white-space: pre-wrap;
}

/* barra di composizione, ancorata in basso nella zona A */
.composer {
  background: var(--surface);
  border-top: 1px solid var(--outline-variant);
  padding: var(--md);
}
.composer .box {
  background: var(--bg-inset);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-md);
}
.composer textarea {
  background: transparent;
  border: none;
  padding: var(--md);
}
.composer textarea:focus { border: none; outline: 2px solid var(--primary); outline-offset: -2px; }
/* `.actions` esisteva già qui, dentro la barra di composizione, e voleva dire un'altra
   cosa: la riga di comandi **incastonata** nel composer, non una barra che galleggia sulla
   pagina. Le due omonime convivono, e questa disdice quello che non le serve — trovato
   scattando la pagina della sessione, non leggendo il foglio. */
.composer .actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sm);
  flex-wrap: wrap;
  padding: var(--sm);
  margin-bottom: 0;
  background: none;
  border: none;
  border-top: 1px solid var(--border-subtle);
  border-radius: 0;
  box-shadow: none;
}

/* --------------------------------------------------------------------- diff */
.diff { display: block; }
.diff .add { background: rgba(16, 185, 129, 0.12); color: #0b6b4f; display: block; }
.diff .del { background: rgba(239, 68, 68, 0.12); color: #93000a; display: block; }
.diff .hunk { color: var(--text-muted); display: block; }

/* --------------------------------------------------------- elenchi densi */
.list { display: flex; flex-direction: column; gap: var(--sm); }
.list-item {
  background: var(--bg-surface);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-sm);
  padding: var(--sm) var(--md);
}
.list-item .head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sm);
}
/* **Un'azione in testata di riquadro è un'azione, non una frase.** «tutte le dev chat» e
   «stanza del progetto» erano due parole azzurre appoggiate a destra del titolo: si
   leggono come una didascalia, e infatti nessuno le premeva. Un bordo e un po' d'aria
   bastano a dire che si preme — senza farne un pulsante pieno, che competerebbe col
   comando principale del riquadro. */
.card-title > a:not(.btn) {
  display: inline-flex;
  align-items: center;
  gap: var(--xs);
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  color: var(--primary);
  font-size: 13px;
  white-space: nowrap;
}
.card-title > a:not(.btn):hover {
  background: var(--primary-soft);
  border-color: var(--primary-border);
  text-decoration: none;
}
.card-title > a:not(.btn):focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

.list-item .links { display: flex; gap: var(--md); margin-top: var(--xs); }
.list-item .links a { font-family: var(--font-mono); font-size: 12px; }

/* sezioni della barra strumenti */
.tool-section { margin-bottom: var(--lg); }
.tool-section > .label { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--sm); }
.tool-box {
  background: var(--bg-surface);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-md);
  padding: var(--md);
}
.status-line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sm);
  background: var(--surface-container);
  border-radius: var(--radius-sm);
  padding: var(--sm) var(--md);
  margin-bottom: var(--md);
}

/* --------------------------------------------------------------------- icone */
.icon { width: 1.15em; height: 1.15em; flex: none; vertical-align: -0.18em; }
.icon-sm { width: 1em; height: 1em; }

/* ------------------------------------------------------------- accessibilità */
:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* --------------------------------- markdown reso (risposte del modello) */
.bubble.rendered, .rendered { white-space: normal; }
.rendered > :first-child { margin-top: 0; }
.rendered > :last-child { margin-bottom: 0; }
.rendered h3, .rendered h4, .rendered h5 {
  font-size: 15px;
  margin: var(--md) 0 var(--xs);
}
.rendered ul, .rendered ol { margin: 0 0 var(--sm); padding-left: 1.3rem; }
.rendered li { margin-bottom: var(--xs); }
.rendered code {
  background: var(--bg-inset);
  border: 1px solid var(--outline-variant);
  border-radius: 3px;
  padding: 0 3px;
}
.rendered pre.inset { margin: var(--sm) 0; }
.rendered pre.inset code { background: none; border: none; padding: 0; }
.rendered hr { border: none; border-top: 1px solid var(--outline-variant); margin: var(--md) 0; }

/* ------------------------------------------------ sotto-navigazione di sezione
   Le pagine dell'amministratore sono quattro e si rimandano l'una all'altra. Prima
   ogni pagina elencava a mano le altre tre: chi arrivava non aveva modo di sapere
   dove fosse, perché la pagina corrente era proprio quella che mancava dall'elenco. */
.subnav { display: flex; gap: var(--xs); flex-wrap: wrap; margin-bottom: var(--md); }
/* **Una riga di link non è una navigazione.** Le cinque voci di un progetto erano cinque
   ancore azzurre sotto il titolo: sembravano una didascalia, e chi le leggeva non capiva
   che portavano da qualche parte — «non trovo X» è stato chiesto tre volte, e ogni volta
   la risposta era «è più in basso, c'è il link in cima».
   Adesso sono schede: una riga sola, la linguetta di quella in cui ti trovi sottolineata,
   e la sottolineatura si sposta **mentre scorri** (`schede.js`). Senza JavaScript restano
   quello che erano, cioè link che funzionano. */
:where(.page, .principale) .pagina-testa > .subnav,
:where(.page, .principale) > .subnav {
  gap: 0;
  margin-bottom: var(--lg);
  border-bottom: 1px solid var(--border-subtle);
  overflow-x: auto;
  scrollbar-width: thin;
}
:where(.page, .principale) .pagina-testa > .subnav > a,
:where(.page, .principale) > .subnav > a {
  flex: 0 0 auto;
  padding: 0.55rem var(--md);
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: transparent;
  color: var(--secondary);
  font-weight: 600;
  font-size: 14px;
  white-space: nowrap;
  min-height: 40px;
  /* L'ombra del pulsante qui disegna una riga verticale fra una scheda e l'altra: da
     lontano sembrano sei caselle, che è il disegno di prima con un altro colore. */
  box-shadow: none;
}
:where(.page, .principale) .pagina-testa > .subnav > a:hover,
:where(.page, .principale) > .subnav > a:hover {
  background: var(--bg-inset);
  color: var(--on-surface);
  text-decoration: none;
}
:where(.page, .principale) .pagina-testa > .subnav > a[aria-current="true"],
:where(.page, .principale) > .subnav > a[aria-current="true"] {
  color: var(--primary);
  border-bottom-color: var(--primary);
  background: transparent;
}
/* Dentro una card sono scorciatoie: restano pulsanti, con un po' d'aria sopra e sotto. */
.card .subnav { gap: var(--sm); margin: var(--md) 0; }
/* **Navigare non è agire.** Nella pagina di ingest queste cinque voci erano identiche
   all'unica azione vera della pagina («prova a inviare adesso»): stesso bordo, stesso
   mono maiuscolo, stessa ombra. Qui si spogliano del vestito da pulsante e diventano
   linguette — il template continua a scrivere `class="btn"`, la differenza è il contesto.

   **Ma solo al primo livello della pagina.** La stessa macro serve anche come gruppo di
   scorciatoie **dentro una card**, nella home: là non c'è nessun «sei qui», sono link da
   premere, e spogliarli del vestito da pulsante li faceva sembrare testo morto. Visto
   scattando la pagina, non leggendo il CSS.

   Il `>` va tenuto — distingue «al primo livello» da «dentro una card» — ma i genitori
   sono due: da quando esiste la colonna di destra il contenuto sta in `.principale`, e
   una regola sul solo `.page` smetteva di valere. Anche questo l'ha trovato lo
   screenshot, non il foglio: le linguette erano tornate pulsanti. */
:where(.page, .principale) > .subnav a.btn,
:where(.page, .principale) > .subnav a {
  min-height: 2rem;
  padding: 0.3rem 0.7rem;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: transparent;
  box-shadow: none;
  color: var(--secondary);
  font-weight: 500;
}
:where(.page, .principale) > .subnav a.btn:hover,
:where(.page, .principale) > .subnav a:hover {
  background: var(--bg-inset);
  border-color: transparent;
  box-shadow: none;
  color: var(--on-surface);
}
:where(.page, .principale) > .subnav a.btn:active { transform: none; }
/* «Sei qui»: piena, del colore dell'azione ma in tono chiaro, così si distingue dalle
   sorelle senza competere col pulsante primario della pagina. */
:where(.page, .principale) > .subnav a.btn[aria-current="page"],
:where(.page, .principale) > .subnav a[aria-current="page"] {
  background: var(--primary-soft);
  border-color: var(--primary);
  color: var(--primary);
  font-weight: 700;
}

/* Salto al contenuto: la testata è appiccicata in alto e ha sette voci più la
   ricerca. Da tastiera, senza questo, si attraversano tutte a ogni pagina. */
.skip {
  position: absolute;
  left: var(--sm);
  top: -3rem;
  z-index: 100;
  background: var(--bg-surface);
  border: 1px solid var(--primary);
  border-radius: var(--radius-sm);
  padding: var(--sm) var(--md);
  transition: top 0.1s;
}
.skip:focus { top: var(--sm); }

/* Aggiornamento dal vivo: la pastiglia dice che la pagina si sta guardando da sola,
   così un valore che cambia sotto gli occhi non sembra un glitch. */
.live-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--xs);
  font-family: var(--font-mono);
  font-size: 12px;          /* era 11: sotto il pavimento di leggibilità del foglio */
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-muted);
}
.live-tag::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 9999px;
  background: var(--status-running);
}
.live-tag[data-stato="fermo"]::before { background: var(--outline); }

@media (max-width: 640px) {
  /* Su telefono la testata diventava cinque righe. La ricerca vive comunque
     nell'archivio, e il nome utente lo sa già chi è connesso. */
  .topbar .search, .topbar .right .label { display: none; }
}

/* Le ancore dell'indice di pagina: la testata è `position: sticky`, quindi un salto a
   un titolo lo lascerebbe mezzo nascosto sotto di essa. `scroll-margin-top` è la
   distanza da tenere, e vale per qualunque salto — anche quelli fatti da tastiera. */
[id] { scroll-margin-top: 4.5rem; }

/* Le pastiglie di stato accanto al titolo, non in una barra loro. Una `.actions` con
   dentro due pastiglie e nessuna azione è un contenitore che non contiene niente: in
   pagina sembrava un riquadro vuoto fra l'indice e la prima sezione vera. */
h1.con-stati {
  display: flex;
  align-items: center;
  gap: var(--sm);
  flex-wrap: wrap;
}
h1.con-stati .stati {
  display: inline-flex;
  align-items: center;
  gap: var(--xs);
  flex-wrap: wrap;
  font-size: 12px;
  font-weight: 400;
}

/* ==========================================================================
   LE BRICIOLE
   ==========================================================================
   Erano una riga di testo grigio con dei `·` in mezzo e dentro dei link. Il colore li
   distingueva, ma la riga nel suo insieme leggeva come una didascalia — cioè come
   qualcosa che si guarda. Qui ogni voce ha un bordo e uno sfondo: **la forma dice che si
   preme prima che il colore dica che è un link**, e lo dice anche a chi i colori non li
   distingue. L'ultima voce è dove sei: nessun bordo, nessun href. */
.briciole { margin: 0 0 var(--md); }
.briciole ol {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--xs);
  margin: 0;
  padding: 0;
  list-style: none;
}
.briciole li { display: flex; align-items: center; gap: var(--xs); }
/* Il separatore è disegnato dal CSS e non scritto nel markup: un `·` fra due `<li>` è
   testo che uno screen reader legge, e «Catalogo punto medio corax» non è un percorso. */
.briciole li + li::before {
  content: "";
  width: 6px; height: 6px;
  border-right: 1.5px solid var(--outline);
  border-top: 1.5px solid var(--outline);
  transform: rotate(45deg);
  flex: none;
}
.briciola {
  display: inline-flex;
  align-items: center;
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--primary-border);
  border-radius: var(--radius-pill);
  background: var(--primary-soft);
  color: var(--on-primary-soft);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.4;
}
a.briciola:hover { background: var(--primary); color: var(--on-primary); text-decoration: none; }
a.briciola:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* Dove sei: si legge, non si preme. Nessun bordo che prometta un click. */
.briciola.corrente {
  border-color: transparent;
  background: transparent;
  color: var(--text-muted);
  font-weight: 500;
  max-width: 40ch;
}

/* ==========================================================================
   LA FASCIA DI COMANDO (pagina di un run)
   ==========================================================================
   Le tre cose che si vogliono sapere di un run stavano tutte in fondo a una pagina lunga
   quattro schermate: com'è andata, cosa ne dice il supervisore, e cosa posso farci. */
.riepilogo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--md);
  margin-bottom: var(--md);
  padding: var(--sm) var(--md);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  box-shadow: var(--shadow-1);
}
.riepilogo-verdetto,
.riepilogo-azioni { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sm); }
.riepilogo-verdetto .muted { font-size: 13px; }
.riepilogo-azioni form { margin: 0; }

/* ==========================================================================
   LA FILA DEI RUN (in cima alla sessione)
   ==========================================================================
   La cronologia completa resta nel pannello: qui c'è solo lo stato, per il colpo
   d'occhio. Scorre in orizzontale invece di andare a capo — in una chat lo spazio
   verticale è la conversazione, e una fila che diventa tre righe la mangia. */
.barra-run {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--md);
  flex-wrap: wrap;
  padding: var(--xs) var(--md);
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-surface);
}
.barra-run .briciole { margin: 0; }
.fila-run {
  display: flex;
  align-items: center;
  gap: var(--xs);
  overflow-x: auto;
  padding-bottom: 2px;
  scrollbar-width: thin;
}
.pastiglia-run {
  display: inline-flex;
  align-items: center;
  gap: var(--xs);
  flex: none;
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: var(--on-surface);
  font-size: 12px;
}
.pastiglia-run:hover { border-color: var(--primary-border); background: var(--primary-soft); text-decoration: none; }
.pastiglia-run:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ==========================================================================
   L'ALBERO DELLA REPO E IL VISORE
   ========================================================================== */
.albero { list-style: none; margin: 0; padding: 0; font-family: var(--font-mono); font-size: 12.5px; }
/* Il rientro è un bordo, non uno spazio: la linea verticale dice a quale cartella
   appartiene una riga anche quando la cartella è scorsa fuori dallo schermo. */
.albero .albero { margin-left: 0.55rem; padding-left: 0.55rem; border-left: 1px solid var(--border-subtle); }
.albero li { margin: 1px 0; }
.albero summary {
  display: flex;
  align-items: center;
  gap: var(--xs);
  padding: 0.15rem 0.3rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--on-surface);
  font-weight: 600;
}
.albero summary:hover { background: var(--bg-inset); }
.albero summary:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.albero summary::marker, .albero summary::-webkit-details-marker { color: var(--secondary); }
/* 12px e non 11,5: è il pavimento di leggibilità che questo foglio si è dato, e
   l'ha trovato lo strumento del giro — a occhio non si distinguono. */
.albero .conta { margin-left: auto; color: var(--text-muted); font-size: 12px; }
.albero a.file {
  display: block;
  padding: 0.15rem 0.3rem 0.15rem 1.1rem;
  border-radius: var(--radius-sm);
  color: var(--on-surface-variant);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.albero a.file:hover { background: var(--primary-soft); color: var(--on-primary-soft); text-decoration: none; }
.albero a.file:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.albero-box > summary { cursor: pointer; font-weight: 600; }
.albero-radice { max-height: 22rem; overflow-y: auto; }
/* A pagina intera l'albero non ha motivo di stare in una finestrella. */
.page .card > .albero-radice { max-height: none; }

/* La finestra a comparsa: un file **sopra** la conversazione, non al posto suo. */
.visore {
  width: min(72rem, 94vw);
  max-height: 88vh;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--on-surface);
  box-shadow: 0 20px 50px rgba(15, 23, 42, 0.35);
  overflow: hidden;
}
.visore::backdrop { background: rgba(15, 23, 42, 0.55); }
.visore-barra {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--md);
  padding: var(--sm) var(--md);
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-inset);
}
.visore-titolo { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.visore-azioni { display: flex; align-items: center; gap: var(--sm); flex: none; }
.visore-corpo { padding: var(--md); overflow: auto; max-height: calc(88vh - 3.5rem); }
.sorgente-testa {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--md);
  flex-wrap: wrap;
  margin-bottom: var(--sm);
}
.sorgente-testa .percorso { font-weight: 600; }
pre.sorgente { max-width: none; margin: 0; white-space: pre; overflow-x: auto; font-size: 12.5px; line-height: 1.55; }

/* ==========================================================================
   ADATTAMENTO A TABLET E TELEFONO
   ==========================================================================

   Il guscio è nato per uno schermo largo: una colonna di navigazione fissa a 15rem, il
   contenuto al centro, il contesto a destra. Su un telefono da 390px quelle tre colonne
   sono tutta la larghezza che c'è, e la prima si mangiava metà della prima schermata.

   Tre soglie, e ognuna toglie una colonna:

   * **oltre 1180px** — tre colonne, com'era;
   * **901–1180px** (tablet in orizzontale, laptop stretti) — la navigazione si stringe,
     il contesto scende sotto il contenuto invece di diventare una colonna di due parole;
   * **fino a 900px** (tablet in verticale, telefoni) — la navigazione diventa un
     pannello che entra da sinistra, il contesto un pannello che sale dal basso, e il
     contenuto ha tutta la larghezza.

   **I due pannelli si nascondono solo se c'è il JavaScript che li riapre** (`.con-js`,
   scritta in testa al documento). Senza, restano impilati in pagina: brutti e
   raggiungibili, che è l'ordine giusto delle due cose. */

.barra-mobile, .apri-rail, .velo { display: none; }

@media (max-width: 1180px) {
  .sidebar { width: 13rem; flex: 0 0 13rem; }
  .page { padding: 1rem var(--md); }
  /* Anche nella chat: 25rem di strumenti su un 1024 lasciavano al filo meno della metà. */
  body.fixed .con-rail { grid-template-columns: minmax(0, 1fr) 21rem; }
  body.fixed .rail { width: 21rem; }
}

@media (max-width: 900px) {
  /* --- il guscio si impila ------------------------------------------------ */
  .shell { flex-direction: column; min-height: 100vh; }
  .sidebar {
    position: static;
    width: auto;
    flex: none;
    height: auto;
    max-height: none;
    border-right: none;
    border-bottom: 1px solid var(--border-subtle);
  }
  .sidebar nav { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--xs); }

  /* --- con JavaScript: la navigazione è un pannello, non una lista in cima --- */
  .con-js .barra-mobile {
    display: flex;
    align-items: center;
    gap: var(--sm);
    position: sticky;
    top: 0;
    z-index: 40;
    padding: var(--xs) var(--sm);
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border-subtle);
  }
  .con-js .barra-mobile .brand { font-size: 18px; margin-right: auto; }
  .apri-menu, .cerca-mobile {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 44px: il bersaglio minimo di un dito. Sotto, si sbaglia — e sbagliare qui
       significa aprire il menu quando si voleva premere il marchio. */
    width: 44px;
    height: 44px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-surface);
    color: var(--on-surface);
    cursor: pointer;
  }
  .apri-menu:hover, .cerca-mobile:hover { background: var(--bg-inset); text-decoration: none; }
  .apri-menu:focus-visible, .cerca-mobile:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

  .con-js .sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 60;
    /* **L'altezza dichiarata, non dedotta da `top`/`bottom`.** Con `height: auto` fra i
       due bordi il pannello avrebbe dovuto allungarsi, e misurandolo era alto 597px su
       una finestra da 844: si fermava dove finivano le voci, e sotto restava un buco da
       cui si vedeva la pagina. `dvh` perché su un telefono la barra dell'indirizzo entra
       ed esce e `vh` è l'altezza senza di essa. */
    height: 100dvh;
    align-self: stretch;
    width: min(19rem, 84vw);
    overflow-y: auto;
    border-right: 1px solid var(--border);
    border-bottom: none;
    box-shadow: 4px 0 24px rgba(15, 23, 42, 0.25);
    transform: translateX(-101%);
    transition: transform 160ms ease-out;
  }
  .con-js .sidebar nav { grid-template-columns: minmax(0, 1fr); }
  .con-js body.menu-aperto .sidebar { transform: none; }
  .con-js body.menu-aperto .velo {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 50;
    background: rgba(15, 23, 42, 0.45);
  }

  /* --- il contesto diventa un pannello che sale dal basso ------------------ */
  .con-rail { grid-template-columns: minmax(0, 1fr); }
  .con-js .apri-rail {
    display: inline-flex;
    align-items: center;
    gap: var(--xs);
    position: fixed;
    right: var(--md);
    bottom: var(--md);
    z-index: 45;
    min-height: 44px;
    padding: 0 1rem;
    border: 1px solid var(--primary-border);
    border-radius: var(--radius-pill);
    background: var(--primary);
    color: var(--on-primary);
    font-weight: 600;
    box-shadow: 0 6px 18px rgba(15, 23, 42, 0.28);
    cursor: pointer;
  }
  .con-js .apri-rail:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
  /* Il pulsante resta **sopra** il pannello che ha aperto, e cambia parola. Aperto senza
     un modo visibile per chiudere, il pannello copriva lo schermo e l'unica via d'uscita
     era il tasto Esc — che su un telefono non c'è. */
  .con-js .apri-rail .chiudi { display: none; }
  .con-js body.rail-aperto .apri-rail { z-index: 60; }
  .con-js body.rail-aperto .apri-rail .etichetta { display: none; }
  .con-js body.rail-aperto .apri-rail .chiudi { display: inline; }
  .con-js .rail {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 55;
    max-height: 82vh;
    overflow-y: auto;
    padding: var(--md);
    background: var(--bg-surface);
    border-top: 1px solid var(--border);
    border-left: none;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    box-shadow: 0 -8px 28px rgba(15, 23, 42, 0.25);
    transform: translateY(101%);
    transition: transform 180ms ease-out;
  }
  .con-js body.rail-aperto .rail { transform: none; }
  /* La maniglia: dice che è un foglio che si tira, non una pagina che è comparsa. */
  .con-js .rail::before {
    content: "";
    display: block;
    width: 2.5rem;
    height: 4px;
    margin: 0 auto var(--sm);
    border-radius: 9999px;
    background: var(--outline);
  }
  .con-js body.rail-aperto .velo:not([hidden]) {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 50;
    background: rgba(15, 23, 42, 0.45);
  }
  .con-js body.rail-aperto .apri-rail { background: var(--bg-surface); color: var(--on-surface); }

  /* --- la chat ------------------------------------------------------------
     `dvh` e non `vh`: su un telefono la barra dell'indirizzo entra ed esce, e `100vh`
     è l'altezza **senza** di essa — cioè la barra di composizione finisce sotto il bordo
     dello schermo proprio mentre si sta scrivendo. */
  body.fixed { height: auto; overflow: visible; }
  body.fixed .shell { height: auto; min-height: 100dvh; }
  body.fixed .page { padding: 0; }
  body.fixed .con-rail { grid-template-columns: minmax(0, 1fr); }
  body.fixed .rail { width: auto; }
  body.fixed .thread { overflow: visible; }
  .composer { position: sticky; bottom: 0; z-index: 20; }
  /* Le briciole e la fila dei run su due righe, e **ognuna scorre per conto suo**.
     Insieme in un unico scorrevole la fila dei run cominciava fuori dallo schermo: per
     vedere il primo run bisognava scorrere oltre tre briciole. */
  .barra-run { flex-direction: column; align-items: stretch; gap: var(--xs); }
  .barra-run .briciole ol { flex-wrap: nowrap; overflow-x: auto; }
  /* **`min-width: 0` su ogni anello, o lo scorrimento non è dove credi.** Un figlio
     flessibile ha larghezza minima «quella del contenuto»: senza questa riga i due
     scorrevoli si allargavano fino a contenere tutto, e a scorrere finiva la **pagina**
     — 727px di documento su una finestra da 390. Non l'ho visto negli screenshot: l'ho
     visto quando lo strumento del giro ha smesso di misurare la finestra sbagliata. */
  .barra-run, .barra-run > *, .barra-run .briciole, .fila-run { min-width: 0; max-width: 100%; }

  /* **Il pulsante degli strumenti non deve coprire il campo per scrivere.** Galleggiando
     in basso a destra finiva sopra la barra di composizione, cioè sopra la cosa per cui
     si è aperta la pagina. La barra si riserva la sua striscia, e il pulsante ci si
     posa dentro invece che sopra al testo. */
  .con-js body.fixed .composer { padding-bottom: 3.25rem; }

  /* --- densità e bersagli ------------------------------------------------- */
  h1 { font-size: 20px; }
  h2 { font-size: 16px; }
  .page { padding: var(--md) var(--sm); }
  .card { padding: 0.875rem; }
  .footer { flex-direction: column; gap: var(--xs); }
  .subnav { flex-wrap: nowrap; overflow-x: auto; padding-bottom: var(--xs); }
  .riepilogo { flex-direction: column; align-items: stretch; }
  .riepilogo-azioni { justify-content: flex-start; }
  .visore { width: 100vw; max-width: 100vw; max-height: 100dvh; height: 100dvh; border-radius: 0; }
  .visore-corpo { max-height: calc(100dvh - 3.75rem); }
  .albero-radice { max-height: none; }
  /* Due `select` di commit affiancati a 390px sono due colonne di sei caratteri: in
     colonna si legge quale revisione si sta scegliendo, che è tutto il punto. */
  .confronto { flex-direction: column; align-items: stretch; }
  .confronto select { max-width: none; width: 100%; }
  .confronto .freccia { display: none; }
  .topbar { padding: var(--sm) var(--md); }
  .search input { width: 100%; max-width: none; }
}

/* La navigazione senza JavaScript su telefono: due colonne di voci in cima alla pagina,
   che è brutto e si usa. La testata compatta invece non si mostra — il suo pulsante non
   avrebbe niente da aprire, e un pulsante che non fa niente è peggio che non averlo. */
html:not(.con-js) .barra-mobile { display: none; }

@media (max-width: 640px) {
  /* Su telefono la testata diventava cinque righe. La ricerca vive comunque
     nell'archivio, e il nome utente lo sa già chi è connesso. */
  .topbar .search, .topbar .right .label { display: none; }
  .briciola { max-width: 14ch; }
  .briciola.corrente { max-width: 20ch; }
  .card-title { align-items: flex-start; flex-direction: column; gap: var(--xs); }
  /* Un form in riga a 390px è una colonna di campi larghi due parole. */
  .row { flex-wrap: wrap; }
  .actions { flex-wrap: wrap; }
}

/* **Il dito non è il puntatore.** `pointer: coarse` è vero su telefono e tablet e falso
   col mouse: qui si allargano i bersagli senza gonfiare l'interfaccia di chi ha un
   puntatore preciso — e senza legare la cosa alla larghezza della finestra, che con un
   tablet in orizzontale direbbe «desktop». */
@media (pointer: coarse) {
  .btn, .sidebar nav a, .briciola, .albero a.file, .albero summary { min-height: 40px; }
  .btn, .briciola { display: inline-flex; align-items: center; }
  .albero a.file, .albero summary { padding-top: 0.5rem; padding-bottom: 0.5rem; }
  /* Anche i piccoli. Il primo giro a 390px li ha trovati a 36px e le pastiglie dei run a
     28: trentasei passa la norma — WCAG 2.2 SC 2.5.8 chiede 24 — ma non è la misura a
     cui si preme bene col pollice, e questi due stanno dove si preme in movimento. */
  .btn-sm, .pastiglia-run { min-height: 40px; }
  .pastiglia-run { padding: 0.35rem 0.7rem; }
  /* La sottonavigazione ha una regola sua, e più specifica: `.btn` da solo non la
     raggiungeva e i suoi pulsanti restavano a 32px — misurato a 820, non dedotto. */
  :where(.page, .principale) > .subnav a.btn { min-height: 40px; }
}

/* **16px sui campi, o iOS ingrandisce la pagina.** Safari su iPhone zooma quando un
   campo ha meno di 16px, e da quello zoom non si torna indietro da soli: la pagina resta
   più larga della finestra per tutto il resto della visita. Non è una scelta di
   tipografia, è una regola del sistema operativo. */
@media (max-width: 900px) {
  input, select, textarea { font-size: 16px; }
}

/* ------------------------------------------------------- il confronto fra revisioni
   «Cos'è cambiato qui?» è la domanda che viene subito dopo «cosa c'è scritto qui»: sta
   nello stesso riquadro, o la seconda costa un'uscita dalla pagina. */
.confronto {
  display: flex;
  align-items: center;
  gap: var(--sm);
  flex-wrap: wrap;
  margin-bottom: var(--md);
  padding-bottom: var(--sm);
  border-bottom: 1px solid var(--border-subtle);
}
.confronto select { max-width: 20rem; font-family: var(--font-mono); font-size: 12px; }
.confronto .freccia { color: var(--text-muted); }
/* I colori sono quelli già misurati per gli stati — `success` e `danger` — e non una
   coppia nuova di verde e rosso: due sistemi di colore nello stesso foglio divergono al
   primo ritocco, e il secondo è sempre quello che nessuno rimisura. Nel tema scuro si
   ribaltano da soli, perché sono token. */
.sorgente-testa .piu { color: var(--success-fg); font-weight: 700; }
.sorgente-testa .meno { color: var(--danger-fg); font-weight: 700; }

pre.diff { max-width: none; margin: 0; white-space: pre; overflow-x: auto; font-size: 12.5px; line-height: 1.5; }
/* `min-height` perché una riga vuota del diff è un blocco senza contenuto: senza,
   sparirebbe e due righe lontane sembrerebbero adiacenti. */
pre.diff span { display: block; min-height: 1.5em; }
/* Il colore **non è l'unica cosa che distingue le due**: il segno `+`/`−` sta già nel
   testo, ed è quello che si legge se i colori non si distinguono. Qui il fondo aiuta chi
   li vede, e il bordo a sinistra dà una seconda forma. */
.diff .d-aggiunta { background: var(--success-bg); color: var(--success-fg); box-shadow: inset 2px 0 0 var(--success-br); }
.diff .d-tolta { background: var(--danger-bg); color: var(--danger-fg); box-shadow: inset 2px 0 0 var(--danger-br); }
.diff .d-salto { color: var(--primary); font-weight: 700; }
.diff .d-testata { color: var(--text-muted); }

/* ==========================================================================
   IL CONTESTO SI PUÒ CHIUDERE ANCHE SULLA SCRIVANIA
   ==========================================================================
   Sotto i 900px il contesto è un foglio che sale, e il pulsante lo apre. Sopra, la
   colonna c'è sempre e si prende 20rem — che su una pagina di **lettura** (un diff, un
   log, una tabella di passi) sono 20rem tolte alla cosa che si sta leggendo. Stesso
   pulsante, altro gesto: qui non apre un pannello, **richiude una colonna**.

   La scelta si ricorda (`localStorage`), e questa è la parte che conta: chiuderla e
   ritrovarla aperta alla pagina dopo è peggio che non poterla chiudere. Si applica nello
   script in testa al documento, prima che la pagina si disegni — applicata dopo si
   vedrebbe la colonna comparire e sparire a ogni caricamento.

   È una preferenza di chi guarda, non un dato: sta nel browser e non tocca il server. */
@media (min-width: 901px) {
  .con-js .apri-rail {
    display: inline-flex;
    align-items: center;
    gap: var(--xs);
    position: fixed;
    right: var(--md);
    bottom: var(--md);
    z-index: 45;
    /* Più discreto del pulsante di telefono: là è l'unico modo di arrivare al contesto,
       qui il contesto è già in pagina e questo lo toglie di mezzo. */
    min-height: 32px;
    padding: 0 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--bg-surface);
    color: var(--on-surface-variant);
    font-size: 12.5px;
    font-weight: 600;
    box-shadow: var(--shadow-1);
    cursor: pointer;
  }
  .con-js .apri-rail:hover { border-color: var(--primary-border); background: var(--primary-soft); color: var(--on-primary-soft); }
  .con-js .apri-rail:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
  /* Le due parole sono le stesse del telefono, e il verso è rovesciato: qui la colonna
     parte **aperta**, quindi finché si vede il pulsante dice «chiudi». */
  .con-js .apri-rail .etichetta { display: none; }
  .con-js.rail-chiuso .apri-rail .etichetta { display: inline; }
  .con-js.rail-chiuso .apri-rail .chiudi { display: none; }

  .con-js.rail-chiuso .rail { display: none; }
  .con-js.rail-chiuso .con-rail { grid-template-columns: minmax(0, 1fr); }
  /* Nella chat la griglia ha la sua larghezza fissa: va rifatta anche lì, o resta il
     buco di 25rem dove stava il pannello. */
  .con-js.rail-chiuso body.fixed .con-rail { grid-template-columns: minmax(0, 1fr); }
}

/* La barra di composizione si riserva la striscia del pulsante a **ogni** larghezza: era
   una regola di telefono, ma adesso il pulsante galleggia anche sulla scrivania, e lì
   copriva l'angolo del campo per scrivere. */
.con-js body.fixed .composer { padding-bottom: 3.25rem; }

/* Il pulsante galleggia in basso a destra, e lì c'è il piede della pagina: i suoi
   collegamenti finivano **sotto** di esso, visibili a metà. Il posto lo fa il piede, e
   solo dove il pulsante c'è davvero — `:has()` guarda se la pagina ha una colonna di
   contesto. Dove `:has()` non c'è, il piede resta com'era: un collegamento coperto a metà
   è meno grave di un piede spostato su tutte le pagine. */
.con-js .colonna:has(.apri-rail) .footer { padding-right: 9.5rem; }

/* ==========================================================================
   LE SEGNALAZIONI
   ==========================================================================
   **La linguetta è verticale e sul fianco**, e non è un vezzo: l'angolo in basso a destra
   è già occupato dal pulsante che chiude la colonna di contesto, e due pastiglie nello
   stesso angolo si premono l'una per l'altra. Il bordo destro a metà altezza è un posto
   che nessun'altra cosa usa: si vede da qualunque pagina e non copre niente. */
.linguetta-segnala {
  position: fixed;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 46;
  display: flex;
  align-items: center;
  /* `vertical-rl` e **nessuna rotazione**: girare la linguetta di mezzo giro girerebbe
     anche la sua scatola, e il bordo tolto e gli angoli arrotondati finirebbero dal lato
     opposto a quello attaccato allo schermo. Gli angoli sono i due di sinistra, che sono
     quelli che si vedono. */
  writing-mode: vertical-rl;
  padding: 1rem 0.45rem;
  border: 1px solid var(--segnala-bg);
  border-right: none;
  border-radius: var(--radius-md) 0 0 var(--radius-md);
  background: var(--segnala-bg);
  color: var(--segnala-fg);
  font-family: var(--font-ui);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  box-shadow: var(--shadow-1);
}
/* **Sempre rossa**, anche sotto il dito: virare al blu passandoci sopra farebbe
   di un colore che significa «qui si segnala» un colore che non significa niente. */
.linguetta-segnala:hover { background: var(--segnala-hover); border-color: var(--segnala-hover); }
.linguetta-segnala:focus-visible { outline: 2px solid var(--segnala-fg); outline-offset: -4px; }

.modulo-segnala {
  width: min(40rem, 94vw);
  max-height: 90vh;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--on-surface);
  box-shadow: 0 20px 50px rgba(15, 23, 42, 0.35);
  overflow: hidden;
}
.modulo-segnala::backdrop { background: rgba(15, 23, 42, 0.55); }
.modulo-segnala form { display: flex; flex-direction: column; max-height: 90vh; }
.modulo-testa, .modulo-piede {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--md);
  padding: var(--sm) var(--md);
  background: var(--bg-inset);
}
.modulo-testa { border-bottom: 1px solid var(--border-subtle); }
.modulo-testa h2 { margin: 0; font-size: 16px; }
.modulo-piede { border-top: 1px solid var(--border-subtle); justify-content: flex-start; }
.modulo-corpo { padding: var(--md); overflow-y: auto; }
.modulo-corpo .field { margin-bottom: var(--md); }
.modulo-corpo input[type="text"], .modulo-corpo textarea, .modulo-corpo select {
  width: 100%;
  max-width: none;
}
.anteprima-scatto {
  display: block;
  margin-top: var(--sm);
  max-width: 100%;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
}

/* Nella pagina di dettaglio: lo scatto e l'istantanea. */
img.scatto {
  display: block;
  max-width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
iframe.istantanea {
  width: 100%;
  height: 28rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
}

@media (max-width: 900px) {
  /* Su telefono la linguetta si accorcia: a mezza altezza di uno schermo stretto una
     parola intera in verticale è una barra che attraversa mezza pagina. */
  .linguetta-segnala { padding: 0.7rem 0.35rem; font-size: 11.5px; }
  iframe.istantanea { height: 20rem; }
}

/* L'immagine allegata a una domanda in stanza: sta nel filo, non in un allegato da
   scaricare — una domanda su uno screenshot si legge guardando lo screenshot. */
img.immagine-stanza {
  display: block;
  margin-top: var(--sm);
  max-width: min(30rem, 100%);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.allega-immagine { flex-wrap: wrap; gap: var(--sm); }
.allega-immagine .muted { font-size: 12.5px; }

/* ==========================================================================
   IL PUNTO INTERROGATIVO CHE APRE UNA SPIEGAZIONE
   ==========================================================================
   Sotto ogni casella di scrittura c'erano tre righe che spiegavano i pulsanti. Si
   leggono una volta e poi si guardano tutti i giorni: occupavano un terzo dello spazio
   della chat per un'informazione che chi scrive ha già. Qui stanno dietro un `?`.

   È un `<details>`, quindi si apre **senza JavaScript**: il popup è solo posizionamento,
   e senza CSS resta un blocco di testo che compare sotto — brutto e funzionante. */
.aiuto { position: relative; display: inline-flex; }
.aiuto > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 1px solid var(--border);
  border-radius: 9999px;
  background: var(--bg-surface);
  color: var(--secondary);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 700;
  cursor: help;
  list-style: none;
}
.aiuto > summary::-webkit-details-marker { display: none; }
.aiuto > summary::marker { content: ""; }
.aiuto > summary:hover { border-color: var(--primary-border); background: var(--primary-soft); color: var(--on-primary-soft); }
.aiuto > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.aiuto[open] > summary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.aiuto-pannello {
  position: absolute;
  /* Sale **sopra** il punto interrogativo: sotto ci sono la casella e i pulsanti, e un
     riquadro che li copre nasconde proprio ciò di cui sta parlando. */
  bottom: calc(100% + var(--xs));
  left: 0;
  z-index: 30;
  width: min(30rem, 88vw);
  padding: var(--md);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.24);
}
.aiuto-pannello p { margin: 0 0 var(--sm); max-width: none; }
.aiuto-pannello p:last-child { margin-bottom: 0; }
/* Vicino al bordo destro si apre verso sinistra, o esce dallo schermo. */
.aiuto.a-sinistra .aiuto-pannello { left: auto; right: 0; }

/* **Su schermo stretto non si apre da nessuna parte: si posa in fondo.** A 390px un
   riquadro da 30rem ancorato a un bottone esce comunque — misurato, 152 pixel fuori dal
   bordo — e la scelta fra «verso destra» e «verso sinistra» non è più fra due opzioni
   buone. `fixed` a tutta larghezza toglie la domanda. */
@media (max-width: 900px) {
  .aiuto-pannello {
    position: fixed;
    left: var(--sm);
    right: var(--sm);
    bottom: var(--md);
    top: auto;
    width: auto;
    max-height: 60vh;
    overflow-y: auto;
    z-index: 60;
  }
  .aiuto.a-sinistra .aiuto-pannello { left: var(--sm); right: var(--sm); }
}

/* La casella di scrittura cresce con quello che si scrive, invece di tenere tre righe di
   vuoto per una domanda di quattro parole. Il tetto è perché una chat resti una chat. */
.composer textarea, #invia textarea {
  min-height: 3.2rem;
  max-height: 40vh;
  resize: vertical;
}

/* ----------------------------------------- «il sistema è stato aggiornato»
   Una striscia in cima, larga quanto la finestra, che compare **solo** quando il codice
   in produzione non è più quello che ha disegnato questa pagina. Non è un avviso di
   cortesia: una scheda lasciata aperta durante un deploy continua a mostrare i pulsanti
   di prima e a chiamare rotte che possono non esserci più, e chi la usa non ha modo di
   accorgersene — se non da un errore che sembra un difetto nostro.

   `sticky` e non `fixed`: deve restare visibile scorrendo, ma senza coprire niente
   quando la pagina è in cima. */
.aggiornamento {
  position: sticky;
  top: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sm) var(--md);
  padding: var(--sm) var(--md);
  background: var(--aggiorna-bg);
  color: var(--aggiorna-fg);
  border-bottom: 1px solid var(--aggiorna-br);
  box-shadow: var(--shadow-1);
}
/* `flex: 1 1 20rem` e non `flex: 1`: sotto le venti righe di base il testo **va a capo
   da solo**, cioè scende su una riga sua e i due pulsanti passano sotto. Con `flex: 1` la
   riga restava una sola e il testo si stringeva fino alla parola più larga — a 390px
   usciva **una parola per riga**, che è lo stesso difetto del riquadro «Cosa manca». */
.aggiornamento .testo { flex: 1 1 20rem; font-size: 14px; }
.aggiornamento .testo b { font-weight: 700; }
.aggiornamento .tech { font-family: var(--font-mono); font-size: 13px; }
.aggiornamento .azioni { display: flex; gap: var(--sm); flex-wrap: wrap; }
/* Quaranta pixel di altezza, che è la soglia del giro dell'interfaccia: questi due si
   premono col dito su un telefono, e `btn-sm` ne fa ventotto. Un bersaglio sotto soglia
   in una striscia che compare da sola è il posto peggiore dove metterlo — chi la vede
   la sta già subendo. */
.aggiornamento .btn { min-height: 40px; padding-inline: var(--md); }
/* I due pulsanti hanno lo stesso peso visivo che hanno nella decisione: ricaricare è
   la cosa da fare, «ho già aggiornato» è la via d'uscita per chi sa cosa sta facendo. */
.aggiornamento .btn-ricarica {
  background: #ffffff;
  color: var(--aggiorna-azione-fg);
  border: 1px solid #ffffff;
  font-weight: 600;
}
.aggiornamento .btn-ricarica:hover { background: #f3f0ff; border-color: #f3f0ff; }
.aggiornamento .btn-dopo {
  background: transparent;
  color: var(--aggiorna-fg);
  border: 1px solid var(--aggiorna-fg);
}
.aggiornamento .btn-dopo:hover { background: rgba(255, 255, 255, 0.14); }
.aggiornamento .btn:focus-visible { outline: 2px solid #ffffff; outline-offset: 2px; }

/* ------------------------------------------- la colonna stretta (solo da 900px in su)
   Quindici rem di navigazione sono giuste finché si naviga, e di troppo quando si legge
   un log o un diff — che è metà di quello che si fa qui dentro. Stretta la colonna resta
   **usabile**, non solo presente: l'icona di ogni voce è diversa da tutte le altre, il
   nome resta nel `title`, e «sei qui» continua a vedersi perché è un fondo pieno.

   Lo stato sta sull'elemento radice e si applica **prima** di disegnare (vedi lo script
   in testa): messo dopo, ogni pagina si aprirebbe larga e si stringerebbe sotto gli
   occhi. */
@media (min-width: 900px) {
  .colonna-stretta .sidebar {
    width: 3.75rem;
    flex: 0 0 3.75rem;
    padding-left: var(--sm);
    padding-right: var(--sm);
    align-items: stretch;
  }
  .colonna-stretta .sidebar .voce,
  .colonna-stretta .sidebar .search,
  /* Stretta resta il solo marchio: adesso è una figura, e una figura da sola è
     riconoscibile — il nome scritto a metà non lo era. */
  .colonna-stretta .brand .nome,
  .colonna-stretta .brand .rilascio { display: none; }
  .colonna-stretta .brand .marchio { margin-right: 0; }
  .colonna-stretta .testa-colonna { flex-direction: column; gap: var(--xs); }
  .colonna-stretta .stringi-colonna svg { transform: rotate(180deg); }
  .colonna-stretta .cerca-stretta {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 2.25rem;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
  }
  .colonna-stretta .cerca-stretta:hover { background: var(--bg-inset); color: var(--primary); }
  .colonna-stretta .sidebar nav a {
    position: relative;
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
  }
  /* Il contatore non ha più una riga accanto a cui stare: si posa sull'icona, come fa
     ovunque un pallino di notifica. Senza questa riga spariva insieme al nome — cioè
     spariva proprio la cosa per cui si guarda la colonna. */
  .colonna-stretta .sidebar nav a .badge-count {
    position: absolute;
    top: 0;
    right: 2px;
    min-width: 1rem;
    height: 1rem;
    padding: 0 0.22rem;
    font-size: 10px;
    /* Un filo di fondo pagina attorno: sull'icona il pallino deve staccarsi, come si
       stacca dal quadrato di un'applicazione. */
    box-shadow: 0 0 0 2px var(--bg-surface);
  }
  .colonna-stretta .sidebar-fondo { flex-direction: column; gap: var(--xs); }
  .colonna-stretta .chi-sono { justify-content: center; }
  .colonna-stretta .esci { padding-left: var(--sm); padding-right: var(--sm); }
}

/* ------------------------------------------------- l'intestazione di una pagina
   **Un posto fisso dove guardare.** Prima ogni pagina cominciava con quattro cose
   impilate — briciole, titolo, riga tecnica, indice delle sezioni — separate dalla stessa
   aria che separa i riquadri fra loro: da lontano erano contenuto come tutto il resto, e
   le azioni di quell'oggetto stavano sparse tre riquadri più in basso.

   Adesso sono una fascia sola, chiusa da una riga: sopra c'è **di cosa stai parlando e
   cosa puoi farci**, sotto c'è il lavoro. Non è appiccicata in cima alla finestra di
   proposito: la striscia dell'aggiornamento sta già lì, e due cose appiccicate che si
   sovrappongono sono peggio di una che scorre. */
.pagina-testa {
  display: flex;
  flex-direction: column;
  gap: var(--sm);
  margin-bottom: var(--lg);
  padding-bottom: 0;
  border-bottom: 1px solid var(--border-subtle);
}
.pagina-testa > .briciole { margin-bottom: 0; }
.pagina-testa > h1 { margin: 0; }
.pagina-testa > p { margin: 0; }
/* Le schede chiudono la fascia: la loro riga È la riga della fascia, non una seconda. */
.pagina-testa > .subnav { margin-bottom: -1px; }
/* La barra delle azioni dentro la fascia **tiene il suo riquadro**: sulla pagina di un
   run non porta solo pulsanti, porta l'esito e il conto delle osservazioni — testo che
   senza un contorno galleggia. Cambia solo l'aria attorno, perché la fascia ne ha già. */
.pagina-testa > .riepilogo { margin: var(--xs) 0 var(--md); }
@media (max-width: 700px) {
  .pagina-testa { gap: var(--xs); }
}

/* --------------------------------------------------------------- gli avvisi
   Il non letto si vede **prima di leggere**: un filo colorato sul bordo sinistro e il
   fondo appena più acceso. Il grassetto sul testo no — un elenco di notizie tutte in
   grassetto è un elenco senza gerarchia, e le notizie sono già corte. */
/* La riga intera è il comando: `display:block` perché è un `<a>` che contiene righe, e
   il colore del testo resta quello del testo — un elenco tutto azzurro non si legge. */
a.avviso { display: block; color: inherit; }
a.avviso:hover { text-decoration: none; border-color: var(--primary-border); }
.avviso .head { display: flex; align-items: center; justify-content: space-between; gap: var(--sm); }
.avviso .testo-avviso { display: block; margin-top: var(--xs); }
/* Il dettaglio conserva gli a capo: l'elenco dei cambiamenti di un rilascio è una riga
   per cambiamento, e su una riga sola diventa un paragrafo che non si legge. */
.avviso .dettaglio-avviso {
  display: block;
  margin-top: var(--xs);
  white-space: pre-line;
  color: var(--text-muted);
  font-size: 13px;
}
.avviso.da-leggere {
  border-left: 3px solid var(--primary);
  background: var(--primary-soft);
}
.avviso .head .label { display: inline-flex; align-items: center; gap: var(--xs); }

/* ---------------------------------------------------------------------------
   Una scelta fra pochi valori: pulsanti, non un menù
   ---------------------------------------------------------------------------
   Un `<select>` con tre voci nasconde due opzioni dietro la prima, e chiede due gesti
   invece di uno. Qui le opzioni si vedono tutte e quella attiva si riconosce senza
   aprire niente.

   **Sono radio con l'aspetto di pulsanti**, non pulsanti: la form funziona senza
   JavaScript, le frecce muovono la selezione, e un lettore di schermo annuncia un gruppo
   di scelta. Il radio resta nel DOM ma fuori dallo schermo — `display:none` lo
   toglierebbe dal percorso di tabulazione, e l'anello di fuoco andrebbe perso.

   **Pastiglie separate e non un blocco unico con i bordi uniti.** La colonna degli
   strumenti è stretta e i cinque livelli di sforzo vanno a capo: con i bordi uniti la
   riga sotto si presenta come un secondo gruppo, e il primo elemento a capo mostra un
   angolo arrotondato che non c'entra niente. Il `gap` è la scelta che regge alla
   larghezza vera.

   L'altezza è **40px** e non i 36 di `.btn`: è la soglia di `scripts/giro-interfaccia.py`
   per un bersaglio da premere col dito, e su un controllo nuovo non c'è motivo di
   ereditare un valore che sta appena sotto. */
.scelta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--xs);
}
.scelta input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  pointer-events: none;
}
.scelta label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.5rem;
  padding: 0.4rem 0.8rem;
  font-family: var(--font-ui);
  font-size: 13.5px;
  font-weight: 600;
  border: 1px solid var(--primary-border);
  border-radius: var(--radius-sm);
  background: var(--primary-soft);
  color: var(--on-primary-soft);
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}
.scelta label:hover { background: var(--primary-soft-hover); border-color: var(--primary); }
/* La scelta attiva è **piena**, non un bordo più spesso: su tre pastiglie affiancate un
   bordo si confronta, un fondo si vede.

   Il testo è `--on-primary` e **non `#fff`**. Era bianco scritto a mano, e sul tema scuro
   `--primary` è un azzurro chiaro (`#93b4ff`): bianco su quello dà **2,06** di contrasto,
   dove il minimo leggibile è 4,5. Misurato il 3 settembre 2026 chiedendo al browser i due
   colori calcolati — il giro d'interfaccia non l'aveva visto, perché guarda il testo su
   fondo trasparente e non questa coppia. Con il token: 6,7 sul chiaro, 11,8 sullo scuro,
   ed è lo stesso paio che usa `.btn-primary`. Un colore scritto a mano in un tema con due
   palette è un colore giusto in una delle due. */
.scelta input[type="radio"]:checked + label {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
}
.scelta input[type="radio"]:focus-visible + label {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
/* Disattivata **e col perché nel `title`**: una pastiglia spenta senza motivo è un
   divieto che si prova a forzare. Lo stesso criterio del menù di prima, che scriveva il
   motivo accanto al nome. */
.scelta input[type="radio"]:disabled + label {
  opacity: .5;
  cursor: not-allowed;
  background: var(--surface-variant, var(--primary-soft));
}
.scelta input[type="radio"]:disabled + label:hover { border-color: var(--primary-border); }
/* Un gruppo che riguarda solo un agente compare quando serve. Senza JavaScript restano
   visibili tutti: i valori sono comunque validi, e il server li controlla. */
.scelta-legata[hidden] { display: none; }

/* ---------------------------------------------------------------------------
   Il campo «scegli un file»
   ---------------------------------------------------------------------------
   Era il controllo nativo, e in mezzo a pastiglie e pulsanti tonali si presentava come
   un pezzo di un'altra applicazione: bordo di sistema, «Choose Files» in inglese
   qualunque sia la lingua della pagina, altezza sua. Il testo accanto («No file
   chosen») lo scrive il browser e non si tocca — quello resta, ed è giusto che resti:
   dice se un file è stato scelto.
   Si veste **il suo pulsante** (`::file-selector-button`), che è la parte che si preme,
   con le stesse regole di `.btn` e la stessa altezza di 40px delle pastiglie. */
input[type="file"] {
  font-family: var(--font-ui);
  font-size: 13px;
  color: var(--on-surface-variant);
  max-width: 100%;
}
input[type="file"]::file-selector-button {
  font-family: var(--font-ui);
  font-size: 13.5px;
  font-weight: 600;
  min-height: 2.5rem;
  padding: 0.4rem 0.85rem;
  margin-right: var(--sm);
  border: 1px solid var(--primary-border);
  border-radius: var(--radius-sm);
  background: var(--primary-soft);
  color: var(--on-primary-soft);
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease;
}
input[type="file"]::file-selector-button:hover {
  background: var(--primary-soft-hover);
  border-color: var(--primary);
}
