/* =========================================================================
   Serenlibrità — Base CSS (D-23 font vendorizzati, D-24 motion convention)
   =========================================================================

   CONVENZIONE MOTION (D-24) — LANDMINE DEL DESIGN HANDOFF:
   NEVER animate `opacity` on content. In un iframe/tab "throttled" un elemento
   che parte da opacity:0 può restare invisibile a frame 0 e non comparire mai
   (è esattamente il bug trovato nel prototipo). Quindi: ogni motion usa SOLO
   `transform` (translate/scale). Niente transizioni/animazioni su `opacity`
   per il contenuto. Questa regola vale per tutti i fogli delle fasi successive.
   ========================================================================= */

/* --- Font vendorizzati localmente (D-23, NO CDN) ------------------------ */
/* I woff2 vivono in static/fonts/ e sono GENERATI, non upstream: li produce
   `scripts/slim_web_fonts.py` dai TTF variabili in apps/library/assets/fonts/
   (gli stessi che usa il generatore di immagini social; lì sta anche OFL.txt).
   Assottigliati il 24-08-2026 da 487KB a 255KB restringendo gli assi variabili
   a quelli che questo foglio usa davvero — wght 400-600 (Newsreader) / 400-700
   (Hanken), opsz 12-44 — e tenendo i 425 codepoint che il sito rende davvero.
   ⚠️ Toccare i `font-weight` qui sotto, o usare un peso fuori range, vuol dire
   rigenerare i woff2: gli assi non coprono più tutto l'arco upstream. */
@font-face {
  font-family: "Newsreader";
  src: url("../fonts/Newsreader.9b0380a81dd6.woff2") format("woff2");
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Newsreader";
  src: url("../fonts/Newsreader-Italic.98e286dffc67.woff2") format("woff2");
  font-weight: 400 600;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Hanken Grotesk";
  src: url("../fonts/HankenGrotesk.4a5f0a9fc3e1.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

/* --- Reset minimale ----------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}
html {
  -webkit-text-size-adjust: 100%;
  /* Scroll: niente overscroll elastico oltre i limiti, che su mobile mostrava lo
     sfondo carta "dopo il footer" (segnalazione 2026-08-17). Blocca anche il chaining. */
  overscroll-behavior-y: none;
}
body {
  margin: 0;
  /* Sfondo "carta": gradiente morbido + texture a righe sottilissime (dal
     prototipo), su carta crema. */
  background:
    radial-gradient(circle at 18% 8%, rgba(255, 255, 255, 0.55), transparent 42%),
    repeating-linear-gradient(0deg, rgba(33, 27, 19, 0.016) 0 1px, transparent 1px 5px),
    var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-sans);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
}

/* --- Tipografia: i titoli usano il serif letterario --------------------- */
h1,
h2,
h3 {
  font-family: var(--font-serif);
  /* Phase 8 (UI-SPEC Typography): solo due pesi, 400 e 600 — i titoli usano 600. */
  font-weight: 600;
  line-height: var(--line-height-tight);
  letter-spacing: -0.4px;
  color: var(--color-ink);
}
em {
  font-style: italic;
  color: var(--color-ocra);
}

/* --- Topbar / brand ----------------------------------------------------- */
.sl-topbar {
  display: flex;
  align-items: center;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}
.sl-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  color: var(--color-ink);
}
.sl-brand-mark {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  color: var(--color-paper);
  display: grid;
  place-items: center;
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: 20px;
}
.sl-brand-name {
  font-family: var(--font-serif);
  /* Phase 8: `--font-size-lg` rimosso dalla scala; rimappato a `--font-size-h2`. */
  font-size: var(--font-size-h2);
  letter-spacing: 0.2px;
}

/* --- Layout principale -------------------------------------------------- */
.sl-main {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5) var(--space-7);
}
.sl-panel {
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-card);
}
.sl-eyebrow {
  text-transform: uppercase;
  letter-spacing: 1.5px;
  font-size: 12px;
  font-weight: 600;
  color: var(--color-primary);
  margin: 0 0 var(--space-2);
}
.sl-h1 {
  font-size: var(--font-size-h1);
  margin: 0 0 var(--space-4);
}
.sl-h2 {
  font-size: var(--font-size-h2);
  margin: var(--space-6) 0 var(--space-3);
}
.sl-lead {
  color: var(--color-text-warm);
  max-width: 52ch;
  margin: 0 0 var(--space-5);
}

/* --- Form --------------------------------------------------------------- */
.sl-form {
  display: grid;
  /* Colonna esplicita ``minmax(0, 1fr)``: senza, la griglia crea una colonna
     implicita ``auto`` dimensionata al max-content del campo più largo (es. il file
     input ~289px) → i campi sforavano il viewport <360px. La colonna fluida clampata
     fa stare/andare a capo i campi nella larghezza della card. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
/* Nessun controllo di form può eccedere la colonna del campo (anti overflow-x
   mobile): i file input hanno una larghezza intrinseca che sforava il viewport. I
   campi testo/number/select sono già ``width: 100%``; questo clampa il resto. */
.sl-field input,
.sl-field textarea,
.sl-field select {
  max-width: 100%;
}
.sl-field-row {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: 1fr;
}
.sl-field {
  display: block;
}
.sl-label {
  display: block;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-warm);
  margin-bottom: var(--space-1);
}
/* CLEAN-01 (2026-09-01): `.sl-input` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-input:focus` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */

/* Icone SVG inline dentro testo e bottoni. Esistono perché le emoji usate come
   icone escono come rettangolo vuoto su ogni sistema senza font emoji (verificato
   2026-08-27: 5 glifi su 5 fuori dal BMP mancanti in chromium senza Noto Color
   Emoji). Un SVG inline non dipende dai font installati. */
.sl-icon {
  vertical-align: -0.18em;
  flex: none;
}
/* --- Bottone primario --------------------------------------------------- */
/* Motion SOLO via transform (D-24): l'hover solleva con translateY, mai con
   opacity. Transizione esplicitamente limitata a transform + box-shadow. */
/* CLEAN-01 (2026-09-01): `.sl-btn` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-btn:hover` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-btn:active` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* Bottone reso come link (es. "Accedi" post reset password): niente sottolineatura. */
/* CLEAN-01 (2026-09-01): `a.sl-btn` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */

/* --- Lista GeoPing ------------------------------------------------------ */
.sl-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}
.sl-list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
/* .sl-list-item--stack: la stessa cornice, ma il contenuto IMPILATO.
   ⚠️ Perche' esiste (UAT umana dei gruppi, 2026-08-27). `.sl-list-item` qui sopra e'
   `display: flex` + `space-between`: e' pensata per UNA riga con due figli — etichetta a
   sinistra, valore a destra. Con tre o piu' figli quei figli diventano COLONNE, e su un
   telefono da 390px il risultato e' illeggibile: il nome di un gruppo si spezzava
   verticalmente ("Lettori / del / giovedi"), il testo di un post finiva in una colonna da
   dodici caratteri e la card del libro SBORDAVA fuori dal riquadro portandosi via il
   bottone "Segnala".
   Il difetto era PREESISTENTE — un post aveva gia' quattro figli — ma nessuno l'aveva mai
   visto perche' i test asseriscono la presenza del contenuto, non la sua disposizione. E'
   esattamente cio' per cui serviva guardare le pagine con gli occhi. */
.sl-list-item--stack {
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  gap: var(--space-2);
}
/* Niente puo' sfondare la cornice: le card e i testi lunghi restano dentro. */
.sl-list-item--stack > * {
  max-width: 100%;
  min-width: 0;
}

.sl-list-label {
  font-weight: 600;
}
.sl-list-distance {
  white-space: nowrap; /* evita che "m" vada a capo (fix dal prototipo) */
  color: var(--color-text-warm);
  font-variant-numeric: tabular-nums;
}
.sl-list-empty {
  color: var(--color-text-warm);
  font-style: italic;
}

/* =========================================================================
   AGGIUNTE PHASE 8 — componenti .sl-* dell'app-shell (additive, D-21/D-24)
   =========================================================================
   Tutti i componenti seguono la convenzione MOTION (D-24): SOLO `transform`,
   mai `opacity` sul contenuto. Ogni classe animata ha il suo no-op nel blocco
   `prefers-reduced-motion: reduce` in fondo. Mobile-first: le regole base sono
   per mobile, le aggiunte desktop stanno sotto `@media (min-width: 860px)`.
   NB: queste classi sono ADDITIVE — non rimpiazzano né alterano le .sl-*
   esistenti (i template attuali continuano a rendere identici finché i Plan
   02+ non li riscrivono). I `@keyframes` d'ingresso sono `transform`-only:
   il pulse-su-opacity del mockup (landmine D-24/G-04) è volutamente NON portato
   ed è sostituito da un pulse in `scale`. ===================================== */

/* --- @keyframes d'ingresso: SOLO transform (D-24) ----------------------- */
/* Risalita d'ingresso (card/toast): solo translateY, mai opacity. */
@keyframes slUp {
  from { transform: translateY(8px); }
  to   { transform: translateY(0); }
}
/* "Fade" reinterpretato senza opacity: micro-translate (D-24, anti-landmine). */
@keyframes slFade {
  from { transform: translateY(4px); }
  to   { transform: translateY(0); }
}
/* Pop d'ingresso (badge/chip): scala, mai opacity. */
@keyframes slPop {
  from { transform: scale(0.96); }
  to   { transform: scale(1); }
}
/* Pulse: sostituisce il pulse-su-opacity del mockup con uno scale (D-24, G-04). */
@keyframes slScalePulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.04); }
}

/* --- App shell: wrapper a griglia (rimpiazza .sl-main a livello base.html) */
/* Mobile-first: singola colonna (la sidebar è nascosta, c'è la tab-bar). */
.sl-app-shell {
  display: grid;
  /* ``minmax(0, 1fr)`` non ``1fr``: una colonna grid ``1fr`` ha min-width:auto e si
     espande al max-content del figlio più largo, sfondando il viewport (overflow-x
     orizzontale su mobile). ``minmax(0, …)`` la clampa alla larghezza disponibile e
     forza i figli a rientrare/andare a capo. */
  grid-template-columns: minmax(0, 1fr);
  /* ⛔ Su iOS ``100vh`` NON è l'altezza visibile: è la *large viewport height*, cioè
     quella che la pagina avrebbe con le barre di Safari RITRATTE. Con le barre visibili
     — lo stato normale all'apertura — l'area vera è 80-110px più bassa, il guscio resta
     più alto della finestra e la pagina scorre di quella quantità oltre il footer.
     Trovato il 09-09-2026 dalla UAT dell'owner sulla HOME PUBBLICA, da anonimo: lì la
     tab-bar e il suo spaziatore (56-17) non esistono nemmeno, quindi la causa non poteva
     che essere qui sotto. Difetto del GUSCIO, preesistente e di TUTTE le pagine per tutti
     gli utenti su iOS — non della fase 56, che l'ha solo fatto emergere.
     L'ordine della coppia è la correzione: il ripiego prima, l'unità dinamica dopo, perché
     nella cascata vince l'ultima capita. ``dvh`` e non ``svh``: segue il viewport MENTRE le
     barre entrano ed escono, e non lascia una fascia vuota in fondo alle pagine corte. Il
     ``100svh`` del wizard (``.sl-sequenza-passo`` e sorelle) risponde al criterio OPPOSTO —
     là il contenuto deve ENTRARE senza scorrere — e non va uniformato a questo.
     Guardia: apps/core/tests/test_guscio_altezza_viewport.py. */
  min-height: 100vh; /* ripiego per i browser senza le unità dinamiche */
  min-height: 100dvh; /* iOS: segue il viewport VISIBILE, barre incluse */
  /* ``min-height`` a piena altezza su un grid con righe ``auto`` distribuirebbe lo spazio
     verticale libero NELLE righe (align-content di default = stretch): su una pagina
     corta la topbar mobile (riga auto) si gonfiava da ~63px a ~225px (mezzo schermo).
     ``align-content: start`` impacchetta le righe in alto a altezza-contenuto — la
     topbar resta compatta; lo spazio libero resta in fondo (sfondo carta). Desktop
     (colonne sidebar+main, sidebar height:100vh) invariato (verificato a 1024px). */
  align-content: start;
}

/* --- Sidebar desktop (sticky, ≥860px) ----------------------------------- */
/* Pattern dal .sl-panel ma sticky a tutta altezza, sfondo carta-sidebar. */
.sl-sidebar {
  display: none; /* mobile: nascosta, navigazione via tab-bar in basso */
  position: sticky;
  top: 0;
  height: 100vh;
  padding: var(--space-5) var(--space-4);
  background: var(--color-sidebar);
  border-right: 1px solid var(--color-border);
}
.sl-sidebar__logo {
  display: block;
  margin-bottom: var(--space-5);
}
.sl-sidebar__nav {
  display: grid;
  gap: var(--space-1);
}
/* Item di nav: pillola; l'attivo prende il token --color-nav-active. */
.sl-nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  text-decoration: none;
  color: var(--color-text-warm);
  font-weight: 600;
  transition: transform 0.15s ease;
}
.sl-nav-item:hover {
  transform: translateX(2px);
}
/* CLEAN-01 (2026-09-01): `.sl-nav-item--active` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */

/* --- Topbar mobile (sticky, <860px): carta traslucida + blur ------------ */
.sl-topbar-mobile {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  background: rgba(252, 248, 239, 0.82); /* paper-light traslucida */
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--color-border);
}

/* --- Tab-bar mobile (FIXED bottom, 5 tab, safe-area iOS) ----------------- */
/* FIXED (non sticky): una bottom-bar deve restare ancorata al viewport SEMPRE.
   Con `position: sticky` la barra si "sgancia" e fluttua a metà pagina quando il
   contenuto è più corto del viewport (bug "rimane bloccata al centro scrollando").
   `.sl-main-content` riserva già padding-bottom = altezza barra, quindi fixed non
   nasconde nulla. Resta nascosta ≥860px (media query in fondo). */
.sl-tab-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: flex;
  justify-content: space-around;
  align-items: center; /* center: "Pubblica" allineata verticalmente coi 5 tab */
  padding: var(--space-2) var(--space-2);
  padding-bottom: calc(var(--space-2) + env(safe-area-inset-bottom));
  background: var(--color-paper-light);
  border-top: 1px solid var(--color-border);
}
/* I tab ereditano .sl-nav-item (orizzontale, largo per la sidebar): nel tab-bar
   diventano verticali e flex:1 così i 5 tab stanno in 320px senza overflow
   orizzontale (icona impilata sopra la micro-etichetta, UI-SPEC). */
.sl-tab-bar .sl-nav-item {
  flex: 1 1 0;
  min-width: 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: var(--space-1) 2px;
  font-size: 11px;
  line-height: 1.1;
  text-align: center;
}
.sl-tab-bar .sl-nav-item svg { flex-shrink: 0; }
/* Attivo tab-bar (redesign 2026-07-04): niente pillola di sfondo — un pallino ocra
   sopra l'icona, stile editoriale coerente col masthead. */
.sl-tab-bar .sl-nav-item--active {
  background: transparent;
  color: var(--color-ink);
  position: relative;
}
.sl-tab-bar .sl-nav-item--active::before {
  content: "";
  position: absolute;
  top: 1px;
  left: 50%;
  transform: translateX(-50%);
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--color-ocra);
}
/* FAB centrale "Pubblica": cerchio terracotta rialzato + micro-etichetta sotto
   (A11Y: label visibile, non icona-sola). Strip del chrome .sl-btn ereditato. */
.sl-tab-bar .sl-tab-bar__fab {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  width: auto;
  padding: 0;
  background: transparent;
  box-shadow: none;
  border-radius: 0;
  color: var(--color-primary);
  font-size: 11px;
  font-weight: 700;
  line-height: 1.1;
  text-decoration: none;
}
.sl-tab-bar .sl-tab-bar__fab:hover {
  transform: none;
  box-shadow: none;
}
.sl-tab-bar__fab svg {
  width: 46px;
  height: 46px;
  padding: 11px;
  box-sizing: border-box;
  background: var(--color-primary);
  color: #fff;
  border-radius: 50%;
  margin-top: -20px;
  box-shadow: 0 8px 16px rgba(184, 80, 47, 0.4);
  flex-shrink: 0;
}

/* =========================================================================
   MASTHEAD editoriale (redesign 2026-07-04) — barra orizzontale desktop che
   rimpiazza la sidebar verticale. Mobile: nascosta (topbar sottile + tab-bar).
   ========================================================================= */
.sl-masthead {
  display: none; /* mobile: nascosta; compare ≥860px (media query in coda) */
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--color-paper-light);
  border-bottom: 2px solid var(--color-ocra);
}
.sl-masthead__inner {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 var(--space-6);
  height: 74px;
}
.sl-masthead__brand {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}
.sl-masthead__brand img {
  display: block;
}
/* Il logo orizzontale della rev. agosto 2026 e' molto piu' largo a parita' di
   altezza (rapporto 5,45 contro il 3,64 del precedente): a 34px passa da 124 a
   185px e spinge di 61px tutto il cluster di destra. Fra 860 e 1100px la
   masthead quello spazio non ce l'ha (il contenuto sfora gia' oggi), quindi li'
   il brand scende a 23px: 125px di larghezza, cioe' gli stessi 124px di prima, e
   la fascia stretta resta com'era. Sopra i 1100px il logo sta a 34px pieni.
   23px non e' un numero a caso: e' il valore che riporta la larghezza a quella
   del logo precedente restando sopra i 120px minimi del brand book. */
@media (max-width: 1100px) {
  .sl-masthead__brand img {
    height: 23px;
  }
}
/* Fascia stretta 860-1023px: è quella in cui il masthead compare per la prima volta e ha
   il minimo di spazio. La barra chiedeva 1102px (misurato in italiano, con Gruppi attivo:
   è il caso peggiore) e sforava su tutta la fascia. Spostare lingua e social nel menù
   avatar ne toglie 233, ma a 860px netti ne restavano ancora 9 di troppo. Qui si stringe
   il resto: spaziature giù di uno scalino (24 → 16) e le due azioni ridotte a sola icona.
   Larghezza finale richiesta, rimisurata nel caso peggiore (italiano, Gruppi attivo,
   badge non-letti acceso): 800px fra 860 e 1023, 973 fra 1024 e 1099 (là tornano
   etichette e spaziature piene), 1033 da 1100 in su (là il logo torna a 34px). Ogni
   fascia ha almeno 51px di margine sul proprio bordo sinistro. ⚠️ Se aggiungi una voce
   alla nav o un'azione, rimisura: questi tre numeri sono il budget, non un'opinione. */
@media (max-width: 1023px) {
  .sl-masthead__inner {
    gap: var(--space-4);
  }
  .sl-masthead__nav {
    gap: var(--space-4);
  }
  /* Pubblica e Notifiche restano solo icona. L'etichetta è nascosta VISIVAMENTE ma
     resta nel DOM (clip-path, mai display:none): così il nome accessibile del link
     continua a essere "Pubblica" / "Notifiche" per chi usa uno screen reader, e non
     serve un aria-label che duplichi la stringa.
     ⚠️ ``:not([id])`` non è pleonastico: dentro la campanella c'è un SECONDO span nudo,
     ``#sl-notif-badge``, il contenitore htmx del contatore non-letti. Senza escluderlo
     per id il badge sparirebbe proprio nella fascia in cui la barra è più stretta. */
  .sl-masthead .sl-btn--publish > span:not([class]):not([id]),
  .sl-masthead__actions .sl-nav-item > span:not([class]):not([id]) {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    overflow: hidden;
    white-space: nowrap;
  }
  /* Il bottone diventa un tondo vero: con il solo ``border-radius: 50%`` restava
     un'ellisse (la larghezza del .sl-btn non è legata all'altezza) e stonava accanto
     alla campanella e all'avatar, che tondi lo sono. 44px = il target minimo che il
     resto della chrome già usa (A11Y-01). */
  .sl-masthead .sl-btn--publish {
    gap: 0;
    width: 44px;
    height: 44px;
    padding: 0;
    justify-content: center;
    border-radius: 50%;
  }
  .sl-masthead__actions .sl-nav-item {
    gap: 0;
    padding: var(--space-3);
  }
}
/* MASTHEAD (app, >=860px): i social entrano solo da 1160px, e la ragione è aritmetica.
   Con i due tondi in riga la barra chiede, nel caso peggiore: 896px nella fascia
   860-1023 (etichette a sola icona, logo a 23px), 1069 fra 1024 e 1099 (tornano le
   etichette), 1129 da 1100 in su (torna anche il logo a 34px). Le tre fasce "ci stanno"
   a partire da 896, 1069 e 1129: una soglia sola piu' bassa di 1160 farebbe comparire i
   social, sparire, ricomparire mentre si ridimensiona la finestra. 1160 e' il primo
   valore monotono, con 31px di margine sul caso peggiore. Sotto, valgono quelli nel menu'. */
@media (min-width: 1160px) {
  .sl-masthead .sl-social-inline {
    display: inline-flex;
  }
  .sl-masthead .sl-menu-social {
    display: none;
  }
  /* qui lo spazio c'e': i tondi tornano ai 36px del disegno originale */
  .sl-masthead .sl-social-inline .sl-ig-link {
    width: 36px;
    height: 36px;
  }
  .sl-masthead .sl-social-inline .sl-ig-link svg {
    width: 19px;
    height: 19px;
  }
}
.sl-masthead__nav {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin-left: var(--space-2);
}
.sl-masthead__link {
  position: relative;
  font-family: var(--font-serif);
  font-size: 16.5px;
  font-weight: 500;
  color: var(--color-text-warm);
  text-decoration: none;
  padding: 6px 2px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  transition: color 0.15s ease;
}
.sl-masthead__link:hover {
  color: var(--color-ink);
}
.sl-masthead__link--active {
  color: var(--color-ink);
}
.sl-masthead__link--active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 2px;
  background: var(--color-ocra);
  border-radius: 2px;
}
.sl-masthead__actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
/* Pubblica nel masthead: pillola terracotta a CONTORNO (non la CTA piena della sidebar).
   ``flex: 0 0 auto`` + ``white-space: nowrap`` non sono decorativi: senza, quando la barra
   è più larga del viewport è QUESTO bottone a cedere, e l'etichetta si spezza lettera per
   lettera in colonna ("Pu / bb / lic / a", visto a 880px il 2026-08-28). Un cuscinetto del
   genere nasconde lo sfondamento invece di segnalarlo. */
.sl-masthead .sl-btn--publish {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  white-space: nowrap;
  gap: 7px;
  width: auto;
  padding: 8px 16px;
  border-radius: 999px;
  background: transparent;
  color: var(--color-primary);
  border: 1.5px solid var(--color-primary);
  font-weight: 700;
  font-size: 14px;
  box-shadow: none;
}
.sl-masthead .sl-btn--publish:hover {
  background: var(--color-primary);
  color: var(--color-paper-light);
}

/* --- Link social IG/FB (masthead + topbar mobile) ------------------------
   La classe è condivisa da _instagram_link.html e _facebook_link.html. */
.sl-ig-link {
  display: inline-grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  color: var(--color-text-warm);
  border: 1.5px solid var(--color-border);
  flex: 0 0 auto;
  transition: transform 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.sl-ig-link svg {
  width: 19px;
  height: 19px;
}
.sl-ig-link:hover {
  color: var(--color-primary);
  border-color: var(--color-primary);
  transform: translateY(-1px);
}

/* --- Menù avatar (details/summary dropdown, no-JS) ----------------------- */
.sl-avatar-menu {
  position: relative;
  flex: 0 0 auto;
}
.sl-avatar-menu__trigger {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  border-radius: 50%;
}
.sl-avatar-menu__trigger::-webkit-details-marker {
  display: none;
}
.sl-avatar-menu__trigger:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 2px;
}
.sl-avatar-menu[open] .sl-avatar-round {
  box-shadow: 0 0 0 3px rgba(184, 80, 47, 0.25);
}
.sl-avatar-menu__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 10px);
  z-index: 40;
  min-width: 236px;   /* regge il piede: segmentato lingua + due tondi social in riga */
  padding: var(--space-2);
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: 0 14px 34px rgba(33, 27, 19, 0.2);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sl-avatar-menu__name {
  font-weight: 700;
  font-size: 13px;
  color: var(--color-ink);
  padding: 6px 10px 8px;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: 4px;
}
.sl-avatar-menu__item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: 8px;
  color: var(--color-text-warm);
  text-decoration: none;
  font-weight: 600;
  font-size: 14px;
}
.sl-avatar-menu__item svg {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
}
.sl-avatar-menu__item:hover {
  background: var(--color-paper);
  color: var(--color-ink);
}
.sl-avatar-menu__item--logout {
  color: var(--color-primary);
}
/* Lista dei menuitem: è lei ad avere role="menu", non il pannello (dentro un
   role="menu" possono stare solo menuitem, e il pannello contiene anche il nome
   utente e il piede lingua/social). Eredita la colonna che prima stava sul pannello. */
.sl-avatar-menu__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* Piede del menù (2026-08-28): preferenza lingua + social, separati dalle voci di
   navigazione da una riga. Qui finisce quello che prima stava sciolto nella barra:
   con il logo della rev. agosto 2026 (185px invece di 124 a parità d'altezza) la
   riga non lo reggeva più, né su telefono né nella fascia 860-1100px. */
.sl-avatar-menu__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-top: 6px;
  padding: 10px 10px 4px;
  border-top: 1px solid var(--color-border);
}
/* --- Social: a vista dove entrano, nel menù dove non entrano (28-08-2026) ---
   Regola chiesta da Alberto. Le copie in markup sono DUE — ``.sl-social-inline`` nella
   barra (partials/_social_bar.html) e ``.sl-menu-social`` nel menù — e ne è visibile
   sempre e solo UNA: l'altra è ``display: none``, quindi sparisce anche dall'albero di
   accessibilità e nessuno sente "Instagram" due volte. Il default è la copia nel menù,
   così una shell nuova parte dal caso che sta sempre dentro; è la barra che deve
   guadagnarsi lo spazio, non il contrario.

   Le tre soglie qui sotto NON sono a occhio: sono la larghezza naturale della riga
   (figli a max-content, sommati coi gap) misurata nel caso peggiore — italiano, Gruppi
   attivi, badge non-letti acceso. ⚠️ Rimisurale se aggiungi una voce o un'azione. */
.sl-menu-social {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.sl-social-inline {
  display: none;
  align-items: center;
  gap: var(--space-2);
}
/* I tondi in barra sono 32px, non 36: sui telefoni quei 4px per lato sono la differenza
   fra entrarci e no, e accanto all'avatar (36) non stonano. Nel masthead, dove di spazio
   ce n'è, restano 36. */
.sl-social-inline .sl-ig-link {
  width: 32px;
  height: 32px;
}
.sl-social-inline .sl-ig-link svg {
  width: 17px;
  height: 17px;
}

/* TOPBAR MOBILE (app, <860px): ci stanno SEMPRE, anche a 320px — la riga è logo 152 +
   due tondi + avatar e chiede 303px. Nessuna soglia, nessuna media query. */
.sl-topbar-mobile .sl-social-inline {
  display: inline-flex;
}
.sl-topbar-mobile .sl-menu-social {
  display: none;
}
/* I tondi social nel menù sono più piccoli dei 36px della barra: qui competono con
   il segmentato lingua dentro un pannello stretto, non con una riga larga. */
.sl-avatar-menu__foot .sl-ig-link {
  width: 32px;
  height: 32px;
}
.sl-avatar-menu__foot .sl-ig-link svg {
  width: 17px;
  height: 17px;
}

/* --- Topbar mobile: brand + menù avatar (che contiene lingua e social) ---- */
.sl-topbar-mobile__brand {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}
.sl-topbar-mobile__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* --- Contenitore centrato del contenuto (allineato al masthead) ---------- */
.sl-main-content__inner {
  max-width: 1180px;
  margin: 0 auto;
}

/* --- Skip link: visibile al :focus via transform (mai opacity, D-24) ----- */
/* Fuori schermo finché non riceve focus da tastiera, poi entra con translateY. */
.sl-skip {
  position: absolute;
  left: var(--space-4);
  top: 0;
  z-index: 100;
  padding: var(--space-2) var(--space-4);
  background: var(--color-secondary);
  color: var(--color-paper);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  text-decoration: none;
  transform: translateY(-150%);
  transition: transform 0.15s ease;
}
.sl-skip:focus {
  transform: translateY(0);
}

/* --- Indicatore di caricamento lookup ISBN (htmx-request, display-based) -- */
/* Visibile SOLO durante la richiesta: htmx aggiunge .htmx-request all'elemento
   puntato da hx-indicator. display-based (non opacity) → niente spazio riservato. */
.sl-lookup-loading {
  display: none;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  color: var(--color-secondary);
  font-size: 14px;
}
.sl-lookup-loading.htmx-request {
  display: inline-flex;
}
.sl-spinner {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border: 2px solid var(--color-border);
  border-top-color: var(--color-secondary);
  border-radius: 50%;
  animation: sl-spin 0.7s linear infinite; /* transform-only (D-24) */
}
@keyframes sl-spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .sl-spinner {
    animation: none;
  }
}

/* --- Marcatore campo obbligatorio (asterisco) --------------------------- */
.sl-required {
  color: var(--color-primary);
  font-weight: 700;
}

/* --- Stato copertina nell'anteprima lookup ISBN ------------------------- */
.sl-cover-status {
  margin-top: var(--space-2);
  font-size: 14px;
}
.sl-cover-status--ok {
  color: var(--color-secondary);
}
.sl-cover-status--missing {
  color: var(--color-primary);
}

/* --- Dorso libro (cloth-spine): raggio asimmetrico tipo costola ---------- */
.sl-book-spine {
  border-radius: 4px 8px 8px 4px;
  overflow: hidden;
  transition: transform 0.15s ease;
}
.sl-book-spine:hover {
  transform: translateY(-3px);
}
/* Placeholder "stoffa" quando manca la copertina (decorativo, aria-hidden). */
.sl-book-spine__cloth {
  width: 100%;
  aspect-ratio: 2 / 3;
  background: linear-gradient(135deg, var(--color-secondary), var(--color-brand-ink));
}
.sl-book-spine__info {
  padding: var(--space-3);
}

/* --- Card libro (sposta gli stili inline di _shelf.html in classe) ------- */
.sl-book-card {
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  box-shadow: var(--shadow-card);
}

/* --- Dettaglio libro (Plan 08-08): cover + colonna info, scheda proprietario --- */
/* Layout mobile-first: copertina sopra le info; a ≥860px due colonne affiancate. */
.sl-book-detail {
  color: var(--color-ink);
}
.sl-book-detail__back {
  margin-bottom: var(--space-4);
}
.sl-book-detail__layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}
.sl-book-detail__cover {
  width: 220px;
  max-width: 100%;
  height: auto;
  border-radius: 4px 8px 8px 4px;
  box-shadow: var(--shadow-card);
}
.sl-book-detail__cover--placeholder {
  width: 220px;
  max-width: 100%;
  aspect-ratio: 2 / 3;
  border-radius: 4px 8px 8px 4px;
}
/* Cover del dettaglio Edizione: l'immagine ha una larghezza intrinseca (~322px) e
   senza vincolo sforava il viewport <360px (overflow-x mobile). ``max-width: 100%``
   la clampa alla colonna; ``height: auto`` preserva il rapporto. Scoped alla classe
   (non un reset globale ``img``) per non toccare i tile della mappa Leaflet. */
.sl-edition-detail__cover {
  max-width: 100%;
  height: auto;
}
.sl-book-detail__title {
  font-family: var(--font-serif);
  margin: var(--space-2) 0 0;
}
.sl-book-detail__author {
  color: var(--color-text-warm);
}
.sl-book-detail__categories {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  list-style: none;
  padding: 0;
  margin: var(--space-3) 0;
}
.sl-book-detail__views {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}
/* Scheda proprietario: nome + zona/bucket + contatto (solo se consent-gated). */
.sl-book-detail__owner {
  margin: var(--space-4) 0;
}
.sl-book-detail__owner-name {
  font-weight: 600;
}
.sl-book-detail__owner-meta {
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
}
.sl-book-detail__owner-contact {
  margin-top: var(--space-2);
}
.sl-book-detail__loan-flow {
  margin-top: var(--space-4);
}
.sl-book-detail__affini {
  margin-top: var(--space-6);
}
@media (min-width: 860px) {
  .sl-book-detail__layout {
    grid-template-columns: 220px 1fr;
    align-items: start;
  }
}

/* --- Bottone pillola (es. "Rimescola"): tutto arrotondato, carta-light --- */
.sl-btn-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: 30px;
  background: var(--color-paper-light);
  color: var(--color-ink);
  font-family: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.15s ease;
}
.sl-btn-pill:hover {
  transform: translateY(-1px);
}

/* --- Chip (filtri/categorie): bottone con aria-pressed lato template ----- */
.sl-chip {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: 20px;
  background: var(--color-paper-light);
  color: var(--color-text-warm);
  font-size: var(--font-size-sm);
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.15s ease;
}
.sl-chip:hover {
  transform: translateY(-1px);
}
.sl-chip[aria-pressed="true"] {
  background: var(--color-nav-active);
  color: var(--color-ink);
}

/* --- Badge di stato: coppie fg/bg semantiche dai nuovi token ------------- */
.sl-badge--status {
  display: inline-block;
  padding: 2px var(--space-2);
  border-radius: 20px;
  font-size: var(--font-size-sm);
  font-weight: 600;
}
.sl-badge--avail {
  background: var(--color-status-avail-bg);
  color: var(--color-status-avail-fg);
}
.sl-badge--lent {
  background: var(--color-status-lent-bg);
  color: var(--color-status-lent-fg);
}
/* Stato "in attesa" (pending/declined/cancelled/returned): tinta sabbia neutra
   dai token (rimpiazza l'--color-muted MAI definito, G-10). */
.sl-badge--attesa {
  background: var(--color-status-attesa-bg);
  color: var(--color-status-attesa-fg);
}
/* Pallino di stato: indicatore NON-cromatico accanto al testo della pill, così lo
   stato non è MAI veicolato dal solo colore (A11Y-02, WCAG 1.4.1). È un glifo nel
   markup (non un ::before) così resta percepibile anche a CSS spento ed è
   asseribile dai test di rendering. ``aria-hidden`` perché il testo accanto è già
   esplicito (es. "In attesa") e lo screen reader non deve annunciare "pallino". */
.sl-badge__dot {
  font-size: 0.7em;
  line-height: 1;
  vertical-align: middle;
  margin-right: 0.35em;
}

/* --- Inbox "Richieste" (Plan 08-09): sezioni Ricevute/Inviate + righe-card ---
   Surface di gestione prestiti (LOAN-02/D-05). Mobile-first: una colonna; le
   card riusano il pattern .sl-book-card (paper-light + border + radius). Le due
   sezioni sono landmark etichettati (aria-labelledby → H2 serif), il badge
   pending riusa ``loans_pending_received`` (nessuna nuova query). */
.sl-richieste {
  color: var(--color-ink);
}
.sl-richieste__heading {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
/* Badge conteggio pending (riusa il var di contesto del context processor D-05). */
.sl-badge--count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  padding: 0 var(--space-2);
  border-radius: 20px;
  background: var(--color-status-lent-bg);
  color: var(--color-status-lent-fg);
  font-size: var(--font-size-sm);
  font-weight: 600;
}
.sl-richieste__section {
  margin-bottom: var(--space-6);
}
.sl-richieste__section-title {
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
}
/* Lista righe: nessun bullet, gap verticale fra le card. */
.sl-loan-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
/* Riga-card del prestito: eredita .sl-book-card; hover transform-only (D-24). */
.sl-loan-row {
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.sl-loan-row:hover {
  transform: translateY(-1px);
}
.sl-loan-row__title {
  margin: 0 0 var(--space-2);
}
.sl-loan-row__meta {
  margin: 0 0 var(--space-2);
  color: var(--color-text-warm);
  font-size: var(--font-size-sm);
}
.sl-loan-row__who {
  font-weight: 600;
  color: var(--color-ink);
}
.sl-loan-row__note {
  margin: 0 0 var(--space-3);
  color: var(--color-text-warm);
  font-style: italic;
}
.sl-loan-row__contact {
  margin: var(--space-3) 0 0;
  font-size: var(--font-size-sm);
}
.sl-loan-row__contact--none {
  color: var(--color-text-muted);
  font-style: italic;
}
.sl-loan-row__contact-value {
  font-weight: 600;
  color: var(--color-ink);
}
/* Cluster azioni owner: bottoni in fila, vanno a capo su mobile. */
.sl-loan-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
}
/* Empty-state della sezione: pattern italico come .sl-list-empty. */
/* CLEAN-01 (2026-09-01): `.sl-empty` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* Link "Apri conversazione" sotto una riga-prestito (F11/MSG-10/D-15): sostituisce
   l'auto-reveal del contatto, sta su una propria riga sotto le azioni. */
.sl-loan-row__conversation {
  margin: var(--space-3) 0 0;
}

/* --- Conversazione (Phase 11): bolle messaggi + form invio (MSG-01/02/03/D-14) ---
   La chat loan-scoped (la conversazione È la LoanRequest) SOSTITUISCE l'auto-reveal
   del contatto (D-15). Mobile-first, una colonna; le bolle riusano il precedente-card
   .sl-loan-row (border/radius/paper-light). Autore veicolato da TESTO + allineamento,
   mai dal solo colore (A11Y-02). TOKEN-ONLY, nessun hex (G-10). Niente overflow
   orizzontale a 320px: bolle con max-width in % + overflow-wrap. */
.sl-chat {
  color: var(--color-ink);
}
.sl-chat__header {
  margin-bottom: var(--space-5);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}
.sl-chat__title {
  margin: 0 0 var(--space-1);
}
.sl-chat__with {
  margin: 0;
  color: var(--color-text-warm);
  font-size: var(--font-size-sm);
}
.sl-chat__who {
  font-weight: 600;
  color: var(--color-ink);
}
/* Lista messaggi (regione aria-live): nessun bullet, colonna flex, gap verticale —
   modellata su .sl-loan-list. */
.sl-message-list {
  list-style: none;
  margin: 0 0 var(--space-5);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
/* Bolla: card su precedente .sl-loan-row (border/radius/paper-light). Larghezza
   limitata in %, va a capo su parole lunghe → nessun overflow a 320px. */
.sl-message {
  max-width: 85%;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-paper-light);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  overflow-wrap: anywhere;
}
/* I MIEI messaggi: a destra, tinta verde "disponibile" dai token. */
.sl-message--mine {
  align-self: flex-end;
  background: var(--color-status-avail-bg);
}
/* Messaggi dell'ALTRO: a sinistra, tinta sabbia "attesa" dai token. */
.sl-message--theirs {
  align-self: flex-start;
  background: var(--color-status-attesa-bg);
}
.sl-message__author {
  font-weight: 600;
  font-size: var(--font-size-sm);
  color: var(--color-ink);
}
.sl-message__body {
  color: var(--color-ink);
}
/* Faccine della chat (filtro msn_emoticons): SVG a tratto, non emoji. Misurate in
   `em` così seguono il corpo del testo della bolla, e in `currentColor` così vanno
   bene sia sulla bolla verde sia su quella sabbia. */
.sl-emoticon {
  display: inline-block;
  line-height: 0;
}
.sl-emoticon__glifo {
  width: 1.25em;
  height: 1.25em;
  vertical-align: -0.27em;
}
/* Il trillo: la mano dritta si legge come "alt", inclinata come un saluto. */
.sl-emoticon--saluto .sl-emoticon__glifo {
  transform: rotate(-18deg);
}
.sl-message__footer {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.sl-message__time {
  color: var(--color-text-warm);
  font-size: var(--font-size-sm);
}
/* "Segnala": controllo testuale leggero, hit-area adeguata, mai icona-sola. */
.sl-message__report {
  background: none;
  border: none;
  padding: var(--space-1) 0;
  color: var(--color-text-warm);
  font-size: var(--font-size-sm);
  font-family: inherit;
  text-decoration: underline;
  cursor: pointer;
}
.sl-message__report:focus {
  outline: 2px solid var(--color-secondary);
  outline-offset: 1px;
}
/* Form di invio: riusa .sl-form/.sl-input/.sl-btn--primary (nessun re-style degli input). */
.sl-send-form__readonly {
  color: var(--color-text-warm);
  font-style: italic;
  padding: var(--space-4) 0;
}
.sl-send-form__error {
  color: var(--color-status-lent-fg);
  font-weight: 600;
  margin: 0 0 var(--space-3);
}

/* --- Toggle consenso: switch 46×26 (input type=checkbox role=switch) ----- */
/* Il <label> garantisce ≥44px di hit area (A11Y-01). Track animato in transform. */
.sl-toggle-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 44px;
  cursor: pointer;
}
/* CLEAN-01 (2026-09-01): `.sl-toggle` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-toggle::after` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-toggle:checked` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-toggle:checked::after` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-toggle:focus-visible` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */

/* --- Sezione consensi: titolo, riga policy, righe switch ----------------- */
.sl-consent-toggles__title {
  font-family: var(--font-display, serif);
  font-size: var(--font-size-h2);
  color: var(--color-ink);
  margin: 0 0 var(--space-2);
}
.sl-consent-toggles__policy {
  font-size: var(--font-size-sm, 0.85rem);
  color: var(--color-text-muted, #7A6B53);
  margin: 0 0 var(--space-4);
}
/* La riga è etichetta+switch a sinistra, stato a destra. ``flex-wrap`` + ``min-width: 0``
   sono la coppia che la tiene dentro i 320px: senza, con un'etichetta lunga e lo stato
   "non concesso" (78px, flex 0 0 auto) la riga sfondava di 8px e il PROFILO scorreva di
   lato sui telefoni piccoli — trovato scansionando le pagine il 2026-08-28. Ora, quando
   non ci sta, lo stato scende sotto invece di spingere fuori dal viewport. */
.sl-consent-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}
.sl-consent-row .sl-toggle-label {
  flex: 1 1 auto;
  min-width: 0;
}
.sl-consent-row:last-child {
  border-bottom: 0;
}
.sl-toggle__label-text {
  color: var(--color-ink);
  font-weight: 600;
}
.sl-toggle__state {
  font-size: var(--font-size-sm, 0.85rem);
  font-weight: 600;
  flex: 0 0 auto;
}
.sl-toggle__state--on {
  color: var(--color-secondary);
}
.sl-toggle__state--off {
  color: var(--color-text-muted, #7A6B53);
}

/* --- Profilo: header (avatar 76px + meta) e griglia libri ---------------- */
.sl-profile {
  max-width: 920px;
  margin: 0 auto;
}
.sl-profile-header {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
.sl-profile-header__avatar {
  width: 76px;
  height: 76px;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
}
.sl-profile-header__avatar--initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-primary);
  color: var(--color-paper-light);
  font-family: var(--font-display, serif);
  font-size: 1.9rem;
  font-weight: 700;
}
.sl-profile-header__meta {
  min-width: 0;
}
.sl-profile-header__name {
  margin: 0;
}
.sl-profile-header__zone {
  margin: var(--space-1) 0 var(--space-2);
  color: var(--color-text-muted, #7A6B53);
}
.sl-profile__books-lead {
  color: var(--color-text-muted, #7A6B53);
  margin: 0 0 var(--space-3);
}
.sl-field-help {
  font-size: var(--font-size-sm, 0.85rem);
  color: var(--color-text-muted, #7A6B53);
  margin: var(--space-1) 0 0;
}

/* --- Nota privacy: banner info privacy-by-design ------------------------- */
.sl-note--privacy {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-panel-info);
  border: 1px solid var(--color-panel-info-border);
  border-radius: var(--radius-md);
  color: var(--color-text-warm);
}

/* --- Stat card: bordo-accento in alto + valore tabular-nums ------------- */
.sl-stat-card {
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-top: 3px solid var(--color-primary);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}
.sl-stat-card__value {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  font-variant-numeric: tabular-nums;
}
/* Etichetta e sotto-linea della metric-card (meta in muted, ≥4.5:1 su carta). */
.sl-stat-card__label {
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-muted);
}
.sl-stat-card__sub {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}

/* --- Statistiche: intestazione, griglia metric-card e griglia grafici ---- */
/* Plan 08-08: layout di contorno (le card e i canvas restano invariati). */
.sl-stats__head {
  margin-bottom: var(--space-5);
}
.sl-stats__lead {
  color: var(--color-text-muted);
}
.sl-stats__metrics {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
.sl-stats__charts {
  display: grid;
  /* ``minmax(0, 1fr)`` non ``1fr``: la card grafico (canvas 300px + padding) ha un
     max-content ~350px che con ``1fr`` (min-width:auto) sfondava il viewport <360px.
     Clampata, la card sta nella colonna e Chart.js (responsive) ridimensiona il canvas. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
}
/* Desktop ≥860px: metric-card a 4 colonne, grafici a 2 colonne (mobile-first). */
@media (min-width: 860px) {
  .sl-stats__metrics { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .sl-stats__charts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --- Utility: contenuto solo-screen-reader (A11Y-04 fallback testuale) --- */
/* Visivamente nascosto ma nell'albero di accessibilità (riepiloghi dati canvas). */
.sl-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --- Chart card: struttura .sl-panel + canvas 250px --------------------- */
.sl-chart-card {
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-card);
}
.sl-chart-card canvas {
  max-height: 250px;
  /* Il canvas Chart.js ha un attributo width (~300px); senza questo clamp eccede la
     card su viewport stretti (overflow-x mobile). Con responsive:true Chart.js segue. */
  max-width: 100%;
}
/* Titolo del grafico (figcaption non è h*): serif 18px/600 (08-fidelity G5 stat). */
.sl-chart-card figcaption {
  font-family: var(--font-serif);
  font-size: 18px;
  font-weight: 600;
  margin-bottom: var(--space-1);
}

/* --- Segmented control (Mappa/Lista): 2 bottoni, aria-pressed ----------- */
/* NON deve JS-gateare la lista: è solo presentazione (la lista resta accessibile). */
.sl-segmented {
  display: inline-flex;
  padding: 3px;
  border: 1px solid var(--color-border);
  border-radius: 30px;
  background: var(--color-paper-light);
}
.sl-segmented button {
  padding: var(--space-2) var(--space-4);
  border: none;
  border-radius: 30px;
  background: transparent;
  color: var(--color-text-warm);
  font-weight: 600;
  cursor: pointer;
}
.sl-segmented button[aria-pressed="true"] {
  background: var(--color-nav-active);
  color: var(--color-ink);
}

/* --- Progress wizard: SOLO `width` in transizione (mai opacity) ---------- */
.sl-progress__track {
  height: 6px;
  border-radius: 3px;
  background: var(--color-border);
  overflow: hidden;
}
.sl-progress__fill {
  height: 100%;
  background: var(--color-primary);
  transition: width 0.2s ease;
}
.sl-progress__labels {
  display: flex;
  justify-content: space-between;
  margin-bottom: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}
/* L'etichetta del passo corrente (aria-current="step") e' enfatizzata, non
   solo-colore: peso + colore accento insieme. */
.sl-progress__labels [aria-current="step"] {
  color: var(--color-primary);
  font-weight: 700;
}

/* --- Wizard PUBBLICA: contenitore step + barra + nav ---------------------- */
.sl-progress {
  margin-bottom: var(--space-5);
}
.sl-wizard-step {
  max-width: 660px;
  margin: 0 auto;
  color: var(--color-ink);
}
.sl-wizard-step h1 {
  margin-bottom: var(--space-4);
}
.sl-wizard-nav {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
  margin-top: var(--space-5);
}
.sl-wizard-nav .sl-btn--primary {
  margin-left: auto;
}

/* Campi testuali estesi del wizard (number/textarea/select) — stesso look
   dell'input testo gia' stilato in .sl-field. */
.sl-field input[type="number"],
.sl-field textarea,
.sl-field select {
  width: 100%;
  padding: 13px 15px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-paper-light);
  color: var(--color-ink);
  font-family: inherit;
  font-size: 15px;
}
/* CLEAN-01 (2026-09-01): `.sl-field textarea` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
.sl-field textarea:focus,
.sl-field input[type="number"]:focus,
/* CLEAN-01 (2026-09-01): `.sl-field textarea:focus, .sl-field input[type="number"]:focus, .sl-field select:focus` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
.sl-field textarea[aria-invalid="true"],
/* CLEAN-01 (2026-09-01): `.sl-field textarea[aria-invalid="true"], .sl-field input[type="number"][aria-invalid="true"]` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-field-help` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */

/* Temi: fieldset di chip-checkbox (Step 2). I chip sono <label class="sl-chip">
   che avvolgono un checkbox nativo (a11y senza JS). */
.sl-chip-categories {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}
.sl-chip-categories legend {
  font-weight: 700;
  color: var(--color-text-warm);
  padding: 0 var(--space-2);
}
.sl-chip-categories__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.sl-chip-categories .sl-chip {
  gap: var(--space-2);
}

/* --- Step 3: copertina come slot tratteggiato (input file nativo stilato) -
   L'input NON e' rimosso dall'albero a11y: opacity:0 + position:absolute lo
   tengono raggiungibile da tastiera/SR (mai display:none), con un'etichetta
   trigger visibile (.sl-cover-slot__label) sopra. La catena clean_cover (EXIF
   strip + re-encode) resta intatta: e' lo stesso <input type=file> del form. */
.sl-cover-slot {
  position: relative;
  width: 150px;
  height: 222px;
  border: 2px dashed var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-paper-light);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.sl-cover-slot input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}
.sl-cover-slot input[type="file"]:focus-visible + .sl-cover-slot__label,
.sl-cover-slot:focus-within {
  border-color: var(--color-secondary);
  outline: 2px solid var(--color-secondary);
  outline-offset: 2px;
}
.sl-cover-slot__label {
  padding: var(--space-3);
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-warm);
  pointer-events: none;
}
.sl-cover-slot[data-invalid="true"] {
  border-color: var(--color-primary);
}

/* Riepilogo di pubblicazione + nota posizione (read-only, derivata dal profilo). */
.sl-riepilogo {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
  color: var(--color-text-warm);
}
.sl-riepilogo h2 {
  font-size: var(--font-size-h2);
  margin-bottom: var(--space-2);
}
.sl-nota-posizione {
  margin-top: var(--space-3);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}

/* --- Toast: ingresso via slUp (transform), aria-live lato template ------- */
.sl-toast {
  padding: var(--space-3) var(--space-4);
  background: var(--color-secondary);
  color: var(--color-paper);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  animation: slUp 0.2s ease;
}

/* --- Rail: scroll orizzontale, scrollbar nascosta (serendipity/chip) ----- */
.sl-rail {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  scrollbar-width: none; /* Firefox */
  -webkit-overflow-scrolling: touch;
}
.sl-rail::-webkit-scrollbar {
  display: none; /* WebKit */
}

/* --- Auth split: editoriale + form (due pannelli desktop, stack mobile) -- */
.sl-auth-split {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
}
/* Pannello editoriale (verde bottiglia, aria-hidden lato template). */
.sl-auth-panel {
  display: none; /* mobile: nascosto, mostra solo il form */
  padding: var(--space-7) var(--space-6);
  /* 08-fidelity auth G1: gradiente DUE-VERDI (mai navy in coda). */
  background: linear-gradient(160deg, #335446, #243a30 70%);
  color: var(--color-paper);
}
.sl-auth-form-col {
  max-width: 380px;
  width: 100%;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5);
}

/* Contenuto del pannello editoriale (sinistra, su sfondo scuro). */
.sl-auth-panel__logo {
  display: block;
  margin-bottom: var(--space-6);
}
/* Eyebrow su pannello scuro: tono salvia desaturato + tracking 3px (08-fidelity G6). */
.sl-auth-panel__eyebrow {
  color: #C9D9CC;
  letter-spacing: 3px;
}
/* Titolo editoriale serif; l'<em> eredita l'ocra-chiara su scuro (vedi sotto).
   08-fidelity auth G5: ~36px / line-height 1.04 / tracking -.5px (più piccolo del
   clamp globale h1). Peso 600 per la regola dei 2-pesi (mockup usa 500 → accettato). */
.sl-auth-panel__headline {
  font-family: var(--font-serif);
  font-size: clamp(2rem, 4vw, 36px);
  font-weight: 600;
  line-height: 1.04;
  letter-spacing: -0.5px;
  margin: 0 0 var(--space-4);
}
.sl-auth-panel__headline em {
  font-style: italic;
  /* Enfasi su superficie scura: ocra-chiara (08-UI-SPEC, riga 158). */
  color: var(--color-ocra-light);
}
.sl-auth-panel__privacy {
  font-size: var(--font-size-sm);
  color: var(--color-paper);
  opacity: 0.88; /* statico, non animato: non viola D-24 (nessuna transizione su opacity). */
  max-width: 34ch;
}

/* Brand cliccabile sopra il form (logo primario su carta). */
.sl-auth-form-col__brand {
  display: inline-block;
  margin-bottom: var(--space-5);
}
/* Titolo + lead della card form. */
.sl-auth-title {
  font-family: var(--font-serif);
  /* 08-fidelity auth G5: ~30px / tracking -.4px (più piccolo del clamp globale). */
  font-size: clamp(1.75rem, 4vw, 30px);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.4px;
  margin: 0 0 var(--space-2);
  color: var(--color-ink);
}
.sl-auth-lead {
  color: var(--color-text-warm);
  font-size: var(--font-size-sm);
  margin: 0 0 var(--space-5);
}
/* Form auth: stessa griglia spaziata del resto del progetto. */
.sl-auth-form {
  display: grid;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
/* La <label for> generata da allauth (label_tag): bold, sopra l'input (A11Y-02). */
.sl-field label {
  display: block;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-warm);
  margin-bottom: var(--space-1);
}
/* Input/email/password: ereditano lo stile .sl-input via selettore di tipo. */
.sl-field input[type="text"],
.sl-field input[type="email"],
.sl-field input[type="password"],
.sl-field input:not([type]) {
  width: 100%;
  padding: 13px 15px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-paper-light);
  color: var(--color-ink);
  font-family: inherit;
  font-size: 15px;
}
/* CLEAN-01 (2026-09-01): `.sl-field input:focus` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* Stato non valido (aria-invalid="true"): bordo terracotta, segnale non-solo-colore
   perché accompagnato dal testo d'errore associato (.sl-field-error). */
/* CLEAN-01 (2026-09-01): `.sl-field input[aria-invalid="true"]` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* Checkbox "ricordami"/consensi: label in linea accanto al box, hit-area comoda. */
.sl-field-check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  min-height: 44px; /* target touch A11Y-01 (WCAG 2.2). */
}
.sl-field-check label {
  margin: 0;
  font-weight: 400;
  font-size: var(--font-size-sm);
  color: var(--color-text-warm);
}
.sl-field-check input[type="checkbox"] {
  margin-top: 3px;
  width: 18px;
  height: 18px;
  accent-color: var(--color-secondary);
  flex-shrink: 0;
}
/* Errore di campo (problema + fix): terracotta, associato via aria-describedby. */
.sl-field-error {
  color: var(--color-status-lent-fg);
  font-size: var(--font-size-sm);
  margin-top: var(--space-1);
}
.sl-field-error ul {
  margin: 0;
  padding-left: 1.1em;
  list-style: disc;
}
/* Errori non-di-campo (es. credenziali errate): banner terracotta in cima al form. */
.sl-form-errors {
  background: var(--color-status-lent-bg);
  color: var(--color-status-lent-fg);
  border: 1px solid var(--color-status-lent-fg);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  font-size: var(--font-size-sm);
}
.sl-form-errors ul {
  margin: 0;
  padding-left: 1.1em;
}
/* Gruppo consensi (signup/reconsent): fieldset etichettato da <legend>. */
.sl-consent-group {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin: var(--space-2) 0 0;
  display: grid;
  gap: var(--space-2);
}
.sl-consent-group legend {
  font-weight: 600;
  font-size: var(--font-size-sm);
  color: var(--color-ink);
  padding: 0 var(--space-2);
}
.sl-consent-note {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin: 0 0 var(--space-2);
}
/* Campo composto da PIÙ controlli (.sl-field--group) — oggi solo la data di
   nascita del signup, che SelectDateWidget rende come TRE <select> fratelli.
   Senza questa regola i tre ereditano `.sl-field select { width: 100% }` e si
   impilano a tutta larghezza; qui il campo diventa una riga: legenda sopra, i
   select affiancati, l'errore di nuovo a capo. È un <fieldset>, quindi
   bordo/margine/padding di default del browser vanno azzerati (a differenza di
   .sl-consent-group, che il bordo lo vuole). */
.sl-field--group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  border: 0;
  padding: 0;
  margin: 0;
  min-width: 0; /* i fieldset nascono min-width:min-content: senza, non si stringono */
}
/* La <legend> fa da label del gruppo (A11Y-02): stesso trattamento tipografico
   di `.sl-field label`, a piena larghezza sopra i controlli. */
.sl-field--group legend {
  display: block;
  width: 100%;
  padding: 0;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-warm);
  margin-bottom: var(--space-1);
}
.sl-field--group .sl-field-error {
  width: 100%;
}
/* Specificità (0,2,1) VOLUTA: deve battere `.sl-field select { width: 100% }` di
   components.css, che carica DOPO base.css e a parità vincerebbe per source-order.
   `flex: 1 1 auto` + `width: auto` = ogni select parte dalla propria larghezza
   naturale (il mese in italiano è la parola più lunga) e si spartisce lo spazio
   restante; se la riga non basta vanno a capo invece di sfondare il viewport. */
.sl-field.sl-field--group select {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  /* Fianchi più stretti dei 15px degli altri campi: con quelli, a 360px di
     viewport la somma delle tre larghezze naturali sfora di una manciata di
     pixel e l'anno finisce a capo da solo. Qui i tre restano in riga fin sotto
     i 360px (misurato), senza troncare il mese più lungo ("settembre"). */
  padding-left: 10px;
  padding-right: 10px;
}
/* Link secondari sotto il form (registrati / password dimenticata / torna). */
.sl-auth-links {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
}
.sl-auth-links a {
  color: var(--color-primary);
  text-decoration: none;
}
.sl-auth-links a:hover {
  text-decoration: underline;
}
.sl-auth-links a:focus {
  outline: 2px solid var(--color-secondary);
  outline-offset: 2px;
}

/* --- Responsive: mobile-first, due colonne lat/lng da 860px (D-26) ------ */
@media (min-width: 860px) {
  .sl-field-row {
    grid-template-columns: 1fr 1fr;
  }
  /* Shell desktop: colonna unica (il masthead è una barra IN CIMA, non una colonna
     laterale). ``minmax(0, 1fr)`` protegge dal blowout da max-content. */
  .sl-app-shell {
    grid-template-columns: minmax(0, 1fr);
  }
  /* ≥860px: compare il masthead orizzontale; spariscono topbar + tab-bar mobile. */
  .sl-masthead {
    display: block;
  }
  .sl-topbar-mobile,
  .sl-tab-bar {
    display: none;
  }
  /* Auth: due pannelli affiancati. */
  .sl-auth-split {
    grid-template-columns: 1fr 1fr;
  }
  /* 08-fidelity auth G2: pannello a colonna full-height 3-zone (brand / headline /
     dorsi-libro), con glow circolare dietro al testo. */
  .sl-auth-panel {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    min-height: 100vh;
    position: relative;
    overflow: hidden;
  }
  /* Glow circolare in alto a destra (G4), dietro il contenuto. */
  .sl-auth-panel::before {
    content: "";
    position: absolute;
    right: -60px;
    top: -60px;
    width: 280px;
    height: 280px;
    border-radius: 50%;
    background: rgba(184, 80, 47, 0.16);
    z-index: 0;
  }
  /* Dorsi di libri decorativi in fondo (G3): striscia di "costole" colorate
     mappate alla palette --cat-* via gradiente a bande (pure CSS, NO-CDN). */
  .sl-auth-panel::after {
    content: "";
    position: relative;
    z-index: 1;
    height: 82px;
    border-radius: 3px 3px 0 0;
    background:
      linear-gradient(var(--cat-narrativa), var(--cat-narrativa)) 0 100% / 26px 78px no-repeat,
      linear-gradient(var(--cat-ragazzi), var(--cat-ragazzi)) 33px 100% / 22px 64px no-repeat,
      linear-gradient(var(--cat-poesia), var(--cat-poesia)) 62px 100% / 28px 82px no-repeat,
      linear-gradient(#C9D9CC, #C9D9CC) 97px 100% / 20px 58px no-repeat,
      linear-gradient(var(--cat-gialli), var(--cat-gialli)) 124px 100% / 26px 72px no-repeat,
      linear-gradient(var(--color-ocra-light), var(--color-ocra-light)) 157px 100% / 23px 68px no-repeat;
  }
  /* Logo + eyebrow + headline + privacy restano nel flusso sopra i dorsi, sopra il glow. */
  .sl-auth-panel > * {
    position: relative;
    z-index: 1;
  }
}

/* =========================================================================
   PHASE 8 FIDELITY — vocabolario .sl-* mancante (additive)
   =========================================================================
   I template Phase 8 furono scritti contro un vocabolario di componenti .sl-*
   (cloth-spine, book-card, suggestion-card, badge, chip, weight-slider, ecc.)
   mai definito in base.css: rendevano "piatti", tenuti insieme da stili inline.
   Questo blocco AGGIUNGE quelle regole (08-fidelity-audit, 9 report) senza mai
   toccare le classi già fedeli. Motion SOLO `transform` (D-24); ogni hover
   animato ha il suo no-op nel blocco prefers-reduced-motion in fondo.
   Invarianti: NO-CDN (nessun asset esterno), privacy (bucket-only / no email /
   no coord), A11y (controlli reali, contrasto ≥4.5:1, --color-text-muted #6B5C45
   non schiarito), scala tipografica 2 pesi (400/600) / 4 size.
   ========================================================================= */

/* --- main content: clearance per la tab-bar mobile (G-04/G-12 app-shell) ---
   La tab-bar mobile è fixed-bottom: senza clearance coprirebbe le ultime righe.
   Il padding LATERALE e SUPERIORE resta; quello inferiore no — vedi sotto. */
.sl-main-content {
  padding: var(--space-5) var(--space-4);
  /* padding-bottom ZERO sotto 860px (rilievo 3 della UAT del 2026-09-09).
     Il footer è l'ULTIMO figlio di questo main e porta già il suo margin-top:
     lo spazio fra contenuto e footer non cambia di un pixel. Quel padding, invece,
     era 24px di sfondo carta SOTTO il verde del footer — cioè 24px di scroll che
     continuavano oltre la fine della pagina, con una striscia chiara in mezzo.
     ≥860px la regola in coda rimette padding-bottom: var(--space-7), ed è voluto:
     lì non c'è tab-bar, il footer non deve arrivare a filo e la regola è esplicita. */
  padding-bottom: 0;
}
/* Clearance per la tab-bar mobile FIXED: NON un padding-bottom sul main (lasciava
   una striscia di sfondo carta tra il footer verde e la barra — segnalazione 2026-08-17).
   È uno spacer verde in coda al footer, così il --color-secondary arriva fino alla
   tab-bar. Solo per l'utente loggato (body senza .sl-anon) e solo <860px, dove la barra
   esiste. Su desktop la tab-bar sparisce → spacer rimosso.

   ⚠️ 2026-09-09 (rilievo 3 della UAT): l'altezza era DICHIARATA 72px e la barra vera ne
   misura 81 a 390×844 — nove pixel di verde mancanti sotto il footer, più i 24 di carta
   qui sopra. Un numero scritto a mano invecchia il giorno in cui si cambia il padding
   della barra o la dimensione delle sue etichette, e nessuno se ne accorge. Adesso
   l'altezza la misura il JavaScript, e il valore letterale resta solo come ripiego per
   chi non esegue script: sbagliato di poco, e mai peggiore di prima.

   ⚠️ 2026-09-09, SECONDA passata della UAT sui due telefoni: «su Android si ferma, su
   iPhone continua a scorrere». Il rimedio qui sopra chiudeva il difetto su Android e non
   su iOS, e il perché è tutto in `env(safe-area-inset-bottom)`.
   La tab-bar è `contenuto + padding verticali + area sicura`. Il JavaScript ne misurava
   l'`offsetHeight` — che l'area sicura la comprende già — e la scriveva in `--sl-tabbar-h`,
   che il CSS usava tale e quale. Su Android l'inset vale sempre 0 e una misura sola resta
   buona per sempre. Su iPhone quel valore CAMBIA MENTRE SI SCORRE: quando la barra
   inferiore di Safari si ritrae l'inset passa da 0 a ~34px e la tab-bar cresce, quando
   riappare torna a 0 e si accorcia. La variabile restava ferma all'ultima misura (e su iOS
   `window.resize` non scatta per quel cambio), spaziatore e barra si disallineavano, e la
   pagina tornava a scorrere oltre il footer.
   La misura è quindi SCOMPOSTA: `--sl-tabbar-core` (sl-ux.js) è la sola parte che non
   cambia — `offsetHeight` MENO l'area sicura corrente, letta con una sonda — e
   `env(safe-area-inset-bottom)` lo somma QUI il foglio di stile, che lo ricalcola da sé a
   ogni cambio di viewport senza dipendere da un evento JavaScript.
   ⛔ L'area sicura deve comparire UNA volta sola in questa catena. Zero volte = su iPhone
   lo spacer è più basso della barra e la pagina scorre oltre il footer; due volte = ~34px
   di verde di troppo, cioè lo stesso difetto con un'altra causa. Per questo il ripiego è
   un numero NUDO (81px, la parte «core» misurata a 390×844 dove l'inset vale 0) e non un
   `calc` che se la ri-sommi. Guardia: apps/core/tests/test_guscio_tabbar.py. */
body:not(.sl-anon) .sl-app-shell .sl-footer::after {
  content: "";
  display: block;
  height: calc(var(--sl-tabbar-core, 81px) + env(safe-area-inset-bottom));
  background: var(--color-secondary);
}
@media (min-width: 860px) {
  body:not(.sl-anon) .sl-app-shell .sl-footer::after { display: none; }
}
@media (min-width: 860px) {
  .sl-main-content {
    padding: var(--space-6) var(--space-6) var(--space-7);
    padding-bottom: var(--space-7);
  }
}

/* --- App-shell anonimo: niente sidebar/tab-bar → colonna unica full-width ---
   Su /accounts/* l'anonimo NON vede la nav dell'app: la sidebar/tab-bar sono
   gateate su {% if user.is_authenticated %} nel base.html. Quando il body porta
   la classe `sl-anon`, la griglia collassa a 1 colonna anche ≥860px. */
.sl-anon .sl-app-shell {
  grid-template-columns: 1fr;
}
.sl-anon .sl-main-content {
  padding: 0;
}

/* --- App-shell: brand lockup mark + name (G-01/G-08/G-10 app-shell) -------- */
/* Avatar utente ROTONDO (mini-profilo + topbar): il TILE del brand resta quadrato. */
.sl-avatar-round {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--color-primary);
  color: var(--color-paper);
  display: grid;
  place-items: center;
  font-weight: 600;
  font-size: 14px;
  flex: 0 0 auto;
}
.sl-topbar-mobile .sl-avatar-round {
  width: 34px;
  height: 34px;
  font-size: 13px;
}
/* Topbar mobile: tinta carta-crema più opaca del mockup (G-09). */
.sl-topbar-mobile {
  background: rgba(244, 235, 218, 0.9);
}
/* Sidebar nav: scala tipografica/icona del mockup (G-02). */
.sl-sidebar .sl-nav-item {
  font-size: 14.5px;
  padding: 11px 13px;
  gap: 13px;
}
/* Icona nav sidebar a 19px (il markup è 22; ridimensiona via CSS senza toccare
   gli SVG condivisi con la tab-bar). */
.sl-sidebar .sl-nav-item svg {
  width: 19px;
  height: 19px;
}
/* Mini-profilo a due righe in fondo alla sidebar (G-03). */
.sl-mini-profile {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px;
  border-radius: 12px;
  background: var(--color-nav-active);
  text-decoration: none;
  color: var(--color-ink);
}
.sl-mini-profile__meta {
  line-height: 1.3;
  min-width: 0;
  flex: 1;
}
.sl-mini-profile__name {
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sl-mini-profile__zone {
  font-size: 12px;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* CTA "Pubblica un libro" sidebar: VERDE full-width centrata con icona (G-06). */
.sl-btn--publish {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  padding: 12px;
  background: var(--color-secondary);
  color: var(--color-paper);
  box-shadow: none;
}
.sl-btn--publish:hover {
  box-shadow: 0 8px 20px rgba(44, 74, 59, 0.28);
}

/* --- Bottoni: varianti ghost / primary / block (G1/G5 pubblica, G5 dettaglio) */
/* CLEAN-01 (2026-09-01): `.sl-btn--primary` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-btn--secondary` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-btn--ghost` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-btn--ghost:hover` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* CLEAN-01 (2026-09-01): `.sl-btn--block` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */
/* Cluster nav form generico (needs_location, book_form): flex con gap (G9 pubblica). */
.sl-form-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

/* --- Card generica: alias di .sl-panel (G1 profilo) ----------------------- */
/* CLEAN-01 (2026-09-01): `.sl-card` rimossa — superata da components.css, stesso selettore, caricato dopo. Inerzia PROVATA in browser: togliendola da sola, i valori calcolati non cambiano. */

/* --- Badge base (shell pill) + modificatori categoria/distanza (G1 dettaglio) */
.sl-badge {
  display: inline-flex;
  align-items: center;
  padding: 6px 13px;
  border-radius: 20px;
  font-size: var(--font-size-sm);
  font-weight: 600;
  line-height: 1;
}
.sl-badge--category {
  background: #F0E6D2;
  border: 1px solid var(--color-border);
  color: var(--color-text-warm);
}
/* Distanza: pill leggera "meta" (mai pesante verde-pieno; valore = bucket). */
.sl-badge--distance {
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
}

/* --- SCAFFALE: head-row + cloth-spine card (sez. 1-2 home) ---------------- */
.sl-scaffale__head-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-1);
}
.sl-scaffale__rail-wrap {
  margin: 0 -4px;
}
.sl-shelf__rail {
  padding: 22px 2px 10px;
  margin: 0;
  list-style: none;
}
.sl-shelf__card {
  flex: 0 0 auto;
  width: 158px;
}
.sl-shelf__link {
  display: block;
  text-decoration: none;
  color: inherit;
}
/* La costola: box fisso 158×234, ombra, padding; seam + frame via pseudo. */
.sl-shelf__card .sl-book-spine__cover,
.sl-shelf__card .sl-book-spine__cloth {
  width: 158px;
  height: 234px;
  max-width: 100%;
  border-radius: 4px 8px 8px 4px;
  box-shadow: 0 10px 24px rgba(33, 27, 19, 0.22);
  object-fit: cover;
  display: block;
}
/* Cloth placeholder: tinta per-libro (cycle --cat-* via inline style nel template),
   con seam (costola scura) + cornice hairline interna — i due dettagli editoriali. */
.sl-shelf__card .sl-book-spine__cloth {
  position: relative;
  color: var(--color-paper);
  padding: 20px 16px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: linear-gradient(135deg, var(--color-secondary), var(--color-brand-ink));
}
.sl-shelf__card .sl-book-spine__cloth::before {
  content: "";
  position: absolute;
  left: 8px;
  top: 0;
  bottom: 0;
  width: 3px;
  background: rgba(0, 0, 0, 0.16);
}
.sl-shelf__card .sl-book-spine__cloth::after {
  content: "";
  position: absolute;
  inset: 14px;
  border: 1px solid currentColor;
  opacity: 0.28;
  border-radius: 2px;
  pointer-events: none;
}
/* Info: sul cloth il testo è dentro al placeholder (vedi template); l'info-block
   esterno (quando c'è cover reale) resta sotto. */
.sl-shelf__card .sl-book-spine__info {
  padding: var(--space-2) 0 0;
}
/* Overline categoria: piccola, tracciata, spinge il titolo in basso (margin auto). */
.sl-book-spine__cat {
  font-size: 10px;
  letter-spacing: 2px;
  margin: 0 0 auto;
  color: inherit;
  opacity: 0.7;
}
.sl-book-spine__title {
  font-family: var(--font-serif);
  font-size: 18px;
  line-height: 1.15;
  font-weight: 600;
  margin: 0 0 var(--space-2);
  text-wrap: pretty;
}
.sl-book-spine__author {
  font-size: 12px;
  opacity: 0.82;
  margin: 0;
}
.sl-book-spine__author em {
  color: inherit;
}
/* Meta sotto la costola (pin + zona · bucket): testo muted, nowrap. */
.sl-shelf__meta {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-top: 10px;
  color: var(--color-text-muted);
  font-size: 12.5px;
  white-space: nowrap;
}
.sl-shelf__empty {
  padding: var(--space-5) 0;
}

/* --- AFFINI grid + suggestion-card (sez. 3 home / G6 ricerca) -------------- */
.sl-scaffale__affini {
  margin-top: 38px;
}
.sl-affini-grid,
.sl-suggestions-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
@media (min-width: 860px) {
  .sl-affini-grid,
  .sl-suggestions-list {
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  }
}
.sl-suggestions__title {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  margin: 0 0 var(--space-4);
}
/* Card affinità: flex orizzontale (mini-costola | testo), radius 16. */
.sl-suggestion-card {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  border-radius: 16px;
  padding: var(--space-3);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.sl-suggestion-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 14px 30px rgba(33, 27, 19, 0.1);
}
/* Cover/mini-costola della card affinità: 74×108. */
.sl-suggestion-card .sl-book-card__cover {
  flex: 0 0 auto;
  width: 74px;
  height: 108px;
  border-radius: 3px 5px 5px 3px;
  box-shadow: 0 6px 14px rgba(33, 27, 19, 0.2);
  object-fit: cover;
  display: block;
}
.sl-book-card__body {
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.sl-book-card__title {
  font-family: var(--font-serif);
  font-size: 17px;
  line-height: 1.15;
  font-weight: 600;
  margin: 0 0 3px;
  text-wrap: pretty;
}
.sl-book-card__author {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 13px;
  color: var(--color-text-warm);
  margin: 0;
}
/* Riga reason-chips: wrap con gap. */
.sl-suggestion__reasons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
/* Reason-chip "perché": sabbia calda + terra, niente bordo (sez. 3.4 home). */
.sl-chip--distance,
.sl-chip--theme,
.sl-chip--reason {
  background: var(--color-status-lent-bg);
  color: var(--color-status-lent-fg);
  border-color: transparent;
  font-size: 11px;
  align-self: flex-start;
  cursor: default;
}

/* --- CATEGORIE chip-rail: wrap (non scroll) + chip grandi (sez. 4 home) ---- */
.sl-chip-rail {
  flex-wrap: wrap;
  overflow: visible;
  gap: 10px;
  list-style: none;
  padding: 0;
  margin: 0;
}
.sl-chip-rail li {
  list-style: none;
}
.sl-chip--categoria {
  padding: 11px 16px;
  border-radius: 30px;
  font-size: 14px;
  gap: var(--space-2);
}
.sl-chip--categoria:hover {
  border-color: var(--color-primary);
  color: var(--color-primary);
}
.sl-chip__label {
  white-space: nowrap;
}
.sl-chip__count {
  /* Token del TESTO, non delle icone: su .sl-chip il muted delle icone dava 3.15:1,
     sotto il 4.5:1 richiesto per il testo normale (audit 10-09-2026). Ora 6.11:1. */
  color: var(--color-text-muted);
  font-weight: 600;
}

/* --- RICERCA: weight-slider dual-axis (G5 ricerca) ------------------------ */
.sl-weight-slider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.sl-weight-slider input[type="range"] {
  flex: 1 1 auto;
  width: 100%;
  accent-color: var(--color-primary);
}
.sl-weight-slider__pole {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  white-space: nowrap;
}

/* --- Liste risultati (ricerca + esplora): griglia di card orizzontali ------
   Card = miniatura-costola a sinistra | corpo (eyebrow/title/author/meta) a destra
   (08-fidelity G1/G2 ricerca, G1 dettaglio). La griglia base copre /esplora/ (la
   ricerca ha già la sua griglia scoped); auto-fill responsive. */
.sl-search-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
@media (min-width: 860px) {
  .sl-search-list {
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  }
}
.sl-search-list .sl-book-card {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  border-radius: 16px;
}
.sl-search-list .sl-book-card__cover {
  flex: 0 0 auto;
  width: 76px;
  height: 112px;
  border-radius: 3px 5px 5px 3px;
  object-fit: cover;
  box-shadow: 0 6px 14px rgba(33, 27, 19, 0.2);
}
.sl-search-list .sl-book-card__title {
  font-size: 17px;
}
.sl-search-list .sl-book-card__title a {
  color: inherit;
  text-decoration: none;
}
.sl-search-list .sl-book-card__author {
  margin-bottom: var(--space-2);
}

/* --- MAPPA: layout esplora + Leaflet harmonization (04-mappa) ------------- */
.sl-esplora__header {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
.sl-esplora__lead {
  color: var(--color-text-warm);
  font-size: 15px;
  margin: var(--space-2) 0 0;
}
.sl-esplora__map {
  height: 60vh;
  min-height: 420px;
  border-radius: 18px;
  overflow: hidden;
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-card);
}
.sl-esplora__map-help,
.sl-esplora__list {
  margin-top: var(--space-4);
}
/* Leaflet overrides (NON toccare i vendor byte-exact; override qui in cascata). */
.leaflet-container {
  background: var(--color-map-canvas);
  font-family: var(--font-sans);
}
.leaflet-popup-content-wrapper {
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 34px rgba(33, 27, 19, 0.22);
  padding: 2px;
}
.leaflet-popup-content {
  margin: 14px 16px;
}
.leaflet-popup-tip {
  box-shadow: none;
}
/* Cluster: una sola tinta terracotta (al posto del verde/giallo/arancio stock). */
.marker-cluster-small,
.marker-cluster-medium,
.marker-cluster-large {
  background: rgba(184, 80, 47, 0.25);
}
.marker-cluster-small div,
.marker-cluster-medium div,
.marker-cluster-large div {
  background: var(--color-primary);
  color: var(--color-paper);
  font-weight: 700;
}
/* Pin a goccia (divIcon): teardrop ruotato, glifo controruotato. */
.sl-pin {
  width: 34px;
  height: 34px;
  border-radius: 50% 50% 50% 2px;
  transform: rotate(45deg);
  border: 2.5px solid var(--color-paper);
  box-shadow: 0 4px 10px rgba(33, 27, 19, 0.3);
  display: grid;
  place-items: center;
  background: var(--color-primary);
}
.sl-pin span {
  transform: rotate(-45deg);
  font-size: 13px;
}
.sl-pin svg {
  display: block;
}
/* Thumb nel popup mappa (G-04 mappa). */
.sl-map-popup__thumb {
  width: 56px;
  height: auto;
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2);
  display: block;
}

/* --- PUBBLICA wizard: start + labels + riepilogo-card (05-pubblica) ------- */
.sl-wizard-start {
  max-width: 660px;
  margin: 0 auto;
}
.sl-progress__labels span {
  white-space: nowrap;
}
.sl-wizard-step h1 {
  margin-bottom: var(--space-1);
}
/* Riepilogo come scheda-libro orizzontale (G5 pubblica). */
.sl-riepilogo {
  display: flex;
  gap: var(--space-4);
  align-items: center;
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: 15px;
}
.sl-riepilogo__thumb {
  flex: 0 0 auto;
  width: 64px;
  height: 94px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  box-shadow: 0 6px 14px rgba(33, 27, 19, 0.2);
}
.sl-riepilogo__thumb--placeholder {
  background: linear-gradient(135deg, var(--color-secondary), var(--color-brand-ink));
}
.sl-riepilogo__title {
  font-family: var(--font-serif);
  font-size: 19px;
  font-weight: 600;
  margin: 0 0 2px;
}
.sl-riepilogo__author {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 14px;
  color: var(--color-text-warm);
  margin: 0 0 var(--space-2);
}
.sl-riepilogo__meta {
  font-size: 12.5px;
  color: var(--color-icon-muted);
  margin: 0;
}
.sl-needs-location {
  max-width: 660px;
  margin: 0 auto;
}

/* --- PROFILO: my-books grid + zona pin (06-profilo) -----------------------
   La <ul class="sl-book-list"> diventa una griglia di tile-card (cover + titolo +
   stato + controlli). I controlli owner (toggle/modifica/elimina) restano reali e
   nel flusso: non li convertiamo in div cliccabili (A11Y). */
.sl-my-books {
  color: var(--color-ink);
}
.sl-my-books .sl-book-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 18px;
  list-style: none;
  margin: var(--space-5) 0 0;
  padding: 0;
}
.sl-my-books .sl-book-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.sl-my-books .sl-book-card__cover {
  width: 100%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border-radius: 4px 8px 8px 4px;
  box-shadow: 0 8px 18px rgba(33, 27, 19, 0.18);
}
.sl-my-books .sl-book-card .sl-btn,
.sl-my-books .sl-book-card .sl-availability-toggle {
  font-size: var(--font-size-sm);
}
/* Controlli owner (toggle disponibilità, aggiorna posizione, modifica, elimina)
   CENTRATI e a larghezza uniforme dentro la tile: la cover è full-width sopra, quindi
   il corpo left-align faceva sembrare i bottoni "buttati a sinistra" (spec. su mobile,
   dove la tile è a tutta larghezza). Larghezza uniforme cap 220px → stack ordinato. */
.sl-my-books .sl-book-card__body {
  align-items: center;
  text-align: center;
  gap: var(--space-2);
}
.sl-my-books .sl-book-card__body > .sl-btn,
.sl-my-books .sl-book-card__body > button,
.sl-my-books .sl-book-card__body .sl-availability,
.sl-my-books .sl-book-card__body .sl-availability > .sl-btn {
  width: 100%;
  max-width: 220px;
}
.sl-profile-header__zone {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.sl-consent-toggles__lead {
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
  margin: 0 0 var(--space-4);
}

/* --- DETTAGLIO libro: titolo grande, descrizione, owner-card, back (08) ---- */
.sl-book-detail__title {
  font-size: clamp(1.9rem, 4vw, 36px);
  line-height: 1.06;
  letter-spacing: -0.6px;
  margin: 0 0 var(--space-2);
}
.sl-book-detail__author {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 19px;
  margin: 0 0 var(--space-4);
}
.sl-book-detail__description {
  font-size: 16px;
  line-height: 1.65;
  color: var(--color-text-warm);
  max-width: 520px;
}
.sl-book-detail__categories {
  margin-bottom: var(--space-5);
}
.sl-book-detail__back a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--color-text-warm);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
}
.sl-book-detail__back a:hover {
  text-decoration: underline;
}
/* Owner card: avatar tondo 44 + blocco a due righe. */
.sl-book-detail__owner {
  display: flex;
  align-items: center;
  gap: 13px;
}
.sl-book-detail__owner-avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--color-secondary);
  color: var(--color-paper);
  display: grid;
  place-items: center;
  font-weight: 600;
  flex: 0 0 auto;
}
.sl-book-detail__owner-meta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Rail affini nel dettaglio: card-costola a larghezza fissa (G3 dettaglio). */
.sl-book-detail__affini .sl-suggestions-list {
  display: flex;
  gap: var(--space-3);
  grid-template-columns: none;
}
.sl-book-detail__affini .sl-suggestion-card {
  flex: 0 0 auto;
  width: 280px;
}

/* =========================================================================
   FINE PHASE 8 FIDELITY
   ========================================================================= */

/* --- Rispetto delle preferenze di moto (D-24, no-op per OGNI classe animata) */
@media (prefers-reduced-motion: reduce) {
  .sl-btn,
  .sl-nav-item,
  .sl-skip,
  .sl-book-spine,
  .sl-btn-pill,
  .sl-chip,
  .sl-toggle::after,
  .sl-progress__fill,
  .sl-loan-row,
  .sl-suggestion-card {
    transition: none;
  }
  .sl-toast {
    animation: none;
  }
}

/* =========================================================================
   PHASE 13 — Barcode scanner (additive, no existing rule modified)
   Tutti i valori riferiscono token esistenti di tokens.css: nessun nuovo
   colore/token introdotto. Le varianti pulsante (.sl-btn--secondary /
   .sl-btn--ghost) sono GIA' definite sopra — qui NON ridefinite.
   ========================================================================= */

/* Alpine x-cloak: i pannelli x-show restano nascosti finche' Alpine non
   inizializza (evita il "flash" del pannello scanner prima del boot JS).
   Regola globale: vale per QUALSIASI widget x-show futuro (additiva). */
[x-cloak] {
  display: none !important;
}

/* .sl-note: testo guida/istruzione muto. Base per lo status dello scanner,
   gli hint di campo, ecc. Companion della variante esistente .sl-note--privacy
   (finora la classe .sl-note era usata nei template ma priva di stile base). */
.sl-note {
  font-size: var(--font-size-sm);
  color: var(--color-text-warm);
  line-height: var(--line-height-base);
  margin: var(--space-1) 0 0;
}

/* .sl-note--ok: conferma di un'azione riuscita (segnalazione 2026-08-16).
   Verde bottiglia su carta chiara, MAI il rosso degli errori: queste note dicono
   "fatto" o "era gia' salvato", cioe' proprio i casi in cui l'utente temeva di aver
   sbagliato. Il bordo a sinistra la stacca dal testo attorno senza aggiungere un
   riquadro pieno, che a fianco del contenuto vero suonerebbe come un allarme. */
.sl-note--ok {
  background: var(--color-paper-light);
  border-left: 3px solid var(--color-secondary);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-3);
  color: var(--color-text-warm);
}
.sl-note--ok a {
  color: var(--color-primary);
  font-weight: 600;
  white-space: nowrap;
}

/* .sl-note--attenzione: sorella di .sl-note--ok per il verso opposto — "manca
   qualcosa e vale la pena rimediare". Ocra e non rosso: non e' un errore che la
   persona ha commesso, e' un pezzo che non c'e' ancora. Stessa forma (barra a
   sinistra, nessun riquadro pieno) cosi' le due si leggono come una coppia. */
.sl-note--attenzione {
  background: var(--color-paper-light);
  border-left: 3px solid var(--color-ocra);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-2);
  color: var(--color-text-warm);
}

/* .sl-badge-bozza: riga "non ancora visibile" dentro una card di "Le mie copie".
   Ocra come il banner globale, per dire che e' la stessa cosa vista da vicino. */
.sl-badge-bozza {
  font-size: var(--font-size-sm);
  color: var(--color-text-warm);
  background: var(--color-paper);
  border-left: 3px solid var(--color-ocra);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2);
  margin: var(--space-2) 0;
}
.sl-badge-bozza a {
  color: var(--color-primary);
  font-weight: 600;
  white-space: nowrap;
}

/* .sl-scan-panel: pannello scanner inline. Superficie nidificata leggera
   (NON .sl-panel: la pagina e' gia' dentro un pannello). */
.sl-scan-panel {
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

/* Fallback non-Alpine alla chiusura scanner (WR-02): scan-isbn.js applica questa
   classe in closePanel() cosi' il pannello sparisce davvero anche se la sync
   x-show non avviene (es. bump di Alpine). Il trigger la rimuove alla riapertura;
   !important per vincere il display:grid e l'x-show inline. */
.sl-scan-panel--closed {
  display: none !important;
}

/* .sl-scan-video: viewport fotocamera. Sfondo scuro = "mirino" prima che lo
   stream si attacchi. */
.sl-scan-video {
  width: 100%;
  max-width: 480px;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
  background: var(--color-ink);
  object-fit: cover;
  display: block;
}

/* -------------------------------------------------------------------------
   Sequenza di codici a barre (Phase 48, SHELF-02) — la fotocamera resta aperta
   -------------------------------------------------------------------------
   Classi PROPRIE, non un riuso di .sl-scan-*: gli id e le classi della sequenza
   sono separati da quelli dello scan singolo perche' il lazy loader dello scan
   aggancia il PRIMO trigger del documento, e due superfici che si somigliano
   nel nome finiscono per somigliarsi anche nei bug. Solo token esistenti.
   ------------------------------------------------------------------------- */
.sl-sequenza-panel {
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-4);
  /* Piano 48-19: dentro la colonna a schermo pieno il pannello prende lo spazio
     che avanza, cosi' il mirino cresce con lo schermo invece di restare un
     francobollo in mezzo al vuoto. */
  flex: 1 1 auto;
  align-content: start;
}

/* Fallback non-Alpine alla chiusura (gemello di .sl-scan-panel--closed):
   scan-sequenza.js applica questa classe quando ferma lo stream, cosi' il
   pannello sparisce davvero anche se la sincronizzazione x-show non avviene.
   !important per vincere il display:grid e l'x-show inline. */
.sl-sequenza-panel--closed {
  display: none !important;
}

/* Viewport fotocamera: sfondo scuro = "mirino" prima che lo stream si attacchi.
   Piano 48-19: a schermo pieno il mirino prende l'altezza disponibile. Un 4/3 su
   360px sarebbe alto 270 pixel, cioe' un francobollo in cui il codice a barre di
   un dorso non arriva mai a fuoco. Ripiego in rem prima, `svh` poi: `svh` e'
   l'altezza della viewport con la barra del browser APERTA, lo scenario della
   specifica. */
.sl-sequenza-video {
  width: 100%;
  max-width: 480px;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
  background: var(--color-ink);
  object-fit: cover;
  display: block;
  margin-inline: auto;
  min-height: 15rem;
  min-height: 34svh;
}

/* -------------------------------------------------------------------------
   Le tre schermate del percorso (piano 48-19, D-19, specifica §1)
   -------------------------------------------------------------------------
   «Tutte a schermo pieno, senza scroll su un telefono da 360x640 con la barra
   del browser aperta.» Qui sotto vestono le tre schermate — scansione, passo,
   riepilogo — piu' la ripresa, che e' della stessa famiglia.

   Tre regole di scrittura, nessuna negoziabile:
   1. ogni colore, spaziatura e raggio arriva da una var(--…) di tokens.css.
      Nessuna palette parallela: il progetto ha un sistema di token dal v2.3 e
      un valore letterale qui dentro sarebbe un colore che nessun tema futuro
      sapra' di dover aggiornare (test_sequenza_a11y.py lo rende rosso);
   2. i comandi riusano .sl-btn* di components.css: qui si dice solo COME si
      dispongono, mai come sono fatti;
   3. nessuna media query nuova. Le fasce del progetto sono quelle a verbale
      (nota del 28-08 sulla masthead) e non si aggiungono soglie a intuito:
      dove servirebbe, il lavoro lo fa flex-wrap, che si adatta al contenuto
      invece che a un numero.

   Quante cose puo' contenere una schermata non e' scritto qui: e' misurato in
   apps/library/tests/test_sequenza_a11y.py, che tiene il tetto dei blocchi.

   `svh` e non `vh`, ovunque: `svh` e' l'altezza PICCOLA della viewport, cioe'
   quella con la barra del browser APERTA — esattamente lo scenario della
   specifica. `vh` misura la finestra SENZA barra: promettere quello spazio
   vorrebbe dire mettere i tasti sotto la barra di Safari proprio quando
   servono. Ogni `svh` ha davanti il suo ripiego in rem per i browser che non
   conoscono le unita' dinamiche: la seconda dichiarazione vince dove e'
   capita, la prima resta dove non lo e'.
   ------------------------------------------------------------------------- */

/* Lo scheletro condiviso: una colonna alta quanto lo schermo, contenuto sopra
   e comandi ancorati in fondo. */
.sl-sequenza-scan,
.sl-sequenza-passo,
.sl-sequenza-fine,
.sl-sequenza-ripresa {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-height: 30rem;
  min-height: calc(100svh - var(--space-7));
}

/* Il pannello a schermo pieno respira meno di un .sl-panel da desktop: i 32px
   per lato di .sl-panel, su 360, sono quasi un quinto della larghezza utile. */
.sl-sequenza-passo,
.sl-sequenza-fine,
.sl-sequenza-ripresa {
  padding: var(--space-4);
}

/* Le insegne delle schermate. --font-size-h1 e' un clamp che parte da 2rem:
   su 360px sono due righe di titolo prima ancora di cominciare. Qui il titolo
   dice dove sei, non e' la copertina di un articolo. */
.sl-sequenza-scan__titolo,
.sl-sequenza-passo__titolo-schermata,
.sl-sequenza-fine .sl-h1,
.sl-sequenza-ripresa .sl-h1 {
  font-size: var(--font-size-h2);
  margin: 0;
}

/* L'intestazione del passo e' anche il bersaglio del focus dopo lo scambio
   htmx: senza contorno visibile, chi naviga da tastiera non saprebbe dove e'
   finito il cursore. */
.sl-sequenza-passo__titolo-schermata:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--space-1);
}

/* --- 1. Scansione (§1.1) -------------------------------------------------- */

/* La fotocamera prende tutto lo spazio che avanza fra il titolo e i comandi. */
.sl-sequenza-scan__camera {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-3);
}

/* Il contatore e' una riga sola: e' la misura del lavoro fatto, e se andasse a
   capo spingerebbe giu' i tasti proprio mentre le letture aumentano. */
.sl-sequenza-contatore {
  margin: 0;
  font-weight: 600;
  color: var(--color-text-warm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* La striscia delle letture scorre in ORIZZONTALE. In verticale, alla decima
   lettura, avrebbe gia' spinto «Ho finito» fuori dallo schermo: avrebbe tolto
   il tasto di uscita proprio a chi ha finito. `list-style: none` toglie i
   numeri, che in una striscia sono rumore; le semantiche di lista le tiene in
   piedi il `role="list"` sull'<ol> (senza, Safari le butta via insieme al
   marcatore, e la striscia diventa muta per VoiceOver). */
.sl-sequenza-miniature {
  display: flex;
  gap: var(--space-2);
  margin: 0;
  padding: 0 0 var(--space-2);
  list-style: none;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
}

.sl-sequenza-miniatura {
  flex: 0 0 auto;
  width: 4.5rem;
  display: grid;
  gap: var(--space-1);
  justify-items: center;
  text-align: center;
  font-size: var(--font-size-sm);
  scroll-snap-align: start;
}

.sl-sequenza-miniatura__cover {
  width: 100%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
}

/* Il segnaposto e' un SVG: gli si da' l'ingombro della copertina vera, o la
   striscia salterebbe a ogni lettura senza immagine. */
.sl-sequenza-miniatura__cover--vuota {
  padding: var(--space-3);
  color: var(--color-icon-muted);
}

.sl-sequenza-miniatura__titolo,
.sl-sequenza-miniatura__isbn {
  display: block;
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--color-text-warm);
}

.sl-sequenza-miniatura__isbn {
  font-variant-numeric: tabular-nums;
}

.sl-sequenza-miniatura__nota {
  margin: 0;
}

/* I comandi della scansione — contatore, striscia e i due tasti — ancorati in
   fondo. `sticky` e non `fixed`: su iOS la barra del browser che compare e
   scompare ridisegna la viewport, e un elemento `fixed` ci salta dentro. E' lo
   scenario letterale della specifica, quindi qui `fixed` e' vietato (e il test
   lo controlla). Lo spazio in coda tiene conto della tacca. */
.sl-sequenza-scan__azioni {
  position: sticky;
  bottom: 0;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-3) 0 calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.sl-sequenza-scan__azioni > * {
  flex: 1 1 100%;
}

/* I due tasti stanno affiancati finche' ci stanno e si impilano da soli quando
   non ci stanno piu': nessuna soglia dichiarata, quindi nessuna fascia nuova da
   rimisurare al prossimo tasto. */
.sl-sequenza-scan__azioni .sl-btn {
  flex: 1 1 9rem;
}

/* --- 2. Un libro per schermata (§1.2) ------------------------------------- */

.sl-sequenza-passo__testata {
  display: grid;
  gap: var(--space-1);
}

.sl-sequenza-passo__conteggio {
  margin: 0;
  font-size: var(--font-size-sm);
  color: var(--color-text-warm);
}

/* Barra «sottile» della specifica: alta quanto il token piu' piccolo. */
.sl-sequenza-passo__barra {
  width: 100%;
  height: var(--space-1);
  appearance: none;
  border: none;
  border-radius: var(--radius-sm);
  background: var(--color-nav-active);
  color: var(--color-secondary);
}

.sl-sequenza-passo__barra::-webkit-progress-bar {
  border-radius: var(--radius-sm);
  background: var(--color-nav-active);
}

.sl-sequenza-passo__barra::-webkit-progress-value {
  border-radius: var(--radius-sm);
  background: var(--color-secondary);
}

.sl-sequenza-passo__barra::-moz-progress-bar {
  border-radius: var(--radius-sm);
  background: var(--color-secondary);
}

.sl-sequenza-passo__errori {
  margin: 0;
}

/* Il modulo e' il corpo della schermata: prende lo spazio che avanza fra la
   testata e la barra dei comandi. Nessun `overflow` suo: a scorrere e' la
   pagina, e un riquadro che scorre dentro un altro che scorre e' il modo piu'
   sicuro di perdere il dito sul telefono sbagliato. */
.sl-sequenza-passo__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  flex: 1 1 auto;
  min-height: 0;
}

/* La copertina grande e in alto (D-20), ma non tanto da mangiare i campi: il
   tetto e' relativo alla viewport, con il solito ripiego in rem. */
.sl-sequenza-passo__copertina {
  display: flex;
  justify-content: center;
  margin: 0;
}

.sl-sequenza-passo__cover {
  width: auto;
  max-height: 11rem;
  max-height: 26svh;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}

.sl-sequenza-passo__scatto {
  margin: 0;
  min-inline-size: 0;
}

/* «Ce l'hai gia'»: l'icona e il testo su una riga sola. E' un avviso, non un
   divieto, quindi non prende ne' il rosso degli errori ne' un riquadro pieno. */
.sl-sequenza-passo__duplicato {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}

/* Le caselle sono etichette in linea e vanno a capo da sole nel flusso del
   testo: nessuna barra orizzontale, che nasconderebbe meta' delle categorie
   dietro uno scroll che su un telefono nessuno va a cercare.
   `min-inline-size: 0` perche' un <fieldset> nasce con una larghezza minima
   intrinseca che, con 23 caselle dentro, sfonda i 360px. */
.sl-sequenza-passo__categorie {
  margin: 0;
  padding: 0;
  border: none;
  min-inline-size: 0;
}

.sl-sequenza-passo__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 var(--space-2) var(--space-2) 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  font-size: var(--font-size-sm);
}

/* Il contorno di focus sta sulla chip, non sulla casella nascosta dentro: e'
   la chip che si vede. */
.sl-sequenza-passo__chip:focus-within {
  outline: var(--focus-ring);
  outline-offset: var(--space-1);
}

/* La descrizione, chiusa, occupa una riga: e' tutto il senso di D-21. */
.sl-sequenza-passo__descrizione {
  margin: 0;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}

.sl-sequenza-passo__descrizione > summary {
  cursor: pointer;
}

.sl-sequenza-passo__provvisoria {
  margin: 0;
}

.sl-sequenza-passo__segnala {
  font-size: var(--font-size-sm);
}

/* La barra dei comandi del passo: `sticky`, mai `fixed` (vedi sopra), con lo
   spazio di sicurezza per la tacca. Sta in fondo al DOM e in fondo allo
   schermo: prima si legge di quale libro si parla, poi si decide. */
.sl-sequenza-passo__azioni {
  position: sticky;
  bottom: 0;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: auto;
  padding: var(--space-3) 0 calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.sl-sequenza-passo__azioni .sl-btn {
  flex: 1 1 9rem;
}

/* --- 3. Riepilogo (§1.3) -------------------------------------------------- */

.sl-sequenza-fine__conteggio {
  margin: 0;
  font-weight: 600;
}

/* `auto-fill` con una larghezza minima: la griglia si riempie da sola, e su 360
   ci stanno tre copertine per riga senza dichiarare nessuna fascia. E' l'unico
   blocco che puo' scorrere, perche' e' l'unico che cresce col numero di libri. */
.sl-sequenza-fine__griglia {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
}

.sl-sequenza-fine__libro {
  display: grid;
  gap: var(--space-1);
  justify-items: center;
  text-align: center;
  font-size: var(--font-size-sm);
}

.sl-sequenza-fine__cover {
  width: 100%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
}

.sl-sequenza-fine__cover--vuota {
  padding: var(--space-3);
  color: var(--color-icon-muted);
}

.sl-sequenza-fine__titolo {
  width: 100%;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Il titolo che manca non e' un errore: e' un lavoro rimasto indietro. Ambra e
   corsivo, non il rosso di un modulo rifiutato. */
.sl-sequenza-fine__titolo--manca {
  color: var(--color-status-attesa-fg);
  font-style: italic;
}

.sl-sequenza-fine__completa {
  font-size: var(--font-size-sm);
}

/* L'unico tasto della schermata, largo quanto lo schermo e ancorato in fondo. */
.sl-sequenza-fine__azione {
  position: sticky;
  bottom: 0;
  z-index: 1;
  margin: 0;
  padding: var(--space-3) 0 calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.sl-sequenza-fine__azione .sl-btn {
  width: 100%;
}

/* --- 4. La ripresa (D-22, §2) --------------------------------------------- */

.sl-sequenza-ripresa__conteggio {
  margin: 0;
  font-weight: 600;
}

.sl-sequenza-ripresa__riprendi {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
}

/* L'avviso e il tasto distruttivo scendono in fondo: «Riprendi» e' la strada
   normale e deve restare la prima cosa che il pollice incontra. */
.sl-sequenza-ripresa__avviso {
  margin: auto 0 0;
}

.sl-sequenza-ripresa__butta {
  margin: 0;
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

.sl-sequenza-ripresa__butta .sl-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* =========================================================================
   PHASE 20 — Chrome pubblica anonima + superfici home/come-funziona (D-11)
   -------------------------------------------------------------------------
   Tutte le classi qui consumano SOLO i token esistenti di tokens.css: nessun
   nuovo --color-* / --font-size-* / peso introdotto (tetto Phase 8: 4 size /
   2 pesi 400-600 / palette esistente). Moto SOLO transform (mai opacity, D-24),
   con no-op in coda dentro prefers-reduced-motion. Breakpoint unico letterale
   860px (= --bp-tablet; le media-query non possono usare le custom property).
   Promuove gli stili inline finora presenti in base_public.html / public_edition
   in classi documentate (deliverable d'esame "HTML/CSS commentato").
   ========================================================================= */

/* --- Header pubblico anonimo: logo a sinistra + nav anonima a destra ------
   Promosso da base_public.html (header finora logo-only, padding inline). Riga
   flex; ``min-width: 0`` + ``flex-wrap`` sui figli così a 320px non c'è MAI
   scroll orizzontale (Pitfall 6; un overflow di tab-bar fu già preso a Phase 8).
   Padding agganciato alla scala token (snap, non valori frazionari inline). */
.sl-public-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-paper-light);
}
.sl-public-header__brand {
  display: inline-flex;
  align-items: center;
  min-width: 0;            /* il brand non forza larghezza minima (320px-safe) */
  text-decoration: none;
}
.sl-public-header__brand img {
  display: block;
  height: 30px;           /* mobile: logo più compatto (UI-SPEC Surface 3) */
  width: auto;
  max-width: 100%;
}
/* Nav anonima: riga di link che possono andare a capo / restringersi senza
   sforare; "Registrati" (CTA filled) resta sempre visibile a 320px. */
.sl-public-nav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  min-width: 0;
}
.sl-public-nav a {
  min-width: 0;
}
/* Link testuali della nav (Come funziona / Accedi): 44px min touch target su
   mobile (WCAG 2.2 AA target size — eccezione a11y, non violazione di scala). */
.sl-public-nav__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: var(--space-2) var(--space-2);
  color: var(--color-text-warm);
  font-weight: 600;
  text-decoration: none;
}
.sl-public-nav__link:hover {
  text-decoration: underline;
}

/* --- Hamburger pubblico (redesign mobile 2026-08-17) ----------------------
   Sempre visibili nella barra: logo + lingua + social. Le voci di navigazione/CTA
   (Come funziona · Guide · Accedi · Registrati) stanno inline ≥860px e in un menù ☰
   a scomparsa <860px. Toggle CSS-only: checkbox visually-hidden ma FOCUSABILE (mai
   `hidden`, che lo toglierebbe dal tab order → menù non apribile da tastiera). */
.sl-public-header { position: relative; }   /* ancora per il dropdown assoluto */
.sl-pubmenu-toggle {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}
.sl-pubmenu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px; min-height: 44px;   /* WCAG 2.2 target size */
  border-radius: var(--radius-md);
  color: var(--color-text-warm);
  cursor: pointer;
}
.sl-pubmenu-btn svg { width: 24px; height: 24px; }
.sl-pubmenu-toggle:focus-visible + .sl-pubmenu-btn {
  outline: 2px solid var(--color-secondary);
  outline-offset: 1px;
}
/* Menù voci: dropdown a scomparsa su mobile (chiuso di default). */
.sl-pubmenu {
  display: none;
  position: absolute;
  top: 100%; left: 0; right: 0;
  z-index: 40;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4) var(--space-4);
  background: var(--color-paper-light);
  border-bottom: 1px solid var(--color-border);
  box-shadow: 0 8px 16px rgba(33, 27, 19, 0.12);
}
.sl-pubmenu-toggle:checked ~ .sl-pubmenu { display: flex; }
/* Gruppo lingua + social. Dal 2026-08-28 sta DENTRO il menù, non più sciolto in barra:
   il logo della rev. agosto 2026 è largo 185px contro 124 a parità d'altezza e la riga
   logo + lingua + social + ☰ non ci stava più nemmeno a 320px, andava a capo su due
   righe. Nel dropdown scende in fondo (order: 2): prima le voci di navigazione, poi la
   preferenza lingua, separata da una riga. Inline torna al suo posto a sinistra. */
.sl-pubmenu__extra {
  order: 2;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}
/* Soglia del menù inline: 960px, non più 860px. A 860 il menù tornava inline ma la riga
   non ci stava e andava a capo su due righe (l'header passava da 85px a 137px di
   altezza). Misurata a mano forzando nowrap, la riga piena chiede 884px: logo desktop
   218 + nav 606 + padding. 960 la fa stare con 76px di margine, abbastanza per l'inglese
   e per una voce in più senza tornare a sforare. Sotto, c'è l'hamburger — che risponde
   anche al "sul desktop l'hamburger non c'è": ora c'è fin dove serve davvero.
   ⚠️ Se aggiungi una voce al menù, rimisura: la soglia va spostata con lei. */
@media (min-width: 960px) {
  .sl-pubmenu-btn { display: none; }
  .sl-pubmenu {
    display: flex;
    position: static;
    flex-direction: row;
    align-items: center;
    gap: var(--space-3);
    padding: 0;
    background: none;
    border: 0;
    box-shadow: none;
  }
  .sl-pubmenu__extra {
    order: 0;
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }
  /* Il logo desktop da 40px (218px di larghezza) sta con la stessa soglia del menù
     inline: prima stava a 860px ed era metà del motivo per cui la riga sforava. */
  .sl-public-header__brand img {
    height: 40px;          /* logo desktop (UI-SPEC Surface 3) */
  }
}
/* HEADER PUBBLICO: i social stanno in barra da 360 a 959px. Sotto i 360 non ci stanno
   (la riga logo + due tondi + hamburger chiede 352px: a 360 avanzano 8px, a 320 ne
   mancano 32 — e 352 e' un numero fermo, sono tutte larghezze fisse, non c'e' testo che
   possa allungarsi cambiando lingua), e da 960 in
   su non serve la copia in barra perche' il menu' e' gia' inline nella riga: la copia
   ``.sl-menu-social`` dentro .sl-pubmenu__extra e' li' a vista, accanto alla lingua,
   nell'ordine storico [lingua][IG][FB][voci][CTA]. Duplicarli sarebbe quattro icone. */
@media (min-width: 360px) and (max-width: 959px) {
  .sl-public-header .sl-social-inline {
    display: inline-flex;
  }
  .sl-public-header .sl-menu-social {
    display: none;
  }
}
/* Focus ring visibile su OGNI elemento interattivo della chrome (A11Y, ≥3:1). */
.sl-public-header a:focus-visible,
.sl-public-nav a:focus-visible,
.sl-footer a:focus-visible,
.sl-hero a:focus-visible,
.sl-how a:focus-visible,
.sl-featured a:focus-visible,
.sl-featured-empty a:focus-visible {
  outline: 2px solid var(--color-secondary);
  outline-offset: 1px;
}

/* --- Selettore lingua IT/EN (Phase 24 / I18N-05) -------------------------
   Toggle segmentato sempre visibile nei 4 slot (public-nav, sidebar bottom,
   topbar mobile, anon-split). Composto SOLO da token pre-esistenti (zero nuovi
   token, il tetto Phase-8 regge). Verde bottiglia --color-secondary per il
   segmento attivo (chrome di nav/preferenza, MAI il terracotta --color-primary
   riservato alle CTA di conversione). 44px min target (WCAG 2.2 Target Size,
   come .sl-public-nav__link). NESSUNA animazione opacity (D-24); nessuna
   transizione → niente no-op reduced-motion necessario. */
/* Selettore lingua (redesign 2026-07-04): globo decorativo + segmentato IT|EN.
   Il globo (aria-hidden) fa capire "lingua" a colpo d'occhio; il controllo resta
   il segmento a due opzioni (un solo bordo+radius, attivo = fill verde). */
.sl-lang-switch {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  color: var(--color-text-warm);
  min-width: 0;            /* 320px-safe: non forza larghezza minima */
}
.sl-lang-switch__globe {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
}
.sl-lang-switch__seg {
  display: inline-flex;
  align-items: stretch;
  padding: 2px;
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
/* I due bottoni vivono in un <form> ciascuno (bugfix switcher: next per-lingua già
   tradotto). display:contents li fa partecipare al flex del segmento come voci adiacenti. */
.sl-lang-switch__form {
  display: contents;
}
.sl-lang-switch__opt {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 34px;        /* ≥ 24px WCAG 2.2 SC 2.5.8 target size */
  min-width: 40px;
  padding: 5px 12px;
  background: transparent;
  border: 0;
  border-radius: calc(var(--radius-md) - 2px);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--color-text-warm);
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
/* Segmento attivo = lingua corrente: fill verde bottiglia, testo carta (8.25:1 AA). */
.sl-lang-switch__opt--active {
  background: var(--color-secondary);
  color: var(--color-paper);
}
/* Hover solo sull'inattivo: lieve lift paper-tone (mai sull'attivo). */
.sl-lang-switch__opt:not(.sl-lang-switch__opt--active):hover {
  background: var(--color-nav-active);
}
/* Focus ring verde 2px, offset 2px ≥3:1 anche attorno al fill verde attivo. */
.sl-lang-switch__opt:focus-visible {
  outline: 2px solid var(--color-secondary);
  outline-offset: 2px;
}

/* --- Banner "View in English?" (Phase 29 / DOM-03) ------------------------
   Banda sottile e non-intrusiva in cima alla pagina pubblica: messaggio +
   link a /en/ + X di chiusura. Fill verde bottiglia --color-secondary (chrome
   di preferenza/nav, MAI il terracotta --color-primary delle CTA di conversione);
   testo carta per contrasto. Composto SOLO da token pre-esistenti. Va a capo a
   320px (flex-wrap) e non forza scroll orizzontale (Pitfall 6). */
.sl-en-banner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-5);
  background: var(--color-secondary);
  color: var(--color-paper);
  font-size: var(--font-size-sm);
}
.sl-en-banner__msg {
  font-weight: 600;
}
/* Link "View in English": testo carta sottolineato, 44px min target (WCAG 2.2 AA). */
.sl-en-banner__go {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--color-paper);
  font-weight: 600;
  text-decoration: underline;
}
.sl-en-banner__go:hover {
  text-decoration: none;
}
/* X di chiusura: spinta a destra, bottone trasparente, 44px min target. */
.sl-en-banner__dismiss {
  margin-left: auto;
  min-height: 44px;
  min-width: 44px;
  padding: 0 var(--space-2);
  background: transparent;
  border: 0;
  color: var(--color-paper);
  font-size: var(--font-size-h2);
  line-height: 1;
  cursor: pointer;
}
/* Focus ring carta ≥3:1 sul fill verde, per link e X. */
.sl-en-banner__go:focus-visible,
.sl-en-banner__dismiss:focus-visible {
  outline: 2px solid var(--color-paper);
  outline-offset: 2px;
}

/* --- Barra "Passa all'italiano" (PUB-08 / fase 51) -------------------------
   Gemella di .sl-en-banner ma per il verso opposto: italiano su /en/. Nasce
   `hidden` lato server (it-invite.js decide se mostrarla via navigator.languages)
   così la pagina resta cacheabile e crawler-safe. Stesso fill --color-secondary,
   stesso testo carta, stesso layout a riga con wrap a 320px (Pitfall 6). */
.sl-it-invite {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-5);
  background: var(--color-secondary);
  color: var(--color-paper);
  font-size: var(--font-size-sm);
}
.sl-it-invite[hidden] {
  display: none;
}
.sl-it-invite__testo {
  font-weight: 600;
}
/* Link "Passa all'italiano": testo carta sottolineato, 44px min target (WCAG 2.2 AA). */
.sl-it-invite__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--color-paper);
  font-weight: 600;
  text-decoration: underline;
}
.sl-it-invite__link:hover {
  text-decoration: none;
}
/* X di chiusura: spinta a destra, bottone trasparente, 44px min target. */
.sl-it-invite__chiudi {
  margin-left: auto;
  min-height: 44px;
  min-width: 44px;
  padding: 0 var(--space-2);
  background: transparent;
  border: 0;
  color: var(--color-paper);
  cursor: pointer;
}
/* Focus ring carta ≥3:1 sul fill verde, per link e X. */
.sl-it-invite__link:focus-visible,
.sl-it-invite__chiudi:focus-visible {
  outline: 2px solid var(--color-paper);
  outline-offset: 2px;
}

/* --- Footer editoriale condiviso (app + pubblico) -------------------------
   Banda verde bottiglia (--color-secondary) con testo crema (--color-paper):
   coppia già validata AA 8.26:1 dal gate check_contrast.py (--bg × --strong).
   Layout a colonne: brand+tagline + colonne di link (Naviga/Legale/Contatti),
   filetto oro decorativo, riga copyright. Impilato su mobile, a griglia >=860px.
   Sostituisce il vecchio .sl-public-footer e stila l'ex-.sl-app-footer (che era
   privo di CSS = look "anni 2000"). */
.sl-footer {
  /* Mobile: footer compatto (occupava >1 schermo in fondo allo scroll). Padding e
     margine ridotti; ripristinati a desktop nella media-query in coda. */
  margin-top: var(--space-6);
  padding: var(--space-5) var(--space-5);
  background: var(--color-secondary);
  color: var(--color-paper);
  font-size: var(--font-size-sm);
}
.sl-footer__inner {
  max-width: 1080px;
  margin: 0 auto;
  display: grid;
  /* Mobile: le 3 colonne nav AFFIANCATE (2 per riga) invece che impilate → dimezza
     l'altezza. Il brand resta a tutta larghezza sopra. Desktop = 4 colonne (media-query). */
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4) var(--space-5);
}
.sl-footer__brand {
  grid-column: 1 / -1;
  max-width: 34ch;
}
.sl-footer__logo {
  display: block;
  margin-bottom: var(--space-3);
}
.sl-footer__tagline {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--font-size-base);
  line-height: 1.5;
  color: var(--color-paper);
}
.sl-footer__col {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.sl-footer__heading {
  margin: 0 0 var(--space-2);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--color-paper);
}
.sl-footer__col a {
  color: var(--color-paper);
  text-decoration: none;
  min-height: 32px;
  display: inline-flex;
  align-items: center;
}
.sl-footer__col a:hover {
  text-decoration: underline;
}
.sl-footer__bar {
  max-width: 1080px;
  margin: var(--space-4) auto 0;
  padding-top: var(--space-3);
  /* filetto "oro" decorativo (non-testo → nessun requisito di contrasto). */
  border-top: 1px solid rgba(212, 155, 60, 0.4);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-2);
  color: var(--color-paper);
}
.sl-footer__bar p {
  margin: 0;
}
.sl-footer__bar a {
  color: var(--color-paper);
  font-weight: 600;
}
.sl-footer__bar a:hover {
  text-decoration: underline;
}

/* --- Hero: eyebrow + H1 serif (un <em> ocra) + sub-paragrafo + coppia CTA ---
   Nessun media/mappa (anon = niente stato geo). Ritmo verticale generoso:
   --space-6 mobile, --space-7 desktop (vedi media-query in coda). */
.sl-hero {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5);
  text-align: center;
}
.sl-hero__title {
  font-family: var(--font-serif);
  font-size: var(--font-size-h1);
  font-weight: 600;
  line-height: var(--line-height-tight);
  margin: 0 0 var(--space-4);
}
/* Enfasi singola nell'H1: corsivo serif in ocra (riusa il register esistente). */
.sl-hero__title em {
  font-style: italic;
  color: var(--color-ocra);
}
.sl-hero__sub {
  color: var(--color-text-warm);
  max-width: 52ch;
  margin: 0 auto var(--space-5);
  line-height: var(--line-height-base);
}
/* Coppia CTA: si dispone in riga, va a capo a schermo stretto senza sforare. */
.sl-hero__cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
}

/* --- "Come funziona" teaser (D-08): 3 step card -------------------------- */
.sl-how {
  max-width: 1080px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5);
}
.sl-how__head {
  text-align: center;
  margin: 0 0 var(--space-5);
}
.sl-how__grid {
  display: grid;
  grid-template-columns: 1fr;       /* mobile: colonna singola */
  gap: var(--space-4);
}
/* Step card: stessa DNA di .sl-book-card (paper-light + border + radius-lg). */
.sl-how-step {
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
}
.sl-how-step__num {
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-primary);
}
.sl-how-step__title {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  font-weight: 600;
  margin: var(--space-1) 0 var(--space-2);
}
.sl-how-step__body {
  color: var(--color-text-warm);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-base);
  margin: 0;
}
.sl-how__more {
  display: block;
  margin-top: var(--space-4);
  text-align: center;
  font-weight: 600;
  color: var(--color-primary);
  min-height: 44px;
}

/* Contenitore del modulo di ricerca in home (fase 51, Task 8/9): STESSA larghezza e
   respiro di ".sl-featured" appena sotto, per allinearsi alla griglia "Libri da
   scoprire" invece di restare senza contenitore (larghezza piena, niente margini). */
.sl-home-search {
  max-width: 1080px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5);
}

/* --- Griglia "Libri da scoprire" (D-02/D-04): card-link auto-fill ---------
   Nessun badge / conteggio / zona / distanza (PUB-06): solo cover + titolo +
   autore, link a /libro/<pk>/<slug>/. Hover = solo translateY (mai opacity). */
.sl-featured {
  max-width: 1080px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5);
}
.sl-featured__head {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  font-weight: 600;
  margin: 0 0 var(--space-5);
}
.sl-featured__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--space-4);
}
.sl-featured-card {
  display: flex;
  flex-direction: column;
  min-width: 0;                     /* card fluida: niente overflow a 320px */
  text-decoration: none;
  color: var(--color-ink);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.sl-featured-card:hover {
  transform: translateY(-2px);      /* transform-only (D-24), MAI opacity */
  box-shadow: var(--shadow-card);
}
.sl-featured-card__cover {
  width: 100%;
  max-width: 100%;
  aspect-ratio: 2 / 3;
  height: auto;
  object-fit: cover;
  border-radius: 4px 8px 8px 4px;   /* costola libro, come .sl-book-spine */
  box-shadow: var(--shadow-card);
}
/* Segnaposto "copertina mancante": e' il mark del brand, non una copertina. Col mark
   della rev. agosto 2026 serve dirlo, perche' il pin ha ormai quasi lo stesso rapporto
   della card (200x305 = 0,656 contro il 2/3 = 0,667 dello slot): con ``cover``, che e'
   la regola giusta per una copertina vera, il pin verrebbe scalato fino a riempire la
   card da bordo a bordo. ``contain`` + padding lo riportano a un segno dentro la carta,
   come era col mark precedente (che aveva un rapporto diverso e restava piccolo da se'). */
.sl-featured-card__cover--placeholder {
  object-fit: contain;
  padding: var(--space-6);
  background: var(--color-paper-light);
}
.sl-featured-card__title {
  font-family: var(--font-serif);
  font-weight: 600;
  margin: var(--space-2) 0 0;
}
.sl-featured-card__author {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: var(--font-size-sm);
  color: var(--color-text-warm);
  margin: var(--space-1) 0 0;
}

/* --- Cold-start / empty state (D-03): invito, MAI una griglia muta -------- */
.sl-featured-empty {
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  text-align: center;
}
.sl-featured-empty__head {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  font-weight: 600;
  margin: 0 0 var(--space-3);
}
.sl-featured-empty__body {
  color: var(--color-text-warm);
  margin: 0 0 var(--space-4);
}

/* --- Desktop (>= 860px): ritmo più ampio + teaser a 3 colonne ------------- */
@media (min-width: 860px) {
  .sl-footer {
    margin-top: var(--space-7);
    padding: var(--space-6) var(--space-5);
  }
  .sl-footer__inner {
    grid-template-columns: 2fr 1fr 1.2fr 1.4fr;
    gap: var(--space-6);
    align-items: start;
  }
  .sl-footer__brand {
    grid-column: auto;
  }
  .sl-footer__bar {
    margin-top: var(--space-5);
    padding-top: var(--space-4);
  }
  .sl-hero {
    padding: var(--space-7) var(--space-5);
  }
  .sl-how__grid {
    grid-template-columns: repeat(3, 1fr);
  }
  .sl-featured__grid {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  }
}

/* --- No-op moto per le superfici Phase 20 (D-24): rispetta reduce --------- */
@media (prefers-reduced-motion: reduce) {
  .sl-featured-card {
    transition: none;
  }
}

/* --- Fase 1 GDPR: tooltip accessibili consensi signup (Task 8) ------------- */
.sl-consent__tip {
  margin: var(--space-1, 0.25rem) 0 0 0;
  font-size: 0.8125rem; /* ~13px */
  color: var(--color-text-muted, #666);
  line-height: 1.4;
}

/* ==========================================================================
   Frontend polish (2026-07): filter bar, select custom, empty-state, link brand
   ========================================================================== */

/* --- Link di contenuto on-brand (prima erano <a> blu di default) -----------
   Verde bottiglia su superfici chiare = 8.25:1 AA. Underline sempre presente
   (affordance non dipendente dal colore). Hover terracotta (transiente). */
.sl-link {
  color: var(--color-secondary);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.sl-link:hover {
  color: var(--color-primary);
}
.sl-link:focus-visible {
  outline: 2px solid var(--color-secondary);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Alias: alcuni template usano .sl-btn-primary (single dash) accanto a .sl-btn.
   Uniformato a .sl-btn--primary così il bottone è sempre terracotta pieno. */
.sl-btn-primary {
  background: var(--color-primary);
  color: var(--color-paper);
}

/* --- Empty-state riutilizzabile (Scaffale/Mappa/Richieste) -----------------
   Contenitore centrato + icona discreta + titolo serif + sottotesto + CTA bottone.
   Sostituisce le frasi isolate nel vuoto (che sembravano il sito rotto). */
.sl-emptystate {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  max-width: 40ch;
  margin: var(--space-5) auto;
  padding: var(--space-6) var(--space-4);
}
.sl-emptystate__icon {
  color: var(--color-icon-muted);
}
.sl-emptystate__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  line-height: 1.25;
  color: var(--color-ink);
}
.sl-emptystate__text {
  margin: 0;
  color: var(--color-text-muted);
  line-height: 1.55;
}
.sl-emptystate .sl-btn {
  margin-top: var(--space-1);
}

/* --- Filter bar Mappa (/esplora) + Cerca: label sopra il campo, controlli
   della STESSA altezza allineati in riga, select custom on-brand, contenitore
   con padding (niente controlli attaccati ai bordi). --------------------- */
.sl-esplora__form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-4);
  margin: var(--space-4) 0;
  padding: var(--space-4);
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}
.sl-esplora__row {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
  min-width: 0;
}
.sl-esplora__row:first-of-type {
  flex: 1 1 240px;   /* il campo "Cerca" cresce */
}
.sl-esplora__row--radius {
  flex: 0 0 auto;
}
.sl-esplora__label {
  font-size: var(--font-size-sm);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--color-text-warm);
}
/* Campo testo + select: STESSA altezza (48px) e stessa cornice on-brand. */
.sl-esplora__form input[type="text"],
.sl-esplora__form input:not([type]),
.sl-esplora__form select {
  height: 48px;
  box-sizing: border-box;
  padding: 0 var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: inherit;
  font-size: var(--font-size-base);
}
.sl-esplora__form input:focus,
.sl-esplora__form select:focus {
  outline: 2px solid var(--color-secondary);
  outline-offset: 1px;
  border-color: var(--color-secondary);
}
/* Select custom: freccia SVG on-brand (chevron text-warm) invece del nativo. */
.sl-esplora__form select,
.sl-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: calc(var(--space-6) + var(--space-2));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%234A3F30' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  background-size: 16px 16px;
  cursor: pointer;
}
/* Bottone "Filtra la lista": stessa altezza dei controlli, in linea (non sotto). */
.sl-esplora__submit {
  height: 48px;
  display: inline-flex;
  align-items: center;
  padding-top: 0;
  padding-bottom: 0;
  flex: 0 0 auto;
}
/* La pagina "Cerca" (page.html) riusa lo stesso trattamento del select custom. */
.sl-search__radius-card select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: calc(var(--space-6) + var(--space-2));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%234A3F30' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  background-size: 16px 16px;
  cursor: pointer;
}

/* UAT 2026-07-10 #1: le chip devono STARE nella card (overflow gestito) e le card
   della stessa riga devono allinearsi (reasons ancorate in basso, altezze uniformi
   via grid stretch di default). min-width:0 sul body c'è già (riga sopra). */
.sl-book-card__body { flex: 1 1 auto; }
.sl-suggestion__reasons {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: auto; /* chips in basso: griglia non "sfalsata" fra card della riga */
  padding-top: var(--space-2);
  min-width: 0;
}
.sl-suggestion__reasons .sl-chip {
  max-width: 100%;
  min-width: 0;
}
.sl-suggestion__reasons .sl-chip__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* UAT 2026-07-10 #4: il blocco slider era SENZA regole (label inline, zero margini →
   sovrapposizioni col titolo dei suggeriti). Componente contenuto: label sopra,
   range al centro, poli agli estremi; respiro sotto verso la sezione seguente. */
.sl-search__suggestions-axis {
  margin: var(--space-5) 0;
  max-width: 36rem;
}
.sl-search__suggestions-axis .sl-search__label {
  display: block;
  margin-bottom: var(--space-2);
  font-weight: 600;
  color: var(--color-ink);
}
.sl-weight-slider input[type="range"]:focus-visible {
  outline: 2px solid var(--color-secondary);
  outline-offset: 2px;
}
@media (max-width: 400px) {
  .sl-weight-slider__pole { font-size: 0.72rem; }
}

/* UAT 2026-07-10 #3: contrasto del cluster numerato su qualsiasi tile OSM (parco
   verde o urbano grigio): disco pieno brand + numero bianco + anello bianco e ombra. */
.marker-cluster-small div,
.marker-cluster-medium div,
.marker-cluster-large div {
  border: 2px solid var(--color-paper);
  box-shadow: 0 3px 8px rgba(33, 27, 19, 0.35);
}

/* -------------------------------------------------------------------------
   Punti: cassette e biblioteche — Phase 56, POINT-02
   -------------------------------------------------------------------------
   Classi PROPRIE, non un riuso di .sl-map-popup__*: sulla stessa mappa vivono
   due strati con regole opposte — i libri delle persone (coordinate offuscate)
   e i luoghi pubblici (coordinate esatte) — e devono restare distinguibili
   anche nel foglio di stile. Altrimenti un domani un ritocco al pin dei libri
   cambia in silenzio anche l'aspetto delle cassette.

   L'unica cosa condivisa è la GOCCIA: .sl-pin resta una sola in tutto il
   progetto (34x34, ancora a 17,32) e qui se ne cambia solo il colore, così non
   nasce una seconda misura di pin che nessuno saprà tenere allineata.

   Ogni valore viene da un token esistente: un colore scritto a mano qui dentro
   sarebbe una palette parallela che nessun tema futuro saprà di dover
   aggiornare e che nessun controllo di contrasto misurerà mai.
   ------------------------------------------------------------------------- */

/* Le due varianti della goccia. Il glifo diventa chiaro: sopra una tinta piena
   il colore di testo ereditato dalla pagina scenderebbe sotto il contrasto
   utile alla dimensione di un'icona da 16 pixel. */
.sl-pin--cassetta {
  background: var(--color-primary);
  color: var(--color-paper);
}
.sl-pin--biblioteca {
  background: var(--color-secondary);
  color: var(--color-paper);
}

/* Legenda: sta SOTTO la mappa, non dentro (niente controllo dei layer nativo di
   Leaflet — il perché è nel template). Scheda come le altre della pagina. */
.sl-punti-legenda {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-top: var(--space-4);
  display: grid;
  gap: var(--space-3);
}
.sl-punti-legenda__toggle {
  justify-self: start;
  font: inherit;
  font-weight: 600;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text);
  cursor: pointer;
}
/* Acceso: bottone pieno. Cambiano insieme riempimento e contorno, così lo stato
   non è affidato al solo colore (WCAG 1.4.1). */
.sl-punti-legenda__toggle[aria-pressed="true"] {
  background: var(--strong);
  border-color: var(--strong);
  color: var(--primary-contrast);
}
.sl-punti-legenda__voci {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}
.sl-punti-legenda__voce {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.sl-punti-legenda__icona {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid var(--color-paper);
  color: var(--color-paper);
}
.sl-punti-legenda__icona--cassetta {
  background: var(--color-primary);
}
.sl-punti-legenda__icona--biblioteca {
  background: var(--color-secondary);
}
/* Il tasto «Aggiungi una cassetta» (56-16, rilievo 2 della UAT del 09-09-2026).
   A TUTTA LARGHEZZA e alto almeno 44px: è il bersaglio del pollice di chi guarda la
   mappa in piedi, con una mano sola, e i 44px sono il minimo di WCAG 2.2 (le linee
   guida iOS dicono 44, Android 48 — qui il contenuto porta l'altezza reale sopra
   entrambi). L'icona e l'etichetta stanno su una riga sola, centrate: `inline-flex`
   e non `block`, perché il testo si tradurrà e in inglese è più corto. */
.sl-punti-legenda__aggiungi {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  text-align: center;
}
.sl-punti-legenda__nota {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}
/* Attribuzione ODbL: piccola ma leggibile, e il collegamento resta sottolineato
   perché è quello a soddisfare l'obbligo di dare accesso a origine e licenza. */
.sl-punti-legenda__attribuzione {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-kicker);
}
.sl-punti-legenda__attribuzione a {
  color: inherit;
  text-decoration: underline;
}

/* Popup del marker: nome, operatore quando c'è, e il link alla pagina del punto. */
.sl-punti-popup {
  display: grid;
  gap: var(--space-1);
  min-width: 11rem;
}
.sl-punti-popup__nome {
  font-family: var(--font-serif);
  font-size: var(--fs-h3);
  color: var(--text);
}
.sl-punti-popup__operatore {
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}
.sl-punti-popup__link {
  color: var(--primary);
  font-weight: 600;
  text-decoration: underline;
}

/* -------------------------------------------------------------------------
   Punti: la pagina pubblica di un punto — Phase 56, POINT-04
   -------------------------------------------------------------------------
   Stessa disciplina del blocco qui sopra: ogni colore da un token, nessuna
   misura inventata, nessuna regola che un template non usi.

   La pagina è mobile-first e verticale per costruzione (una griglia a una
   colonna con un solo passo di spaziatura): chi la apre l'ha aperta da un QR,
   in piedi davanti a una cassetta, con una mano sola.
   ------------------------------------------------------------------------- */
.sl-punti-scheda {
  max-width: 62ch;
  margin: 0 auto;
  padding: var(--space-5) var(--container-pad);
  display: grid;
  gap: var(--space-4);
}
.sl-punti-scheda__nome {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  color: var(--text);
  margin: 0;
}
.sl-punti-scheda__presentazione {
  margin: 0;
  color: var(--text);
}
/* Il tipo del punto accanto al nome: riusa .sl-badge per forma e dimensione,
   qui cambia solo la tinta, così la cassetta si distingue dalle etichette del
   catalogo senza che nasca un secondo badge da tenere allineato. */
.sl-punti-badge {
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-muted);
}
/* Indirizzo, orari, chi lo gestisce, sito: coppie etichetta/valore. Su telefono
   restano incolonnate — un affiancamento a due colonne spezzerebbe un
   opening_hours di OSM, che è lungo e non si può mandare a capo dove capita. */
.sl-punti-dati {
  margin: 0;
  display: grid;
  gap: var(--space-1);
}
.sl-punti-dati dt {
  font-size: var(--fs-kicker);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.sl-punti-dati dd {
  margin: 0 0 var(--space-2) 0;
  color: var(--text);
  overflow-wrap: anywhere;
}
/* La mini-mappa. L'altezza è fissa perché Leaflet ha bisogno di un contenitore
   già dimensionato al momento dell'init: un container alto 0 disegna una mappa
   grigia e non dà nessun errore. */
.sl-punti-mappa {
  height: 220px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
}
.sl-punti-sezione {
  display: grid;
  gap: var(--space-3);
}
.sl-punti-sezione__titolo {
  font-family: var(--font-serif);
  font-size: var(--fs-h3);
  color: var(--text);
  margin: 0;
}
.sl-punti-libri {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}
/* Una riga = una schermata di lettura: copertina a sinistra, testo a destra,
   e il bersaglio del tap è la riga intera (le tre azioni arrivano col 56-12). */
.sl-punti-libro {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-3);
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}
.sl-punti-libro__copertina {
  width: 56px;
  height: 84px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  background: var(--bg);
}
/* Il segnaposto quando l'Edizione non ha copertina: quasi sempre. */
.sl-punti-libro__copertina--vuota {
  display: inline-grid;
  place-items: center;
  border: 1px dashed var(--border);
  color: var(--text-muted);
}
.sl-punti-libro__testo {
  display: grid;
  gap: var(--space-1);
  min-width: 0;
}
.sl-punti-libro__titolo {
  font-weight: 600;
  color: var(--text);
  overflow-wrap: anywhere;
}
.sl-punti-libro__autore {
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}
/* La freschezza: piccola, ma è l'informazione che decide se attraversare il
   quartiere. Non è un badge di stato — non c'è nessuno stato da comunicare. */
.sl-punti-libro__freschezza {
  color: var(--text-muted);
  font-size: var(--fs-kicker);
}
/* Lo stato a PAROLE quando la riga e' chiusa: prende il posto della freschezza, che su una
   riga chiusa non vuol piu' dire niente. L'informazione e' la parola, non il colore — chi non
   distingue i toni, chi ascolta la pagina e chi guarda lo schermo in strada col sole devono
   capire la stessa cosa — quindi qui basta il tono attenuato. */
.sl-punti-libro__stato {
  color: var(--text-muted);
  font-size: var(--fs-kicker);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* L'esito del tocco, DENTRO la riga (fuori sparirebbe insieme al bersaglio dello swap):
   a tutta larghezza sotto copertina e testo. Vale anche per .sl-field-error, che e' la classe
   d'errore gia' in casa e non va duplicata con un nome della fase. */
.sl-punti-libro__messaggio,
.sl-punti-libro .sl-field-error {
  grid-column: 1 / -1;
  margin: 0;
}
.sl-punti-libro__messaggio {
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}
/* I tre comandi di POINT-06. Mobile-first davvero: stanno sotto copertina e testo, a tutta
   larghezza, uno per riga sui telefoni stretti e affiancati appena c'e' spazio. I 48px di
   altezza minima non sono un numero tondo: sono il bersaglio che si preme in piedi davanti a
   una cassetta, con una mano sola, senza guardare — ed e' il minimo che soddisfa insieme le
   linee guida iOS (44) e Android (48). */
.sl-punti-libro__azioni {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.sl-punti-libro__azioni .sl-btn {
  flex: 1 1 9rem;
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.sl-punti-vuoto {
  margin: 0;
  color: var(--text-muted);
}
.sl-punti-azioni {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
/* Rilievo 1 della UAT del 09-09-2026. Sul telefono il gesto e' «Condividi il punto»
   (il bottone primario, primo nel DOM); il cartello da stampare passa in SECONDO
   PIANO — piu' piccolo e a peso normale, mai nascosto: da un telefono si stampa via
   cloud, e chi ha il JavaScript spento ha solo quel link. La gerarchia e' gia' scritta
   nell'ordine del markup: qui si dice soltanto quanto pesa. Sopra 860px (desktop, dove
   una stampante c'e' davvero) torna un comando come gli altri. */
.sl-punti-azioni__stampa {
  font-size: var(--font-size-sm);
  font-weight: 400;
}
@media (min-width: 860px) {
  .sl-punti-azioni__stampa {
    font-size: inherit;
  }
}
/* L'esito della copia del link, per il desktop dove il foglio di condivisione del
   sistema non esiste. Occupa la riga intera cosi' non spinge i due comandi quando
   compare, e sparisce da solo dopo tre secondi. */
.sl-punti-azioni__esito {
  flex-basis: 100%;
  margin: 0;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}
/* Il rimando a «Segnala un'altra cassetta» (56-16, rilievo 2). Stessa famiglia del
   cartello — un comando di servizio, non il gesto principale della pagina — ma NON
   più piccolo: chi lo cerca è già convinto, e rimpicciolirlo lo renderebbe di nuovo
   difficile da trovare, che è il difetto da cui nasce questa riga. */
.sl-punti-azioni__segnala {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.sl-punti-nota {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}
/* Attribuzione ODbL in fondo alla scheda: visibile SENZA interazione. Dentro un
   accordion chiuso sarebbe ammessa per una mappa, dove lo spazio è prezioso;
   qui la pagina è testo e la riga costa nulla. */
.sl-punti-attribuzione {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-kicker);
}
.sl-punti-attribuzione a {
  color: inherit;
  text-decoration: underline;
}

/* -------------------------------------------------------------------------
   Punti: la schermata «Aggiungi una cassetta» — Phase 56, POINT-03
   -------------------------------------------------------------------------
   Chi compila questo modulo e' in piedi in strada, con una mano sola, davanti
   all'oggetto che sta descrivendo. Da qui tutto il resto: bersagli grandi,
   scroll corto, comandi che restano raggiungibili senza tornare in fondo.

   Tre regole, le stesse gia' a verbale per le schermate della Sequenza:
   1. ogni colore, spaziatura e raggio arriva da una var(--...) di tokens.css.
      Un valore letterale qui dentro sarebbe una palette parallela che nessun
      tema futuro sapra' di dover aggiornare (test_markup_disciplina lo rende
      rosso);
   2. i comandi riusano .sl-btn* di components.css: qui si dice solo COME si
      dispongono, mai come sono fatti;
   3. nessuna soglia nuova. Dove servirebbe una media query, il lavoro lo fa
      flex-wrap o una griglia che si adatta al contenuto invece che a un
      numero.

   `svh` e non `vh`: `svh` e' l'altezza PICCOLA della viewport, quella con la
   barra del browser APERTA, che e' lo scenario di chi sta segnalando col
   telefono in mano. Ogni `svh` ha davanti il suo ripiego in `rem` per i
   browser che non conoscono le unita' dinamiche.
   ------------------------------------------------------------------------- */
.sl-punti-segnala {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-height: 30rem;
  min-height: calc(100svh - var(--space-7));
}

/* Un passo per volta: ogni gruppo e' un blocco staccato, cosi' l'occhio sa
   dove finisce una domanda e comincia la successiva. Il fieldset perde il
   bordo di default del browser, che qui disegnerebbe una scatola dentro una
   scatola. */
.sl-punti-segnala__passo {
  display: grid;
  gap: var(--space-2);
  border: 0;
  padding: 0;
  margin: 0;
}

/* Le due scelte grandi del tipo. Una griglia che si adatta: due colonne dove
   ci stanno, una sola su un telefono stretto, senza dichiarare una fascia. */
.sl-punti-segnala__scelte {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-3);
}

/* Il bersaglio e' la scheda intera, non il pallino: su un telefono un radio da
   16 pixel si sbaglia, una scheda alta tre righe no. `min-height` per stare
   sopra i 44 pixel del bersaglio tattile chiesto da WCAG 2.2. */
.sl-punti-segnala__scelta {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: var(--space-1) var(--space-3);
  min-height: 3.25rem;
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  cursor: pointer;
}

.sl-punti-segnala__scelta input {
  grid-row: 1 / span 2;
  align-self: center;
  width: 1.25rem;
  height: 1.25rem;
  accent-color: var(--color-primary);
}

/* La scelta fatta si vede senza dover cercare il pallino: cambia il fondo e il
   bordo. `:has` e' la sola strada senza JavaScript, e la schermata deve
   funzionare identica anche senza. */
.sl-punti-segnala__scelta:has(input:checked) {
  border-color: var(--color-primary);
  background: var(--color-nav-active);
}

.sl-punti-segnala__scelta-nome {
  font-weight: 600;
}

.sl-punti-segnala__scelta-nota {
  grid-column: 2;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

/* Il comando della geolocalizzazione sta largo quanto il contenuto e non
   quanto la riga: e' un aiuto, non l'azione principale della schermata. */
.sl-punti-segnala__posizione {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* I due numeri stanno affiancati dove ci stanno e si impilano da soli quando
   non ci stanno piu': nessuna soglia dichiarata, nessuna fascia da rimisurare
   al prossimo campo. */
.sl-punti-segnala__coordinate {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--space-3);
}

/* I due controlli della foto (fotocamera e galleria) dentro un riquadro solo,
   cosi' si legge che sono due modi della stessa cosa e non due domande. */
.sl-punti-segnala__foto {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
}

/* La barra dei comandi, ancorata in fondo. `sticky` e NON `fixed`: su iOS la
   barra del browser che compare e scompare ridisegna la viewport e un elemento
   `fixed` ci salta dentro. Lo spazio in coda tiene conto della tacca. Il tasto
   d'invio prende la riga finche' ci sta comodo; «Annulla» gli si affianca da
   solo quando c'e' spazio. */
.sl-punti-segnala__azioni {
  position: sticky;
  bottom: 0;
  z-index: 1;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-3) 0 calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.sl-punti-segnala__azioni .sl-btn {
  flex: 1 1 12rem;
}

/* -------------------------------------------------------------------------
   Punti: la conferma della segnalazione — Phase 56, POINT-03 / D-07
   -------------------------------------------------------------------------
   Una schermata corta e centrata: chi arriva qui ha finito. Non c'e' un
   collegamento alla pagina del punto, e non e' una dimenticanza — la
   segnalazione e' nata IN ATTESA e quella pagina risponde 404 finche' l'owner
   non approva (il perche' e' scritto in testa al template).
   ------------------------------------------------------------------------- */
.sl-punti-inviata {
  display: grid;
  justify-items: start;
  gap: var(--space-3);
  max-width: 46ch;
}

.sl-punti-inviata__segno {
  color: var(--color-primary);
}

.sl-punti-inviata__azioni {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* -------------------------------------------------------------------------
   Punti: la schermata del rilascio «Lo lascio qui» — Phase 56, POINT-05
   -------------------------------------------------------------------------
   Chi usa questa schermata e' in piedi davanti a una cassetta, con il libro in
   una mano e il telefono nell'altra. La forma e' quella gia' a verbale per
   «Aggiungi una cassetta»: un passo per volta, bersagli grandi, scorrimento
   corto e il comando che resta raggiungibile senza tornare in fondo.

   Le stesse tre regole, e nessuna in meno:
   1. ogni colore, spaziatura e raggio arriva da una var(--...) di tokens.css;
   2. i comandi riusano .sl-btn* di components.css: qui si dice solo COME si
      dispongono, mai come sono fatti;
   3. nessuna soglia nuova: dove servirebbe una media query il lavoro lo fa
      flex-wrap o una griglia che si adatta al contenuto.

   Lo scanner NON ha regole qui: il pannello e' quello di attach_copia, riusato
   verbatim, e le sue classi (.sl-scan-*) stanno gia' nel foglio. Ridichiararle
   sotto un prefisso nostro darebbe due aspetti alla stessa cosa.
   ------------------------------------------------------------------------- */
.sl-punti-lascia {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-height: 30rem;
  min-height: calc(100svh - var(--space-7));
}

/* La riga che dice DOVE si sta lasciando il libro. Ha un bordo marcato su un
   lato solo: e' l'unico errore irreversibile della schermata, e deve saltare
   all'occhio senza rubare il posto al titolo. */
.sl-punti-lascia__dove {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-left: 4px solid var(--color-primary);
  border-radius: var(--radius-md);
  background: var(--surface);
  font-weight: 600;
}

/* La goccia accanto al nome del punto: non si restringe quando il nome e'
   lungo, o su un telefono stretto diventerebbe una virgola. */
.sl-punti-lascia__segno {
  flex: 0 0 auto;
  color: var(--color-primary);
}

/* Un passo per volta, come nella segnalazione: il fieldset perde il bordo di
   default del browser, che qui disegnerebbe una scatola dentro una scatola. */
.sl-punti-lascia__passo {
  display: grid;
  gap: var(--space-2);
  border: 0;
  padding: 0;
  margin: 0;
}

.sl-punti-lascia__scanner {
  display: grid;
  gap: var(--space-2);
}

/* Il contenitore della scheda: e' il bersaglio dello scambio htmx, quindi qui
   dentro il contenuto cambia sotto le dita. Una griglia con la sua spaziatura
   e nient'altro — nessun bordo e nessun fondo, o il passaggio da tre campi
   vuoti a tre campi pieni sembrerebbe l'apertura di un riquadro nuovo. */
.sl-punti-lascia__scheda {
  display: grid;
  gap: var(--space-2);
}

/* Il genere, ripiegato: un riquadro chiuso che si apre con un tocco. */
.sl-punti-lascia__generi {
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
}

.sl-punti-lascia__generi summary {
  cursor: pointer;
}

/* La promessa di POINT-05 detta a chi legge, appena sopra il bottone che la
   mantiene: tono sommesso, perche' e' una spiegazione e non un avviso. */
.sl-punti-lascia__promessa {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

/* La barra dei comandi, ancorata in fondo. `sticky` e NON `fixed`: su iOS la
   barra del browser che compare e scompare ridisegna la viewport e un elemento
   `fixed` ci salta dentro. Lo spazio in coda tiene conto della tacca. */
.sl-punti-lascia__azioni {
  position: sticky;
  bottom: 0;
  z-index: 1;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-3) 0 calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.sl-punti-lascia__azioni .sl-btn {
  flex: 1 1 12rem;
}

/* -------------------------------------------------------------------------
   Punti: il foglio stampabile con il QR — Phase 56, POINT-04 / D-08
   -------------------------------------------------------------------------
   È l'unico artefatto di questo progetto che finisce SU CARTA: si stampa, si
   ritaglia e si attacca sul coperchio di una cassetta all'aperto. Da qui la
   forma — una colonna stretta e centrata, il quadrato grande, l'indirizzo
   scritto sotto — e da qui il blocco di stampa in fondo a questo file, che è
   l'unica regola per la carta scritta da noi in tutto il foglio di stile.
   ------------------------------------------------------------------------- */
.sl-punti-stampa {
  max-width: 46ch;
  margin: 0 auto;
  padding: var(--space-6) var(--container-pad);
  display: grid;
  gap: var(--space-4);
  justify-items: center;
  text-align: center;
}
.sl-punti-stampa__nome {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  color: var(--text);
  margin: 0;
}
.sl-punti-stampa__dove {
  margin: 0;
  color: var(--text-muted);
}
/* Il riquadro del QR. Il margine bianco intorno NON è estetica: è la «zona di
   quiete» che gli scanner cercano per riconoscere i bordi della matrice. */
.sl-punti-stampa__qr {
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--primary-contrast);
  line-height: 0;
}
/* La matrice vera e propria: ``sl-qr`` è la classe che il servizio mette
   sull'elemento SVG, ``sl-qr__line`` quella sul tracciato. Il colore non si
   tocca mai da qui — resta il nero di default, che è l'unico che gli scanner
   leggono in ogni condizione di luce. */
.sl-qr {
  width: min(60vw, 240px);
  height: auto;
  display: block;
}
.sl-punti-stampa__istruzioni {
  margin: 0;
  color: var(--text);
}
/* L'indirizzo per esteso: su carta un collegamento non si tocca. Va a capo
   dove serve, perché uno slug lungo non deve sfondare il foglio. */
.sl-punti-stampa__url {
  margin: 0;
  font-size: var(--font-size-sm);
  overflow-wrap: anywhere;
}
.sl-punti-stampa__url a {
  color: var(--text);
}
.sl-punti-stampa__nota {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}
.sl-punti-stampa__attribuzione {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-kicker);
  overflow-wrap: anywhere;
}
.sl-punti-stampa__attribuzione a {
  color: inherit;
  text-decoration: underline;
}
.sl-punti-stampa__comandi {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: center;
}

/* -------------------------------------------------------------------------
   L'UNICO blocco di stampa del progetto.
   -------------------------------------------------------------------------
   Prima del 2026-09-09 una ricerca della regola di stampa su questo foglio non
   trovava niente: nessuna pagina del sito era pensata per la carta. Il foglio
   del QR sì, ed è l'unica ragione per cui questo blocco esiste — se un giorno
   ne servirà un secondo, va aggiunto QUI dentro e non in un blocco nuovo.

   Non c'è nessuna navigazione da nascondere: ``punto_qr.html`` non estende la
   shell pubblica proprio per non averla (il perché è in testa a quel file).
   Restano da togliere i comandi, che su carta sono solo inchiostro sprecato.
   ------------------------------------------------------------------------- */
@media print {
  .sl-punti-stampa__comandi {
    display: none;
  }
  /* Il foglio prende la pagina intera, senza margini nostri: quelli li mette
     il dialogo di stampa, ed è giusto che li decida chi stampa. */
  .sl-punti-stampa {
    max-width: none;
    padding: 0;
    gap: var(--space-4);
    background: var(--color-paper-light);
    color: var(--color-ink);
    /* Il riquadro non si deve spezzare fra due fogli: mezzo QR non è un QR. */
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .sl-punti-stampa__qr {
    break-inside: avoid;
    page-break-inside: avoid;
    /* ``--primary-contrast`` è l'unico bianco pieno dei token, e qui serve
       davvero: sotto la matrice nera ci vuole il fondo più chiaro possibile,
       altrimenti il contrasto scende e gli scanner sbagliano. ``print-color-
       adjust`` chiede al browser di stamparlo comunque — di default i fondi
       vengono scartati, e su carta avorio il quadrato perderebbe il bordo. */
    background: var(--primary-contrast);
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  /* Misura FISICA, l'unico posto del blocco in cui un valore non viene da un
     token: 55 mm è quanto serve perché un telefono legga una versione 4-M da
     una ventina di centimetri, che è la distanza da cui si guarda un adesivo
     su una cassetta. Sotto i 40 mm la lettura comincia a fallire. */
  .sl-qr {
    width: 55mm;
    height: 55mm;
  }
}

/* ============================================================================
   Fase 51 — Ricerca pubblica anonima (/it/libri/cerca/).
   Pagina senza login: un form GET, una griglia di schede senza persone né
   distanze, lo zero-risultati in tre passi. Tutti i valori vengono dai token di
   tokens.css; nessuno stile inline nei template (Pitfall 4).
   ========================================================================== */
.sl-public-search {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: var(--space-6) var(--container-pad);
  color: var(--color-ink);
}
.sl-public-search__h1 {
  font-family: var(--font-display);
  font-size: var(--font-size-h1);
  line-height: var(--line-height-tight);
  margin: 0 0 var(--space-3);
}
.sl-public-search__lead {
  max-width: 62ch;
  color: var(--color-text-warm);
  margin: 0 0 var(--space-5);
}

/* Il form è UNA riga su schermo largo (titolo, comune, bottone) e una colonna
   sotto i 640px: le due etichette restano sopra il proprio campo in entrambi i
   casi, perché la griglia le mette sulla prima riga e i campi sulla seconda. */
.sl-public-search__form {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-2) var(--space-4);
  align-items: end;
  margin-bottom: var(--space-6);
}
.sl-public-search__form .sl-input {
  width: 100%;
}
@media (min-width: 640px) {
  .sl-public-search__form {
    grid-template-columns: 2fr 1fr auto;
  }
  /* Etichette in riga 1 (colonne 1 e 2), campi in riga 2, bottone a destra. */
  .sl-public-search__form > .sl-label:nth-of-type(1) { grid-column: 1; grid-row: 1; }
  .sl-public-search__form > .sl-label:nth-of-type(2) { grid-column: 2; grid-row: 1; }
  .sl-public-search__form > #sl-q { grid-column: 1; grid-row: 2; }
  .sl-public-search__form > #sl-dove { grid-column: 2; grid-row: 2; }
  .sl-public-search__form > [type="submit"] { grid-column: 3; grid-row: 2; }
}

/* Griglia delle schede: stessa densità di .sl-featured__grid, card più larghe
   perché qui ogni card porta stato e luogo oltre a titolo e autore. */
.sl-public-results {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-4);
}
.sl-public-result {
  display: flex;
  gap: var(--space-4);
  align-items: flex-start;
}
.sl-public-result .sl-book-card__cover {
  width: 96px;
  height: 144px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  flex: none;
}
/* Segnaposto quando la copertina manca: un rettangolo di carta, non un'immagine
   rotta (e niente testo dentro, è decorazione). */
.sl-book-card__cover--vuota {
  display: block;
  background: var(--color-paper);
  border: 1px solid var(--color-border);
}
.sl-public-result .sl-book-card__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  min-width: 0;
}
.sl-public-result__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}

/* Zero risultati: i tre passi sono una lista ORDINATA perché l'ordine conta
   (prima allarga, poi avvisami, poi invita). */
.sl-public-zero {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-paper-light);
  padding: var(--space-5);
}
.sl-public-zero__passi {
  margin: var(--space-4) 0 0;
  padding-left: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.sl-public-zero__catalogo {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.sl-public-zero__titolo {
  font-weight: 600;
}
.sl-public-zero__autore {
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
}

.sl-public-search__come {
  max-width: 62ch;
  margin-bottom: var(--space-6);
  color: var(--color-text-warm);
}
.sl-public-search__condividi {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

/* Bottone piccolo dentro le card. La specificità è DOPPIA (.sl-btn.sl-btn--sm)
   apposta: components.css è caricato dopo base.css, e un solo selettore di
   classe perderebbe contro le regole di .sl-btn che stanno là. */
.sl-btn.sl-btn--sm {
  padding: var(--space-2) var(--space-3);
  font-size: var(--font-size-sm);
}
.sl-note.sl-note--warn {
  border-left: 3px solid var(--color-primary);
  padding-left: var(--space-3);
}

/* Selettore lingua a LINK (solo su questa pagina): il selettore normale è un
   POST verso set_language e porterebbe un csrf_token, cioè un Set-Cookie, che
   renderebbe la pagina non cacheabile al bordo. */
.sl-lang-switch__btn {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--color-text-warm);
  text-decoration: none;
  font-size: var(--font-size-sm);
}
.sl-lang-switch__btn.is-active {
  background: var(--color-nav-active);
  color: var(--color-ink);
  font-weight: 600;
}
