/* =============================================================================
   SPINA · BASE COMUNE — le primitive che ogni demo eredita (v2, 24/08/2026)

   Non e' un tema e non decide come una pagina e' fatta: decide come si COMPORTANO
   i pezzi che tutte le pagine hanno. La composizione la sceglie l'archetipo, il
   colore e i font il token file della demo. Qui c'e' solo il meccanismo.

   Va importata DOPO tokens.css e PRIMA del CSS della demo, cosi' la demo puo'
   sovrascrivere i colori senza poter sovrascrivere per sbaglio le regole dure.

   Copre — vedi contract/MATRICE-v2.md sezione A:
     DUR-13/148 hover fluidi che non spostano il controllo
     DUR-38     marker di titolo per-riga (mai una sbarratura quando va a capo)
     DUR-68     recapiti cliccabili
     DUR-72     testo verticale laterale, sinistra ruotato a favore di lettura
     DUR-147    reveal che parte DENTRO la viewport
     DUR-156    logo scontornato staccato dal fondo
     DUR-167    cursore custom
     DUR-192/193 il movimento lo comanda lo scroll, con la corsa dei token
     DUR-199/200/201 il telefono: altezza vera, spazio sicuro, campi a 16px
     + banner cookie in basso a SINISTRA (decreto 10/08)
   ============================================================================= */

/* ---------- il telefono, senza pensarci ---------- */
input, select, textarea, button { font-size: var(--fs-field); font-family: inherit; }
body { -webkit-text-size-adjust: 100%; }
.spina-fixed-bottom { padding-bottom: calc(var(--sp-3) + var(--safe-b)); }

/* ---------- hover: cambia il colore, non la posizione (DUR-13) ---------- */
a, button, .btn, [role="button"] {
  transition: color var(--t-hover) var(--ease),
              background-color var(--t-hover) var(--ease),
              border-color var(--t-hover) var(--ease),
              box-shadow var(--t-hover) var(--ease),
              opacity var(--t-hover) var(--ease);
}
.btn, [role="button"] { padding: var(--pad-control-y) var(--pad-control-x); }

/* ---------- recapiti cliccabili (DUR-68) ----------
   La primitiva JS li trasforma in link; qui si assicura solo che un recapito
   linkato non sembri un link di navigazione a caso in mezzo alla prosa. */
a[href^="tel:"], a[href^="mailto:"], a[href*="wa.me"] { text-decoration-thickness: 1px; text-underline-offset: .18em; }
.spina-canale { white-space: nowrap; }

/* ---------- marker di titolo per riga (DUR-38) ----------
   Un ::after posizionato sul box diventa una SBARRATURA appena il titolo va a capo.
   Il background con box-decoration-break segue ogni riga, sempre. */
.spina-marker {
  background-image: linear-gradient(transparent 62%, var(--em, currentColor) 62%, var(--em, currentColor) 92%, transparent 92%);
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
  padding-inline: .08em;
}

/* ---------- testo verticale laterale (DUR-72) ----------
   Il laterale di SINISTRA e' ruotato 180 gradi: si legge dal basso verso l'alto,
   a favore di lettura. Quello di destra no. E' l'errore che si ripete a mano. */
.spina-vert { writing-mode: vertical-rl; letter-spacing: .18em; text-transform: uppercase; font-size: var(--fs-small); }
.spina-vert--left { transform: rotate(180deg); }

/* ---------- logo scontornato che si stacca dal fondo (DUR-156) ---------- */
.spina-logo-alpha { filter: drop-shadow(0 1px 2px rgba(0,0,0,.28)); }

/* ---------- reveal: enhancement-only, comandato dallo scroll (DUR-147/192/193) ----
   Senza JS resta tutto visibile: la classe .js la mette lo script in <head>.
   La corsa e la rotazione vengono dai token, non dalla fantasia della demo. */
.js .spina-reveal { opacity: 0; transform: translateY(var(--mov-corsa)) rotate(0deg); will-change: opacity, transform; }
.js .spina-reveal.is-in { opacity: 1; transform: none; transition: opacity var(--t-reveal) var(--ease), transform var(--t-reveal) var(--ease); }
.js .spina-reveal--tilt { transform: translateY(var(--mov-corsa)) rotate(var(--mov-rot-max)); }

/* ---------- desincrono degli elementi ripetuti ----------
   La primitiva che uccide l'effetto copia-incolla: dieci loghi che scorrono
   ALL'UNISONO si leggono come un template. Velocita' 0,9-1,1 e ritardo diverso
   per istanza li rendono un movimento organico. Il JS scrive le due variabili;
   qui vivono i default, cosi' senza JS non si rompe niente. */
[data-spina-desync] > * { --desync-speed: 1; --desync-delay: 0s; }
.spina-drift {
  animation: spina-drift calc(var(--drift-dur, 22s) / var(--desync-speed)) linear infinite;
  animation-delay: var(--desync-delay);
}
@keyframes spina-drift { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .spina-drift { animation: none; } }

/* ---------- cursore custom (DUR-167) ----------
   Enhancement-only: esiste solo su puntatore fine, non copre mai i target
   (pointer-events:none) e sparisce con reduced-motion. */
/* DUR-208 (#1295, 01/09) — il cursore custom sta SOPRA i widget. Il widget WhatsApp
   e' a z-index 2147483000 (lo porta smarti-widgets.js): con un valore piu' basso il
   cursore ci finiva dietro e, siccome sul body c'e' cursor:none, passando sul widget
   il puntatore SPARIVA. Si alza il cursore, il widget non si tocca. */
.spina-cursor { position: fixed; top: 0; left: 0; z-index: 2147483001; pointer-events: none; opacity: 0;
  width: 34px; height: 34px; margin: -17px 0 0 -17px; border-radius: var(--r-round);
  border: 1.5px solid var(--brand, currentColor);
  transition: opacity var(--t-hover) var(--ease), width var(--t-micro) var(--ease), height var(--t-micro) var(--ease), background-color var(--t-hover) var(--ease); }
.spina-cursor.is-on { opacity: .75; }
.spina-cursor.is-grow { width: 54px; height: 54px; margin: -27px 0 0 -27px; background: color-mix(in srgb, var(--brand, #000) 12%, transparent); }
@media (pointer: coarse), (prefers-reduced-motion: reduce) { .spina-cursor { display: none; } }

/* ---------- scroll-cue: l'invito a scendere (task #2100, 05/09) ----------
   Spento di default; lo accende window.SMARTI.scrollCue = true e lo costruisce spina.js.
   Sta qui e non nelle demo per la stessa ragione del cursore: una primitiva riscritta a
   mano ogni volta non arriva, e sulle 6 demo nate dopo il 27/08 non e' arrivata mai. */
.spina-scroll-cue {
  /* --cue-alza lo scrive spina.js MISURANDO cosa occupa gia' il fondo della prima
     schermata (barretta recensioni, CTA fisse). Default 0: senza JS il cue non c'e'
     affatto, quindi la variabile serve solo quando il cue esiste davvero. */
  position: absolute; left: 50%;
  bottom: calc(var(--sp-4) + var(--safe-b, 0px) + var(--cue-alza, 0px));
  transform: translateX(-50%); z-index: 3;
  opacity: 0; pointer-events: none;
  transition: opacity var(--t-reveal) var(--ease);
}
/* la hero non e' un contenitore posizionato: spina.js NON le cambia il CSS, ripiega qui. */
.spina-scroll-cue.is-fixed { position: fixed; }
.spina-scroll-cue.is-on { opacity: .9; }
/* Colore: `currentColor`, EREDITATO dalla hero — non `--brand`. Motivo misurato il 05/09 su
   giuliapapes-it-v2: la hero e' una foto chiara, `--brand` e' il verde scuro del marchio e la
   riga da 1px a .65 di opacita' non si vedeva nello scatto. `currentColor` e' il colore con cui
   la hero scrive gia' il suo h1: se il titolo si legge, si legge anche il cue — su una hero
   scura come su una chiara, senza che nessuno debba sceglierlo demo per demo.
   La drop-shadow serve al caso opposto: testo chiaro su foto chiara. */
.spina-scroll-cue-line {
  display: block; width: 2px; height: 46px; border-radius: 2px; transform-origin: top center;
  background: linear-gradient(to bottom, transparent, currentColor);
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .35));
  animation: spina-cue 1.9s var(--ease) infinite;
}
@keyframes spina-cue {
  0%   { transform: scaleY(0); opacity: 0; }
  30%  { opacity: 1; }
  100% { transform: scaleY(1); opacity: 0; }
}
/* Fermo, non assente: chi ha chiesto meno movimento vede comunque dove si scende. */
@media (prefers-reduced-motion: reduce) {
  .spina-scroll-cue-line { animation: none; opacity: .6; }
}

/* ---------- banner cookie: SEMPRE in basso a SINISTRA (decreto 10/08) ----------
   Il basso a destra e' occupato dal widget WhatsApp canonico: il banner non ci
   finisce mai sopra, ne' su desktop ne' su telefono. */
.spina-cookie {
  position: fixed; z-index: var(--z-cookie);
  left: calc(var(--sp-3) + var(--safe-l));
  /* --cookie-alza lo scrive spina.js MISURANDO cosa c'e' gia' davanti al banner: la
     barretta recensioni M7 (DUR-78, absolute in basso a sinistra dentro la hero) e — dal
     25/08, P0 di Federico — LA PRIMA AZIONE DELLA HERO, che su telefono il banner copriva
     al 100% su tre demo su tre. Due mobili canonici che si contendono lo stesso angolo non
     si risolvono a occhio una demo alla volta: si misurano.
     Default 0, cosi' senza JS il banner sta dove il CSS dice. `--consenso-h`, sempre scritta
     da spina.js, dice quanto spazio dal fondo si sta prendendo il consenso: serve al gate e
     puo' servire a una demo che voglia lasciargli respiro. */
  bottom: calc(var(--sp-3) + var(--safe-b) + var(--cookie-alza, 0px));
  width: min(23rem, calc(100vw - 2 * var(--sp-3)));
  background: var(--surface, #fff); color: var(--ink, #16181a);
  border-radius: var(--r-1); padding: var(--sp-4);
  box-shadow: 0 10px 34px rgba(0,0,0,.16); font-size: var(--fs-small); line-height: 1.5;
  transform: translateY(calc(100% + var(--sp-6))); opacity: 0;
  transition: transform var(--t-reveal) var(--ease), opacity var(--t-reveal) var(--ease);
}
.spina-cookie.is-on { transform: none; opacity: 1; }
.spina-cookie p { margin: 0 0 var(--sp-3); }
.spina-cookie-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.spina-cookie button {
  padding: var(--pad-control-y) var(--pad-control-x); border-radius: var(--r-2);
  border: 1px solid color-mix(in srgb, currentColor 26%, transparent);
  background: transparent; color: inherit; cursor: pointer; font-size: var(--fs-small);
}
.spina-cookie button[data-cc="accetta"] { background: var(--brand, #16181a); color: var(--surface, #fff); border-color: transparent; }
.spina-cookie-link { background: none; border: 0; padding: 0; text-decoration: underline; cursor: pointer; }
/* il bollino che resta dopo la scelta: stessa colonna, mai sotto il widget WA */
.spina-cookie-badge {
  position: fixed; z-index: var(--z-float);
  left: calc(var(--sp-3) + var(--safe-l)); bottom: calc(var(--sp-3) + var(--safe-b));
  width: 38px; height: 38px; border-radius: var(--r-round); border: 0; cursor: pointer;
  background: var(--surface, #fff); box-shadow: 0 4px 16px rgba(0,0,0,.16);
  display: grid; place-items: center; opacity: .72;
}
.spina-cookie-badge:hover, .spina-cookie-badge:focus-visible { opacity: 1; }
/* Su telefono il banner e' largo quanto lo schermo, e sotto a destra c'e' il widget
   WhatsApp: a 390px si sovrapponevano (misurato, non temuto — il banner arrivava a x=374,
   la bolla parte da x=310). Il decreto del 10/08 dice «basso a sinistra» proprio per non
   finire sotto la bolla: su mobile la sinistra da sola non basta, serve anche SOPRA.
   La bolla e' 60px a 20px dal fondo: il banner parte da 96px e la scavalca sempre. */
@media (max-width: 560px) {
  .spina-cookie {
    width: calc(100vw - 2 * var(--sp-3));
    /* 5rem = la bolla WhatsApp (60px a 20px dal fondo), scavalcata sempre. E' una BASE,
       non un massimo: dal 25/08 `--cookie-alza` la SOMMA invece di gareggiarci dentro un
       max(). Con il max() le prime correzioni del giro misura-correggi (quelle sotto gli
       80px) non muovevano niente e il banner restava sopra la CTA: la variabile c'era, il
       banner non si spostava. Additivo, il giro converge e si verifica da solo. */
    bottom: calc(var(--sp-3) + var(--safe-b) + 5rem + var(--cookie-alza, 0px));
    /* IL CONSENSO NON SI PRENDE MEZZO SCHERMO. Misurato il 25/08 a 320x568: il banner era
       alto 259px su 568, il 46% della pagina. Anche sgombrando la CTA, un pannello cosi'
       seppellisce la hero. Qui si stringe (respiro, corpo, interlinea) e si mette un tetto
       con scorrimento interno: il testo resta INTERO, non si accorcia una parola. */
    padding: var(--sp-3);
    /* 14px e' un PAVIMENTO, non un gusto: e' la soglia sotto cui `mobile_check.py` considera
       il testo illeggibile (TESTO-1, clausola della coda). Al primo giro questo valore era
       13,5px e la coda di autofficinacausio passava da 3,1% a 6,5%: la riparazione del
       consenso creava un rosso nuovo su un'altra misura. Il grosso della compattazione lo
       fanno respiro e interlinea, non il corpo. */
    font-size: max(0.875rem, 14px);
    line-height: 1.42;
    max-height: 40svh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  .spina-cookie p { margin: 0 0 var(--sp-2); }
  .spina-cookie-row { gap: var(--sp-2); flex-wrap: nowrap; }
  .spina-cookie button { flex: 1 1 auto; white-space: nowrap; }
}
