/* =========================================================================
   Serenlibrità — Components CSS (v2.3 design-system component layer)
   =========================================================================
   Caricato DOPO base.css in entrambe le shell; vince per source-order a parità
   di specificità single-class (nessun cascade-layer — base.css è unlayered).
   Nessuna dichiarazione forzata (il conteggio delle forzature resta 3, tutto in
   base.css). Vuoto allo scaffold Phase 31; i corpi dei componenti arrivano in
   Phase 32.
   ========================================================================= */

/* =========================================================================
   Buttons (COMP-01) — vocabolario bottoni consolidato
   =========================================================================
   D-01: 5 varianti + 1 modificatore di larghezza. Una sola famiglia .sl-btn:
     --primary (terracotta, azione) · --secondary/strong (verde) · --ghost
     (vedi nota D-19) · --outline (Task 2, net-new) · --block (modificatore
     larghezza, NON una variante di colore).

   POLICY D-19 (inert-now): le regole qui sotto per selettori GIÀ APPLICATI al
   markup live (.sl-btn, --primary, --secondary, --ghost, --block, a.sl-btn)
   riproducono il look CORRENTE di base.css BYTE-PER-BYTE. Vincono per
   source-order (caricato DOPO base.css) a parità di specificità single-class,
   ma i valori sono identici → ZERO regressione visiva. Le migliorie canoniche
   (#FFFFFF su primary, ghost senza bordo) sono registrate SOLO come commenti
   DEFERRED TARGET: si applicano nella prima PAGE phase (33+) che ritocca i
   bottoni, dove il cambiamento è atteso e screenshot-verificato.

   Equivalenza alias→primitivo (provata, byte-identica):
     --primary  → --color-primary      #B8502F
     --strong   → --color-secondary    #2C4A3B  (verde "strong"; NON esiste un
                                                  alias --secondary in tokens.css)
     --surface-raised → --color-paper-light  #FCF8EF
     --border   → --color-border        #E0D2B8
     --radius-md → 11px · --shadow-cta (literal) · --t → 0.15s ease
   Dove la regola live usa --color-paper (crema) o --color-text-warm e NON
   esiste un alias semantico equivalente, si tiene il primitivo raw per
   restare byte-identici.
   Supersede in base.css (documentati come DEAD, MAI cancellati in v2.3):
     .sl-btn L198-222 · varianti L1690-1718. (CLEAN-01 futuro li rimuoverà.)
   ========================================================================= */

/* --- Base .sl-btn (inert = base.css L198-211) --------------------------- */
.sl-btn {
  display: inline-block;
  padding: 14px 18px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--primary);
  /* APPLICATO in Phase 33 (D-04, era DEFERRED in P32): il testo primario passa
     da crema #F4EBDA (4.20:1, sub-AA latente) a --primary-contrast (#FFFFFF,
     4.97:1 su terracotta). La .sl-btn è shell-visible (FAB "Pubblica" tab-bar,
     "Registrati" public) → il fix è atteso e screenshot-verificato qui, con
     scripts/check_contrast.py ri-verde (#FFFFFF×--primary già in PAIRS). */
  color: var(--primary-contrast);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow-cta);
  transition: transform var(--t), box-shadow var(--t);
  /* Long-text wrapping (Task 2): etichette lunghe vanno a capo invece di
     traboccare. INERTE per le label corte attuali — white-space:normal è già
     il default di inline-block, overflow-wrap:anywhere non incide su testo
     breve. */
  white-space: normal;
  overflow-wrap: anywhere;
}
.sl-btn:hover {
  transform: translateY(-1px);
  /* Literal (non token): ombra hover specifica della terracotta. */
  box-shadow: 0 12px 26px rgba(184, 80, 47, 0.34);
}
.sl-btn:active {
  transform: translateY(0);
}
/* Una eccezione intenzionale alla specificità single-class: a.sl-btn (0,1,1),
   bottone reso come link (es. "Accedi" post reset password). Preservata esatta. */
a.sl-btn {
  text-decoration: none;
}

/* --- Variante primary (= base.css L1690-1693; #FFFFFF APPLICATO Phase 33) -- */
.sl-btn--primary {
  background: var(--primary);
  /* APPLICATO in Phase 33 (D-04): vedi .sl-btn — testo → --primary-contrast
     (#FFFFFF, 4.97:1 su terracotta). La "Registrati" public CTA è shell-visible. */
  color: var(--primary-contrast);
}

/* --- Variante secondary/strong = verde (inert = base.css L1694-1698) ---
   Il PRD .btn-strong mappa QUI: nessun namespace .btn parallelo (D-01). */
.sl-btn--secondary {
  background: var(--strong);
  color: var(--color-paper);
  box-shadow: none;
}

/* --- Variante ghost (inert = base.css L1699-1707) ----------------------
   DEFERRED TARGET (D-01/D-19): il ghost CANONICO è trasparente + NESSUN bordo
   con tinta tenue in hover. NON applicato qui (cambierebbe le call-site live
   di --ghost). Il look bordato attuale è catturato dal net-new .sl-btn--outline
   (Task 2), su cui le call-site migreranno in una PAGE phase. Qui si riproduce
   byte-per-byte il bordato corrente. (color: nessun alias → primitivo raw.) */
.sl-btn--ghost {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  color: var(--color-text-warm);
  box-shadow: none;
}
.sl-btn--ghost:hover {
  box-shadow: none;
}

/* --- Modificatore --block (inert = base.css L1708-1718) ----------------
   Resta un MODIFICATORE DI LARGHEZZA, non una variante di colore. */
.sl-btn--block {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  max-width: 360px;
  padding: 15px;
  border-radius: 13px; /* literal: raggio specifico del block, non --radius-md */
  font-size: 15.5px;
}

/* --- Alias riconducibili (D-02, documentati — call-site NON toccate) ----
   .sl-btn-pill  (base.css L589) → primary + raggio pill (border-radius:30px).
   .sl-btn--publish (base.css L1674) → --secondary (verde) + --block (full-width).
   Allineano ai token canonici; la razionalizzazione delle call-site è rimandata
   a una PAGE phase / CLEAN-01. Qui sono SOLO documentati come aliases. */

/* --- Variante --outline (NET-NEW, inert finché non applicata) ----------
   D-01: trasparente CON bordo brand (navy). Distinta dal ghost: cattura il
   look che le .sl-btn--ghost live portano OGGI (paper-light + bordo) → le
   call-site ghost esistenti potranno migrare a --outline in una PAGE phase.
   Non referenziata in nessun markup attuale → inerte. (--brand = navy #1b2d49,
   altissimo contrasto su carta, ben oltre AA.) */
.sl-btn--outline {
  background: transparent;
  border: 1px solid var(--brand);
  color: var(--brand);
  box-shadow: none;
}
.sl-btn--outline:hover {
  /* Tinta brand tenue (navy 8%): il testo navy resta su superficie quasi-carta
     → contrasto invariato, ben sopra AA. */
  background: rgba(27, 45, 73, 0.08);
}

/* --- Ghost CANONICO trasparente (DEFERRED TARGET, solo commento) --------
   D-19: il ghost genuinamente nuovo = trasparente + NESSUN bordo, tinta tenue
   in hover. È il TARGET di .sl-btn--ghost applicato in una PAGE phase. NON
   aggiunto qui come regola live: una seconda regola ghost colliderebbe e
   cambierebbe le call-site correnti. Forma canonica deferita:
     .sl-btn--ghost { background: transparent; border: none;
                      color: var(--strong); box-shadow: none; }
     .sl-btn--ghost:hover { background: rgba(44, 74, 59, 0.08); } */

/* --- Stato :disabled (NET-NEW, inert) ----------------------------------
   Nessun bottone live porta l'attributo `disabled` (verificato via grep su
   templates/ + apps/*/templates → 0 occorrenze) → regola inerte. È
   l'affordance disabled canonica applicata nelle fasi successive. */
.sl-btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
}
.sl-btn:disabled:hover {
  /* Neutralizza il sollevamento hover quando disabilitato. */
  transform: none;
  box-shadow: none;
}

/* =========================================================================
   Surfaces & empty-states (COMP-03 card · COMP-02 empty-state)
   =========================================================================
   POLICY D-19 (inert-now): .sl-card e .sl-empty sono GIÀ APPLICATI al markup
   live. Le loro regole qui sotto riproducono il look CORRENTE di base.css
   BYTE-PER-BYTE (vincono per source-order ma con valori identici → ZERO
   regressione visiva). I valori canonici migliori sono registrati SOLO come
   commenti DEFERRED TARGET, applicati nella PAGE phase che ritocca le superfici.
   .sl-empty--inline è invece NET-NEW (non referenziato in nessun markup) →
   inerte per costruzione, definito con i valori canonici pieni.

   Equivalenza alias→primitivo (provata, byte-identica):
     --surface-raised → --color-paper-light  #FCF8EF
     --border         → --color-border        #E0D2B8
     --radius-lg 14px · --space-5 24px · --shadow-card · --space-4 16px
   Dove non esiste un alias semantico (--color-text-warm) si tiene il primitivo
   raw per restare byte-identici.
   ========================================================================= */

/* --- Card primitivo .sl-card (INERT = base.css L1729-1735) --------------
   Look corrente: alias di .sl-panel (G1 profilo) — radius-lg/space-5.
   DEFERRED TARGET (D-07/D-19): il .sl-card CANONICO adotta i valori di
   .sl-book-card (base.css L500-506): border-radius var(--radius-md) (11px) +
   padding var(--space-4) (16px). NON applicato qui: ridichiararli adesso
   restringerebbe la card live del profilo (G1) 14→11px raggio e 24→16px
   padding = regressione visiva (viola D-16). Il target si applica nella PAGE
   phase che ritocca le card, dove il cambiamento è atteso e screenshot-verificato.

   Siblings riconducibili al primitivo (DOCUMENTATI come DEAD, MAI cancellati —
   CLEAN-01 futuro li rimuoverà):
     .sl-book-card  (base.css L500) — IL valore canonico deferito (radius-md/space-4).
     .sl-stat-card  (base.css L1008) — aggiunge border-top 3px accent --color-primary.
     loan-row       (base.css ~L707, commento "eredita .sl-book-card").
     .sl-message    (base.css L801)  — bolla chat, stessa DNA superficie.
     .sl-how-step   (base.css L2631) — card "come funziona".
   Grid gap canonico: var(--space-4) (uniforme, = .sl-affini-grid L1872 /
   .sl-featured__grid L2680). NON sovrascritto qui sulle classi grid live (lo
   farebbe regredire) — è il valore che le PAGE phase applicano. */
.sl-card {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-card);
}

/* --- Empty-state di sezione .sl-empty (INERT = base.css L751-756) --------
   Look corrente: pattern italico (come .sl-list-empty).
   DEFERRED TARGET (D-04/D-19): il .sl-empty CANONICO diventa lo stato ricco
   centrato (icona discreta + titolo + frase + CTA primaria che riusa
   .sl-btn--primary), il look oggi in .sl-featured-empty (base.css L2717-2733,
   con __head/__body). NON applicato qui: definirlo ricco adesso cambierebbe
   OGNI uso corrente di .sl-empty (box/bordo/CTA) = regressione. Si applica
   nella PAGE phase. (color: nessun alias semantico → primitivo raw.)

   Variants superseded ma TENUTE (DOCUMENTATE come DEAD, MAI cancellate):
     .sl-list-empty    (base.css L249)  — italico, micro-empty inbox → migra a --inline.
     .sl-shelf__empty  (base.css L1864) — bare padding space-5 0, sibling riconducibile.
     .sl-featured-empty(base.css L2717) — il look RICCO = target deferito di .sl-empty. */
.sl-empty {
  color: var(--color-text-warm);
  font-style: italic;
  padding: var(--space-4) 0;
}

/* --- Empty-state inline .sl-empty--inline (NET-NEW, full canonical) ------
   Tier leggero (D-04): la frase muted/italica discreta per i micro/inline
   empty (es. la .sl-list-empty italica dentro una sezione inbox). Porta i
   valori italici CORRENTI così che gli usi esistenti (.sl-list-empty /
   .sl-empty) possano migrare aggiungendo `--inline` con ZERO cambiamento
   visivo. Inerte: nessun markup live lo referenzia (grep-verificato).
   (color: --color-text-warm per byte-match con gli empty italici correnti —
   non --text-muted — così la migrazione resta a zero delta.)
   TODO (IN-02): è byte-identico a .sl-empty (anch'esso inert-now) finché la fase
   PAGE che possiede l'empty-state non promuove .sl-empty al look ricco canonico
   (icona+titolo+CTA, D-04); a quel punto questo tier resta il caso "inline". */
.sl-empty--inline {
  color: var(--color-text-warm);
  font-style: italic;
  padding: var(--space-4) 0;
}

/* =========================================================================
   Typographic & layout primitives (COMP-05 kicker · COMP-06 container)
   =========================================================================
   Entrambi NET-NEW. .sl-kicker è ora LIVE: la card "personaggio letterario" del
   profilo (templates/accounts/profile.html) ne è il primo consumatore. .sl-container
   resta INERTE (le PAGE phase 33-40 lo applicheranno). Definiti coi valori
   canonici pieni. Consumano solo gli alias semantici.

   Equivalenza alias→primitivo (provata, byte-identica):
     --fs-kicker 12px (= dimensione di .sl-eyebrow)
     --primary  → --color-primary #B8502F (accento kicker)
     --container-max 1080px · --container-pad var(--space-5) 24px
   ========================================================================= */

/* --- Kicker canonico .sl-kicker (NET-NEW, da .sl-eyebrow base.css L124-131)
   Valore sorgente: .sl-eyebrow (uppercase / letter-spacing 1.5px / 12px / 600 /
   accento terracotta / margin 0 0 space-2).
   Supersede .sl-eyebrow (base.css L124) — riconducibile, DOCUMENTATO come DEAD,
   MAI cancellato (CLEAN-01 futuro lo rimuoverà).
   TENUTO SEPARATO (NON canonicalizzato): .sl-auth-panel__eyebrow (base.css L1333)
   è un override context-specific sulla superficie scura del pannello auth
   (color #C9D9CC salvia, letter-spacing 3px). NON va fuso nel kicker canonico:
   la sua tinta/tracking sono legate al fondo scuro → resta una regola dark-surface
   a sé in base.css. */
.sl-kicker {
  text-transform: uppercase;
  letter-spacing: 1.5px;
  font-size: var(--fs-kicker);
  font-weight: 600;
  color: var(--primary);
  margin: 0 0 var(--space-2);
}

/* --- Container canonico .sl-container (NET-NEW, COMP-06) -----------------
   Survey layout più larghi: .sl-how (base.css L2616) e .sl-featured (L2666)
   = max-width 1080px → sorgente di --container-max. Centratura + padding
   orizzontale coerente via --container-pad (= --space-5 / 24px).
   Le colonne di contenuto più STRETTE restano per-pagina e NON sono
   auto-applicate (per le note in tokens.css L145-146): .sl-main 720px
   (base.css L112), .sl-profile ~920px (L949), .sl-hero 720px (L2583),
   .sl-auth-form-col 380px (L1320).

   D-01 = per-pagina opt-in, lo SHELL resta full-width. NON si wrappa il
   <main> dello shell pubblico in .sl-container: le sue sezioni (.sl-hero,
   .sl-how, .sl-featured) hanno GIÀ max-width+margin auto+padding 24px proprio
   → un wrap aggiungerebbe un secondo padding (48px/lato, regressione CR-01).
   Il primitivo + questo pattern documentato SONO il deliverable D-01; le pagine
   34-40 con contenuto "grezzo" (non auto-impaginato) applicheranno .sl-container
   dentro il proprio {% block content %}. */
.sl-container {
  max-width: var(--container-max);
  margin-left: auto;
  margin-right: auto;
  padding-inline: var(--container-pad);
}

/* =========================================================================
   Form-field & native controls
   (COMP-04 form-field · CTRL-03 input/textarea share the box ·
    CTRL-02 checkbox/radio/toggle · CTRL-01 styled <select> in Task 2)
   =========================================================================
   POLICY D-19 (inert-now): i selettori del form-field qui sotto (.sl-input,
   .sl-field input/textarea/select, .sl-label/.sl-field label, .sl-field-help,
   .sl-toggle, gli accent-color di checkbox/range) sono GIÀ APPLICATI al markup
   live. Le regole riproducono il look CORRENTE di base.css BYTE-PER-BYTE
   (vincono per source-order ma con valori identici → ZERO regressione visiva).
   L'UNICO cambiamento permesso su un selettore applicato è esprimere il literal
   di focus `outline: 2px solid var(--color-secondary)` come `var(--focus-ring)`:
   l'alias risolve allo STESSO valore (tokens.css L149: 2px solid
   var(--color-secondary)) → byte-equivalente.

   ECCEZIONE SPECIFICITÀ (IN-01): le riproduzioni inert qui sotto usano selettori
   compound discendenti (es. `.sl-field input[type="number"]`, da (0,1,1) a (0,3,1))
   perché DEVONO eguagliare la specificità delle regole base.css che riproducono
   per restare byte-identiche; vincono comunque per source-order. La regola
   generale "single-class (0,1,0)" (D-14) vale per i selettori NET-NEW; le
   riproduzioni inert ereditano deliberatamente la specificità dell'originale.

   <select> = COLLISIONE (PATTERNS #5): NON si scope-a `appearance:none` su
   `.sl-field select` (restilizzerebbe ogni select live del wizard/form). Il
   chevron canonico è definito in Task 2 come classe NET-NEW opt-in (.sl-select),
   inerte; l'applicazione a `.sl-field select` è un DEFERRED TARGET della PAGE phase.

   Equivalenza alias→primitivo (provata, byte-identica):
     --border        → --color-border      #E0D2B8
     --radius-md     → 11px
     --surface-raised→ --color-paper-light #FCF8EF
     --text          → --color-ink         #211B13
     --text-muted    → --color-text-muted  #6B5C45
     --focus-ring    → 2px solid var(--color-secondary)
     --primary       → --color-primary     #B8502F (bordo aria-invalid)
     --strong        → --color-secondary   #2C4A3B (accent verde checkbox/toggle)
     --t             → 0.15s ease (transizione thumb del toggle)
   Dove non esiste un alias semantico (--color-text-warm sulle label, font-size-sm,
   space-1, --color-toggle-off sul track "off" del toggle) si tiene il primitivo/
   token raw per restare byte-identici.
   Supersede in base.css (documentati come DEAD, MAI cancellati in v2.3):
     .sl-input L180-193 · box esteso L1169-1199 · type-selector L1396-1417 ·
     .sl-label L173-178 + .sl-field label L1388-1394 · .sl-field-help L1195-1199 ·
     .sl-toggle L875-906 · .sl-field-check checkbox L1431-1436 · range L1990-1994.
   ========================================================================= */

/* --- Box campo testo .sl-input (INERT = base.css L180-189) -------------- */
.sl-input {
  width: 100%;
  padding: 13px 15px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
}
/* Focus literal espresso via --focus-ring (byte-equivalente: l'alias risolve a
   2px solid var(--color-secondary), identico al literal live L191). */
.sl-input:focus {
  outline: var(--focus-ring);
  outline-offset: 1px;
}

/* --- Box esteso number/textarea/select (INERT = base.css L1169-1194) ----
   STESSO box dell'input testo. NB: NESSUN appearance:none su .sl-field select
   (collisione PATTERNS #5 — restilizzerebbe ogni select live). Il chevron è
   la classe NET-NEW .sl-select in Task 2. */
.sl-field input[type="number"],
.sl-field textarea,
.sl-field select {
  width: 100%;
  padding: 13px 15px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
}
.sl-field textarea {
  min-height: 96px;
  resize: vertical;
}
.sl-field textarea:focus,
.sl-field input[type="number"]:focus,
.sl-field select:focus {
  outline: var(--focus-ring);
  outline-offset: 1px;
}
/* aria-invalid="true": bordo terracotta — segnale NON-solo-colore (accompagnato
   dal testo d'errore .sl-field-error associato via aria-describedby). */
.sl-field textarea[aria-invalid="true"],
.sl-field input[type="number"][aria-invalid="true"] {
  border-color: var(--primary);
}

/* --- Input per selettore di tipo (INERT = base.css L1396-1416) ---------- */
.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(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
}
.sl-field input:focus {
  outline: var(--focus-ring);
  outline-offset: 1px;
}
.sl-field input[aria-invalid="true"] {
  border-color: var(--primary);
}

/* --- Label & hint (INERT = base.css L173-178 / L1388-1394 / L1195-1199) --
   Spaziatura label→campo coerente. color: --color-text-warm non ha alias
   semantico → primitivo raw (byte-match). L'hint usa --text-muted (= L1198). */
.sl-label,
.sl-field label {
  display: block;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-warm);
  margin-bottom: var(--space-1);
}
.sl-field-help {
  margin-top: var(--space-1);
  font-size: var(--font-size-sm);
  color: var(--text-muted);
}

/* --- Checkbox / radio: accent-color (CTRL-02) --------------------------
   INERT: riproduce gli accent-color live sui selettori esistenti.
   Checkbox consenso/ricordami = verde (--strong = --color-secondary, L1435);
   range peso = terracotta (--primary = --color-primary, L1993). Sovrascrive
   SOLO la proprietà accent-color (le altre proprietà del box restano da base.css)
   con valore risolto identico → nessun cambiamento visivo.
   Radio: nessun <input type=radio> con accent dedicato in base.css/markup live
   → un eventuale radio riconduce allo stesso accent verde (--strong) quando una
   PAGE phase lo introdurrà (NON aggiunto qui come regola element-globale, che
   colorerebbe i radio nativi blu correnti = regressione). */
.sl-field-check input[type="checkbox"] {
  accent-color: var(--strong);
}
.sl-weight-slider input[type="range"] {
  accent-color: var(--primary);
}

/* --- Toggle consenso .sl-toggle — RIFERIMENTO QUALITÀ CTRL-02 (D-09) ----
   Promosso/re-homed in components.css come switch canonico (input type=checkbox
   role=switch). Re-home BYTE-IDENTICO del blocco base.css L875-906 consumando
   alias che risolvono identici: --surface-raised (thumb = --color-paper-light),
   --strong (track :checked = --color-secondary), --focus-ring (= 2px solid
   var(--color-secondary)), --t (transition = 0.15s ease). Il track "off" usa
   --color-toggle-off (nessun alias) → primitivo raw. Semantica tastiera/AT
   intatta: nessun widget JS, gli hook id/role="switch" NON sono rinominati.
   Supersede base.css L875-906 (DEAD, mai cancellato; CLEAN-01 futuro). */
.sl-toggle {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  width: 46px;
  height: 26px;
  border-radius: 13px;
  background: var(--color-toggle-off);
  cursor: pointer;
  flex: 0 0 auto;
}
.sl-toggle::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--surface-raised);
  transition: transform var(--t);
}
.sl-toggle:checked {
  background: var(--strong);
}
.sl-toggle:checked::after {
  transform: translateX(20px);
}
.sl-toggle:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 2px;
}

/* --- Styled <select> .sl-select — chevron SVG (CTRL-01, NET-NEW opt-in) --
   Classe COSMETICA net-new, applicata a NULLA per ora → INERTE per costruzione.
   De-defaulta il chevron nativo con appearance:none + un chevron SVG data-URI
   STATICO, author-controlled, SENZA interpolazione (nessun {{ }} / variabile →
   nessun vettore di tampering, T-32-03-T mitigato). Stroke palette-matched
   sull'ink attenuato (%236B5C45 = --color-text-muted), bg --surface-raised,
   bordo/raggio dal box campo condiviso, padding-destro extra per liberare il
   glifo. Semantica tastiera/AT preservata: nessun widget JS (D-08).

   DEFERRED TARGET (CTRL-01/D-08/D-19): l'applicazione del chevron (appearance:none)
   ai live `.sl-field select` è rimandata alla PAGE phase che ristiliza i form —
   farlo ORA restilizzerebbe ogni select del wizard/form esistente (collisione
   PATTERNS #5), perciò NON è scope-ato su `.sl-field select` qui. Migrazione:
   aggiungere la classe .sl-select (o promuovere il chevron su .sl-field select)
   nella fase di apply, screenshot-verificata. */
.sl-select {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  padding: 13px 40px 13px 15px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background-color: var(--surface-raised);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' stroke='%236B5C45' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 15px center;
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
}
.sl-select:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 1px;
}

/* =========================================================================
   Focus ring (COMP-07) + brand links (CTRL-04) — Wave 4
   =========================================================================
   Sezione finale di components.css. Appesa DOPO la sezione form-field/native
   controls (Wave 3); non tocca le sezioni precedenti. Stessa disciplina
   inert-now / source-order / token-alias delle Wave 1-3.
   ========================================================================= */

/* --- COMP-07: UNA sola regola :focus-visible canonica ---------------------
   D-13/COMP-07: una sola regola a bassa specificità (0,1,0) guidata da
   var(--focus-ring) consolida i ~21 blocchi duplicati `outline: 2px solid
   var(--color-secondary)` sparsi in base.css. Si usa `outline` (MAI box-shadow)
   così il ring resta visibile in High-Contrast Mode (D-13).

   PARITÀ BYTE-IDENTICA (provata): --focus-ring risolve esattamente a
   `2px solid var(--color-secondary)` (#2C4A3B verde, tokens.css L149) — lo
   stesso literal ripetuto in ogni blocco superseduto → il rendering del focus
   da tastiera sugli elementi consolidati è byte-identico. Offset canonico = 2px
   (convenzione .sl-toggle L905 / .sl-auth-links a:focus L1498); i blocchi
   base.css con offset bespoke (input/chrome a 1px) hanno specificità maggiore
   (es. .sl-input:focus = 0,2,0) e VINCONO dove presenti, quindi questa regola
   globale (0,1,0) tocca SOLO gli elementi focusabili che oggi NON hanno alcuno
   stile di focus — aggiungendo loro il ring keyboard coerente richiesto da QA-01.

   CAMBIO DI COMPORTAMENTO INTENZIONALE (NON una regressione, D-19): diversi
   blocchi live usano `:focus` "nudo" (ring anche al click del mouse) —
   .sl-input:focus L190, .sl-field input:focus L1409, .sl-message__report:focus
   L850, .sl-auth-links a:focus L1496. Lo shift a `:focus-visible` significa che
   per gli elementi che usavano bare `:focus` il ring non compare più al click di
   mouse, solo da tastiera. Questo è l'ESPLICITO intento di COMP-07 (best practice
   focus-visible moderna), NON una regressione — è il requisito.

   SUPERSEDED-MA-TENUTI (documentati DEAD, MAI cancellati — CLEAN-01 futuro):
   i ~21 blocchi `outline: 2px solid var(--color-secondary)` in base.css:
   L191 (.sl-input:focus), L851 (.sl-message__report:focus), L903
   (.sl-toggle:focus-visible), L1188 (.sl-field textarea/number/select:focus),
   L1252 (.sl-cover-slot:focus-within), L1410 (.sl-field input:focus), L1497
   (.sl-auth-links a:focus), il gruppo public-chrome a 7 selettori L2431-2440,
   L2493 (.sl-lang-switch__opt:focus-visible).

   ECCEZIONE TENUTA (NON ripiegata nella regola canonica): il blocco dark-surface
   L2542-2546 (.sl-en-banner__go/__dismiss:focus-visible) usa `outline: 2px solid
   var(--color-paper)` (ring color carta ≥3:1 sopra il fill verde scuro del
   banner) — diverge volutamente dal verde canonico per restare visibile su
   superficie scura. Resta in base.css intatto, con specificità maggiore (0,2,0)
   che vince comunque sulla regola globale qui. Documentato come eccezione.

   Nessuna dichiarazione forzata, nessun cascade-layer. */
:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 2px;
}

/* --- CTRL-04: brand link SCOPED su content-container (.sl-prose) -----------
   D-05/D-06: i link di body-copy diventano "brand link" — testo terracotta
   (--primary), hover verde (--strong), focus ring (--focus-ring), e
   SOTTOLINEATURA TENUTA nel corpo testo (WCAG 1.4.1 — il colore non è l'unico
   discriminante del link). Analogo più vicino: .sl-auth-links a (base.css
   L1489-1499, terracotta + underline-on-hover + focus) — qui si AGGIUNGE lo
   shift di colore in hover (verde --strong) che l'analogo non aveva, e si
   mantiene la sottolineatura sempre presente nel corpo testo.

   SCOPING (D-06): la regola è ancorata a una classe-wrapper di contenuto
   net-new (.sl-prose), NON un selettore globale di tipo. Questo lascia INTATTI
   senza reset-list: a.sl-btn (link resi come bottoni, L220), la nav pubblica
   (.sl-public-nav__link), il footer, il back del dettaglio (.sl-book-detail__back
   a, L2241), lo user-chip e il selettore lingua (.sl-lang-switch__opt, L2492).
   Zero rischio di regressione sulla chrome di navigazione.

   INERTE ORA (D-19): nessun markup applica oggi .sl-prose (classe cosmetica
   net-new, non presente nell'allowlist behavioural HOOK-INVENTORY.md né come
   id/hook frozen). La regola è quindi inerte per costruzione finché una PAGE
   phase (33-40) non avvolge le regioni di body-copy in .sl-prose, dove il
   restyle dei link è atteso e screenshot-verificato.

   Token (alias, mai raw --color-*): --primary (link), --strong (hover),
   --focus-ring (ring). Nessuna dichiarazione forzata, nessun cascade-layer. */
/* :not(.sl-btn) esclude i link-bottone annidati nel body-copy: senza, .sl-prose a
   (0,1,1) batterebbe a.sl-btn (0,1,1, definito prima) per source-order e
   ri-stilerebbe un bottone come link (regressione latente, WR-02). */
.sl-prose a:not(.sl-btn) {
  color: var(--primary);
  text-decoration: underline;
}
.sl-prose a:not(.sl-btn):hover {
  color: var(--strong);
}
.sl-prose a:not(.sl-btn):focus-visible {
  outline: var(--focus-ring);
  outline-offset: 2px;
}

/* =========================================================================
   Phase 33 — SHELL RESTYLE (sidebar)  ·  PAGE-01 / D-02 · D-03 · D-05
   =========================================================================
   PRIMA PAGE phase: applica il design system alla shell globale. A differenza
   delle Wave 1-4 (inert-now / byte-identiche), QUI il cambiamento visivo È
   ATTESO e screenshot-verificato (QA-03). Le regole vincono per SOURCE-ORDER
   (caricato DOPO base.css). Disciplina invariata: nessun cascade-layer, NESSUNA
   nuova forzatura (il conteggio resta 0 qui / 3 in base.css). Solo alias semantici.
   Meccanismo attivo (nav_active + aria-current) INVARIATO: si ristiliza il
   VISIVO, mai l'hook server-side (HOOK-INVENTORY §1, QA-04).
   ========================================================================= */

/* --- Active-state: discriminatore di peso (D-02, UI-SPEC §Typography) -----
   Supersede base.css L336 (.sl-nav-item font-weight:600 su OGNI item) per
   source-order a (0,1,0): l'inattivo torna regular 400, l'attivo resta 600 →
   il peso diventa un VERO segnale di stato dentro il tetto 2-pesi (400/600).
   Caveat specificità (PATTERNS item 1): .sl-sidebar .sl-nav-item (base L1632,
   0,2,0) NON setta font-weight né color → questa (0,1,0) vince per ordine. La
   L336/L342 superseduta resta DEAD in base.css, MAI cancellata (CLEAN-01). */
.sl-nav-item {
  font-weight: 400;
}
/* Stato attivo sidebar (D-02): tinta tenue + label inchiostro + semibold +
   barra-accento SINISTRA 3px terracotta via box-shadow INSET (nessun reflow di
   larghezza vs item inattivi; base non setta box-shadow sui nav-item → applica
   pulito). Supersede base.css L342-345 (.sl-nav-item--active, 0,1,0) per ordine. */
.sl-nav-item--active {
  background: var(--color-nav-active);
  color: var(--text);
  font-weight: 600;
  box-shadow: inset 3px 0 0 0 var(--primary);
}

/* --- Gap icona→testo COSTANTE 12px (D-03, PATTERNS trap B) ----------------
   ECCEZIONE alla regola single-class: la base.css L1632 .sl-sidebar .sl-nav-item
   è compound (0,2,0) e setta gap:13px → VINCE su un single-class .sl-nav-item.
   Per rendere davvero 12px (--space-3) l'override DEVE essere a (0,2,0) e vince
   per source-order a parità di specificità — NESSUNA forzatura. La (0,2,0) è
   precedentata da base.css L1632 (stesso selettore compound). L1635 (13px)
   resta DEAD in base.css, MAI cancellata (CLEAN-01). */
.sl-sidebar .sl-nav-item {
  gap: var(--space-3);
}

/* --- Allineamento chip + toggle lingua (D-05, PATTERNS trap D) ------------
   Ritmo verticale del footer sidebar: il toggle lingua prende margin-top
   --space-3 (12px) sotto il mini-profilo. SCOPED a (0,2,0) .sl-sidebar
   .sl-lang-switch così le istanze public-nav e topbar del medesimo partial
   .sl-lang-switch NON sono toccate. Markup _language_switcher.html FROZEN
   (set_language form / name=language / name=next — HOOK-INVENTORY §1): si
   ristiliza solo via classe. I literal di .sl-mini-profile (base L1644-1672,
   nome già nowrap+ellipsis) sono TENUTI: allineamento, non re-scaling (D-05). */
.sl-sidebar .sl-lang-switch {
  margin-top: var(--space-3);
}

/* =========================================================================
   Phase 33 — SHELL RESTYLE (tab-bar + button AA fix)  ·  D-02 · D-03 · D-04
   =========================================================================
   Refine, not rebuild: il layout tab-bar esistente (base.css L368-407:
   icon-above-label, gap:2px, 11px, safe-area, FAB compatta) è TENUTO. Qui si
   aggiungono SOLO lo stato attivo terracotta scoped, la rifinitura badge/ellipsi
   e il fix #FFFFFF su .sl-btn--publish. Markup tab-bar e badge (base.html L125)
   FROZEN — solo CSS. Nessun cascade-layer, nessuna forzatura.
   ========================================================================= */

/* --- Stato attivo tab-bar (D-02/D-03, PATTERNS trap A — SCOPING) ----------
   Compound (0,2,0) .sl-tab-bar .sl-nav-item--active: REQUIRED per SCOPING (non
   per battere specificità). Un single-class .sl-nav-item--active{color:--primary}
   sanguinerebbe terracotta sulla label attiva della SIDEBAR (vietato da D-02:
   sidebar attiva = inchiostro + barra). Scopandolo al tab-bar la terracotta
   resta confinata alla barra inferiore. Precedentato da base.css L385 (stesso
   compound) → batte la (0,1,0) .sl-nav-item--active per specificità, NESSUNA
   forzatura. font-weight:600 EREDITATO dalla regola .sl-nav-item--active sopra.
   background:transparent + box-shadow:none annullano tinta e barra della sidebar
   (layout verticale: accento di COLORE, niente barra — coerenza cross-device).
   Contrasto: --primary su --surface = 4.69:1 (AA, margine sottile — verificato
   da scripts/check_contrast.py, coppia aggiunta in Task 3). */
.sl-tab-bar .sl-nav-item--active {
  background: transparent;
  color: var(--primary);
  box-shadow: none;
}

/* --- Rifinitura tab-bar: badge sull'icona + ellissi label (D-03) ----------
   Posizionamento CSS-ONLY dei badge conteggio sull'angolo dell'icona (NESSUNO
   spostamento di markup: i var loans_pending_received/messages_unread e gli
   span .sl-badge--count sono FROZEN, HOOK-INVENTORY §1). Scoped al tab-bar così
   il badge inline della sidebar (base.html L125) resta invariato. La label tab
   resta su UNA riga con ellissi a 360px (EN Shelf/Search/Map/Profile). */
.sl-tab-bar .sl-nav-item {
  position: relative;
}
/* Net-new (0,3,0) compound (IN-01): la markup è FROZEN (nessun class-hook sul
   conteggio né sul <span> label, HOOK-INVENTORY §1) → l'unico modo di colpirli
   è il discendente .sl-tab-bar .sl-nav-item <child>. Eccezione intenzionale alla
   regola "single-class per net-new": qui non c'è classe da agganciare. Nessuna
   forzatura, nessun cascade-layer. */
.sl-tab-bar .sl-nav-item .sl-badge--count {
  position: absolute;
  /* top allineato al bordo reale dell'icona: il .sl-nav-item del tab-bar ha
     padding-top var(--space-1)=4px, quindi top:0 farebbe fluttuare il badge 4px
     sopra l'icona (WR-02). */
  top: var(--space-1);
  left: 50%;
  transform: translateX(20%);
  min-width: 1rem;
  padding: 0 var(--space-1);
}
.sl-tab-bar .sl-nav-item span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* --- #FFFFFF AA fix su .sl-btn--publish (D-04, PATTERNS trap C) ------------
   base.css L1674 .sl-btn--publish forza ancora crema (--color-paper) sul CTA
   verde "Pubblica un libro" e NON è ridichiarato altrove in components.css:
   fixare solo .sl-btn/--primary lascerebbe il publish crema. Questa (0,1,0)
   vince per source-order e porta il testo a --primary-contrast (#FFFFFF,
   9.77:1 su verde --strong). Completa il fix dei tre bottoni shell-visible. */
.sl-btn--publish { color: var(--primary-contrast); }

/* =========================================================================
   Phase 34 — Home pubblica anonima: restyle source-order (PAGE-02 / D-01)
   =========================================================================
   Targeted-hybrid (D-01): il grosso del restyle della home atterra QUI come
   override source-order sui selettori bespoke .sl-hero*/.sl-how*/.sl-featured*
   (base.css L2583-2760, READ-ONLY questa fase). base.css ha ZERO-diff: ogni
   blocco superseduto è DOCUMENTATO con la riga citata, MAI cancellato (la
   rimozione è di CLEAN-01). Tutte le regole sono single-class (0,1,0) sopra una
   regola base single-class → vincono per SOURCE-ORDER, nessuna forzatura
   important (resta 3/0), nessun cascade-layer. Solo alias semantici, nessun px
   arbitrario.
   ========================================================================= */

/* --- D-06: ritmo verticale uniforme tra le tre sezioni (LA SOSTANZA) -------
   Problema PRD #2: .sl-how/.sl-featured erano flat var(--space-6) a ogni
   larghezza (base.css L2619/L2669), mentre l'hero già scalava 32→48
   (base.css L2586 mobile + L2744 desktop). Qui replico lo stesso step 32→48
   UNIFORMEMENTE su tutte e tre → ritmo inter-sezione coerente. Tocco SOLO
   padding-block: i gap intra-sezione (head→content space-5, grid gap space-4,
   ecc.) sono GIÀ sulla scala e NON vengono ri-autorati. */
.sl-hero, .sl-how, .sl-featured {
  padding-block: var(--space-6);   /* 32px mobile, uniforme */
}

/* --- D-03: .sl-container è l'UNICO width-owner (fix CR-01 double-padding) ---
   Il wrapper <div class="sl-container"> (Task 2) possiede ora la larghezza
   1080px + il gutter orizzontale. Annullo quindi il box orizzontale ridondante
   di .sl-how (supersede base.css L2617-2619) e .sl-featured (supersede base.css
   L2667-2669): equivalenza ESATTA a 1080px (queste sezioni SONO la sorgente di
   --container-max). Lasciato a 0 SOLO l'orizzontale — il padding-block verticale
   è di competenza della regola D-06 sopra, non qui. .sl-hero NON è toccato:
   tiene il suo max-width:720px; margin:0 auto interno (misura editoriale più
   stretta, centrata nel container — base.css L2583-2588). DEAD-doc, mai cancellato. */
.sl-how, .sl-featured {
  max-width: none;
  margin-inline: 0;
  padding-inline: 0;
}

/* --- D-04: superficie .sl-featured-card allineata alla famiglia card (alias) -
   Swap puri raw-hex→alias semantico, byte-equivalenti (zero cambio ratio):
   --color-paper-light → --surface-raised (#FCF8EF), raw border → --border
   (#E0D2B8), shadow → --shadow-card. La card legge come la stessa famiglia
   della .sl-card dello Scaffale (P35) PER TOKEN, NON per rename: TENGO la classe
   .sl-featured-card (struttura grid distinta, D-04 — mai .sl-card). Confermo la
   grid gap canonica var(--space-4). PRESERVO il border-radius decorativo
   4px 8px 8px 4px della cover (costola libro, base.css L2700) — literal, non
   tokenizzato. Ink/warm restano primitivi raw (nessun alias semantico esiste).
   Contrasto invariato (alias byte-identici): title 16.10:1, author 9.69:1 ≫ AA. */
.sl-featured__grid {
  gap: var(--space-4);
}
.sl-featured-card:hover {
  box-shadow: var(--shadow-card);
}
.sl-featured-card__cover {
  box-shadow: var(--shadow-card);
}

/* --- D-05: .sl-featured-empty normalizzata ai token IN PLACE (resta bespoke) -
   Supersede base.css L2717-2723: stessi valori via alias semantici
   (--surface-raised/--border/--radius-lg/--shadow-card). KEPT bespoke per D-05:
   NON promuovo l'inerte .sl-empty (components.css L235) al look ricco — sarebbe
   una regressione cross-page (deferita a P35/CLEAN-01). La CTA riusa già
   .sl-btn--primary. */
.sl-featured-empty {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}

/* --- D-06 desktop: estende lo step 32→48 alla breakpoint ESISTENTE (860px) --
   Stessa media-query di base.css L2736 (NON una nuova breakpoint): porto le tre
   sezioni a var(--space-7) come l'hero faceva già da solo (base.css L2744). */
@media (min-width: 860px) {
  .sl-hero, .sl-how, .sl-featured {
    padding-block: var(--space-7);   /* 48px desktop, uniforme */
  }
}

/* --- D-07 DEAD-doc: .sl-btn-pill superseduto da .sl-btn--outline (home) ------
   La CTA secondaria dell'hero migra da .sl-btn-pill (pill 30px, base.css
   L589-602) a .sl-btn--outline (navy bordato quieto, components.css L140) — lo
   swap del markup è in Task 2. .sl-btn-pill resta DOCUMENTATO come DEAD, MAI
   cancellato (CLEAN-01 lo rimuoverà). Navy --brand su crema = 11.69:1 ≫ AA. */

/* --- HOTFIX home (UAT post-deploy P34): reset marker della lista 3-passi ----
   .sl-how__grid è un <ol> senza reset list-style: il browser disegnava i marker
   decimali "1. 2. 3." staccati a sinistra delle card (il design usa l'icona SVG
   + titolo dentro ogni .sl-how-step → i numeri UA non vanno mostrati). Azzero
   anche padding-inline-start 40px / margin-block di default dell'<ol> così la
   griglia si allinea al resto della sezione. Single-class, source-order. */
.sl-how__grid {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Mini-mappa zona offuscata (profilo, "La tua zona") — read-only, mobile-first. */
.sl-zone-map {
  height: 200px;
  width: 100%;
  border-radius: var(--radius-md);
  margin-block-end: var(--space-3);
  overflow: hidden;
}
/* Cerchio della zona offuscata (300 m). Stile via className Leaflet → token CSS,
   perché var() non si risolve negli attributi di presentazione SVG. */
.sl-zone-circle {
  /* UAT 2026-07-10 #3: il fill chiaro si perdeva sui parchi OSM — bordo verde
     bottiglia SCURO più spesso + fill tenue: legge su verde e su grigio. */
  stroke: var(--color-secondary);
  stroke-width: 3;
  fill: var(--color-secondary);
  fill-opacity: 0.12;
}

/* Campo di ricerca "search-first" sopra il select dei personaggi (profilo).
   Eredita la veste input da .sl-field input; qui solo lo stacco dal select sotto. */
.sl-char-search {
  margin-block-end: var(--space-2);
}

/* Combobox personaggi (2026-07-02): tendina custom filtrante al posto del
   <datalist> nativo (filtro inaffidabile). Ancorata sotto il campo; max-height
   in vh così su viewport mobile resta scrollabile senza coprire la pagina.
   Colori = stessa coppia surface/ink dei campi form (contrasto già validato). */
.sl-char-combo {
  position: relative;
}
.sl-char-listbox {
  position: absolute;
  inset-inline: 0;
  top: 100%;
  z-index: 30;
  margin: 0;
  padding: var(--space-1) 0;
  list-style: none;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-cta);
  max-height: 40vh;
  overflow-y: auto;
}
.sl-char-option {
  padding: 10px 15px;
  font-size: 15px;
  color: var(--text);
  cursor: pointer;
}
.sl-char-option:hover,
.sl-char-option[aria-selected="true"] {
  background: var(--color-paper); /* #F4EBDA su ink #211B13: coppia bg/testo già in uso */
}
.sl-char-option--empty {
  color: var(--text-muted);
  cursor: default;
}

/* Chat-skin "anima MSN" (2026-07-02): status personale — la riga in corsivo
   accanto al nome, erede del "messaggio personale" MSN. Token esistenti. */
/* Tab Richieste mobile (batch 4): badge ancorato all'angolo del tab. Con 6
   elementi la bar overflowava a 360px (verificato: 371px) → si stringe la CTA
   "Pubblica" (icona sola sotto i 400px) e si riduce il padding degli item. */
.sl-nav-item--richieste {
  position: relative;
}
.sl-tab-bar__badge {
  position: absolute;
  top: 0;
  right: 2px;
}
/* Con 5 elementi (Profilo → avatar topbar, 2026-07-03) la CTA "Pubblica" tiene
   la label piena anche a 360px: pill flex NOWRAP (il base .sl-btn è inline-block
   con overflow-wrap:anywhere → la label andava a capo e restava solo il "+"). */
.sl-tab-bar .sl-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  overflow-wrap: normal;
  padding: 10px 14px;
  border-radius: 999px;
  flex: 0 0 auto;
}

/* Onboarding 3-step (batch 3): card checklist first-run. */
.sl-onboarding {
  border-radius: var(--radius-md);
  margin-block-end: var(--space-4);
}
.sl-onboarding__steps {
  margin: var(--space-2) 0 0;
  padding-inline-start: 1.4em;
}
.sl-onboarding__step {
  padding-block: 4px;
}
.sl-onboarding__step--done {
  color: var(--strong);
}

/* Chat-skin MSN: empty-state "rompi il ghiaccio" — visibile solo finché è
   l'unico figlio della lista (appena arriva una bolla sparisce, zero JS). */
.sl-message-list__empty {
  list-style: none;
  padding: var(--space-4);
  color: var(--text-muted);
  font-style: italic;
  text-align: center;
}
.sl-message-list__empty:not(:only-child) {
  display: none;
}

/* Chat-skin MSN: shake del trillo (nudge). Disattivato con reduced-motion. */
@keyframes sl-nudge-shake {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(-4px, 1px); }
  40% { transform: translate(4px, -1px); }
  60% { transform: translate(-3px, -1px); }
  80% { transform: translate(3px, 1px); }
}
.sl-chat--shake {
  animation: sl-nudge-shake 0.6s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
  .sl-chat--shake { animation: none; }
}
/* I due stati della campanella stanno entrambi nel DOM: lo stato lo dice
   aria-pressed e il CSS sceglie quale mostrare — così il JS non deve
   riscrivere il contenuto del bottone (e non può cancellare gli SVG). */
.sl-chat__mute .sl-chat__mute-off { display: none; }
.sl-chat__mute[aria-pressed="true"] .sl-chat__mute-on { display: none; }
.sl-chat__mute[aria-pressed="true"] .sl-chat__mute-off { display: inline; }
.sl-chat__mute {
  float: right;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  padding: 6px 10px;
  font-size: 16px;
  cursor: pointer;
}
.sl-chat__emoticon-preview {
  margin: 0 0 var(--space-1);
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}
.sl-chat__nudge-row {
  margin-block-start: var(--space-2);
  text-align: right;
}
.sl-chat__last-seen {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}
.sl-chat__status-message,
.sl-profile-header__status {
  margin: 0 0 var(--space-2);
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

/* Footer dell'app (autenticato): riga brand discreta in fondo al contenuto.
   Stessi token/colori del .sl-public-footer (combinazione già a contrasto valido). */
.sl-app-footer {
  margin-block-start: var(--space-7);
  padding: var(--space-5);
  border-top: 1px solid var(--color-border);
  text-align: center;
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}
.sl-app-footer a {
  color: var(--color-text-warm);
  font-weight: 600;
  text-decoration: none;
}
.sl-app-footer a:hover {
  text-decoration: underline;
}

/* B1 — centro notifiche (lista pagina /notifiche/). Token-based, coerente col DS. */
.sl-notifiche { max-width: 46rem; margin: 0 auto; }
.sl-notifiche__heading { margin: 0 0 1rem; }
.sl-avvisi { max-width: 46rem; margin: 0 auto; }
.sl-notif-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.sl-notif-item { border-radius: 10px; }
.sl-notif-item__link { display: flex; align-items: center; gap: 0.6rem; padding: 0.75rem 1rem; text-decoration: none; color: var(--color-ink); border-radius: 10px; }
.sl-notif-item__link:hover { background: var(--color-paper-light); }
.sl-notif-item__text { flex: 1 1 auto; }
.sl-notif-item__time { flex: 0 0 auto; font-size: 0.85rem; color: var(--color-text-muted); }
.sl-notif-item__dot { flex: 0 0 auto; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--color-status-lent-bg); }
.sl-notif-item--read .sl-notif-item__text { color: var(--color-text-muted); }
.sl-notif-item--unread .sl-notif-item__text { font-weight: 600; }
.sl-pagination { display: flex; align-items: center; justify-content: center; gap: 1rem; margin-top: 1.25rem; }
.sl-pagination__status { font-size: 0.9rem; color: var(--color-text-muted); }

/* --- Centro segnalazioni: FAB + dialog (shell app) + form (anche pagina pubblica). --- */
/* Honeypot: fuori viewport, mai display:none (certi bot saltano i campi display:none). */
.sl-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.sl-feedback-fab {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 60;
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.55rem 0.9rem; border: none; border-radius: 999px; cursor: pointer;
  background: var(--color-secondary); color: var(--color-paper);
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.25); font-size: 0.95rem;
}
.sl-feedback-fab__icon { flex: none; }
.sl-feedback-fab:hover { filter: brightness(1.1); }
@media (max-width: 640px) {
  .sl-feedback-fab__label { display: none; }
  .sl-feedback-fab { padding: 0.6rem 0.75rem; }
}
.sl-feedback-dialog { border: none; border-radius: 12px; padding: 1.25rem; max-width: 28rem; width: calc(100vw - 2rem); }
.sl-feedback-dialog::backdrop { background: rgb(0 0 0 / 0.4); }
.sl-feedback-dialog__head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 0.75rem; }
.sl-feedback-dialog__head h2 { margin: 0; font-size: 1.15rem; }
.sl-feedback-dialog__close { border: none; background: none; font-size: 1.5rem; line-height: 1; cursor: pointer; }
.sl-feedback__form select, .sl-feedback__form textarea,
.sl-feedback--page select, .sl-feedback--page textarea,
.sl-feedback--page input[type="email"] { width: 100%; }
.sl-feedback--page { max-width: 34rem; margin: 2rem auto; padding: 0 1rem; }
.sl-feedback__privacy { font-size: 0.85rem; color: var(--color-text-muted); }
.sl-feedback__done { text-align: center; padding: 1rem 0; }

/* --- Scaffale "Dal catalogo della community" (spec 2026-07-10): heading del partial
   riusabile library/_catalog_shelf.html; le card riusano .sl-book-spine. --- */
/* Phase 35 (2026-09-01): questo blocco è nato a luglio, mentre la milestone era in
   pausa, quindi non è mai passato dal tier dei token: usava 2rem / 1.25rem / 0.95rem /
   1rem / 0.25rem grezzi. Rimappato sulla scala esistente — nessuna misura nuova.
   `1.25rem` (20px) cade fra h2 (24px) e base (16px): è esattamente il caso per cui
   esiste `--fs-h3` (19px, "cluster titoli-card"). Il `color` è stato tolto perché
   ridondante: la regola globale `h1,h2,h3` già dà serif 600 in `--color-ink`, e
   `.sl-catalog-shelf__title` è un `<h2>`. */
.sl-catalog-shelf {
  margin-top: var(--space-6);
}
.sl-catalog-shelf__title {
  font-size: var(--fs-h3);
  margin: 0 0 var(--space-1);
}
.sl-catalog-shelf__subtitle {
  margin: 0 0 var(--space-4);
  color: var(--text-muted);
  font-size: var(--font-size-base);
}

/* Stato B scaffale (brief UAT 2026-07-10 #2): invito "sii il primo" sopra il fallback.
   Phase 35 (2026-09-01): rimappata sui token E consolidata. La regola era DUPLICATA in
   questo stesso file — questa è una seconda ~220 righe più in basso (brief UAT 2026-07-22
   #13, "CTA centrata e non a filo bordo") che, vincendo per ordine di sorgente, ne
   sovrascriveva il `gap`. Le due sono ora una sola, con i valori EFFETTIVI di prima
   (gap 12px = --space-3, centrata): zero cambio visivo, un solo posto da leggere.
   La media query <=640px che ne stila i .sl-btn discendenti resta dov'era. */
.sl-scaffale__first-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: 0 0 var(--space-4);
  text-align: center;
  color: var(--text-muted);
}

/* =========================================================================
   Brief UAT 2026-07-22 — leggibilità e affordance
   ========================================================================= */

/* --- #9 Card risultato ricerca -------------------------------------------
   Era una riga nuda: bordo via style= inline, nessun padding, nessun fondo,
   e i meta impilati come testo semplice. */
.sl-search-list .sl-book-card {
  padding: var(--space-3);
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.sl-search-list .sl-book-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 26px rgba(33, 27, 19, 0.10);
  border-color: var(--color-ocra);
}
/* Il body è flex column: senza questo i figli (badge compresi) si stirano per
   tutta la larghezza — è la ragione per cui il badge distanza era una barra. */
.sl-search-list .sl-book-card__body {
  align-items: flex-start;
}
/* Riga meta: pill omogenee che vanno a capo invece di impilarsi. */
.sl-book-card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
}
.sl-book-card__meta > * {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 10px;
  border-radius: 20px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.25;
}
.sl-book-card__meta svg {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
}
.sl-meta-pill--offered {
  background: var(--color-status-avail-bg);
  color: var(--color-status-avail-fg);
}
.sl-meta-pill--copies {
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  color: var(--color-text-warm);
}
/* "In prestito" non aveva NESSUNA regola CSS: esisteva solo come style= inline
   (terracotta pieno). Ora usa i token di stato già validati per il prestito. */
.sl-badge--in-prestito {
  background: var(--color-status-lent-bg);
  color: var(--color-status-lent-fg);
}
.sl-book-card__invite {
  margin: 10px 0 0;
}

/* --- #7 Rimescola: l'effetto ora è percepibile (rientro sfalsato delle costole).
   Il riordino resta cosmetico e client-side: il seed dello scaffale non cambia. */
@keyframes sl-rail-shuffle-in {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
.sl-rail--shuffled > * {
  animation: sl-rail-shuffle-in 0.34s cubic-bezier(0.2, 0.7, 0.3, 1) backwards;
}
.sl-rail--shuffled > *:nth-child(2) { animation-delay: 0.03s; }
.sl-rail--shuffled > *:nth-child(3) { animation-delay: 0.06s; }
.sl-rail--shuffled > *:nth-child(4) { animation-delay: 0.09s; }
.sl-rail--shuffled > *:nth-child(5) { animation-delay: 0.12s; }
.sl-rail--shuffled > *:nth-child(n + 6) { animation-delay: 0.15s; }

/* --- #6 FAB "Serve aiuto": non deve più coprire il tab "Richieste" ---------
   La tab-bar è fixed-bottom sotto gli 860px e .sl-main-content le riserva
   72px + safe-area: il FAB stava a bottom:1rem, cioè esattamente sopra. */
@media (max-width: 859.98px) {
  .sl-feedback-fab {
    bottom: calc(72px + env(safe-area-inset-bottom) + 0.5rem);
  }
}
/* Desktop: la tab-bar non c'è, il FAB diventa una linguetta agganciata al
   bordo destro e centrata verticalmente (fuori dal flusso di lettura). */
@media (min-width: 860px) {
  .sl-feedback-fab {
    top: 50%;
    bottom: auto;
    right: 0;
    transform: translateY(-50%);
    border-radius: 999px 0 0 999px;
    padding-right: 1.1rem;
  }
}

/* --- #8 Ricerca: filtro e ordinamento come due gruppi distinti ------------
   .sl-search__filter-check era un hook CSS MORTO (nessuna regola): checkbox e
   select erano controlli nativi non stilizzati accanto al raggio già a tema. */
.sl-search__row--filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}
.sl-search__filter-group--sort {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;   /* separazione grafica: l'ordinamento va a destra */
}
.sl-search__filter-check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;              /* WCAG 2.2 AA target size */
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: 24px;
  background: var(--color-paper-light);
  color: var(--color-text-warm);
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.sl-search__filter-check input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--color-secondary);
  cursor: pointer;
}
/* Stato attivo leggibile senza affidarsi al solo colore: cambia anche il fondo
   e resta la spunta nativa come segnale ridondante (A11Y). */
.sl-search__filter-check:has(input:checked) {
  background: var(--color-status-avail-bg);
  border-color: var(--color-status-avail-fg);
  color: var(--color-status-avail-fg);
}
/* Select "Ordina": stessa famiglia visiva della pill, freccia SVG inline in
   data-URI (NO-CDN, nessun asset esterno). */
.sl-search__filter-group--sort select {
  appearance: none;
  -webkit-appearance: none;
  min-height: 44px;
  padding: var(--space-2) 34px var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: 24px;
  background-color: var(--color-paper-light);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234A3F30' stroke-width='2.4' 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 12px center;
  background-size: 13px;
  color: var(--color-text-warm);
  font: inherit;
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
}

/* --- #10 Chip categorie: dichiarare che il rail scorre ---------------------
   .sl-rail nasconde la scrollbar e non aveva né sfumatura né snap: niente
   diceva che il contenuto continua oltre il bordo. */
.sl-search__cats {
  padding: var(--space-2) 0;
  scroll-snap-type: x proximity;
  scroll-padding-left: var(--space-1);
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 40px), transparent 100%);
  mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 40px), transparent 100%);
}
.sl-search__cats .sl-chip {
  scroll-snap-align: start;
  flex: 0 0 auto;
}
/* Entrata sfalsata: le chip "arrivano" da destra, così l'occhio capisce che la
   fila prosegue in quella direzione. */
@keyframes sl-chip-in {
  from { opacity: 0; transform: translateX(14px); }
  to   { opacity: 1; transform: none; }
}
.sl-search__cats .sl-chip {
  animation: sl-chip-in 0.4s cubic-bezier(0.2, 0.7, 0.3, 1) backwards;
}
.sl-search__cats .sl-chip:nth-child(1) { animation-delay: 0.02s; }
.sl-search__cats .sl-chip:nth-child(2) { animation-delay: 0.06s; }
.sl-search__cats .sl-chip:nth-child(3) { animation-delay: 0.10s; }
.sl-search__cats .sl-chip:nth-child(4) { animation-delay: 0.14s; }
.sl-search__cats .sl-chip:nth-child(5) { animation-delay: 0.18s; }
.sl-search__cats .sl-chip:nth-child(6) { animation-delay: 0.22s; }
.sl-search__cats .sl-chip:nth-child(7) { animation-delay: 0.26s; }
.sl-search__cats .sl-chip:nth-child(n + 8) { animation-delay: 0.30s; }
.sl-search__cats .sl-chip:hover {
  transform: translateY(-2px);
  border-color: var(--color-primary);
  color: var(--color-primary);
}

/* --- #11 Toggle Mappa/Lista: .sl-segmented è inline-flex dentro un div non
   centrato → restava incollato a sinistra. */
.sl-esplora__view-toggle {
  display: flex;
  justify-content: center;
}

/* --- #13 CTA "Pubblica un libro" in home: centrata e non a filo bordo -----
   Phase 35 (2026-09-01): il blocco `.sl-scaffale__first-cta` di questo brief è stato
   fuso nella definizione unica più in alto (stessi valori effettivi). Resta qui solo
   la media query, che stila i discendenti e non il componente. */
@media (max-width: 640px) {
  .sl-scaffale__first-cta .sl-btn,
  .sl-emptystate .sl-btn {
    width: auto;
    max-width: calc(100% - var(--space-5));
    padding: 12px 16px;
    font-size: 14.5px;
  }
}

/* Motion: tutte le animazioni introdotte qui sopra si annullano per chi ha
   chiesto meno movimento (WCAG 2.3.3 / prefers-reduced-motion). */
@media (prefers-reduced-motion: reduce) {
  .sl-rail--shuffled > *,
  .sl-search__cats .sl-chip {
    animation: none;
  }
  .sl-search-list .sl-book-card,
  .sl-search__cats .sl-chip:hover {
    transition: none;
    transform: none;
  }
}

/* --- #2 Mirino dello scanner: telaio ad angoli + barra che scorre ----------
   Il <video> faceva da riquadro da solo. Il wrapper .sl-scan-frame porta il
   contesto di posizionamento; il reticolo e' decorativo e non intercetta il
   tocco (pointer-events: none). */
.sl-scan-frame {
  position: relative;
  width: 100%;
  max-width: 480px;
  overflow: hidden;
  border-radius: var(--radius-md);
}
.sl-scan-frame .sl-scan-video {
  max-width: none;
  border-radius: 0;
}
.sl-scan-frame__reticle {
  position: absolute;
  inset: 8%;
  pointer-events: none;
}
.sl-scan-frame__corner {
  position: absolute;
  width: 26px;
  height: 26px;
  border: 3px solid var(--color-ocra);
}
.sl-scan-frame__corner--tl { top: 0; left: 0; border-right: 0; border-bottom: 0; border-radius: 6px 0 0 0; }
.sl-scan-frame__corner--tr { top: 0; right: 0; border-left: 0; border-bottom: 0; border-radius: 0 6px 0 0; }
.sl-scan-frame__corner--bl { bottom: 0; left: 0; border-right: 0; border-top: 0; border-radius: 0 0 0 6px; }
.sl-scan-frame__corner--br { bottom: 0; right: 0; border-left: 0; border-top: 0; border-radius: 0 0 6px 0; }
/* La barra: gradiente con il centro pieno, cosi' legge come un fascio e non
   come una linea piatta. Va e torna in loop (alternate). */
.sl-scan-frame__beam {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--color-ocra) 18%, #FFF3D6 50%, var(--color-ocra) 82%, transparent);
  box-shadow: 0 0 12px rgba(212, 155, 60, 0.85);
  animation: sl-scan-beam 2.4s ease-in-out infinite alternate;
}
/* Si anima `top`, non `transform`: la barra e' alta 2px, quindi un translateY
   percentuale la sposterebbe di 2px invece che per tutta l'altezza del telaio. */
@keyframes sl-scan-beam {
  from { top: 0; }
  to   { top: calc(100% - 2px); }
}
@media (prefers-reduced-motion: reduce) {
  .sl-scan-frame__beam { animation: none; opacity: 0.7; }
}

/* --- #3 Banner citazione celebre sotto la navbar (brief 2026-07-22) --------
   Marquee: la frase scorre da destra a sinistra in loop. Fondo verde bottiglia
   (stessa banda del footer, coppia AA validata), testo crema. Sitewide: reso da
   app-shell e shell pubblica sotto gli header. */
.sl-quote-banner {
  position: relative;
  overflow: hidden;
  background: var(--color-secondary);
  color: var(--color-paper);
  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
  /* sfumature laterali: la frase "entra" ed "esce" invece di apparire tagliata */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
}
.sl-quote-banner__track {
  display: inline-block;
  white-space: nowrap;
  padding: 7px 0;
  padding-left: 100%;               /* parte fuori campo a destra */
  will-change: transform;
  animation: sl-quote-marquee 34s linear infinite;
}
.sl-quote-banner:hover .sl-quote-banner__track {
  animation-play-state: paused;     /* pausa al passaggio del mouse per leggere */
}
.sl-quote-banner__item {
  font-family: var(--font-serif);
  font-size: 14px;
  line-height: 1.3;
}
.sl-quote-banner__mark {
  font-size: 18px;
  opacity: 0.7;
  margin-right: 2px;
}
.sl-quote-banner__author {
  font-style: italic;
  opacity: 0.85;
  margin-left: 6px;
}
.sl-quote-banner__src {
  color: var(--color-paper);
  opacity: 0.6;
  text-decoration: underline;
  margin-left: 10px;
  font-size: 11px;
  font-style: normal;
}
.sl-quote-banner__src:hover { opacity: 0.9; }
@keyframes sl-quote-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-100%); }
}
/* Reduced motion: niente scorrimento. La frase resta ferma, troncata con ellissi
   (niente padding-left:100% che la spingerebbe fuori campo). */
@media (prefers-reduced-motion: reduce) {
  .sl-quote-banner__track {
    animation: none;
    padding-left: 0;
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    padding-inline: var(--space-4);
  }
  .sl-quote-banner__item { white-space: nowrap; }
}

/* --- #4 Citazioni nella pagina libro (brief 2026-07-22) -------------------- */
.sl-book-quotes { margin-top: var(--space-6); }
.sl-book-quotes__head {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  margin: 0 0 var(--space-4);
}
.sl-book-quotes__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.sl-book-quotes__item {
  border-left: 3px solid var(--color-ocra);
  background: var(--color-paper-light);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  padding: var(--space-3) var(--space-4);
}
.sl-book-quotes__text {
  font-family: var(--font-serif);
  font-size: 15.5px;
  line-height: 1.5;
  margin: 0;
  color: var(--color-ink);
}
.sl-book-quotes__text::before { content: "\201C"; opacity: 0.5; }
.sl-book-quotes__text::after { content: "\201D"; opacity: 0.5; }
.sl-book-quotes__meta {
  margin: var(--space-2) 0 0;
  font-size: 12.5px;
  color: var(--color-text-muted);
}
.sl-book-quotes__meta a { color: var(--color-text-muted); }

/* --- Striscia "sostieni il progetto" (sotto il banner citazioni) ------------
   Sta SOTTO .sl-quote-banner (verde pieno) e sopra il contenuto: tinta sabbia
   chiara, così è la citazione a tenere il colore forte e questa resta una nota
   di servizio. Una riga sola, che su mobile va a capo invece di troncare. */
.sl-donate {
  background: var(--color-paper-light);
  border-bottom: 1px solid var(--color-border);
  padding: var(--space-2) var(--space-4);
  text-align: center;
}
.sl-donate__text {
  margin: 0;
  font-size: var(--font-size-sm);
  line-height: var(--line-height-base);
  color: var(--color-text-warm);
}
.sl-donate__link {
  color: var(--color-primary);
  font-weight: 600;
  white-space: nowrap;
}

/* --- Ricerca localita' + segnalino (segnalazione 2026-08-16) ------------------
   La strada principale per chi non ha (o nega) il GPS: si scrive il comune, si
   sceglie da una lista corta, si aggiusta il segnalino. Le voci sono <button>,
   quindi vanno ripulite dell'aspetto di default e rese righe piene cliccabili. */
.sl-localita-risultati { margin: var(--space-2) 0; }
.sl-localita-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--color-paper-light);
}
.sl-localita-lista li + li { border-top: 1px solid var(--color-border); }
.sl-localita-voce {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  width: 100%;
  /* Bersaglio comodo al pollice: 44px e' il minimo touch (WCAG 2.5.8). */
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  background: none;
  border: 0;
  font: inherit;
  color: var(--color-ink);
  text-align: left;
  cursor: pointer;
}
.sl-localita-voce:hover,
.sl-localita-voce:focus-visible { background: var(--color-paper); }
.sl-localita-voce__nome { font-weight: 600; }
.sl-localita-voce__meta {
  font-size: var(--font-size-sm);
  color: var(--color-text-warm);
  white-space: nowrap;
}

/* Mappa di scelta: interattiva, a differenza di .sl-zone-map che e' read-only. */
.sl-mappa-picker {
  height: 260px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}

/* Le coordinate a mano, ora ultima spiaggia invece che richiesta principale. */
.sl-coord-manuali {
  margin: var(--space-4) 0;
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-3);
}
.sl-coord-manuali > summary {
  cursor: pointer;
  font-size: var(--font-size-sm);
  color: var(--color-text-warm);
}

/* --- Il raggio chiesto nel passo della posizione (fase 52, ALERT-07) --------------
   Chip in riga che vanno A CAPO su schermo stretto: una tendina qui costringerebbe a
   un secondo tap per vedere le scelte, e la scala dei raggi e' corta abbastanza da
   stare tutta a vista. Il radio nativo resta nel DOM (tastiera e screen reader). */
.sl-raggio {
  border: none;
  margin: var(--space-4) 0;
  padding: 0;
}
.sl-raggio__scelte {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-2) 0;
}
.sl-raggio__scelta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: var(--space-1) var(--space-3);
  font-size: var(--font-size-sm);
  cursor: pointer;
}
.sl-raggio__scelta:focus-within {
  outline: 2px solid var(--color-ocra);
  outline-offset: 2px;
}
.sl-raggio__scelta:has(input:checked) {
  border-color: var(--color-ocra);
  background: var(--color-paper);
}

/* --- Avviso "i tuoi libri non sono ancora visibili" (segnalazione 2026-08-16) --
   Stessa forma della striscia donazione (una riga di servizio a tutta larghezza,
   che su mobile va a capo invece di troncare), ma in ocra: è un'azione che manca,
   non una nota. Non usa il rosso degli errori — l'utente non ha sbagliato niente,
   e colorarlo da errore farebbe sembrare rotto un caricamento che è andato bene. */
.sl-avviso-zona {
  background: var(--color-paper);
  border-bottom: 2px solid var(--color-ocra);
  padding: var(--space-2) var(--space-4);
  text-align: center;
}
.sl-avviso-zona__text {
  margin: 0;
  font-size: var(--font-size-sm);
  line-height: var(--line-height-base);
  color: var(--color-text-warm);
}
.sl-avviso-zona__link {
  color: var(--color-primary);
  font-weight: 600;
  white-space: nowrap;
}

/* =========================================================================
   Profilo — form a sezioni (COMP, design "Profilo - campi moderni")
   =========================================================================
   Il form profilo era un elenco piatto di 19 campi; il design lo divide in sei
   pannelli tematici. Qui c'è SOLO la presentazione: i campi, i loro name e il
   form action non cambiano (vedi il commento in templates/accounts/profile.html).

   Tutti i valori vengono dai token: il mockup portava hex propri (#B4502D,
   #2A4739, #FDFAF3…) che sono la stessa palette già in tokens.css con una deriva
   di 2-4 punti. Ricopiarli avrebbe creato un secondo sistema di colori accanto a
   quello esistente — e avrebbe scavalcato il lavoro di contrasto A11Y-01, dove
   ogni coppia testo/superficie è stata misurata. Stesso discorso per i font:
   Newsreader e Hanken Grotesk sono GIÀ --font-serif/--font-sans, vendorizzati;
   il <link> a Google Fonts del mockup resta fuori (nessun CDN, D-05).
   ========================================================================= */
.sl-profile-edit__lead {
  color: var(--color-text-muted);
  line-height: var(--line-height-base);
  margin: 0 0 var(--space-5);
  max-width: 34rem;
}

/* --- Il pannello di sezione (un <fieldset>, non un <div>) ---------------- */
/* fieldset/legend invece di div/h3: è un raggruppamento di controlli, e gli
   screen reader annunciano la sezione entrando in ogni campo. `min-width: 0`
   non è cosmesi — il fieldset ha un `min-width: min-content` implicito che gli
   fa sfondare il contenitore quando dentro c'è un campo largo. */
.sl-fieldgroup {
  min-width: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-paper-light);
  padding: var(--space-5);
  margin: 0 0 var(--space-4);
}
.sl-fieldgroup__title {
  font-family: var(--font-serif);
  font-size: var(--fs-h3);
  font-weight: 600;
  color: var(--color-ink);
  /* Il <legend> nasce con un padding orizzontale suo che disallinea il titolo
     dal testo sotto. */
  padding: 0;
  margin: 0 0 var(--space-1);
}
.sl-fieldgroup__lead {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  line-height: var(--line-height-base);
  margin: 0 0 var(--space-5);
}

/* --- Avatar: anteprima tonda + controlli di caricamento ------------------ */
.sl-avatar-field {
  display: flex;
  gap: var(--space-4);
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: var(--space-5);
}
.sl-avatar-field__preview {
  width: 116px;
  height: 116px;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
  border: 1px solid var(--color-border);
  background: var(--color-paper);
}
.sl-avatar-field__preview--empty {
  display: grid;
  place-items: center;
  border-style: dashed;
  font-family: var(--font-serif);
  font-size: 2.5rem;
  color: var(--color-text-muted);
}
/* Il campo file può essere largo (nome del file + "Cancella"): senza min-width:0
   in un flex item il testo lungo spinge invece di andare a capo. */
.sl-avatar-field__controls {
  flex: 1 1 15rem;
  min-width: 0;
}
.sl-avatar-field__controls .sl-field {
  margin: 0;
}

/* --- Righe preferenza: etichetta + aiuto a sinistra, interruttore a destra */
.sl-pref-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 44px;
  padding: var(--space-3) 0;
  border-top: 1px solid var(--color-border);
}
.sl-fieldgroup--prefs .sl-pref-row:first-of-type {
  border-top: 0;
  padding-top: 0;
}
.sl-pref-row__text {
  flex: 1 1 auto;
  min-width: 0;
}
.sl-pref-row label {
  display: block;
  font-weight: 600;
  color: var(--color-ink);
  cursor: pointer;
}
.sl-pref-row .sl-field-help {
  margin: var(--space-1) 0 0;
}

/* L'interruttore È il checkbox vero, ridisegnato: nessun div cliccabile, quindi
   tastiera, spunta e invio del form restano quelli del browser. Stessa geometria
   dello switch consenso in base.css (46×26, pomello 20) perché sulla stessa
   pagina due interruttori diversi si leggerebbero come due meccanismi diversi. */
.sl-pref-row input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  width: 46px;
  height: 26px;
  margin: 0;
  border-radius: 13px;
  background: var(--color-toggle-off);
  cursor: pointer;
  flex: 0 0 auto;
}
.sl-pref-row input[type="checkbox"]::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--color-paper-light);
  transition: transform 0.15s ease;
}
.sl-pref-row input[type="checkbox"]:checked {
  background: var(--color-secondary);
}
.sl-pref-row input[type="checkbox"]:checked::after {
  transform: translateX(20px);
}
.sl-pref-row input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--color-secondary);
  outline-offset: 2px;
}
/* Chi ha chiesto meno animazioni non vede scivolare il pomello. */
@media (prefers-reduced-motion: reduce) {
  .sl-pref-row input[type="checkbox"]::after {
    transition: none;
  }
}

/* --- Barra di salvataggio ------------------------------------------------ */
/* Nel mockup è fissa in fondo alla finestra. Qui NO: su mobile il fondo schermo
   è già occupato dalla barra di navigazione, e una seconda barra fissa le si
   siederebbe sopra. Resta in fondo al form, separata da una linea. */
.sl-savebar {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

/* =========================================================================
   Guide — la sezione editoriale (2026-08-14)
   =========================================================================
   Le pagine legali vivono senza una riga di CSS: `.sl-legal` non è mai stato
   stilato, quindi la prosa esce a larghezza piena e su un monitor da 27" arriva
   a righe da 200 caratteri. Per un'informativa che si consulta a colpi di Ctrl+F
   è tollerabile; per una guida che si legge dall'inizio alla fine no — e la
   stessa mail di Google che parla di contenuti di scarso valore chiede «una
   buona esperienza utente».

   Blocco AUTOSUFFICIENTE e in fondo al file: usa solo token già definiti, non
   tocca nessun selettore esistente e non introduce nuove variabili. In
   particolare NON usa `.sl-prose`, che components.css dichiara inerte finché
   non la applica una PAGE phase 33-40: attivarla qui cambierebbe i link altrove.
   ------------------------------------------------------------------------- */
.sl-guida {
  /* 68ch è la misura leggibile classica (60-75 caratteri). `margin-inline: auto`
     centra la colonna; il padding evita che il testo tocchi il bordo a 320px. */
  max-width: 68ch;
  margin-inline: auto;
  padding: var(--space-6) var(--space-4) var(--space-7);
  color: var(--color-text-warm);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
}

.sl-guida__crumb {
  margin-bottom: var(--space-4);
  font-size: var(--font-size-sm);
}

.sl-guida__head h1 {
  font-family: var(--font-display);
  color: var(--color-ink);
  margin-bottom: var(--space-3);
}

/* L'occhiello: più grande del corpo, più chiaro del titolo. */
.sl-guida__standfirst {
  font-size: 1.15rem;
  color: var(--color-ink);
  margin-bottom: var(--space-6);
}

.sl-guida__body h2 {
  font-family: var(--font-display);
  font-size: var(--font-size-h2);
  color: var(--color-ink);
  /* Lo spazio SOPRA un titolo di sezione deve essere maggiore di quello sotto,
     altrimenti il titolo sembra appartenere al paragrafo che lo precede. */
  margin-top: var(--space-7);
  margin-bottom: var(--space-3);
}

.sl-guida__body h3 {
  font-family: var(--font-display);
  color: var(--color-ink);
  margin-top: var(--space-5);
  margin-bottom: var(--space-2);
}

.sl-guida__body p,
.sl-guida__body li {
  margin-bottom: var(--space-4);
}

.sl-guida__body ul,
.sl-guida__body ol {
  padding-left: var(--space-5);
  margin-bottom: var(--space-4);
}

/* Il richiamo: un paragrafo che vale la pena isolare dal flusso. Filetto a
   sinistra in terracotta, come i bordi-accento già usati altrove nel file. */
.sl-guida__nota {
  border-left: 3px solid var(--color-primary);
  padding: var(--space-3) var(--space-4);
  margin: var(--space-5) 0;
  background: var(--color-paper-light);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

/* Tabella dei numeri vivi della guida «casette dei libri» (14-09-2026): righe leggibili su
   telefono (scorre dentro il suo contenitore invece di rompere la pagina). */
.sl-guida__tabella {
  width: 100%;
  border-collapse: collapse;
  margin: var(--space-4) 0 var(--space-5);
  font-size: 0.95em;
  display: block;
  overflow-x: auto;
}

.sl-guida__tabella caption {
  text-align: left;
  caption-side: bottom;
  padding-top: var(--space-2);
  font-size: 0.9em;
  opacity: 0.8;
}

.sl-guida__tabella th,
.sl-guida__tabella td {
  text-align: left;
  vertical-align: top;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border);
}

.sl-guida__tabella thead th {
  border-bottom: 2px solid var(--color-primary);
}

.sl-guida__tabella td:nth-child(2),
.sl-guida__tabella td:nth-child(3) {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.sl-guida__fonte {
  font-size: 0.9em;
  opacity: 0.8;
}

/* Guida «biblioteca diffusa» (fase 54): data di aggiornamento, indice, numeri vivi, FAQ.
   Stessi token della nota e della tabella qui sopra: nessun colore nuovo. */
.sl-guida__aggiornata {
  font-size: 0.9em;
  opacity: 0.8;
  margin: 0 0 var(--space-4);
}

.sl-guida__indice {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  margin: 0 0 var(--space-5);
  background: var(--color-paper-light);
}

.sl-guida__indice ol {
  margin: 0;
  padding-left: var(--space-5);
}

.sl-guida__numeri {
  border-left: 3px solid var(--color-primary);
  padding: var(--space-3) var(--space-4);
  margin: var(--space-4) 0 var(--space-5);
  background: var(--color-paper-light);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.sl-guida__faq dt {
  font-weight: 600;
  margin-top: var(--space-4);
}

.sl-guida__faq dd {
  margin: var(--space-2) 0 0;
}

.sl-guida__body a {
  color: var(--color-primary);
  text-decoration: underline;
}

.sl-guida__body a:hover {
  color: var(--color-secondary);
}

.sl-guida__foot {
  margin-top: var(--space-7);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.sl-guida__cta {
  margin-top: var(--space-4);
}

/* --- Indice della sezione ------------------------------------------------ */
.sl-guide-index {
  max-width: 68ch;
  margin-inline: auto;
  padding: var(--space-6) var(--space-4) var(--space-7);
  color: var(--color-text-warm);
  line-height: var(--line-height-base);
}

.sl-guide-index h1 {
  font-family: var(--font-display);
  color: var(--color-ink);
  margin-bottom: var(--space-3);
}

.sl-guide-index__intro {
  font-size: 1.15rem;
  color: var(--color-ink);
  margin-bottom: var(--space-6);
}

/* Le voci sono <li> di una lista: `list-style: none` toglie il pallino ma la
   lista resta una lista per chi naviga con lo screen reader. */
.sl-guide-index__list {
  list-style: none;
  padding-left: 0;
}

.sl-guide-index__item {
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--color-border);
}

/* 2026-09-10: il titolo di una voce passa da <h2> a <h3>. L'indice ha ora due
   FAMIGLIE di guide — quelle sul prestare libri, che valgono anche senza questo
   sito, e quelle sulle funzioni di Serenlibrità — e le famiglie hanno bisogno di
   un titolo proprio: senza, dodici <h2> di fila si leggono come un elenco piatto
   in cui le ultime quattro sembrano fuori tema. Il livello <h2> va al gruppo, le
   voci scendono a <h3>: la gerarchia del documento torna a dire la verità. */
.sl-guide-index__gruppo {
  font-family: var(--font-display);
  font-size: var(--font-size-h2);
  color: var(--color-ink);
  margin: var(--space-6) 0 var(--space-2);
}

.sl-guide-index__gruppo-nota {
  margin: 0 0 var(--space-3);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}

.sl-guide-index__item h3 {
  font-family: var(--font-display);
  font-size: 1.25rem;
  margin-bottom: var(--space-2);
}

.sl-guide-index__item h3 a {
  color: var(--color-ink);
  text-decoration: none;
}

.sl-guide-index__item h3 a:hover {
  color: var(--color-primary);
  text-decoration: underline;
}

/* =========================================================================
   .sl-post-libro — la card del libro consigliato dentro un post (blocco 3,
   2026-08-26). È un <a> che avvolge copertina + testo: bersaglio unico e
   grande, come la card di un libro nel catalogo.

   Nell'ELENCO dei fili non si usa: lì il libro si nomina soltanto
   (.sl-note--libro), altrimenti una lista di discussioni diventa una galleria
   di copertine in cui il testo dei post sparisce.
   ========================================================================= */
.sl-post-libro {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-paper-light);
  text-decoration: none;
  color: inherit;
}
.sl-post-libro:hover,
.sl-post-libro:focus-visible {
  border-color: var(--color-ocra);
}
/* Copertina piccola e a proporzione fissa: senza aspect-ratio una copertina
   quadrata e una molto alta darebbero due card di altezze diverse nello stesso
   filo. `flex-shrink: 0` o su schermo stretto il testo la schiaccerebbe. */
.sl-post-libro__cover {
  flex-shrink: 0;
  width: 44px;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border-radius: var(--radius-sm);
  display: block;
}
.sl-post-libro__testo {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0; /* permette a title/autore di andare in ellissi invece di sbordare */
}
.sl-post-libro__titolo {
  font-weight: 600;
}
.sl-post-libro__autore {
  font-size: 0.875em;
  color: var(--color-ink-soft, inherit);
}
/* Il badge non deve stirarsi per tutta la colonna flex: `align-self: start` lo
   tiene alla sua larghezza, come i pill della scheda libro. */
.sl-post-libro__possesso {
  align-self: start;
  margin-top: 2px;
}
/* La menzione compatta nell'elenco dei fili: è una nota, non un titolo. */
.sl-note--libro {
  font-style: italic;
}

/* =========================================================================
   Le PERSONE dentro un gruppo (2026-08-26). Fino a oggi di un membro si vedeva
   solo il nome: qui ci sono l'intestazione dell'autore sopra ogni post e la
   scheda che il nome adesso apre.
   ========================================================================= */

/* Intestazione dell'autore sopra un post: avatar piccolo + nome + personaggio. */
.sl-post-autore {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.sl-post-autore__avatar {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}
.sl-post-autore__testo {
  margin: 0;
  min-width: 0;
}
/* Il personaggio letterario è un'ANNOTAZIONE, non un secondo nome: sta più
   piccolo e più chiaro, o due nomi accanto si contendono la lettura. */
.sl-post-autore__char {
  font-size: 0.875em;
  opacity: 0.75;
}
.sl-post-autore__char::before {
  content: "· ";
}

/* Scheda del membro: avatar grande accanto al nome. */
.sl-membro-header {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.sl-membro-header__avatar {
  flex-shrink: 0;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}
.sl-membro-bio {
  margin: var(--space-3) 0;
}
.sl-membro-char {
  margin: var(--space-4) 0;
  padding: var(--space-3);
  border-left: 3px solid var(--color-ocra);
  background: var(--color-paper-light);
}

/* L'invito a presentarsi: una nota, non un allarme — sparisce da sola dopo il
   primo post e non deve sembrare un compito da sbrigare. */
.sl-presentati {
  font-style: italic;
}

/* Il segnale di attività nell'elenco dei gruppi. */
.sl-gruppo-segnale {
  opacity: 0.8;
}

/* Titolo di famiglia nell'elenco categorie dei moduli (29-08). Non e' una nota fra le
   altre: e' il segnale che dice DOVE cercare la casella giusta, quindi si stacca dal
   testo minore con peso e colore, e prende aria sopra per separare i gruppi. Il primo
   non la prende, o il blocco parte sbilanciato sotto la sua legenda. */
.sl-cat-group {
  margin: var(--space-4) 0 var(--space-2);
  font-weight: 600;
  color: var(--color-text-warm);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.sl-cat-group:first-of-type {
  margin-top: var(--space-2);
}

/* =========================================================================
   Phase 36 (2026-09-01) — /esplora/ e /cerca/: i blocchi <style> in-template
   =========================================================================
   Assorbiti da `templates/search/esplora.html` e `templates/search/page.html`, dove
   vivevano dentro `{% block content %}` — quindi nel <body>, dopo i fogli esterni.
   Qui l'ordine di cascata è preservato: components.css è caricato dopo base.css in
   entrambe le shell, quindi queste regole continuano a vincere sulle omonime di
   base.css a parità di specificità, esattamente come prima.

   Verificato prima dello spostamento, selettore per selettore:
   - `.sl-search__cats` — base/components ne stilano padding, scroll-snap e mask; qui
     si imposta solo `margin`. Nessuna sovrapposizione di proprietà.
   - `.sl-search__results .sl-search-list` — due classi, quindi vince per specificità
     sulla `.sl-search-list` di base.css (una classe) ovunque la si metta.
   - `.sl-esplora__view-toggle` — components.css ne dà display e justify-content, qui
     solo il margin. Nessuna sovrapposizione.
   - `.sl-search__radius-card select` — STESSO selettore di base.css:3701, e lì c'è un
     difetto vero, corretto in questo passaggio (vedi sotto).
   ========================================================================= */

/* --- /esplora/ (mappa + lista) ------------------------------------------ */
.sl-esplora { max-width: 1120px; }
/* L'icona scudo si allinea in alto col testo del banner privacy. */
.sl-esplora__privacy { margin: var(--space-4) 0; }
.sl-esplora__privacy-icon { flex: 0 0 auto; color: var(--color-secondary); }
/* Il toggle Mappa/Lista occupa una riga propria sopra mappa+lista. */
.sl-esplora__view-toggle { margin: var(--space-4) 0; }

/* --- /cerca/ ------------------------------------------------------------
   ⚠️ DIFETTO CORRETTO QUI (trovato 2026-09-01 spostando questo blocco).
   `base.css:3701` dà a questo stesso selettore `appearance:none` PIÙ una freccia SVG
   come `background-image`, e un `padding-right` che le riserva lo spazio. Il blocco
   in-template, arrivando dopo, usava lo shorthand `background:` — che azzera
   `background-image` — e uno shorthand `padding` che azzerava il `padding-right`.
   Risultato in produzione: sulla pagina Cerca il select del raggio non aveva NESSUN
   indicatore a discesa, né quello nativo (soppresso da appearance:none) né quello
   custom (cancellato). Sembrava una casella inerte. Misurato in browser prima e dopo:
   `background-image: none` → l'URI della freccia, `padding-right: 12px` → 40px. */
.sl-search { max-width: 1120px; }
/* Input con icona: l'icona è decorativa (aria-hidden), l'input resta etichettato. */
.sl-search__field {
  position: relative;
  display: flex;
  align-items: center;
}
.sl-search__field-icon {
  position: absolute;
  left: var(--space-3);
  color: var(--color-icon-muted);
  pointer-events: none;
}
.sl-search__field input[type="text"],
.sl-search__field input:not([type]) {
  width: 100%;
  padding: var(--space-3) var(--space-3) var(--space-3) calc(var(--space-6) + var(--space-2));
  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: var(--font-size-base);
}
/* Card raggio: etichetta "Entro {radius} da te" + il <select> VINCOLATO (preset). */
.sl-search__radius-card {
  margin-top: var(--space-4);
  padding: var(--space-4);
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}
.sl-search__radius-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}
/* Il select preset stilizzato come controllo on-brand; accent terracotta (10%). */
.sl-search__radius-card select {
  width: 100%;
  /* Il padding-right riserva lo spazio della freccia (vedi il commento sopra): va
     scritto per esteso, perché lo shorthand `padding` azzererebbe quello di base.css. */
  padding: var(--space-2) calc(var(--space-6) + var(--space-2)) var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  /* `background-color`, MAI lo shorthand `background`: vedi il commento sopra. */
  background-color: var(--color-paper);
  color: var(--color-ink);
  font-family: inherit;
  accent-color: var(--color-primary);
}
/* Pillola tempo-di-risposta (opzionale, SRCH-01 <500ms). */
.sl-search__rt-pill {
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-secondary);
}
/* Rail categorie: scroll orizzontale (.sl-rail) di chip presentazionali. */
.sl-search__cats { margin: var(--space-4) 0; }
/* Griglia risultati responsive: 1 colonna mobile → auto-fill desktop. */
.sl-search__results .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__results .sl-search-list {
    grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
  }
}

/* --- Phase 36: inline `style=` assorbiti (2026-09-01) --------------------
   Migrati dai template di /cerca/ e /esplora/. Solo questi tre erano override VERI:
   gli altri sei erano no-op (ridichiaravano un colore o un bordo che la regola del
   componente dava già — misurati in browser prima di toglierli, computed identico).
   `.sl-weight-slider` aveva inline `display:flex; align-items:center; gap:.5rem`: le
   prime due erano identiche a base.css, la terza forzava 8px grezzi sopra il
   `--space-3` (12px) del componente. Tolto l'inline, la riga dei filtri torna al gap
   della scala — unico delta visivo di questa migrazione: 8px → 12px. */
.sl-search__lead,
.sl-esplora__map-help {
  color: var(--color-text-warm);
}

/* --- Phase 37 (2026-09-01) — Profilo, privacy/consensi, GDPR ---------------
   Il criterio "campi e toggle usano i componenti" era già soddisfatto: la pagina
   include `accounts/_profile_field.html` 20 volte e raggruppa in `.sl-fieldgroup`,
   e il toggle dei consensi è già il riferimento di qualità (`.sl-toggle`, restyle
   F7/08-07) — promosso, non reinventato. Restavano nove `style=` inline: qui sotto
   ci sono le loro regole, con lo stesso valore che avevano.

   Le cinque `.sl-card` della pagina profilo portavano tutte lo STESSO override
   inline `border-radius: var(--radius-md)`, cioè un raggio più stretto del
   `--radius-lg` del componente. Verificato che sono esattamente cinque su cinque
   dentro `.sl-profile` (la sesta occorrenza di "sl-card" nel template è un
   commento), quindi la regola con scope è equivalente per costruzione. */
.sl-profile .sl-card {
  border-radius: var(--radius-md);
}

/* I due CTA primari della pagina (salva profilo, vai alle mie copie) avevano
   entrambi `box-shadow: var(--shadow-cta)` inline — `.sl-btn--primary` da sola non
   la porta. Sono gli unici due `.sl-btn--primary` dentro `.sl-profile`. */
.sl-profile .sl-btn--primary {
  box-shadow: var(--shadow-cta);
}

/* Il form di cancellazione account: staccato dal blocco che lo precede. Prende una
   classe perché non ne aveva nessuna; il suo `data-confirm` (letto da sl-ux.js per
   la conferma prima di inviare) resta intatto. */
.sl-profile__danger-form {
  margin-top: var(--space-3);
}

/* Le coordinate nell'anteprima della posizione: non avevano NESSUNA regola CSS,
   il loro unico stile era l'inline. */
.sl-location-preview__coords {
  color: var(--color-secondary);
  font-weight: 600;
}

/* --- Badge sulle superfici di scoperta — decisione presa 2026-09-01 -----------
   La domanda lasciata aperta dalla Phase 38 ("unificare i badge o tenere la variante
   di superficie?") è risolta qui, e non con un compromesso: guardando il sistema, le
   cinque varianti che esistevano — avail, attesa, lent, distance, in-prestito — sono
   TUTTE tonali e tutte legate a una coppia di token di stato. Le due varianti "piene"
   erano gli unici corpi estranei, ed erano nate da `style=` inline ad-hoc, non da una
   scelta di design. Quindi:

   1. `.sl-badge--distance` torna TONALE anche su /esplora/. Una distanza non è mai un
      avvertimento: non c'è ragione perché si legga diversamente su una pagina sola.
   2. «Richiesta inviata» usa `.sl-badge--avail`, il verde tonale di stato che il sistema
      aveva già per le conferme — al posto di un verde pieno fatto su misura.
   3. «In prestito» sulle superfici di SCOPERTA tiene l'enfasi, perché lì dice una cosa
      diversa: dentro le tue richieste lo stato È il soggetto della riga, su un pin di
      mappa o in una lista di risultati è un avvertimento che cambia se agisci o no.
      Ma smette di prendere il terracotta di BRAND e prende il token di STATO, così
      «in prestito» resta la stessa famiglia di colore ovunque — tonale dove lo leggi
      con calma, pieno dove deve fermarti. Il nome lo dice: `--in-prestito-forte` sta
      accanto a `--in-prestito`, non altrove.

   Netto: due modificatori su misura diventano uno, agganciato alla famiglia di stato
   invece che al colore di brand, e il contrasto MIGLIORA (paper-light su terracotta
   4,69 → su lent-fg 5,85). */
.sl-badge--in-prestito-forte {
  background: var(--color-status-lent-fg);
  color: var(--color-paper-light);
}

.sl-chip--riga {
  display: inline-flex;
  gap: var(--space-1);
  color: var(--color-ink);
}

/* --- Phase 40 (2026-09-01) — dettaglio libro, copie, form copia --------------
   Ventidue `style=` inline su questa superficie, ora zero. Misurati tutti in browser
   prima di toccarli: sei erano no-op (ridichiaravano il colore o il bordo che la
   regola del componente dava già) e sono stati semplicemente cancellati. Il resto è
   qui sotto, con una decisione presa e motivata per ciascuna famiglia.

   ── .sl-btn--danger: NON ESISTEVA ──
   La classe era usata nel markup (pagina di conferma eliminazione copia) ma non era
   definita in NESSUN foglio: senza l'inline, il bottone distruttivo sarebbe caduto
   sullo stile del CTA primario e sarebbe stato terracotta identico a "Salva". Su una
   pagina che chiede "vuoi eliminare questa copia?" è un problema di affordance, non
   di estetica. Qui la variante viene definita davvero, con i colori che l'inline
   usava. Contrasto verificato: paper-light su lent-fg = 5,85 (AA).

   ── .sl-field-error: sette override identici, cancellati ──
   Sette volte lo stesso inline forzava il colore dell'errore di campo da
   `--color-status-lent-fg` (quello del componente) a `--color-primary`. Sulla
   superficie prestiti lo stesso componente NON è sovrascritto: la stessa app diceva
   due cose diverse. Vince il componente — è la ragione per cui esiste. Il rosso
   diventa leggermente più scuro (#B8502F → #9A4A28) e il contrasto MIGLIORA:
   4,69 → 5,85 su paper-light. Delta visivo accettato e voluto.

   ── colore del testo dei bottoni ──
   Tre inline forzavano il testo dei bottoni a `--color-paper-light` dove i componenti
   danno bianco (primary) e `--color-paper` (secondary). Tolti: vince il componente,
   coppie già nella matrice di contrasto (bianco su terracotta 4,97; paper su verde
   8,25). */
.sl-btn--danger {
  background: var(--color-status-lent-fg);
  color: var(--color-paper-light);
}
.sl-h1--danger {
  color: var(--color-status-lent-fg);
}

/* I due contenitori di dettaglio erano centrati a 48rem via inline. 48rem non è sulla
   scala dei container (--container-max è 1080px): resta un literal, ma dichiarato UNA
   volta invece che tre, e con il perché accanto — è la misura di colonna leggibile
   scelta per la prosa delle schede. */
.sl-edition-detail,
.sl-public-edition {
  max-width: 48rem;
  margin-inline: auto;
}
.sl-edition-detail__avviso {
  max-width: 48rem;
  margin: 0 auto var(--space-4);
}

/* Anteprima della copertina già caricata, nel form della copia. */
.sl-cover-preview {
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2);
}


/* --- Phase 40: il pallino-categoria, ultimo `style=` inline dell'app -----------
   Era un inline con quattro dichiarazioni statiche più il colore preso da
   `{% cycle %}` sugli 8 token --cat-*. Il criterio della fase lasciava la scelta fra
   tenerlo inline e convertirlo a `data-cat` + tabella di token: scelta la seconda.
   Le statiche stanno qui una volta sola; il colore resta per-iterazione perché lo è
   davvero, ma passa da un attributo dichiarativo invece che da CSS generato.
   `data-cat` è presentazionale — nessun JS lo legge, non entra nell'hook inventory. */
.sl-chip__dot {
  display: inline-block;
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
}
.sl-chip__dot[data-cat="narrativa"] { background: var(--cat-narrativa); }
.sl-chip__dot[data-cat="saggistica"] { background: var(--cat-saggistica); }
.sl-chip__dot[data-cat="fumetto"] { background: var(--cat-fumetto); }
.sl-chip__dot[data-cat="ragazzi"] { background: var(--cat-ragazzi); }
.sl-chip__dot[data-cat="poesia"] { background: var(--cat-poesia); }
.sl-chip__dot[data-cat="gialli"] { background: var(--cat-gialli); }
.sl-chip__dot[data-cat="filosofia"] { background: var(--cat-filosofia); }
.sl-chip__dot[data-cat="arte"] { background: var(--cat-arte); }


/* --- Phase 40.5 (2026-09-01) — voto a stelle (reputation/vota.html) ----------
   Ultimo blocco <style> in-template dell'applicazione, assorbito qui. Arrivò ad agosto,
   mentre la milestone era in pausa, su una superficie che nessuna fase copriva: senza
   questo passaggio la v2.3 si sarebbe chiusa con uno style in pagina ancora in piedi.
   Le regole restano tutte prefissate `.sl-vota`, quindi NON entrano nel vocabolario
   condiviso: è ancora stile di una pagina sola, semplicemente non più dentro l'HTML.
   Spostato verbatim; l'unica regola toccata è il colore della stella del badge, che
   arrivava da un `style=` inline ed è ora `.sl-reputazione__stella`. */
.sl-vota { max-width: 640px; margin: 0 auto; }
.sl-vota__libro { color: var(--color-text-warm); margin-bottom: var(--space-5); }

/* Il gruppo di stelle è un radio-group: la semantica resta quella dei radio
   (tab una volta, frecce per scegliere), l'icona è solo vestizione. */
.sl-vota__stelle { border: 0; padding: 0; margin: 0 0 var(--space-5); }
.sl-vota__stelle legend { font-weight: 600; margin-bottom: var(--space-3); }
.sl-vota__scala { display: flex; gap: var(--space-2); }
.sl-vota__stella { cursor: pointer; line-height: 1; }
/* L'input resta nel flusso e focusabile (mai display:none, che lo toglierebbe
   alla tastiera): è solo sovrapposto alla stella. */
.sl-vota__stella input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.sl-vota__icona {
  display: block;
  font-size: 34px;
  color: var(--color-icon-muted);
  transition: color 120ms ease, transform 120ms ease;
}
.sl-vota__stella:has(input:focus-visible) .sl-vota__icona {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}
/* Fallback universale: la stella scelta è comunque piena anche dove :has()
   non c'è. Le regole cumulative qui sotto sono l'aggiunta, non la base. */
.sl-vota__stella input:checked ~ .sl-vota__icona { color: var(--color-ocra); }
/* Riempimento cumulativo (1..N pieni). Si usa :has() invece del classico
   ``flex-direction: row-reverse``, che manderebbe l'ordine di lettura da 5 a 1
   mentre quello visivo va da 1 a 5 — WCAG 1.3.2, sequenza significativa. */
.sl-vota__scala:has(input[value="2"]:checked) .sl-vota__stella:nth-of-type(-n+2) .sl-vota__icona,
.sl-vota__scala:has(input[value="3"]:checked) .sl-vota__stella:nth-of-type(-n+3) .sl-vota__icona,
.sl-vota__scala:has(input[value="4"]:checked) .sl-vota__stella:nth-of-type(-n+4) .sl-vota__icona,
.sl-vota__scala:has(input[value="5"]:checked) .sl-vota__stella:nth-of-type(-n+5) .sl-vota__icona {
  color: var(--color-ocra);
}
.sl-vota__scala:has(.sl-vota__stella:nth-of-type(1):hover) .sl-vota__stella:nth-of-type(-n+1) .sl-vota__icona,
.sl-vota__scala:has(.sl-vota__stella:nth-of-type(2):hover) .sl-vota__stella:nth-of-type(-n+2) .sl-vota__icona,
.sl-vota__scala:has(.sl-vota__stella:nth-of-type(3):hover) .sl-vota__stella:nth-of-type(-n+3) .sl-vota__icona,
.sl-vota__scala:has(.sl-vota__stella:nth-of-type(4):hover) .sl-vota__stella:nth-of-type(-n+4) .sl-vota__icona,
.sl-vota__scala:has(.sl-vota__stella:nth-of-type(5):hover) .sl-vota__stella:nth-of-type(-n+5) .sl-vota__icona {
  color: var(--color-ocra-light);
}

.sl-vota__esito { display: flex; align-items: baseline; gap: var(--space-3); margin: var(--space-3) 0; }
.sl-vota__voto { font-size: 22px; color: var(--color-ocra); letter-spacing: 2px; }
.sl-vota__chi { color: var(--color-text-warm); }
@media (prefers-reduced-motion: reduce) {
  .sl-vota__icona { transition: none; }
}

/* La stella del badge di reputazione: era l'ultimo `style=` inline della superficie. */
.sl-reputazione__stella {
  color: var(--color-ocra);
}

/* =========================================================================
   Redisegno Richieste (2026-09-01) — fuori milestone v2.3
   =========================================================================
   La v2.3 era presentation-only e non poteva aggiungere comportamento; questo
   redisegno lo aggiunge (schede, filtro), quindi arriva dopo ed è documentato a sé.

   Due misure del brief cadono esattamente su token che esistono già: il titolo a
   19px è `--fs-h3` (nato proprio come "cluster titoli-card") e la meta a 13px è
   `--font-size-sm`. Non è stato inventato nessun valore nuovo tranne la larghezza
   di colonna (820px), che è una misura di leggibilità e sta scritta una volta sola.
   ========================================================================= */

.sl-richieste {
  max-width: 820px;
  margin-inline: auto;
}

/* --- Schede Ricevute / Inviate ------------------------------------------ */
.sl-richieste__tabs {
  display: flex;
  gap: var(--space-5);
  border-bottom: 1px solid var(--color-border);
  margin-bottom: var(--space-4);
}
.sl-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;            /* target touch (A11Y-01) */
  padding: 0 var(--space-1) var(--space-2);
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;         /* la sottolineatura copre il bordo del gruppo */
  background: none;
  font-family: inherit;
  font-size: var(--font-size-base);
  font-weight: 600;
  color: var(--color-text-muted);
  cursor: pointer;
}
.sl-tab[aria-selected="true"] {
  color: var(--color-ink);
  border-bottom-color: var(--color-primary);
}
/* Il conteggio: una pastiglia discreta, non un badge d'allarme. */
.sl-tab__n {
  min-width: 1.5rem;
  padding: 0 var(--space-2);
  border-radius: 20px;
  background: var(--color-status-attesa-bg);
  color: var(--color-status-attesa-fg);
  font-size: var(--font-size-sm);
}
.sl-tab[aria-selected="true"] .sl-tab__n {
  background: var(--color-status-lent-bg);
  color: var(--color-status-lent-fg);
}

/* --- Filtro per stato ---------------------------------------------------- */
.sl-richieste__filtri {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}
.sl-chip--filtro {
  min-height: 44px;
  border: 1px solid var(--color-border);
  background: var(--color-paper-light);
  color: var(--color-text-warm);
  font-family: inherit;
  cursor: pointer;
}
.sl-chip--filtro[aria-pressed="true"] {
  background: var(--color-status-avail-bg);
  border-color: var(--color-secondary);
  color: var(--color-status-avail-fg);
}
.sl-richieste__nessun-filtro {
  color: var(--color-text-warm);
  font-style: italic;
  padding: var(--space-4) 0;
}

/* --- La riga come GRIGLIA, non come pila di paragrafi -------------------- */
/* Mobile: copertina + contenuto, azioni sotto a piena larghezza.
   Da 860px: una terza colonna per le azioni. */
.sl-loan-row__grid {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "cover main" "actions actions";
  gap: var(--space-3) var(--space-4);
  align-items: start;
}
.sl-loan-row__cover { grid-area: cover; }
.sl-loan-row__main  { grid-area: main; min-width: 0; }
.sl-loan-row__actions {
  grid-area: actions;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}
.sl-loan-row__cover-img,
.sl-loan-row__cover-cloth {
  width: 48px;
  height: 72px;
  border-radius: 3px 6px 6px 3px;   /* costola, come lo scaffale */
  object-fit: cover;
  display: block;
}
.sl-loan-row__cover-cloth {
  background: linear-gradient(135deg, var(--color-secondary), var(--color-brand-ink));
}

/* Titolo e stato sulla STESSA riga, allineati alla base. */
.sl-loan-row__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-1);
}
.sl-loan-row__title {
  font-family: var(--font-serif);
  font-size: var(--fs-h3);
  margin: 0;
}

/* La meta: UNA riga che va a capo, mai troncata. */
.sl-loan-row__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  margin: 0 0 var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}
.sl-loan-row__who {
  font-weight: 600;
  color: var(--color-ink);
}
.sl-loan-row__sep { color: var(--color-border); }
.sl-loan-row__bit--assente { font-style: italic; }
.sl-loan-row__stelle { margin: 0 0 var(--space-2); }

/* Le righe che dicono cosa si sta aspettando e da chi: «In attesa che confermi il
   ritiro», «Dice di avertelo consegnato». La classe esisteva da luglio ma NON aveva
   una sola regola — un `<p>` nudo coi margini di default. Passata da un uso a sette
   col passaggio a due conferme, dove è il testo che veicola lo stato (A11Y-02): senza
   stile si legge come un paragrafo qualsiasi invece che come una nota di servizio. */
.sl-loan-actions__hint {
  margin: 0;
  font-size: var(--font-size-sm);
  color: var(--color-text-warm);
}

/* --- Barra secondaria della consegna ------------------------------------ */
/* Il difetto che risolve: il <form> era un blocco dentro un contenitore flex, quindi
   "Segna consegnato" cadeva SOTTO il campo data invece che in linea.
   Va a capo DA SOLA, senza soglie di viewport: `flex-wrap` mette il bottone in linea
   quando ci sta e sotto quando non ci sta, e lo decide lo spazio VERO del contenitore,
   non una media query che indovina la larghezza della card. ⛔ il `max-width` non e'
   decorativo: senza, a 768px il campo data diventava largo 676px — la sproporzione
   segnalata il 01-09. */
.sl-consegna {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3);
  width: 100%;
  max-width: 460px;
  margin: var(--space-3) 0 0;
  padding: var(--space-3);
  background: var(--color-status-attesa-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.sl-consegna__campo {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 1 1 12rem;
}
/* `flex: 1 1 auto` + una base minima: in linea il bottone tiene la sua larghezza, quando
   va a capo si prende tutta la riga da solo. Un solo valore per i due casi. */
.sl-consegna__ok { flex: 1 1 auto; min-width: 11rem; width: auto; }

/* Il modulo della consegna vive dentro un <details>: chiuso finché non serve. Il difetto
   che chiude — segnalato il 01-09 — era averlo sempre aperto in «Accettata», cioè chiedere
   la data di riconsegna quando i due devono ancora incontrarsi. */
.sl-consegna-apri { width: 100%; }
.sl-consegna-apri > summary { display: inline-flex; cursor: pointer; list-style: none; }
.sl-consegna-apri > summary::-webkit-details-marker { display: none; }

.sl-loan-actions__annulla,
.sl-loan-row__annulla { margin-inline-start: auto; }

/* Le azioni stanno su una riga PROPRIA a ogni larghezza (`"actions actions"` della
   griglia base): a piena larghezza la barra della consegna ha lo spazio per stare in
   linea, e il contenuto non divide mai la riga con loro.
   ⛔ Non rimetterle in una terza colonna accanto al contenuto. E' stato provato il 01-09
   e non regge: `grid-template-columns: auto 1fr auto` fa crescere la traccia delle azioni
   fino al suo max-content nel passo "maximize tracks" dell'algoritmo di grid — passo che
   per le tracce `fr` e' SALTATO, quindi l'fr prende solo cio' che avanza. Su una riga
   `accepted` valeva 698px dei 786 disponibili: la colonna del contenuto restava a 0px e
   titolo/meta si spappolavano una parola per riga sopra i bottoni. Limitare la traccia
   (`fit-content()`) toglieva lo sfondamento ma lasciava la barra della consegna in una
   colonna troppo stretta e mezza card vuota a sinistra: il layout giusto e' questo. */
@media (min-width: 860px) {
  .sl-loan-row__cover-img,
  .sl-loan-row__cover-cloth { width: 56px; height: 84px; }
  .sl-loan-actions__annulla,
  .sl-loan-row__annulla { margin-inline-start: 0; }
}
}

/* =========================================================================
   Redisegno Conversazione (2026-09-01) — fuori milestone v2.3
   =========================================================================
   Tre zone verticali invece di un flusso di paragrafi: intestazione appiccicata,
   thread che scorre, composer in basso. ⛔ Tutto resta DENTRO `.sl-chat`, che è la
   radice di scoping di chat-msn.js (inventario §12).
   ========================================================================= */

/* `.sl-visually-hidden` (label del composer, leggibile agli screen reader ma non a
   schermo, D-14) NON è ridefinita qui: esiste già in base.css:1472. Ridichiararla
   sarebbe esattamente il duplicato che questa milestone ha passato il tempo a togliere. */

.sl-chat {
  display: flex;
  flex-direction: column;
  max-width: 720px;
  margin-inline: auto;
}

/* --- Intestazione: UNA riga, appiccicata in alto ------------------------- */
.sl-chat__header {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 64px;
  padding: var(--space-2) 0;
  margin-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-paper);
}
.sl-chat__back,
.sl-chat__menu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;          /* target touch (A11Y-01) */
  flex: 0 0 auto;
  border-radius: var(--radius-md);
  color: var(--color-text-warm);
  cursor: pointer;
  list-style: none;                    /* <summary>: via il triangolino */
}
.sl-chat__menu-btn::-webkit-details-marker { display: none; }
.sl-chat__ident { flex: 1 1 auto; min-width: 0; }
.sl-chat__who {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  font-weight: 600;
  margin: 0;
  color: var(--color-ink);
}
.sl-chat__last-seen {
  margin: 0;
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}
.sl-chat__menu { position: relative; flex: 0 0 auto; }
.sl-chat__menu-pane {
  position: absolute;
  right: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2);
  min-width: 220px;
  padding: var(--space-3);
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
/* La campanella dentro il menù: riga con icona + testo, non più float:right. */
.sl-chat__menu-pane .sl-chat__mute {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  float: none;
  width: 100%;
  min-height: 44px;
  text-align: start;
}

/* --- Striscia di stato del prestito -------------------------------------- */
.sl-chat__prestito {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-status-avail-bg);
  color: var(--color-status-avail-fg);
  font-size: var(--font-size-sm);
}
.sl-chat__prestito-libro { font-weight: 600; }
.sl-chat__scheda { margin-bottom: var(--space-3); }
.sl-chat__scheda-pane { padding-top: var(--space-2); }

/* --- Thread che scorre --------------------------------------------------- */
.sl-message-list {
  flex: 1 1 auto;
  overflow-y: auto;
  /* ⚠️ `svh` (viewport PICCOLA, barre visibili) con ripiego in rem per chi non lo
     supporta: con `vh` — che è la viewport grande — su iPhone la lista era più alta
     dell'area davvero visibile e il campo per scrivere finiva sotto il bordo. È la
     lezione già scritta in base.css e nella pagina 500 (audit 10-09-2026). */
  max-height: 26rem;
  max-height: 58svh;
  padding: var(--space-2) var(--space-1);
  gap: var(--space-1);                 /* dentro il gruppo: righe vicine */
  scroll-behavior: smooth;
}
/* Fra un gruppo e l'altro respira; dentro il gruppo no. */
.sl-message { margin-top: var(--space-4); }
.sl-message--seguito { margin-top: var(--space-1); }
.sl-message--seguito .sl-message__author { display: none; }
.sl-message--mine  { border-radius: var(--radius-lg) var(--radius-lg) 4px var(--radius-lg); }
.sl-message--theirs { border-radius: var(--radius-lg) var(--radius-lg) var(--radius-lg) 4px; }
.sl-message__time { font-size: 11px; }
/* "Segnala" non più sempre a vista: compare al passaggio o col focus da tastiera. */
.sl-message__report-slot { opacity: 0; transition: opacity var(--t); }
.sl-message:hover .sl-message__report-slot,
.sl-message:focus-within .sl-message__report-slot { opacity: 1; }

/* Separatore di giorno: una riga sottile con l'etichetta al centro. */
.sl-message-day {
  align-self: center;
  margin: var(--space-4) 0 var(--space-2);
  padding: 2px var(--space-3);
  border-radius: 20px;
  background: var(--color-status-attesa-bg);
  color: var(--color-status-attesa-fg);
  font-size: var(--font-size-sm);
  list-style: none;
}

/* --- Composer: una riga sola -------------------------------------------- */
.sl-chat__composer {
  position: sticky;
  bottom: 0;
  z-index: 2;
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  padding: var(--space-3) 0;
  background: var(--color-paper);
  border-top: 1px solid var(--color-border);
}
.sl-chat__composer .sl-send-form { flex: 1 1 auto; min-width: 0; }
.sl-send-form__form {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
}
.sl-send-form__textarea {
  flex: 1 1 auto;
  min-height: 44px;
  resize: none;                        /* via la maniglia: cresce da sola */
  overflow-y: hidden;
}
.sl-send-form__invia,
.sl-chat__nudge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  flex: 0 0 auto;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background: var(--color-paper-light);
  color: var(--color-text-warm);
  cursor: pointer;
}
.sl-send-form__invia {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--primary-contrast);
}

@media (min-width: 860px) {
  .sl-chat__back { display: none; }    /* su desktop c'è la nav, la freccia è di troppo */
  .sl-message { max-width: 68%; }
  .sl-message-list { max-height: 28rem; max-height: 62svh; }
}
@media (max-width: 859px) {
  .sl-message { max-width: 78%; }
}

/* La ricevuta di lettura del PROPRIO ultimo messaggio ("Consegnato" / "Letto · 14:32").
   Prima galleggiava come una label orfana sopra "Messaggio", e sembrava — a ragione —
   un'etichetta di form finita fuori posto. NON è lo stato del prestito: quello sta nella
   striscia sotto l'intestazione. È la ricevuta del thread, quindi si legge come tale:
   allineata a destra sotto l'ultima bolla, minuscola, attaccata al thread e staccata dal
   composer. L'id e lo `hx-swap-oob` restano intatti — il poll la aggiorna. */
.sl-read-status {
  align-self: flex-end;
  margin: calc(var(--space-1) * -1) 0 var(--space-2);
  padding-inline-end: var(--space-2);
  text-align: end;
  font-size: 11px;
  color: var(--color-text-muted);
}
.sl-read-status:empty { display: none; }


/* ==========================================================================
   Home 2026-09-10 — ornamento hero, striscia di garanzie, banda "Novità",
   teaser guide. Vale per ENTRAMBE le home: la pubblica anonima
   (core/public_home.html) e lo Scaffale autenticato (core/geoping.html).

   Perché esiste. Le funzioni arrivate fra agosto e settembre (i punti sulla
   mappa, il passaggio di mano a due conferme, il caricamento in sequenza,
   l'accesso col codice) sono documentate nelle guide, ma nessuna delle due
   home le nominava: chi entra ogni giorno non ha modo di scoprire che ci
   sono. La banda "Novità" è quel modo, ed è la stessa su entrambe le
   superfici — un solo blocco di stile, due inclusioni.

   Vincoli rispettati: nessun `style=` in template (v2.3), nessun host esterno
   (NO-CDN), icone SVG inline e mai emoji (il gate `test_icone_non_emoji`),
   e sulla superficie anonima nessun dato che non sia già pubblico.
   ========================================================================== */

/* --- Ornamento: le costole di uno scaffale, sotto la hero ------------------
   Decorazione pura (`aria-hidden`), non un'immagine: un SVG inline non è una
   richiesta di rete in più, scala senza sfocare e prende i colori dai token
   delle categorie, così lo scaffale disegnato ha gli stessi otto colori dei
   pallini delle chip e dei pin sulla mappa. */
.sl-shelf-art {
  display: block;
  width: 100%;
  max-width: 560px;
  height: auto;
  margin: var(--space-5) auto 0;
}
.sl-shelf-art__plank {
  fill: var(--color-border);
}
/* Stessa tabella `data-cat` dei pallini delle chip (vedi sopra): presentazionale,
   nessun JS la legge. Qui colora il riempimento delle costole. */
.sl-shelf-art__spine[data-cat="narrativa"] { fill: var(--cat-narrativa); }
.sl-shelf-art__spine[data-cat="saggistica"] { fill: var(--cat-saggistica); }
.sl-shelf-art__spine[data-cat="fumetto"] { fill: var(--cat-fumetto); }
.sl-shelf-art__spine[data-cat="ragazzi"] { fill: var(--cat-ragazzi); }
.sl-shelf-art__spine[data-cat="poesia"] { fill: var(--cat-poesia); }
.sl-shelf-art__spine[data-cat="gialli"] { fill: var(--cat-gialli); }
.sl-shelf-art__spine[data-cat="filosofia"] { fill: var(--cat-filosofia); }
.sl-shelf-art__spine[data-cat="arte"] { fill: var(--cat-arte); }
/* Il filetto chiaro sulla costola: è quello che fa leggere il disegno come
   libri e non come un istogramma. Opacità bassa, mai un secondo colore. */
.sl-shelf-art__rule {
  fill: var(--color-paper-light);
  opacity: 0.55;
}

/* --- Striscia di garanzie sotto la hero ------------------------------------
   Tre affermazioni corte, non tre slogan: rispondono alle tre domande che una
   persona si fa davanti a un sito di prestito fra sconosciuti (quanto costa,
   cosa vedono di me, devo spedire qualcosa). Sta in riga su desktop e va a capo
   su telefono senza diventare una lista puntata. */
.sl-trust {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* Colonna stretta: la hero è larga 720px e le tre voci ci stanno in riga solo con
     questo passo. Con --space-5 andavano a capo 2+1, che su uno schermo largo si legge
     come un errore di impaginazione. */
  gap: var(--space-3) var(--space-4);
  margin: var(--space-5) 0 0;
  padding: 0;
  list-style: none;
}
.sl-trust__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-text-warm);
}
.sl-trust__icon {
  flex: 0 0 auto;
  color: var(--color-secondary);
}

/* --- Banda "Novità" --------------------------------------------------------
   L'unico blocco scuro delle due home. Serve a due cose insieme: spezzare la
   crema che altrimenti scorre dall'inizio alla fine della pagina, e dare alle
   funzioni nuove un posto che si vede senza essere una notifica.

   NON è a tutta larghezza: `.sl-container` resta l'unico width-owner della home
   pubblica (D-03), quindi la banda è un pannello dentro la colonna, non un
   full-bleed con margini negativi che sfonderebbero il gutter a 320px. */
.sl-novita {
  background: var(--color-secondary);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-5);
  /* Margine inferiore più largo di quello superiore: sullo Scaffale la banda è l'ultima
     sezione della pagina e il footer è dello STESSO verde bottiglia. Con un margine
     simmetrico i due blocchi si leggevano come un'unica macchia scura interrotta da una
     riga di carta; a 48px la banda torna a essere una scheda appoggiata sulla pagina. */
  margin: var(--space-6) 0 var(--space-7);
}
.sl-novita__eyebrow {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-kicker);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-ocra);
}
.sl-novita__head {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  font-weight: 600;
  margin: 0 0 var(--space-2);
  color: var(--color-paper);
}
.sl-novita__head em {
  font-style: italic;
  color: var(--color-ocra);
}
.sl-novita__intro {
  margin: 0 0 var(--space-5);
  max-width: 60ch;
  font-size: var(--font-size-sm);
  line-height: var(--line-height-base);
  /* Crema al 78%: il testo di servizio non deve competere col titolo, ma su
     #2C4A3B resta ben oltre 4.5:1 (il fondo è scuro, non è il caso in cui
     l'opacità mangia il contrasto). */
  color: rgba(244, 235, 218, 0.82);
}
.sl-novita__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* La card È il link: area di tocco piena, niente "leggi tutto" da centrare col
   dito. Stessa DNA di `.sl-featured-card` — hover di solo transform (D-24). */
.sl-novita-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  height: 100%;
  padding: var(--space-4);
  background: var(--color-paper-light);
  border-radius: var(--radius-md);
  text-decoration: none;
  color: var(--color-ink);
  transition: transform var(--t), box-shadow var(--t);
}
.sl-novita-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-card);
}
.sl-novita-card:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 3px;
}
.sl-novita-card__icon {
  color: var(--color-primary);
}
.sl-novita-card__title {
  font-family: var(--font-serif);
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.25;
  margin: 0;
}
.sl-novita-card__body {
  margin: 0;
  font-size: var(--font-size-sm);
  line-height: var(--line-height-base);
  color: var(--color-text-warm);
}
/* "Leggi la guida →" non è un secondo link (sarebbe un link dentro un link):
   è l'ultima riga della card, che il colore terracotta fa leggere come tale. */
.sl-novita-card__more {
  margin: auto 0 0;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-primary);
}
.sl-novita__all {
  margin: var(--space-5) 0 0;
  text-align: center;
}
.sl-novita__all a {
  font-weight: 600;
  color: var(--color-ocra);
  text-decoration: underline;
  text-underline-offset: 3px;
  /* 44px di altezza minima: è un link isolato in mezzo al vuoto, cioè il caso
     in cui il pollice sbaglia (WCAG 2.5.5). */
  display: inline-block;
  min-height: 44px;
  line-height: 44px;
}
.sl-novita__all a:hover {
  color: var(--color-paper);
}
.sl-novita__all a:focus-visible {
  outline: 2px solid var(--color-ocra);
  outline-offset: 3px;
  border-radius: 2px;
}

/* --- Teaser delle guide ----------------------------------------------------
   Prima le tre guide fondamentali riusavano `.sl-how-step`, cioè lo stile dei
   tre passi: la stessa card diceva due cose diverse (un passo da fare / una
   pagina da leggere) e la seconda sezione sembrava una ripetizione della prima.
   Qui hanno una forma loro — filetto ocra a sinistra, come il taglio di un
   libro — e restano riconoscibili come lettura, non come funzione. */
.sl-guide-teaser {
  max-width: 1080px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5);
}
.sl-guide-teaser__head {
  font-family: var(--font-serif);
  font-size: var(--font-size-h2);
  font-weight: 600;
  text-align: center;
  margin: 0 0 var(--space-2);
}
.sl-guide-teaser__intro {
  max-width: 60ch;
  margin: 0 auto var(--space-5);
  text-align: center;
  color: var(--color-text-warm);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-base);
}
.sl-guide-teaser__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}
.sl-guide-teaser-card {
  display: block;
  height: 100%;
  padding: var(--space-4);
  background: var(--color-paper-light);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-ocra);
  border-radius: 4px var(--radius-md) var(--radius-md) 4px;
  text-decoration: none;
  color: var(--color-ink);
  transition: transform var(--t), box-shadow var(--t);
}
.sl-guide-teaser-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-card);
}
.sl-guide-teaser-card:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 3px;
}
.sl-guide-teaser-card__title {
  font-family: var(--font-serif);
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.25;
  margin: 0 0 var(--space-2);
}
.sl-guide-teaser-card__body {
  margin: 0;
  font-size: var(--font-size-sm);
  line-height: var(--line-height-base);
  color: var(--color-text-warm);
}
.sl-guide-teaser__all {
  margin: var(--space-4) 0 0;
  text-align: center;
  font-weight: 600;
}
.sl-guide-teaser__all a {
  color: var(--color-primary);
  display: inline-block;
  min-height: 44px;
  line-height: 44px;
}

/* --- Desktop: le griglie si aprono ---------------------------------------- */
@media (min-width: 860px) {
  .sl-novita {
    padding: var(--space-7) var(--space-6);
  }
  .sl-novita__grid {
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-4);
  }
  .sl-guide-teaser__grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
/* Fascia intermedia (tablet in verticale): quattro card su una riga sono
   strette al punto che il titolo va su tre righe. Due e due. */
@media (min-width: 560px) and (max-width: 859px) {
  .sl-novita__grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .sl-guide-teaser__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* --- Moto: rispetta la preferenza di sistema (D-24) ----------------------- */
@media (prefers-reduced-motion: reduce) {
  .sl-novita-card,
  .sl-guide-teaser-card {
    transition: none;
  }
}
