/*
 * Console — design system "night ops" (Cockpit360).
 * Usa SOLO i token iniettati da @saas/config (var(--...)). Per cambiare
 * l'aspetto, modifica packages/config/src/theme.ts.
 */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

body {
  font-family: var(--font-sans);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  letter-spacing: 0.005em;
  min-height: 100vh;
  /* PAROLE CHE NON SI SPEZZANO. Nel foglio non c'era una sola protezione globale: una stringa
     lunga e senza spazi — uno slug, un'email di lavoro, una URL incollata, un nome composto —
     conta per intero nel `min-content` del suo contenitore e lo allarga OLTRE la finestra.
     Misurato: 99 caratteri attaccati portano un blocco a 348px dentro un viewport da 305, e a
     quel punto è la PAGINA a sfondare, non il testo. `anywhere` (non `break-word`) perché solo
     `anywhere` azzera anche il contributo al min-content, che è ciò che fa cedere flex e grid.
     Non tocca il testo normale: spezza solo quando l'alternativa è uscire dal contenitore. */
  overflow-wrap: anywhere;
}
/* I contenitori che scorrono di proposito (tabelle larghe, codice) non devono spezzare le
   parole: lì la barra di scorrimento è il comportamento voluto. */
.adm-tablewrap, .rt-table-wrap, pre { overflow-wrap: normal; }
/* ---------- Coppie tipografiche (@saas/config → FONT_PAIRS) ----------
   I font sono SELF-HOSTED da next/font (app/layout.tsx), che pubblica una var per font
   sull'<html>. Qui le componiamo nelle COPPIE che il brand può scegliere: il bundle salva
   solo la CHIAVE e `buildBrandStyle` emette
       --font-sans:    var(--font-pair-<key>-body,    <stack di sistema>)
       --font-display: var(--font-pair-<key>-display, var(--font-sans))
   Il motore resta zero-dipendenze: conosce la CONVENZIONE, non il catalogo. Una chiave
   senza alias qui → il var() cade sullo stack di sistema (degrada, non rompe). */
:root {
  --font-pair-poppins-display: var(--font-poppins);
  --font-pair-poppins-body: var(--font-poppins);
  --font-pair-inter-display: var(--font-inter);
  --font-pair-inter-body: var(--font-inter);
  --font-pair-editorial-display: var(--font-playfair);
  --font-pair-editorial-body: var(--font-source-sans);
  --font-pair-tech-display: var(--font-space-grotesk);
  --font-pair-tech-body: var(--font-inter);
  --font-pair-elegant-display: var(--font-dm-serif);
  --font-pair-elegant-body: var(--font-dm-sans);
}

/* ---------- PARITÀ DI TOKEN fra la chrome di piattaforma e `.saas-ui` ----------
   `@saas/config/theme` veste la console (`:root`); il design system "Refined Enterprise"
   veste i SaaS generati (`.saas-ui`). Il secondo dichiara una scala di elevazioni, tint e
   "ink" semantici che il primo non aveva mai avuto — e i componenti CONDIVISI fra i due
   mondi (modale di conferma, toast, palette ⌘K, editor email a blocchi) la usano.
   Fuori da `.saas-ui` quei var() cadevano nel vuoto: niente ombra, niente raggio, niente
   anello di focus. Non è un dettaglio estetico, è la modale della console che si vedeva
   IN LINEA nella pagina invece che sopra uno scrim.
   Qui si colma il buco, derivando TUTTO dai token che `:root` già pubblica (--accent,
   --text, --good/--warn/--crit): un solo contratto di token, due vestiti. Nessuna di
   queste è ridichiarata da `themeCss()`, quindi non c'è conflitto di cascata; dentro
   `.saas-ui` vincono comunque i valori del design system, che le ridefinisce su di sé. */
:root {
  --accent-tint:   color-mix(in srgb, var(--accent) 9%, transparent);
  --accent-tint-2: color-mix(in srgb, var(--accent) 15%, transparent);
  --focus-ring:    color-mix(in srgb, var(--accent) 30%, transparent);
  --info: #2563eb;

  --good-tint: color-mix(in srgb, var(--good) 12%, transparent);
  --warn-tint: color-mix(in srgb, var(--warn) 14%, transparent);
  --crit-tint: color-mix(in srgb, var(--crit) 12%, transparent);
  --info-tint: color-mix(in srgb, var(--info) 12%, transparent);
  /* INK = il testo scritto SOPRA il tint corrispondente: mescolato col testo per restare AA. */
  --good-ink: color-mix(in srgb, var(--good) 68%, var(--text));
  --warn-ink: color-mix(in srgb, var(--warn) 70%, var(--text));
  --crit-ink: color-mix(in srgb, var(--crit) 70%, var(--text));
  --info-ink: color-mix(in srgb, var(--info) 68%, var(--text));

  /* SCALA DEI RAGGI della chrome. Erano 13 valori letterali diversi (2,3,4,5,6,7,8,9,10,11,
     12,14,16) contro i 4 token che il tema pubblicava: due bottoni vicini potevano avere
     10px e 9px, due badge 5px e 6px — differenze che non si sanno spiegare ma si vedono.
     Cinque gradini, ognuno con un mestiere:
       2xs  targhette minime, suggerimenti tastiera, micro-badge
       xs   bottoncini quadrati (22-32px)
       sm   campi e controlli piccoli
       -    pannelli e card (`--radius`, dal tema)
       lg   modali e card grandi
       pill pillole e cerchi */
  --radius-2xs: 5px;
  --radius-xs: 7px;
  --radius-lg: 16px;
  --pill: 999px;

  /* ALTEZZA DEI CONTROLLI — un solo numero per campo e bottone.
     Prima non esisteva: `.input` veniva 42px (padding 10 + testo 13) e `.btn` 34px
     (padding 9 + testo 12,5). Messi uno accanto all'altro — ed è la disposizione più
     comune del prodotto, «campo + Salva» — erano disallineati di 8px, con il bottone
     che galleggiava a mezz'aria. La misura giusta non è né l'una né l'altra: è UNA. */
  --ctrl-h: 38px;
  --ctrl-h-sm: 32px;

  /* STATO DISABILITATO — un solo valore. Ne convivevano tre (.5 sui bottoni, .55 sui pulsanti
     icona della chat, .6 sull'interruttore delle notifiche) e due cursori diversi (`default` e
     `not-allowed`) per la stessa identica condizione. `not-allowed` è il segnale giusto: dice
     «non si può», mentre `default` sembra solo un'area morta della pagina. */
  --disabled-opacity: .5;

  /* Elevazioni: il bordo fa il lavoro, l'ombra è di supporto (stessa filosofia di .saas-ui). */
  --shadow-1: 0 1px 2px rgba(18,24,38,.05);
  --shadow-2: 0 1px 2px rgba(18,24,38,.05), 0 6px 16px -8px rgba(18,24,38,.12);
  --shadow-3: 0 2px 4px rgba(18,24,38,.06), 0 18px 44px -14px rgba(18,24,38,.22);
}
:root[data-theme="dark"] {
  --accent-tint:   color-mix(in srgb, var(--accent) 22%, transparent);
  --accent-tint-2: color-mix(in srgb, var(--accent) 30%, transparent);
  --focus-ring:    color-mix(in srgb, var(--accent) 45%, transparent);
  --info: #5a9bff;
  /* Sui fondi scuri l'ink si schiarisce verso il bianco, o sparisce nel tint. */
  --good-ink: color-mix(in srgb, var(--good) 45%, #fff);
  --warn-ink: color-mix(in srgb, var(--warn) 50%, #fff);
  --crit-ink: color-mix(in srgb, var(--crit) 55%, #fff);
  --info-ink: color-mix(in srgb, var(--info) 45%, #fff);
  --shadow-1: 0 1px 2px rgba(0,0,0,.40);
  --shadow-2: 0 1px 2px rgba(0,0,0,.40), 0 8px 24px -8px rgba(0,0,0,.55);
  --shadow-3: 0 2px 6px rgba(0,0,0,.5), 0 24px 60px -16px rgba(0,0,0,.7);
}

/* Tracking UNICO dei titoli (richiesta Luigi) — il valore vive in @saas/config/theme
   (`--tracking-title`), non ricopiato qui: un token, un posto solo da toccare.
   Perché funzioni davvero sono stati rimossi i letter-spacing ad hoc sugli heading
   (-0.01/-0.02/-0.03, sia in CSS sia inline): erano più specifici e avrebbero reso questa
   regola inerte. Restano SOLO i tracking POSITIVI voluti sulle micro-label maiuscole
   (.eyebrow, .site-price-name, th…), che li richiedono.
   `--font-display` = font dei TITOLI della coppia scelta; senza coppia eredita il body —
   ed è così che la PIATTAFORMA resta su Poppins: non pubblica `--font-display`. */
h1, h2, h3, h4, h5, h6 { font-family: var(--font-display, var(--font-sans)); letter-spacing: var(--tracking-title); }

a { color: inherit; text-decoration: none; }
/* ---------- Link DENTRO IL TESTO ----------
   `a { color: inherit }` è giusto per i link-contenitore (voci di menu, card, tab: si vestono
   da soli). Ma nella PROSA lasciava «Password dimenticata?» e «Registrati» identici alla frase
   che li circonda: nessun colore, nessuna sottolineatura, nessun modo di sapere che si clicca.
   Qui si veste solo il link testuale — dentro un paragrafo, un sottotitolo, una nota, un
   avviso — escludendo tutto ciò che è già un componente con un aspetto suo. La sottolineatura
   è tenue a riposo e piena al passaggio: si vede che è un link senza rumore. */
:is(p, .sub, .adm-note, .banner, .field-hint, .rt-empty, li) > a:not(.btn):not([class]),
:is(p, .sub, .adm-note, .banner, .field-hint, .rt-empty, li) > a.faint {
  color: var(--accent-ink);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, currentColor 32%, transparent);
  transition: color var(--dur-fast) var(--ease), text-decoration-color var(--dur-fast) var(--ease);
}
:is(p, .sub, .adm-note, .banner, .field-hint, .rt-empty, li) > a:not(.btn):not([class]):hover,
:is(p, .sub, .adm-note, .banner, .field-hint, .rt-empty, li) > a.faint:hover {
  color: var(--accent);
  text-decoration-color: currentColor;
}
button { font: inherit; border: 0; background: none; cursor: pointer; color: inherit; }
/* I campi NON ereditano il font dal documento: senza questo un <input> userebbe il font
   del browser (Arial) invece di Poppins. `button` ce l'aveva già; mancavano gli altri —
   ed è per questo che `.input` si portava dietro un font-family esplicito (era mono).
   Con l'ereditarietà, `.input` non deve più dichiararne uno: prende Poppins dal body, e
   `.input.mono` (slug, chiavi, importi) resta monospaziato grazie alla sola `.mono`. */
input, select, textarea { font: inherit; }
::selection { background: color-mix(in srgb, var(--accent) 35%, transparent); }

.app { position: relative; min-height: 100vh; overflow-x: hidden; }

/* Atmosfera ambientale: due glow + griglia a puntini */
.bg-fx {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(900px 500px at 12% -8%, var(--glow-a), transparent 60%),
    radial-gradient(800px 600px at 108% 120%, var(--glow-b), transparent 55%),
    radial-gradient(var(--grid-dot) 1px, transparent 1px);
  background-size: auto, auto, 22px 22px;
}

/* ---------- Toggle tema chiaro/scuro (chrome di piattaforma) ----------
   Il tema CHIARO è il default (:root in @saas/config/theme.ts); lo scuro è opt-in.
   QUALE icona mostrare è deciso dalla CSS su :root[data-theme="dark"], NON dallo stato
   React: così al primo paint è già corretta (coerente con lo script anti-FOUC del layout)
   e non c'è mismatch di idratazione. Vedi components/theme-toggle.tsx. */
.theme-toggle {
  width: 32px; height: 32px; border-radius: var(--radius-sm); display: grid; place-items: center; flex: none;
  color: var(--muted); border: 1px solid var(--line-2); background: transparent;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.theme-toggle:hover { color: var(--text); background: var(--panel-2); }
/* --focus-ring esiste solo dentro .saas-ui → fallback per la chrome admin/dashboard. */
.theme-toggle:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--focus-ring, color-mix(in srgb, var(--accent) 35%, transparent));
}
.theme-toggle .ic-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .ic-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .ic-sun { display: block; }

/* ---------- Tipografia / utility ---------- */
/* ---------- Monospazio: SOLO dove serve davvero ----------
   Regola (richiesta Luigi: "Poppins per i titoli e per i testi"): il monospazio non è
   un'estetica, è uno STRUMENTO. Resta dove il carattere a larghezza fissa fa un lavoro:
   incolonnare cifre (tabelle, saldi, orari) e rendere leggibile ciò che si copia a mano
   (slug, URL, chiavi, id). Tutto ciò che si legge come LINGUA — titoli, titoletti,
   etichette, testi, campi — è Poppins.
   Prima il mono era addosso ai titoletti (.eyebrow, .rail-label, th, .field-label…): la
   console parlava in due caratteri e i "titoletti" sembravano di un'altra app. */
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.muted { color: var(--muted); }
.faint { color: var(--faint); }
.gradient-text {
  background: linear-gradient(120deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.eyebrow {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* --accent-ink, non --accent: nel workspace --accent è quello del BRAND del SaaS e
     può essere chiarissimo (smeraldo → 2.26 su fondo chiaro). L'ink resta di brand ma
     leggibile con qualunque colore scelga il rivenditore. */
  color: var(--accent-ink, var(--accent));
}
.row { display: flex; align-items: center; gap: 10px; }
.between { justify-content: space-between; }
.wrap { flex-wrap: wrap; }
.spacer { flex: 1; }
.stack { display: grid; gap: 6px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 640px) { .grid-2 { grid-template-columns: 1fr; } }

/* ---------- Layout cockpit ---------- */
.cockpit {
  position: relative;
  z-index: 1;
  display: grid;
  /* `minmax(0, 1fr)` e non `1fr`: una traccia `1fr` ha come MINIMO implicito il `min-content`
     del suo contenuto, quindi una tabella larga (`.adm-table` ha `min-width: 640px`) allargava
     la colonna OLTRE la finestra. Su telefono il risultato non era una barra di scorrimento ma
     un TAGLIO: `.app` ha `overflow-x: hidden`, quindi gli ultimi ~110px di ogni pagina della
     console — valori dei KPI, ultime colonne, campanella e avatar — sparivano e basta.
     Con `minmax(0, …)` la traccia può scendere sotto il min-content e sono i contenitori
     interni (`.adm-tablewrap`, già `overflow-x: auto`) a farsi carico del proprio scorrimento. */
  grid-template-columns: 236px minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  min-height: 100vh;
}
/* STESSA TRAPPOLA DI `.saas-shell`, e adesso riguarda anche questa cornice: da quando il
   workspace di un SaaS usa `.cockpit`, l'accent del BRAND arriva come custom property inline su
   questo elemento. Le custom property sostituiscono i `var()` NEL PUNTO IN CUI SONO DICHIARATE:
   `--accent-ink` di `:root` resterebbe legato all'accent di PIATTAFORMA, e ogni testo che lo usa
   (bolle dell'assistenza, pulsanti accesi, «Aa» dei selettori) tornerebbe azzurro dentro un SaaS
   smeraldo. Ridichiarandolo qui si ricalcola sull'accent che questo elemento porta davvero.
   Per /admin e /dashboard il valore è identico a quello di `:root` — lì `--accent` è già quello
   di piattaforma — quindi non cambia niente. */
.cockpit { --accent-ink: color-mix(in srgb, var(--accent) 58%, var(--text)); }
:root[data-theme="dark"] .cockpit { --accent-ink: color-mix(in srgb, var(--accent) 60%, #fff); }

/* ---------- Topbar ---------- */
.topbar {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 16px;
  height: 56px;
  padding: 0 20px;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--bg-2) 82%, transparent);
  backdrop-filter: blur(10px);
  position: sticky;
  top: 0;
  z-index: 6;
}
/* Il marchio può STRINGERSI, sempre — non solo sotto i 560px. Nella console il testo è
   «BuildYourSaaS» e la cosa non si nota; nel workspace è il NOME DEL SAAS, che lo scrive il
   cliente e può essere lungo quanto vuole. Con `flex: none` una ragione sociale kilometrica
   spingeva fuori dalla topbar la campanella e l'avatar. */
.brand { display: flex; align-items: center; gap: 10px; flex: 0 1 auto; min-width: 0; }
.brand > b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Il marchio: il quadratino accanto al nome. Dentro ci va l'INIZIALE — della piattaforma («B»)
   o del SaaS di cui stai guardando il pannello. Prima c'era una squadretta decorativa
   (`::after`, due lati di bordo bianco) che non voleva dire niente e, quel che è peggio, era
   IDENTICA ovunque: il marchio di BuildYourSaaS e quello del SaaS del cliente erano lo stesso
   quadratino. Con l'iniziale il marchio dice di chi è.

   Il colore della lettera è `--on-accent`, non «bianco»: nel tema scuro l'accent della console
   è un azzurro CHIARO (#4EA3FF) e il bianco ci starebbe a 2.62 — illeggibile. Nel chiaro è
   l'opposto. Il token segue il tema (e nei SaaS l'accent del BRAND, dove `--on-accent` è
   calcolato per-brand da `onAccentColor`: con uno smeraldo diventa scuro da solo).
   Il fallback #fff serve solo a non lasciare la lettera senza colore se un contesto non
   pubblica il token. */
.mark {
  width: 26px; height: 26px; border-radius: var(--radius-xs); flex: none;
  background: linear-gradient(150deg, var(--accent), var(--accent-2));
  box-shadow: 0 0 0 1px var(--line-2), 0 6px 16px -6px var(--accent-glow);
  display: grid; place-items: center;
  color: var(--on-accent, #fff);
  font-size: 13px; font-weight: 800; line-height: 1;
  /* una lettera sola non ha spaziatura: il tracking dei titoli qui la sposterebbe dal centro */
  letter-spacing: 0;
  text-transform: uppercase;
  user-select: none;
}
.brand b { font-size: 15px; letter-spacing: var(--tracking-title); font-weight: 700; }
.brand .accent { color: var(--accent); }
/* `.sysline` (il contenitore di PROVIDER/AMBIENTE nella topbar della console) è stata tolta
   insieme a quegli indicatori, e con lei il suo separatore e la media query che ne nascondeva le
   voci in eccesso: senza il markup che le portava erano regole morte.
   `.stat-inline` RESTA: la usa ancora il workspace del SaaS (saas/[slug]/page.tsx). */
.stat-inline { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--muted); white-space: nowrap; }
.stat-inline b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.top-right { margin-left: auto; display: flex; align-items: center; gap: 12px; flex: none; }
/* TOPBAR SU SCHERMO STRETTO. `.brand` e `.top-right` erano entrambi `flex: none`: nessuno dei
   due cedeva un pixel, così a 371px la riga arrivava a 462px e campanella e avatar finivano
   fuori dallo schermo — irraggiungibili, perché `.app` taglia l'overflow orizzontale.
   Ora il marchio può stringersi e troncare, il cambio-ruolo perde l'etichetta (resta il
   simbolo, col `title`), e il gruppo di destra si compatta. */
@media (max-width: 560px) {
  .topbar { gap: 10px; padding: 0 12px; }
  .brand { overflow: hidden; }
  .top-right { gap: 8px; }
  .role-switch .role-switch-label { display: none; }
  .role-switch { padding: 0 9px; }
  /* L'etichetta di contesto («Bozza») è la prima a cedere: il nome del SaaS conta di più. */
  .saas-brand-env { display: none; }
}
.clock { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.06em; color: var(--muted); font-variant-numeric: tabular-nums; }
@media (max-width: 560px) { .clock { display: none; } }
/* `.live` (la pill verde «OPERATIVA») è stata rimossa dalla topbar: era una spia sempre
   accesa che non guardava niente. Con lei se ne va il suo unico stile. */
.avatar {
  width: 30px; height: 30px; border-radius: var(--radius-sm); display: grid; place-items: center;
  font-size: 11px; font-weight: 700; color: #fff; letter-spacing: 0.02em;
  background: linear-gradient(150deg, var(--accent), var(--accent-2));
}

/* ---------- Rail (nav sinistra) ---------- */
.rail {
  border-right: 1px solid var(--line);
  padding: 16px 12px;
  display: flex; flex-direction: column; gap: 18px;
  background: color-mix(in srgb, var(--bg-2) 55%, transparent);
}
/* IL RAIL SU SCHERMO STRETTO NON SPARISCE: si corica.
   Sotto gli 860px la regola di prima era `display: none`, e la console SuperAdmin ci conviveva
   perché è uno strumento da scrivania. Da quando la stessa cornice la indossano anche la console
   del costruttore e il workspace di un SaaS — che PRIMA avevano un menu orizzontale — nascondere
   il rail vorrebbe dire togliere del tutto la navigazione a chi apre dal telefono.
   Diventa quindi una striscia orizzontale scorrevole sotto la topbar: le stesse voci, le stesse
   icone, appoggiate come il menu che c'era prima. Le etichette dei gruppi e il piede (chi sei +
   esci) restano fuori: in una riga sola sarebbero rumore, e l'uscita è comunque nel menu account.
   `grid-template-rows` va RIDICHIARATO: con una colonna sola il rail cade in riga 2 e il palco in
   una riga implicita — senza questa riga il rail si prenderebbe l'`1fr` e il contenuto l'`auto`. */
@media (max-width: 860px) {
  .cockpit { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto minmax(0, 1fr); }
  .rail {
    flex-direction: row; align-items: center; gap: 4px;
    padding: 8px 12px;
    border-right: 0; border-bottom: 1px solid var(--line);
    overflow-x: auto; overscroll-behavior-x: contain;
    position: sticky; top: 56px; z-index: 5;
    background: color-mix(in srgb, var(--bg-2) 92%, var(--bg));
    backdrop-filter: blur(10px);
  }
  /* I gruppi non impilano più: le loro voci diventano figli diretti della striscia.
     `:not(.rail-foot)` perché anche il piede è un div figlio — e `display: contents` lo
     riporterebbe in vita nonostante il `display: none` qui sotto (specificità più alta). */
  .rail > div:not(.rail-foot) { display: contents; }
  .rail-label { display: none; }
  .rail .nav { flex-direction: row; margin-top: 0; gap: 4px; }
  .rail .nav-item { white-space: nowrap; padding: 7px 11px; }
  /* La barretta d'accento sta a sinistra della voce e vive nel padding del rail: in riga
     finirebbe sopra la voce precedente. Attiva = fondo + bordo, che bastano. */
  .rail .nav-item.active::before { display: none; }
  .rail-foot { display: none; }
}
.rail-label {
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--faint);
  font-weight: 700; padding: 0 8px;
}
.nav { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; }
.nav-item {
  display: flex; align-items: center; gap: 11px; padding: 9px 9px; border-radius: var(--radius-sm);
  cursor: pointer; font-size: 13px; color: var(--muted); font-weight: 500;
  position: relative; border: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.nav-item:hover { background: var(--panel); color: var(--text); }
.nav-item.active { color: var(--text); background: var(--panel); border-color: var(--line-2); box-shadow: var(--panel-hi); }
.nav-item.active::before {
  content: ""; position: absolute; left: -12px; top: 9px; bottom: 9px; width: 3px;
  border-radius: 0 3px 3px 0; background: var(--accent); box-shadow: 0 0 12px 1px var(--accent-glow);
}
.nav-item.active .n-ic { color: var(--accent); }
.nav-item.disabled { opacity: 0.55; cursor: default; }
.nav-item .n-ic { width: 15px; height: 15px; display: grid; place-items: center; flex: none; }
/* Contatore accanto a una voce di menu (es. ticket da lavorare). Il testo NON è mai un
   colore fisso: `--on-warn` gira col tema (bianco sul marrone del chiaro, scuro
   sull'ambra dello scuro) — vedi @saas/config/theme. */
.nav-badge {
  margin-left: auto; font-size: 11px; font-weight: 700; line-height: 1.6;
  background: var(--warn); color: var(--on-warn); border-radius: var(--radius-sm); padding: 1px 6px;
}
.nav-soon {
  margin-left: auto; font-size: 10px; padding: 2px 6px;
  border-radius: var(--pill); background: var(--panel-2); color: var(--faint);
  text-transform: uppercase; letter-spacing: 0.08em;
}
.rail-foot {
  margin-top: auto; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); display: flex; align-items: center; gap: 10px;
}
.rail-foot .avatar { width: 32px; height: 32px; }
.rail-foot .who { min-width: 0; }
.rail-foot .who b { font-size: 12px; font-weight: 600; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-foot .who span { font-size: 11px; color: var(--faint); }

/* ---------- Stage ----------
   La MISURA del contenuto la decide questo contenitore, e vale per tutte e tre le console
   (/admin, /dashboard, workspace di un SaaS) da quando condividono la cornice. Il numero non è
   nuovo: 1300px era già la larghezza di `.saas-stage`, cioè quella che la dashboard del
   costruttore aveva e la console SuperAdmin no — su un monitor da 27" le righe delle tabelle
   di /admin arrivavano a 1680px e l'occhio perdeva la riga tornando a capo.
   Le tabelle larghe non ci perdono niente: scorrono nel loro `.adm-tablewrap`. */
.stage { padding: 24px 26px 44px; overflow: auto; min-width: 0; width: 100%; max-width: 1300px; margin: 0 auto; }
@media (max-width: 560px) { .stage { padding: 18px 14px 32px; } }
.greet { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 22px; flex-wrap: wrap; }
.greet h1 { margin: 0; font-size: 22px; font-weight: 700; }
.greet .sub { margin: 6px 0 0; font-size: 13px; color: var(--muted); max-width: 66ch; }

/* ---------- Bottoni ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--font-sans); font-size: 13px; font-weight: 600;
  /* L'altezza la fa `min-height`; il padding verticale resta piccolo ma NON zero, così un
     bottone con etichetta lunga che va a capo non finisce col testo appiccicato al bordo. */
  min-height: var(--ctrl-h); padding: 6px 14px; border-radius: var(--radius-sm); cursor: pointer;
  border: 1px solid var(--line-2); color: var(--text); background: var(--panel);
  white-space: nowrap;
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease), filter var(--dur-fast) var(--ease);
}
.btn:hover { border-color: var(--accent); color: var(--accent); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: var(--disabled-opacity); cursor: not-allowed; transform: none; }
/* Il testo di un bottone pieno NON può essere `#fff` cablato: lo sfondo è il gradiente di
   `--accent`, e l'accent CAMBIA. Nel workspace è quello del BRAND del rivenditore — con lo
   smeraldo di Acme CRM (#10b981) il bianco stava a 2.54, misurato. E nel tema SCURO della
   piattaforma l'accent è un azzurro chiaro (#4EA3FF): bianco = 2.62.
   `--on-accent` la risposta giusta ce l'aveva già (per lo smeraldo pubblica #15181f): era il
   CSS a ignorarla. Un token, 61 bottoni primari, tre contesti — uno solo da tenere allineato. */
.btn.primary {
  color: var(--on-accent, #fff); border-color: transparent;
  background: linear-gradient(150deg, var(--accent), var(--accent-2));
  box-shadow: 0 8px 20px -8px var(--accent-glow);
}
.btn.primary:hover { filter: brightness(1.06); color: var(--on-accent, #fff); }
/* Testo SECONDARIO dentro un bottone PIENO. `--faint`/`--muted` sono grigi tarati per stare
   su superfici CHIARE: dentro un bottone accent diventano illeggibili — il « · base» accanto
   al ruolo predefinito misurava 1,51:1, cioè invisibile. Sopra un fondo pieno l'unico colore
   sicuro è quello del bottone stesso, appena smorzato: funziona con QUALSIASI accent del
   rivenditore e in entrambi i temi, senza dover conoscere il colore in anticipo. */
.btn.primary :is(.faint, .muted, .sub) { color: inherit; opacity: .72; }
.btn.ghost { background: transparent; border-color: transparent; color: var(--muted); }
.btn.ghost:hover { background: var(--panel-2); color: var(--text); border-color: transparent; }
.btn.danger { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 40%, transparent); background: transparent; }
.btn.danger:hover { background: color-mix(in srgb, var(--crit) 12%, transparent); border-color: var(--crit); color: var(--crit); }
.btn.icon { padding: 0; width: var(--ctrl-h); height: var(--ctrl-h); }
/* `.sm` era una bugia tipografica: cambiava padding e corpo in modo compensato e il bottone
   restava alto 34px esatti come quello normale. Ora è un gradino vero, agganciato al token. */
.btn.sm { min-height: var(--ctrl-h-sm); padding: 4px 11px; font-size: 12px; border-radius: var(--radius-xs); }
.btn.icon.sm { width: var(--ctrl-h-sm); height: var(--ctrl-h-sm); padding: 0; }

/* ── A11y: focus da tastiera (WS8.3) ──────────────────────────────────────────
   Anello di focus coerente e ad alto contrasto sui controlli della chrome di
   piattaforma. I pulsanti e i link di navigazione non azzeravano l'outline ma
   nemmeno avevano un anello brandizzato: da tastiera si vedeva solo l'outline di
   default del browser (incoerente e a volte poco visibile sui pulsanti pieni).
   Solo :focus-visible → nessun anello al click del mouse, sempre visibile con Tab. */
.btn:focus-visible,
.nav-item:focus-visible,
.saas-tab:focus-visible,
.site-nav-link:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent);
}

/* BASE per TUTTO il resto. La regola qui sopra nomina quattro classi, e ciò che non ci
   rientra restava senza anello: i pulsantini di riga (`.rec-del`/`.rec-edit`, che sono
   `<button>` senza `.btn`), le card SaaS della dashboard (che sono il bersaglio di
   navigazione principale), i filtri-targhetta di Assistenza, i link nelle tabelle.
   Da tastiera quei controlli erano invisibili: si sapeva di averli raggiunti solo provando.

   `:where()` ha specificità ZERO, quindi questa è davvero una BASE: qualunque regola di
   componente (comprese quelle sopra, e quelle dentro `.saas-ui`) continua a vincere e non
   si formano doppi anelli. Si usa `outline` e non `box-shadow` apposta: non entra in
   conflitto con le ombre già presenti e segue da sé il border-radius dell'elemento. */
:where(a[href], button, summary, [role="button"], [role="tab"], [tabindex="0"]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---------- Panel ---------- */
.panel {
  position: relative;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--panel);
  box-shadow: var(--shadow), var(--panel-hi);
  display: flex; flex-direction: column; min-width: 0;
  transition: border-color var(--dur) var(--ease);
}
/* Spazio fra pannelli IMPILATI. Sta sul PANNELLO (e non sul contenitore, come vorrebbe
   il buon senso moderno) per un motivo misurato: 15 dei ~19 stack dell'app sono <div>
   ANONIMI (/admin/libreria ne impila 23, /dashboard/abbonamento 5, integrazioni 5…).
   Spostare lo spazio sui contenitori vorrebbe dire dare una classe a ognuno, e
   dimenticarne uno = pannelli attaccati.

   ⚠️ TRAPPOLA (era il bug delle "card non allineate"): in un contenitore GRID/FLEX le
   card sono AFFIANCATE, non impilate → questo margine non le distanzia, le SPINGE GIÙ
   tutte tranne la prima. Risultato: la prima card più alta e più in su delle altre
   (misurato su .wk-kpis: y=177 h=130 contro y=191 h=116), mentre lo spazio vero lo
   metteva già `gap`.
   Lì il margine va AZZERATO, come qui sotto. L'eccezione sta sulle griglie e non
   viceversa perché un contenitore grid è sempre NOMINATO (serve una classe per fare
   `display:grid`), quindi è enumerabile; uno stack anonimo no. */
.panel + .panel { margin-top: 14px; }
/* Griglie di pannelli: lo spazio è del contenitore (`gap`), il margine è un bug. */
.wk-kpis > .panel + .panel { margin-top: 0; }
/*
  Il filetto d'accento del pannello PRIMARIO — un'OMBRA INTERNA, non un pseudo-elemento.

  Storia, perché la soluzione è la terza. Il filetto era un `::after` alto quanto il pannello:
  tagliava attraverso le curve e sbordava dalla sagoma (il pannello è arrotondato e non ha
  `overflow:hidden`, di proposito: ci vivono dentro ombre e menu che devono poter uscire). Lo si
  è allora fatto rientrare di `--radius` sopra e sotto, per tenerlo nel tratto DRITTO del bordo.
  Le curve non le incontrava più, ma il lato sinistro restava fatto di DUE cose diverse — misurato:
  bordo pallido a 262..263, filetto blu da 263 — e sottile agli angoli, spesso in mezzo. Un gradino.

  Un'ombra interna non ha quel problema perché il `border-radius` la RITAGLIA: segue le curve da
  sola, per tutta l'altezza, senza schivarle e senza sbordare. Niente pseudo-elemento, un edge solo.
  Le altre due ombre vanno ripetute: `box-shadow` è una proprietà sola, e dichiararne una qui
  cancellerebbe quelle di `.panel`.
*/
.panel.is-primary {
  border-color: color-mix(in srgb, var(--accent) 34%, var(--line-2));
  box-shadow: inset 3px 0 0 var(--accent), var(--shadow), var(--panel-hi);
}
.p-head { display: flex; align-items: center; gap: 12px; padding: 14px 16px; }
.p-head.click { cursor: pointer; user-select: none; }
.p-head + .p-body { border-top: 1px solid var(--line); }
.p-head h3 { margin: 0; font-size: 13px; font-weight: 600; }
.p-head .p-meta { font-size: 11px; color: var(--faint); letter-spacing: 0.06em; }
.p-body { padding: 18px 16px; display: grid; gap: 18px; animation: reveal var(--dur) var(--ease); }

/* Provider brand box */
.pmark {
  width: 40px; height: 40px; border-radius: var(--radius-sm); display: grid; place-items: center;
  font-family: var(--font-sans); font-weight: 800; font-size: 18px; color: #fff; flex: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
.priority-badge {
  width: 24px; height: 24px; border-radius: var(--radius-xs); display: grid; place-items: center;
  font-family: var(--font-mono); font-size: 12px; font-weight: 700; flex: none;
  background: var(--panel-2); color: var(--muted); border: 1px solid var(--line);
}
.priority-badge.p1 { background: linear-gradient(150deg, var(--accent), var(--accent-2)); color: #fff; border-color: transparent; }
.p-title { font-size: 14px; font-weight: 640; }
.p-vendor { font-size: 11px; color: var(--faint); }

/* ---------- Chip / badge di stato ---------- */
/* Una targhetta di stato è un'ETICHETTA, non un paragrafo: deve stare su una riga.
   Senza `nowrap`, dentro una colonna stretta «non attiva» andava a capo e la pillola
   diventava un ovale alto 42px invece di 26 — su ogni riga della tabella account.
   `nowrap` + `max-width` = o sta su una riga, o si tronca con i puntini; mai si gonfia.
   I numeri tabellari tengono ferme le pillole-contatore mentre i valori cambiano. */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
  padding: 4px 9px; border-radius: var(--pill); border: 1px solid var(--line-2); color: var(--muted);
  white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
}
.chip .dot { width: 7px; height: 7px; border-radius: 50%; }
/* Una targhetta CLICCABILE (filtri di Assistenza, switch demo/reali dei SaaS) è un bersaglio,
   non un'etichetta: deve rispondere al passaggio del mouse. Solo `a`/`button`, perché la
   stragrande maggioranza dei chip è testo di stato e non si deve muovere. */
a.chip, button.chip { cursor: pointer; transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
a.chip:hover, button.chip:hover { border-color: var(--accent); color: var(--accent); }
a.chip.accent:hover, button.chip.accent:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
.chip.ok { color: var(--good); border-color: color-mix(in srgb, var(--good) 40%, transparent); background: color-mix(in srgb, var(--good) 12%, transparent); }
.chip.warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); background: color-mix(in srgb, var(--warn) 14%, transparent); }
.chip.crit { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 40%, transparent); background: color-mix(in srgb, var(--crit) 12%, transparent); }
.chip.accent { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.chip.idle { color: var(--faint); }
.dot-ok { background: var(--good); box-shadow: 0 0 8px var(--good-glow); }
.dot-warn { background: var(--warn); }
.dot-crit { background: var(--crit); box-shadow: 0 0 8px var(--crit-glow); }
.dot-idle { background: var(--faint); }

/* ---------- SuperAdmin: griglia KPI + tabelle di oversight ---------- */
.adm-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 18px; }
.adm-kpi { border: 1px solid var(--line); background: var(--panel); border-radius: var(--radius); padding: 14px 16px; display: flex; flex-direction: column; gap: 4px; }
.adm-kpi .k-lbl { font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--faint); }
.adm-kpi .k-val { font-size: 22px; font-weight: 700; letter-spacing: var(--tracking-title); font-variant-numeric: tabular-nums; }
.adm-kpi .k-hint { font-size: 11px; color: var(--muted); }
.adm-tablewrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); }
.adm-table { width: 100%; border-collapse: collapse; font-size: 13px; min-width: 640px; }
.adm-table th { text-align: left; font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint); font-weight: 700; padding: 10px 14px; border-bottom: 1px solid var(--line); background: var(--panel-2); }
.adm-table td { padding: 11px 14px; border-bottom: 1px solid var(--line); color: var(--text); vertical-align: middle; }
.adm-table tr:last-child td { border-bottom: 0; }
.adm-table tbody tr:hover { background: color-mix(in srgb, var(--accent) 5%, transparent); }
.adm-table .num { text-align: right; font-variant-numeric: tabular-nums; }
/* Le CIFRE restano monospaziate: incolonnarle e' il motivo per cui il mono esiste.
   `td` e non `.num`: la stessa classe sta anche sulle INTESTAZIONI ("Crediti", "SaaS"),
   che sono titoletti e vanno in Poppins come tutte le altre. */
.adm-table td.num { font-family: var(--font-mono); }
.adm-note { font-size: 12px; color: var(--muted); border-left: 2px solid color-mix(in srgb, var(--warn) 55%, transparent); padding: 8px 12px; background: color-mix(in srgb, var(--warn) 7%, transparent); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }

/* ---------- Campi ---------- */
.field-label {
  display: block; font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; font-weight: 700; color: var(--faint); margin-bottom: 8px;
}
.input-row { display: flex; gap: 8px; align-items: stretch; flex-wrap: wrap; }
/* Una targhetta usata come SUFFISSO di campo (il «.buildyoursaas.ai» accanto al sottodominio)
   non deve essere stirata dallo `stretch` della riga: a 38px una pillola da 999px diventa un
   lozenge gigante. Resta della sua altezza naturale, centrata sul campo. */
.input-row > .chip { align-self: center; }
.input {
  width: 100%; min-height: var(--ctrl-h); padding: 7px 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--line-2); background: var(--bg-2); color: var(--text);
  font-size: 13px;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.input::placeholder { color: var(--faint); }
.input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent); }
/* CAMPO DISABILITATO. Non esisteva affatto: un campo spento si distingueva da uno attivo solo
   per lo sbiadito di default del browser, che cambia da sistema a sistema ed è quasi invisibile
   su Windows. Non è un caso di nicchia — questo prodotto disabilita i campi MENTRE SALVA
   (`disabled={busy !== null}` è ovunque), quindi per tutta la durata del salvataggio i campi
   sembravano scrivibili e non lo erano. Si cambia la superficie, non l'opacità: abbassare
   l'opacità di un campo rende illeggibile anche il valore che contiene. */
.input:disabled, .input[aria-disabled="true"] {
  cursor: not-allowed;
  background: var(--panel-2);
  color: var(--muted);
  border-color: var(--line);
}
.input:disabled::placeholder { color: var(--faint); }
select.input { cursor: pointer; }
select.input:disabled { cursor: not-allowed; }
/* `.input.sm` esisteva SOLO dentro `.saas-ui`. Fuori — cioè in tutta la console SuperAdmin e
   nella dashboard rivenditore, dove pure viene scritta `className="input sm"` — la classe non
   agganciava niente e il campo restava alto quanto uno normale accanto a un bottone `.sm`.
   Il gradino piccolo dev'essere lo stesso nei due mondi. */
.input.sm { min-height: var(--ctrl-h-sm); padding: 4px 9px; font-size: 12px; border-radius: var(--radius-xs); }
/* Un'area di testo cresce in verticale: l'altezza minima del controllo non la deve schiacciare
   su una riga sola quando l'autore ha chiesto `rows`. */
textarea.input { min-height: 0; padding: 9px 12px; line-height: 1.5; }

/* ---------- Toggle ---------- */
.toggle {
  position: relative; width: 42px; height: 24px; border-radius: var(--pill); border: 1px solid var(--line-2);
  background: var(--panel-2); cursor: pointer; flex: none; transition: background var(--dur) var(--ease);
}
.toggle::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
  background: var(--faint); transition: transform var(--dur) var(--ease), background var(--dur) var(--ease);
}
.toggle.on { background: linear-gradient(150deg, var(--accent), var(--accent-2)); border-color: transparent; }
.toggle.on::after { transform: translateX(18px); background: #fff; }

/* Toggle RIUSABILE (components/toggle.tsx) — switch + etichetta su una riga, self-contained. */
.tgl { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; }
.tgl.is-disabled { opacity: 0.5; cursor: not-allowed; }
.tgl-input { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; }
.tgl-switch {
  position: relative; flex: none; width: 42px; height: 24px; border-radius: var(--pill);
  background: var(--panel-2); border: 1px solid var(--line-2); transition: background var(--dur) var(--ease);
}
.tgl-knob {
  position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
  background: var(--faint); transition: left var(--dur) var(--ease), background var(--dur) var(--ease);
}
.tgl-input:checked + .tgl-switch { background: linear-gradient(150deg, var(--accent), var(--accent-2)); border-color: transparent; }
.tgl-input:checked + .tgl-switch .tgl-knob { left: 20px; background: #fff; }
.tgl-input:focus-visible + .tgl-switch { outline: 2px solid var(--accent); outline-offset: 2px; }
.tgl-label { font-size: 13px; line-height: 1.35; }

/* Checkbox/radio NATIVI (matrici permessi, selezione righe, chip feature): colore d'accento
   coerente ovunque invece del blu di default del browser. */
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }

/* ---------- Feedback ---------- */
.test-result {
  padding: 11px 13px; border-radius: var(--radius-sm); font-size: 12px;
  border: 1px solid var(--line-2); animation: reveal var(--dur) var(--ease);
}
.test-result.ok { border-color: color-mix(in srgb, var(--good) 45%, transparent); background: color-mix(in srgb, var(--good) 9%, transparent); }
.test-result.fail { border-color: color-mix(in srgb, var(--crit) 45%, transparent); background: color-mix(in srgb, var(--crit) 9%, transparent); }
.banner {
  display: flex; align-items: center; gap: 12px; padding: 12px 16px; border-radius: var(--radius-sm);
  border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
  color: var(--text); font-size: 13px; margin-bottom: 20px;
}
.banner.crit { border-color: color-mix(in srgb, var(--crit) 45%, transparent); background: color-mix(in srgb, var(--crit) 10%, transparent); }

/* ---------- Spinner ---------- */
.spinner {
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
  border-top-color: currentColor; animation: spin 0.7s linear infinite;
}

/* ---------- Lock screen ---------- */
.lock-screen { min-height: 100vh; display: grid; place-items: center; padding: 24px; position: relative; z-index: 1; }
.lock-card { max-width: 440px; text-align: center; padding: 28px; }

/* ---------- Animazioni ---------- */
@keyframes ping { 0% { box-shadow: 0 0 0 0 var(--good-glow); } 70% { box-shadow: 0 0 0 8px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes reveal { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
/* Qui c'era `@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition:
   none !important; } }`, e con lei altri tre blocchi che ne spegnevano gli strascichi.
   Tolti su decisione esplicita di Luigi (2026-07-17): le animazioni girano SEMPRE, anche per chi
   ha chiesto al sistema operativo di ridurre il movimento.
   VA DETTO COS'È: `prefers-reduced-motion` non è una preferenza estetica come il tema scuro — chi
   la attiva spesso lo fa perché il movimento sullo schermo gli provoca nausea o vertigini
   (disturbi vestibolari, emicrania). E questo foglio veste anche i SaaS GENERATI (`.saas-ui`), non
   solo la console: la scelta ricade sui clienti finali dei rivenditori, non su di noi.
   La decisione è del proprietario del prodotto ed è dichiarata; questo commento resta perché la
   prossima persona sappia che è una scelta e non una dimenticanza, e per non farla passare per una
   svista da "correggere" alla cieca. Se un giorno si volesse tornare indietro, la strada di mezzo
   è tenere ferme le TRASLAZIONI (rotazioni, scorrimenti, scala — quelle che danno il disturbo) e
   lasciare i battiti di OPACITÀ, che non lo danno e bastano a dire «sto lavorando». */
/* Solo per screen reader: fuori dal flusso visivo ma leggibile e focalizzabile dagli AT. */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
/*
  Riappare al focus SOLO chi lo chiede. La regola stava su `.sr-only` senza distinzioni ed è il
  motivo del titolo fantasma nel wizard: quell'`h2` è nascosto perché ripete a voce ciò che si
  vede già, ma riceve il focus a ogni cambio passo per farlo ANNUNCIARE agli screen reader —
  quindi `:focus` scattava e lo mostrava, con tanto di anello arancione, sopra al pannello.
  Un testo scritto per non essere visto che diventa visibile è un doppione, non un'informazione.

  La versione `-focusable` esiste per il caso che quella regola voleva servire e che oggi non ha
  nessun cliente: gli skip-link («salta al contenuto»), che sono invisibili finché non ci arrivi
  col Tab, e in quel momento DEVONO farsi vedere.
*/
.sr-only-focusable:focus { position: static; width: auto; height: auto; margin: 0; clip: auto; white-space: normal; }

/* ---------- Runtime: rendering del pannello di un SaaS generato ---------- */
/* Padding interno dei panel "semplici" (runtime/workspace/wizard): il contenuto sta
   direttamente nel panel (rt-head/rt-form/wiz-body), non nel pattern p-head/p-body
   dell'admin (che ha il suo padding). Senza questo, il contenuto tocca il bordo. */
.panel:has(> .rt-head),
.panel:has(> .wiz-body) { padding: 18px 20px; }
@media (max-width: 560px) {
  .panel:has(> .rt-head),
  .panel:has(> .wiz-body) { padding: 14px 15px; }
}
.rt-head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; }
.rt-head h2 { margin: 0; font-size: 18px; }
.rt-toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
.rt-table-wrap { overflow-x: auto; }
.rt-table { width: 100%; border-collapse: collapse; font-size: 13px; min-width: 520px; }
.rt-table th {
  text-align: left; padding: 9px 12px; font-size: 11px;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint);
  border-bottom: 1px solid var(--line-2); font-weight: 600; white-space: nowrap;
}
.rt-table td { padding: 11px 12px; border-bottom: 1px solid var(--line); color: var(--text); }
.rt-table tr:last-child td { border-bottom: 0; }
.rt-table tbody tr:hover td { background: var(--panel-2); }
.rt-table .rt-totals-row td { padding: 9px 12px; border-top: 2px solid var(--line-2); border-bottom: 0; background: var(--panel-2); font-weight: 650; }
.rt-total { display: inline-flex; align-items: baseline; justify-content: flex-end; gap: 6px; white-space: nowrap; }
.rt-total-label { color: var(--muted); font-size: 11px; font-weight: 600; }
.rt-num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.rt-title-cell { font-weight: 600; }
/* Cella dell'empty-state in tabella: il visual è fornito da <EmptyState> (.rt-empty-state),
   qui solo azzeriamo il padding di cella e l'hover di riga. */
.rt-empty { padding: 0 !important; }
.rt-empty:hover { background: transparent !important; }
.rt-form { display: grid; gap: 20px; }
.rt-section-title {
  font-size: 11px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 12px;
}
.rt-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
@media (max-width: 720px) { .rt-row { grid-template-columns: 1fr; } }
.rt-field { display: grid; gap: 6px; min-width: 0; }
.rt-ro { opacity: 0.75; }

/* CRUD scrittura: form di creazione record + azione elimina per riga */
.rec-form { flex-basis: 100%; border: 1px solid var(--line-2); border-radius: var(--radius); padding: 16px; background: var(--panel-2); margin-top: 2px; display: grid; gap: 12px; animation: reveal 0.2s ease; }
.rec-form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
@media (max-width: 620px) { .rec-form-grid { grid-template-columns: 1fr; } }
/* Una posizione non è una stringa "lat,lng": il form espone entrambe le coordinate con
   etichetta, limite WGS84 e valori indipendenti, così l'utente non deve conoscere un formato. */
.geo-input { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.geo-coordinate { display: grid; gap: 4px; min-width: 0; }
.geo-coordinate-label { color: var(--faint); font-size: 11px; font-weight: 600; }
@media (max-width: 420px) { .geo-input { grid-template-columns: 1fr; } }
.structured-field-hint { color: var(--faint); font-size: 11px; line-height: 1.35; }
.markdown-editor { display: grid; gap: 7px; }
.markdown-toolbar { display: flex; flex-wrap: wrap; gap: 6px; }
.markdown-preview { border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--panel); padding: 7px 10px; }
.markdown-preview > summary { cursor: pointer; color: var(--muted); font-size: 12px; font-weight: 600; }
.markdown-preview[open] > summary { margin-bottom: 10px; }
.record-markdown { display: grid; gap: 8px; line-height: 1.55; }
.record-markdown > :is(p, h3, h4, h5, blockquote, ul, pre) { margin: 0; }
.record-markdown h3 { font-size: 1.12em; }.record-markdown h4 { font-size: 1.04em; }.record-markdown h5 { font-size: 1em; }
.record-markdown blockquote { border-left: 3px solid var(--accent); padding-left: 10px; color: var(--muted); }
.record-markdown ul { padding-left: 20px; }.record-markdown pre { white-space: pre-wrap; padding: 9px 10px; border-radius: var(--radius-sm); background: var(--panel-2); border: 1px solid var(--line); }
.address-editor { display: grid; gap: 8px; }
.address-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.address-grid > label { display: grid; gap: 4px; min-width: 0; color: var(--muted); font-size: 11px; font-weight: 600; }
.address-grid > .address-wide { grid-column: span 3; }
.address-extra { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 7px 10px; }
.address-extra > summary { cursor: pointer; color: var(--muted); font-size: 12px; font-weight: 600; }
.address-extra[open] > summary { margin-bottom: 8px; }
.address-extra .textarea { margin-bottom: 6px; }
.record-address { display: inline-block; line-height: 1.45; }
@media (max-width: 560px) { .address-grid { grid-template-columns: 1fr; }.address-grid > .address-wide { grid-column: auto; } }
.rating-input { display: inline-flex; align-items: center; gap: 2px; min-height: 34px; }
.rating-star { appearance: none; border: 0; padding: 2px; background: transparent; color: var(--accent); cursor: pointer; font-size: 24px; line-height: 1; }
.rating-star:hover:not(:disabled), .rating-star:focus-visible { color: var(--accent-ink); transform: scale(1.08); outline: none; }
.rating-star:disabled { opacity: .34; cursor: not-allowed; }
.rec-form-actions { display: flex; gap: 8px; }
.rt-actions-col { width: 72px; text-align: right; }
/* Centro notifiche (campanella) */
.notif { position: relative; display: inline-flex; }
.notif-bell { position: relative; background: transparent; border: 1px solid var(--line); border-radius: var(--radius-sm); width: 34px; height: 34px; cursor: pointer; font-size: 15px; color: var(--text); display: inline-grid; place-items: center; }
.notif-bell:hover { border-color: var(--line-2, var(--accent)); }
.notif-badge { position: absolute; top: -5px; right: -5px; min-width: 16px; height: 16px; padding: 0 3px; border-radius: var(--radius-sm); background: var(--crit, #dc2626); color: #fff; font-size: 10px; font-weight: 700; line-height: 16px; text-align: center; }
.notif-scrim { position: fixed; inset: 0; z-index: 40; background: transparent; border: 0; cursor: default; }
.notif-panel { position: absolute; top: 42px; right: 0; z-index: 41; width: 320px; max-width: 90vw; background: var(--panel, #fff); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: 0 16px 40px rgba(0,0,0,.28); overflow: hidden; }
.notif-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border-bottom: 1px solid var(--line); font-size: 13px; }
.notif-empty { padding: 22px 14px; text-align: center; color: var(--muted); font-size: 13px; }
.notif-list { list-style: none; margin: 0; padding: 0; max-height: 340px; overflow-y: auto; }
.notif-item { padding: 10px 14px; border-bottom: 1px solid var(--line); display: grid; gap: 3px; }
.notif-item.is-link { cursor: pointer; }
.notif-item.is-link:hover { background: var(--panel-2, rgba(127,127,127,.06)); }
.notif-item.is-unread { border-left: 3px solid var(--accent); padding-left: 11px; }
.notif-item-title { font-size: 13px; font-weight: 600; }
.notif-item-body { font-size: 12px; color: var(--muted); line-height: 1.4; }
.notif-item-time { font-size: 11px; color: var(--faint); }
/* Paginazione della lista (server-side) */
.rt-pager { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 2px 2px; margin-top: 6px; border-top: 1px solid var(--line); }
.rt-pager-info { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.rt-pager-nav { display: inline-flex; align-items: center; gap: 8px; }
.rt-pager-page { font-size: 12px; color: var(--faint); font-variant-numeric: tabular-nums; }
.btn.is-disabled { opacity: 0.4; pointer-events: none; cursor: default; }
.rec-actions { display: inline-flex; gap: 4px; justify-content: flex-end; }
/* 27px era una misura di nessuno: accanto ai campi `.sm` di riga (32px) restavano corti di 5px
   e la riga di azioni si vedeva sfalsata. Agganciati al gradino piccolo dei controlli. */
.rec-del, .rec-edit { width: var(--ctrl-h-sm, 32px); height: var(--ctrl-h-sm, 32px); border-radius: var(--radius-xs, 7px); display: inline-grid; place-items: center; color: var(--faint); border: 1px solid transparent; font-size: 12px; cursor: pointer; }
.rec-del:hover:not(:disabled) { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 40%, transparent); background: color-mix(in srgb, var(--crit) 10%, transparent); }
.rec-edit:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.rec-del:disabled, .rec-edit:disabled { opacity: var(--disabled-opacity); cursor: not-allowed; }

/* ---------- Wizard "Crea il tuo SaaS" (WP1.4) ---------- */
.wiz { max-width: 760px; margin: 0 auto; }
/*
  PERCORSO a frecce. Ogni passo è un chevron che punta al successivo: la forma stessa dice che
  c'è un ordine e una direzione, cosa che sei pill affiancate e tutte uguali non dicevano.

  Geometria, perché i due numeri sono legati e cambiarne uno solo rompe il disegno:
  la punta a destra e l'incavo a sinistra sono profondi --wiz-arrow (12px), e ogni passo rientra
  sul precedente di 10px. La punta di N entra nell'incavo di N+1 lasciando 2px di scoperto, ed è
  da lì che si vede lo sfondo: la riga diagonale fra un passo e l'altro NON è un bordo — con
  clip-path un bordo seguirebbe il rettangolo, non il poligono, e taglierebbe dritto attraverso
  la freccia. È il buco fra due sagome.
*/
.wiz-steps {
  --wiz-arrow: 12px;
  display: flex;
  margin-bottom: 22px;
  /* Niente `flex-wrap`: un percorso che va a capo non è più un percorso — le frecce
     punterebbero fuori dalla riga e l'ultima tornerebbe indietro verso la prima. A schermi
     stretti si stringe (vedi la media query), non si spezza. */
}
.wiz-step {
  flex: 1 1 0;
  min-width: 0; /* senza, un flex item non scende mai sotto il suo contenuto e il path sborda */
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 11px 10px 11px 22px;
  margin-left: -10px;
  border: 0;
  background: var(--panel-2);
  color: var(--faint);
  font-size: 12px;
  font-weight: 500;
  clip-path: polygon(0 0, calc(100% - var(--wiz-arrow)) 0, 100% 50%, calc(100% - var(--wiz-arrow)) 100%, 0 100%, var(--wiz-arrow) 50%);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
/* Il primo non ha nulla a sinistra da cui arrivare: fronte piatto, e nessun rientro. */
.wiz-step:first-child {
  margin-left: 0;
  padding-left: 14px;
  clip-path: polygon(0 0, calc(100% - var(--wiz-arrow)) 0, 100% 50%, calc(100% - var(--wiz-arrow)) 100%, 0 100%);
}
.wiz-step .num {
  width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center;
  font-family: var(--font-mono); font-size: 11px; border: 1px solid var(--line-2); flex: none;
}
.wiz-step-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* FATTO: blu tenue e cliccabile — «ci sei già passato, puoi tornarci». */
.wiz-step.done {
  background: color-mix(in srgb, var(--accent) 12%, var(--panel-2));
  color: color-mix(in srgb, var(--accent) 80%, var(--text));
  cursor: pointer;
}
.wiz-step.done:hover { background: color-mix(in srgb, var(--accent) 22%, var(--panel-2)); }
.wiz-step.done .num {
  background: color-mix(in srgb, var(--good) 20%, transparent);
  color: var(--good);
  border-color: color-mix(in srgb, var(--good) 45%, transparent);
}
/* QUI: pieno, e l'unico pieno. Il colpo d'occhio deve cadere su un punto solo. */
.wiz-step.active {
  background: linear-gradient(150deg, var(--accent), var(--accent-2));
  color: #fff;
  font-weight: 600;
}
.wiz-step.active .num { background: rgba(255, 255, 255, 0.24); color: #fff; border-color: transparent; }
.wiz-step.todo { color: var(--faint); }
.wiz-step:disabled { cursor: not-allowed; }
/* `outline` seguirebbe il rettangolo, non il chevron: da tastiera si evidenzia col colore. */
.wiz-step:focus-visible {
  outline: none;
  background: color-mix(in srgb, var(--accent) 30%, var(--panel-2));
  color: var(--text);
}
.wiz-step.active:focus-visible { background: linear-gradient(150deg, var(--accent-2), var(--accent)); color: #fff; }
@media (max-width: 720px) {
  /* Sei etichette non ci stanno. Si tiene quella che serve — DOVE SEI — e le altre restano
     numeri: il percorso resta leggibile invece di diventare sei parole tagliate a metà. */
  .wiz-steps { --wiz-arrow: 9px; }
  .wiz-step { padding: 10px 6px 10px 16px; gap: 6px; flex: 0 1 auto; }
  .wiz-step.active { flex: 1 1 auto; }
  .wiz-step:not(.active) .wiz-step-label { display: none; }
}
.wiz-body { display: grid; gap: 16px; animation: reveal 0.25s ease; }
.wiz-foot { display: flex; gap: 10px; margin-top: 22px; }
/* Scelte visive del wizard. `pnl-seg` appartiene alla shell del SaaS e le sue regole sono
   volutamente limitate a `.saas-ui`: riusarlo qui lasciava due semplici parole, senza il
   feedback necessario a capire che fossero pulsanti o quale opzione fosse attiva. */
.wiz-choice { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.wiz-choice-btn { min-height: 44px; padding: 0 14px; display: flex; align-items: center; gap: 9px;
  border: 1px solid var(--line-2); border-radius: var(--radius-sm); background: var(--panel);
  color: var(--muted); font: inherit; font-size: 14px; font-weight: 600; text-align: left;
  cursor: pointer; transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.wiz-choice-btn::before { content: ""; width: 14px; height: 14px; flex: none; box-sizing: border-box;
  border: 1.5px solid currentColor; border-radius: 50%; }
.wiz-choice-btn:hover { background: var(--panel-2); border-color: var(--accent); color: var(--text); }
.wiz-choice-btn.is-active { background: var(--accent-tint); border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); color: var(--accent-ink); }
.wiz-choice-btn.is-active::before { border: 4px solid var(--accent); }
.wiz-choice-btn:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
@media (max-width: 420px) { .wiz-choice { grid-template-columns: 1fr; } }
/* Banner "ripresa bozza" sopra il wizard. */
.wiz-resume { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.wiz-resume > span { flex: 1; }
/* Indicatore disponibilità sottodominio (inline nell'hint). */
.hint .ok { color: var(--good); font-weight: 600; }
/* Riepilogo di conferma sull'ULTIMO step: è attaccato al pulsante «Crea» perché è l'ultima cosa
   che leggi prima di creare — quindi si sposta con il passo finale, non col numero 5. */
.wiz-recap { border: 1px solid var(--line-2); border-radius: var(--radius); padding: 14px 16px; background: var(--panel-2); }
.wiz-recap-title { font-size: 12px; font-weight: 600; margin-bottom: 10px; }
.wiz-recap-list { margin: 0; display: grid; gap: 8px; }
.wiz-recap-list > div { display: grid; grid-template-columns: 120px 1fr; gap: 12px; align-items: baseline; }
.wiz-recap-list dt { font-size: 11px; font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase; color: var(--faint); }
.wiz-recap-list dd { margin: 0; font-size: 13px; color: var(--text); }
@media (max-width: 560px) { .wiz-recap-list > div { grid-template-columns: 1fr; gap: 2px; } }
/* Codici di recupero 2FA — mostrati una volta, in griglia monospazio con avviso. */
.tfa-codes { margin-top: 12px; padding: 14px 16px; border-radius: var(--radius); border: 1px solid color-mix(in srgb, var(--warn, #d97706) 40%, var(--line)); background: color-mix(in srgb, var(--warn, #d97706) 8%, transparent); }
.tfa-codes-head { display: flex; flex-direction: column; gap: 3px; margin-bottom: 10px; }
.tfa-codes-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 16px; }
.tfa-codes-grid li { font-size: 14px; letter-spacing: 0.06em; padding: 4px 8px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-2xs); text-align: center; }
@media (max-width: 480px) { .tfa-codes-grid { grid-template-columns: 1fr; } }
/* Form di authoring dei Piani (SuperAdmin C7): griglia di campi. */
.plan-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 16px; }
.plan-grid .stack { display: flex; flex-direction: column; gap: 4px; }
@media (max-width: 760px) { .plan-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 460px) { .plan-grid { grid-template-columns: 1fr; } }
/* Filtro di lista a INTERVALLO (numerico/data): coppia di input min–max affiancati. */
.saas-ui .rt-filter-range { display: inline-flex; align-items: center; gap: 4px; }
/* Barra di ricerca + FILTRI in un toggle (details): al posto della fila di input affollata,
   la ricerca resta sempre visibile e i filtri per campo stanno in un pannello a comparsa. */
.saas-ui .rt-searchbar{ display:flex; align-items:center; gap:6px; }
.saas-ui .rt-search-input{ flex:1; min-width:120px; }
.saas-ui .rt-filters{ position:relative; }
.saas-ui .rt-filters-summary{ list-style:none; cursor:pointer; display:inline-flex; align-items:center; gap:6px; white-space:nowrap;
  padding:7px 11px; border-radius: var(--radius); border:1px solid var(--line-2, var(--line)); background:var(--panel);
  font-size: 12px; font-weight:600; color:var(--text); user-select:none; transition:border-color .14s ease, background .14s ease; }
.saas-ui .rt-filters-summary::-webkit-details-marker{ display:none; }
.saas-ui .rt-filters-summary:hover{ border-color:color-mix(in srgb, var(--accent) 40%, var(--line)); }
.saas-ui .rt-filters[open] .rt-filters-summary{ border-color:var(--accent); color:var(--accent-ink, var(--accent)); background:var(--accent-tint); }
.saas-ui .rt-filters-ico{ font-size:12px; opacity:.75; }
.saas-ui .rt-filters-badge{ display:inline-grid; place-items:center; min-width:17px; height:17px; padding:0 4px; border-radius:999px;
  background:var(--accent); color:#fff; font-size: 11px; font-weight:700; line-height:1; }
/* Il pannello dei filtri è ancorato a SINISTRA del suo pulsante e largo 440px fissi: quando il
   pulsante sta nella metà destra della barra (e su tablet ci sta quasi sempre) il pannello
   finiva oltre il bordo della finestra. Sotto i 900px si ancora a DESTRA — è il verso naturale
   per un menu che parte da un pulsante di coda — e in ogni caso non supera la finestra. */
.saas-ui .rt-filters-panel{ position:absolute; z-index:40; top:calc(100% + 6px); left:0; width:min(440px, 88vw);
  max-width:calc(100vw - 24px);
  background:var(--panel); border:1px solid var(--line); border-radius: var(--radius); box-shadow:0 14px 38px rgba(15,23,42,.16);
  padding:14px; display:grid; gap:12px; }
@media (max-width:900px){ .saas-ui .rt-filters-panel{ left:auto; right:0; } }
.saas-ui .rt-filters-grid{ display:grid; grid-template-columns:1fr 1fr; gap:11px 12px; }
.saas-ui .rt-filter-field{ display:grid; gap:4px; min-width:0; }
.saas-ui .rt-filter-label{ font-size:11px; font-weight:600; color:var(--muted); }
.saas-ui .rt-filter-field .input{ width:100%; min-width:0; }
.saas-ui .rt-filter-field .rt-filter-range{ display:flex; align-items:center; gap:6px; }
.saas-ui .rt-filter-sep{ color:var(--faint); font-size:12px; }
.saas-ui .rt-filters-foot{ display:flex; justify-content:flex-end; gap:8px; align-items:center; border-top:1px solid var(--line); padding-top:12px; }
@media (max-width:560px){ .saas-ui .rt-filters-grid{ grid-template-columns:1fr; } }
/* Ricerca + paginazione delle liste SuperAdmin (C4). */
.adm-search { display: flex; gap: 8px; align-items: center; margin-bottom: 14px; flex-wrap: wrap; }
.adm-search .input { max-width: 360px; flex: 1; min-width: 180px; }
.adm-pager { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 14px; flex-wrap: wrap; font-size: 12px; }
.adm-pager-nav { display: flex; align-items: center; gap: 10px; }
.textarea { width: 100%; min-height: 130px; resize: vertical; font-family: var(--font-sans); line-height: 1.5; }
.hint { font-size: 12px; color: var(--muted); }
.err { font-size: 12px; color: var(--crit); }
.tags { display: flex; gap: 7px; flex-wrap: wrap; }
.tag-btn {
  padding: 6px 11px; border-radius: var(--pill); border: 1px solid var(--line-2); font-size: 12px;
  color: var(--muted); background: var(--panel-2); cursor: pointer;
}
.tag-btn.on { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.plan-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 14px; background: var(--panel-2); display: grid; gap: 10px; }
.toggle { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; }
.toggle .track { width: 42px; height: 24px; border-radius: var(--pill); background: var(--panel); border: 1px solid var(--line-2); position: relative; transition: 0.2s; }
.toggle .knob { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: var(--faint); transition: 0.2s; }
.toggle.on .track { background: linear-gradient(150deg, var(--accent), var(--accent-2)); border-color: transparent; }
.toggle.on .knob { left: 20px; background: #fff; }
.quad { display: grid; place-items: center; gap: 16px; padding: 40px 20px; text-align: center; }
.quad-ring {
  width: 84px; height: 84px; border-radius: 50%;
  background: conic-gradient(var(--accent) var(--p, 0%), var(--line) 0);
  display: grid; place-items: center; transition: --p 0.6s;
}
.quad-ring::after { content: ""; width: 66px; height: 66px; border-radius: 50%; background: var(--panel); }
.quad-ring .pct { position: absolute; font-family: var(--font-mono); font-size: 15px; }
.quad-log { font-family: var(--font-mono); font-size: 12px; color: var(--muted); display: grid; gap: 4px; }
.quad-log .ok { color: var(--good); }
/* Stato di generazione ONESTO: spinner + barra INDETERMINATA (nessuna percentuale finta). */
.quad-indeterminate { grid-auto-flow: row; gap: 14px; padding: 34px 20px; }
.wiz-spinner { width: 42px; height: 42px; border-radius: 50%; border: 3px solid var(--line); border-top-color: var(--accent); animation: spin 0.9s linear infinite; }
.gen-card-body { padding: 16px 14px 10px; }
.gen-bar { display: block; height: 6px; border-radius: var(--pill); background: var(--line); overflow: hidden; position: relative; }
.gen-bar > span { position: absolute; top: 0; left: 0; height: 100%; width: 40%; border-radius: var(--pill); background: var(--accent); animation: gen-slide 1.3s ease-in-out infinite; }
@keyframes gen-slide { 0% { left: -40%; } 100% { left: 100%; } }

@property --p { syntax: "<percentage>"; inherits: false; initial-value: 0%; }

/* ---------- Sito pubblico (WP1.8) ---------- */
/* Reso dal render-model del sito: stesso motore del pannello, superficie "sito". */
/* 1408 = 1240 di contenuto + 2×64 di gutter + 2×20 di padding: è la larghezza a cui
   l'anteprima e il sito live atterrano sullo STESSO contenuto (vedi .site-canvas). */
.site-root { position: relative; z-index: 1; max-width: 1408px; margin: 0 auto; padding: 22px 20px 60px; --site-radius: 14px; }
/* `site.theme.radius` guida ANCHE i --radius-* del design system DENTRO il sito (card prezzi/FAQ/
   contatti li usano già), non solo il --site-radius della tela: prima «arrotonda gli angoli del sito»
   toccava solo la cornice esterna (per giunta azzerata in embed = ciò che il rivenditore vede) e le card
   restavano identiche. Scala allineata a RADIUS_SCALE del pannello. `md` = default → i --radius-* del
   foglio (12/8/16) restano, nessun cambiamento non richiesto. */
/* I due gradini piccoli (`xs`, `2xs`) vanno scalati INSIEME agli altri: li usano controlli
   condivisi col sito (`.btn.sm`, `.input.sm`, i pulsantini di riga). Senza, il preset
   «squadrato» lasciava i bottoncini con 7px di raggio mentre tutto il resto era a spigolo
   vivo — l'unica cosa tonda in una pagina squadrata. */
.site-radius-none { --site-radius: 0; --radius: 0; --radius-sm: 0; --radius-lg: 0; --radius-xs: 0; --radius-2xs: 0; }
.site-radius-sm { --site-radius: 8px; --radius: 6px; --radius-sm: 4px; --radius-lg: 9px; --radius-xs: 4px; --radius-2xs: 3px; }
.site-radius-md { --site-radius: 14px; }
.site-radius-lg { --site-radius: 22px; --radius: 18px; --radius-sm: 12px; --radius-lg: 24px; --radius-xs: 10px; --radius-2xs: 7px; }
.site-demobar {
  border: 1px dashed var(--line-2); border-radius: var(--radius); padding: 12px 16px; margin-bottom: 20px;
  background: color-mix(in srgb, var(--panel-2) 60%, transparent);
}
.site-demobar a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }

/* ============================================================================
   SITO PUBBLICO — "ATELIER": il sito MOSTRA il prodotto invece di descriverlo.
   Ogni sezione ha una FORMA diversa; i visual sono le schermate VERE costruite
   dai metadati del bundle (<SiteMock/> + mockFromBundle).
   Vive in .saas-ui > .site-root > .site-canvas (anteprima) o .site-embed >
   .site-canvas (live full-bleed). Tutto deriva da color-mix su --accent.
   Chiaro di default; scuro via .saas-ui[data-theme="dark"].
   Self-contained: zero rete, zero font esterni, zero JS, zero immagini.
   ========================================================================== */

/* ---- TOKEN del sito — NUOVI, non si toccano quelli del design system --------
   NON si ridefiniscono --panel-hi / --accent-glow: --panel-hi è uno SLOT di
   box-shadow usato anche da .site-canvas stesso e da .panel/.nav-item.active →
   ridefinirlo qui cambierebbe l'ombra del canvas e di ogni discendente che lo usa.
   Token nuovi = raggio d'esplosione zero. */
.site-canvas {
  --site-hi:     inset 0 1px 0 color-mix(in srgb, #fff 58%, transparent);
  --site-glow-a: color-mix(in srgb, var(--accent) 30%, transparent);
  --site-glow-b: color-mix(in srgb, var(--accent-far) 24%, transparent);

  /* --accent-2 arriva INLINE da buildBrandStyle, e nella palette accent e primary
     sono gemelli (#0d9488 → #14b8a6 sono ~2° di tinta): i gradienti
     linear-gradient(--accent, --accent-2) rendono come TINTA PIATTA. --accent-far
     è un token NUOVO (non si può battere un inline) ruotato di +24° in OKLCH —
     il massimo che resta nella stessa famiglia — con la lightness clampata
     [0.42,0.80] così il contrasto non scappa. Fuori da @supports vale --accent-2,
     cioè esattamente il comportamento di prima. */
  --accent-far: var(--accent-2);
}
@supports (color: oklch(from red l c h)) {
  .site-canvas { --accent-far: oklch(from var(--accent) clamp(0.42, calc(l + 0.05), 0.80) c calc(h + 24)); }
}
.saas-ui[data-theme="dark"] .site-canvas {
  --site-hi:     inset 0 1px 0 color-mix(in srgb, #fff 9%, transparent);
  --site-glow-a: color-mix(in srgb, var(--accent) 42%, transparent);
  --site-glow-b: color-mix(in srgb, var(--accent-far) 32%, transparent);
}

/* ---- CONTENITORE — il sito è funzione del CANVAS, non della viewport --------
   PRIMA: --site-content:1120px ma .site-root era max-width:1120 + padding 20 →
   il canvas era ≤1080 < 1120 → calc((100%-1120)/2) sempre negativo → max()
   prendeva il gutter → il wrapper non ingaggiava MAI: contenuto reale ~952px.
   In .site-embed il canvas è a tutta viewport → sopra ~1248px il calc SI attivava
   → contenuto 1120. Il costruttore approvava 952 e pubblicava 1120.
   ORA il canvas è un CONTAINER e il sito si misura in cqw: l'anteprima a canvas W
   rende come il live a viewport W, e a piena larghezza entrambi atterrano su 1240.
   ATTENZIONE: `cqw` NON si risolve su .site-canvas nelle SUE proprietà (un
   container non interroga se stesso → cadrebbe silenziosamente sulla VIEWPORT).
   Qui i cqw vivono solo in custom property, che risolvono al SITO D'USO (i
   .site-block) e in regole su discendenti. Chi aggiungesse `padding: 4cqw` DIRETTO
   su .site-canvas otterrebbe 4vw senza nessun errore. */
.site-canvas {
  container: site / inline-size;
  --site-content: 1240px;
  --site-gutter:  clamp(20px, 5cqw, 64px);
  --site-pad-x:   max(var(--site-gutter), calc((100% - var(--site-content)) / 2));
  --site-pad-y:   clamp(72px, 9cqw, 112px);
  --site-bleed:   clamp(0px, 7cqw, 120px);
  border: 1px solid var(--line); border-radius: var(--site-radius);
  overflow: clip;                       /* NON hidden: così l'header sticky si aggancia */
  background: var(--bg);
  box-shadow: var(--shadow), var(--panel-hi);
}

/* ---- Shell di sezione: superficie e densità come ATTRIBUTI ------------------
   Prima c'erano cinque `background: var(--bg)` consecutivi e --site-pad-y non era
   mai sovrascritto: cadenza piatta. Il tono lo decide il RENDERER (mai una stringa
   del bundle in un data-*). */
.site-block { padding: var(--site-pad-y) var(--site-pad-x); position: relative; }
.site-block[data-tone="raised"] { background: var(--bg-2); }
.site-block[data-tone="quiet"]  { background: var(--accent-quiet); }
.site-block[data-density="tight"] { --site-pad-y: clamp(40px, 4.5cqw, 64px); }
.site-block[data-density="loose"] { --site-pad-y: clamp(92px, 12cqw, 152px); }

/* La hairline è la CONFESSIONE che due sezioni sono identiche: sopravvive solo fra
   due sezioni SENZA tono, dove nient'altro le separa. */
.site-block + .site-block { border-top: 1px solid var(--line); }
.site-block[data-tone] + .site-block,
.site-block + .site-block[data-tone],
.site-hero + .site-block,
.site-cta-band + .site-block { border-top-color: transparent; }

/* ---- Testa di sezione ------------------------------------------------------
   Il wrapper esiste per due motivi. (1) Chiude un bug reale: la vecchia
   `.site-block > .site-section-title:last-of-type` aveva specificità (0,2,1) e
   batteva il margine base; l'intento era «titolo SENZA sottotitolo → margine
   grande», ma :last-of-type conta gli h2 fra i FRATELLI e ogni blocco ne ha
   esattamente UNO → si applicava SEMPRE, e in pricing/steps il sottotitolo
   galleggiava orfano a 44px dal titolo invece di leggere come suo occhiello.
   (2) Sblocca l'allineamento a sinistra, prima impossibile perché `text-align:
   center` stava nel selettore BASE del titolo.
   VIA anche il trattino 42×2px che UN selettore raggruppato stampava su SEI
   sezioni: era la firma della monotonia. Al suo posto l'eyebrow, e solo dove
   porta gerarchia. */
.site-section-head {
  display: grid; gap: 14px; justify-items: center; text-align: center;
  max-width: 760px; margin: 0 auto clamp(40px, 5cqw, 64px);
}
.site-section-head.is-left { justify-items: start; text-align: left; margin-inline: 0; max-width: 46ch; }
/* NB tracking: NESSUN letter-spacing ad hoc sugli heading — la regola UNICA
   `h1..h6 { letter-spacing: var(--tracking-title) }` (§47, richiesta Luigi) è meno
   specifica di una classe e un valore qui la renderebbe inerte. La leva tipografica di
   questo redesign è il PESO (800 display vs 400 corpo), non il tracking. */
.site-section-title {
  margin: 0; max-width: 20ch;
  font-size: clamp(28px, 4.2cqw, 48px); font-weight: 800;
  line-height: 1.06; text-wrap: balance; color: var(--text);
}
.site-section-head.is-left .site-section-title { max-width: 16ch; }
.site-section-sub {
  margin: 0; max-width: 60ch;
  font-size: 17px; font-weight: 400; line-height: 1.6; color: var(--muted); text-wrap: pretty;
}
.site-eyebrow {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 12px; font-weight: 700; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--accent-ink);
}
.site-eyebrow::before { content: ""; flex: none; width: 20px; height: 2px; border-radius: 999px; background: var(--accent); }
/* Ponte per un markup non migrato (override/bundle custom che emette un titolo nudo). */
.site-block > .site-section-title:last-of-type { margin-bottom: 14px; }

/* ---- Header (sticky) ------------------------------------------------------- */
.site-header {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 14px var(--site-pad-x); border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--panel) 78%, transparent);
  -webkit-backdrop-filter: blur(10px) saturate(1.3); backdrop-filter: blur(10px) saturate(1.3);
  position: sticky; top: 0; z-index: 5;
}
.site-header .site-nav { margin-left: auto; }
.site-header-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.site-brand { display: flex; align-items: center; gap: 9px; font-weight: 700; font-size: 16px; letter-spacing: -0.02em; color: var(--text); }
.site-brand::before {
  content: ""; width: 22px; height: 22px; border-radius: var(--radius-xs); flex: none;
  background: linear-gradient(145deg, var(--accent), var(--accent-far));
  box-shadow: var(--shadow-accent), inset 0 1px 0 rgba(255,255,255,.35);
}
/* Logo VERO del brand nell'header: sostituisce il segnaposto a gradiente (::before) quando c'è. */
.site-brand-logo { display: block; height: 26px; width: auto; max-width: 180px; object-fit: contain; border-radius: 6px; }
.site-brand:has(.site-brand-logo)::before { display: none; }
.site-nav { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.site-nav-link { padding: 7px 12px; border-radius: 9px; font-size: 13px; font-weight: 500; color: var(--muted); border: 1px solid transparent; transition: 0.15s; }
.site-nav-link:hover { color: var(--text); background: var(--panel-2); }
.site-nav-link.is-active { color: var(--accent-ink); background: var(--accent-tint); }
.site-nav-link:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }
.saas-ui .site-canvas a.site-nav-cta {
  padding: 9px 17px; border-radius: var(--radius); font-size: 13px; font-weight: 650; text-decoration: none;
  background: var(--accent); color: var(--on-accent); border: 1px solid transparent;
  box-shadow: var(--shadow-accent); transition: background .15s ease, transform .15s ease;
}
.saas-ui .site-canvas a.site-nav-cta:hover { background: var(--accent-hover); color: var(--on-accent); transform: translateY(-1px); }
.saas-ui .site-canvas a.site-nav-ghost {
  padding: 9px 13px; border-radius: var(--radius); font-size: 13px; font-weight: 600; text-decoration: none;
  color: var(--text); border: 1px solid var(--line-2); background: transparent; transition: border-color .15s, color .15s;
}
.saas-ui .site-canvas a.site-nav-ghost:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--line-2)); color: var(--accent-ink); }
.site-footer-nav { display: flex; gap: 16px; flex-wrap: wrap; }
.site-footer-nav a { color: var(--muted); text-decoration: none; }
.site-footer-nav a:hover { color: var(--accent); }
/* SOTTOMENU dell'header: dropdown su hover o focus (pura CSS, niente JS — la tela è resa lato server). */
.site-nav-group { position: relative; }
.site-nav-dropdown {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 30;
  display: none; min-width: 190px; padding: 6px; flex-direction: column; gap: 2px;
  background: var(--panel); border: 1px solid var(--line-2); border-radius: var(--radius);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.14);
}
.site-nav-group:hover .site-nav-dropdown, .site-nav-group:focus-within .site-nav-dropdown { display: flex; }
.site-nav-dropitem { padding: 7px 12px; border-radius: 9px; font-size: 13px; color: var(--muted); text-decoration: none; white-space: nowrap; }
.site-nav-dropitem:hover { color: var(--text); background: var(--panel-2); }
/* Footer a COLONNE quando le voci hanno figli: titolo (padre) + link (figli). */
.site-footer-cols { align-items: flex-start; gap: 40px; }
.site-footer-col { display: flex; flex-direction: column; gap: 6px; }
.site-footer-heading { font-weight: 600; color: var(--text); font-size: 13px; }
@container site (max-width: 720px) { .site-header { flex-wrap: wrap; } .site-header .site-nav { display: none; } }

/* ---- HERO ------------------------------------------------------------------
   `display: flow-root` NON È DECORATIVO — È IL BLEED. Senza, il margin-bottom
   negativo dello stage COLLASSA ATTRAVERSO il bordo inferiore dell'hero (height
   auto + padding-bottom 0 + niente border) e diventa il margine dell'hero invece
   di accorciarne il contenuto: lo sbordo misurato è 0.00px. Con flow-root l'hero
   è un BFC → sbordo reale ~98px. E `z-index: 2` serve perché il blocco successivo
   è in-flow e più avanti nel DOM: senza, ci dipinge sopra.
   `overflow: clip visible` è una coppia LEGALE (a differenza di hidden/visible,
   che computa ad auto): serve a lasciar uscire lo stage sul lato inferiore. */
.site-hero {
  display: flow-root;
  overflow: clip visible;
  text-align: center; padding-block: clamp(96px, 13cqw, 168px);
  z-index: 1;
  background:
    radial-gradient(1240px 540px at 50% -8%, var(--site-glow-a), transparent 60%),
    radial-gradient(780px 400px at 84% 2%,  var(--site-glow-b), transparent 58%),
    radial-gradient(900px 500px at 50% -20%, color-mix(in srgb, var(--text) 4%, transparent), transparent 62%),
    var(--bg);
}
.site-hero::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(color-mix(in srgb, var(--accent) 26%, transparent) 1px, transparent 1.4px);
  background-size: 22px 22px;
  -webkit-mask-image: radial-gradient(780px 460px at 50% 4%, #000 0%, transparent 72%);
          mask-image: radial-gradient(780px 460px at 50% 4%, #000 0%, transparent 72%);
  opacity: .55;
}
/* `data-figure` lo emette il componente: niente :has() → se :has() mancasse, la
   compensazione sparirebbe e il mockup coprirebbe la sezione dopo. I quattro pezzi
   (flow-root, padding-bottom 0, z-index, compensazione sul fratello) stanno insieme:
   toglierne uno rompe il bleed SILENZIOSAMENTE. */
.site-hero[data-figure] { padding-bottom: 0; z-index: 2; }
.site-hero[data-figure] .site-hero-stage { margin-bottom: calc(-1 * var(--site-bleed)); }
.site-hero[data-figure] + .site-block { padding-top: calc(var(--site-pad-y) + var(--site-bleed)); }

/* Ritmo GERARCHICO: un `gap` uniforme rendeva eyebrow/titolo/sub/CTA equidistanti,
   cioè l'hero non esprimeva nessuna gerarchia. */
.site-hero-inner { position: relative; z-index: 1; max-width: 920px; margin: 0 auto; display: grid; gap: 0; justify-items: center; }
.site-hero-eyebrow {
  display: inline-flex; align-items: center; gap: 7px; margin-bottom: 24px;
  padding: 5px 13px 5px 11px; border-radius: var(--pill);
  font-size: 12px; font-weight: 600; letter-spacing: 0.02em; color: var(--accent-ink);
  background: var(--accent-tint); border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
}
.site-hero-eyebrow::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
/* Il contrasto di PESO (800 display vs 400 corpo) è la leva tipografica vera:
   prima era 700 ovunque = zero contrasto. */
.site-hero-title {
  margin: 0 0 20px; max-width: 15ch;
  font-size: clamp(40px, 7.2cqw, 84px); font-weight: 800;
  line-height: 1.0; text-wrap: balance; color: var(--text);
}
.site-hero-sub { margin: 0 0 34px; max-width: 54ch; font-size: clamp(17px, 2.2cqw, 21px); font-weight: 400; line-height: 1.55; color: var(--muted); text-wrap: pretty; }
.site-hero-actions { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; }
/* CONTRASTO (regola del sito pubblico): il testo che il visitatore deve LEGGERE usa
   --muted (6.4:1 su bianco), mai --faint (#7b8493 → 3.77:1, sotto AA 4.5). --faint
   resta ai soli elementi DECORATIVI (separatori, placeholder, il chrome dei mock). */
.site-hero-proof { margin: 18px 0 0; display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--muted); }
.site-hero-proof::before { content: ""; flex: none; width: 5px; height: 5px; border-radius: 50%; background: var(--good); box-shadow: 0 0 0 3px color-mix(in srgb, var(--good) 20%, transparent); }

/* La composizione di prodotto. Prima qui c'erano 4 pill grigie + 3 KPI VUOTI + 7
   barre anonime, zero testo, 236px di altezza: il cervello lo leggeva come «sta
   caricando», non come un prodotto. */
.site-hero-stage {
  position: relative; z-index: 1; width: min(var(--site-content), 100%);
  margin: clamp(52px, 6cqw, 88px) auto 0;
}
.site-hero-stage::before {
  content: ""; position: absolute; inset: 14% -6% -10% -6%; z-index: 0; pointer-events: none;
  border-radius: 50%; filter: blur(56px); opacity: .55;
  background: radial-gradient(60% 60% at 50% 50%, var(--site-glow-a), transparent 72%);
}
.site-hero-stage > .site-mock { position: relative; z-index: 1; }

/* ---- CTA primarie/secondarie. Scoped (0,3,1) per battere .saas-ui .btn.primary. */
.saas-ui .site-canvas a.site-cta { padding: 14px 28px; font-size: 15px; font-weight: 650; border-radius: 12px; box-shadow: var(--shadow-accent); text-decoration: none; }
.saas-ui .site-canvas a.site-cta:hover { transform: translateY(-1px); box-shadow: var(--shadow-3), var(--shadow-accent); }
.saas-ui .site-canvas a.site-cta-2 {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 14px 24px; font-size: 15px; font-weight: 600; border-radius: var(--radius); text-decoration: none;
  color: var(--text); background: color-mix(in srgb, var(--panel) 70%, transparent);
  border: 1px solid var(--line-2); box-shadow: var(--site-hi);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: border-color .15s, transform .15s;
}
.saas-ui .site-canvas a.site-cta-2:hover { transform: translateY(-1px); border-color: color-mix(in srgb, var(--accent) 45%, var(--line-2)); color: var(--accent-ink); }
.saas-ui .site-canvas a.site-cta-2 svg { width: 15px; height: 15px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* ---- LIBRERIA MOCKUP — riusabile, alimentata dai METADATI VERI --------------
   Tre regole non negoziabili: (1) mai barre anonime — ogni pixel è testo vero,
   chip o asse etichettato; (2) mai i tre semafori macOS: dicono «screenshot», non
   «prodotto» — al loro posto una toolbar APPLICATIVA; (3) mai emoji.
   NIENTE grana/scanline: una repeating-linear-gradient stesa sul TESTO del mockup
   è un effetto CRT che degrada proprio ciò che deve vendere — e un data-URI in
   background-image morirebbe sotto una CSP `img-src 'self'`. Profondità = ombra +
   glow + bordo-luce interno. */
.site-mock {
  position: relative; overflow: hidden; text-align: left; min-width: 0;
  background: var(--panel); border: 1px solid var(--line-2); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-3), var(--site-hi),
              0 44px 90px -46px color-mix(in srgb, var(--accent) 50%, transparent);
  font-size: 12px; line-height: 1.35; color: var(--text);
}
.site-mock-bar {
  display: flex; align-items: center; gap: 10px; padding: 0 14px; height: 44px;
  background: var(--panel-2); border-bottom: 1px solid var(--line);
}
.site-mock-bar b { font-size: 12px; font-weight: 700; letter-spacing: -0.01em; color: var(--text); }
.site-mock-search {
  margin-left: auto; flex: 0 1 200px; min-width: 0; display: flex; align-items: center; gap: 7px;
  padding: 5px 10px; border-radius: var(--radius-sm); font-size: 11px; color: var(--faint);
  background: var(--bg-2); border: 1px solid var(--line);
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.site-mock-search::before { content: ""; flex: none; width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid currentColor; box-shadow: 4px 4px 0 -1px currentColor; }
.site-mock-btn { flex: none; padding: 6px 13px; border-radius: 8px; font-size: 11px; font-weight: 650; color: var(--on-accent); background: var(--accent); box-shadow: var(--shadow-accent); white-space: nowrap; }

/* Shell "app": sidebar coi MODULI VERI + area di lavoro. È la figura dell'hero. */
.site-mock-app { display: grid; grid-template-columns: 190px minmax(0, 1fr); min-height: 460px; }
.site-mock-side { border-right: 1px solid var(--line); background: var(--bg-2); padding: 14px 11px; display: grid; gap: 3px; align-content: start; }
.site-mock-side span {
  display: flex; align-items: center; gap: 9px; padding: 8px 9px; border-radius: var(--radius-sm);
  font-size: 12px; font-weight: 500; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.site-mock-side .is-active { color: var(--accent-ink); background: var(--accent-tint); font-weight: 650; }
.site-mock-side svg { width: 15px; height: 15px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.site-mock-work { min-width: 0; display: grid; align-content: start; }

/* TABELLA — colonne VERE della vista, testo VERO, chip di stato VERI.
   --mock-cols lo calcola il componente da columns.length; il validatore impone
   cells.length === columns.length, quindi header e righe non possono disallinearsi. */
.site-mock-table { display: grid; align-content: start; min-width: 0; }
.site-mock-row {
  display: grid; grid-template-columns: var(--mock-cols, minmax(0,1.6fr) minmax(0,.9fr) minmax(0,.7fr));
  gap: 12px; align-items: center; padding: 11px 14px; border-top: 1px solid var(--line);
}
.site-mock-row.is-head {
  border-top: 0; background: var(--bg-2); padding-block: 9px;
  font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--faint);
}
.site-mock-row.is-sel { background: var(--accent-quiet); box-shadow: inset 2px 0 0 var(--accent); }
.site-mock-cell { min-width: 0; display: flex; align-items: center; gap: 8px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.site-mock-cell:first-child { color: var(--text); font-weight: 600; }

/* CHIP di stato: le option REALI del campo stato del modello. */
.site-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: var(--pill);
  font-size: 11px; font-weight: 650; white-space: nowrap;
  color: var(--accent-ink); background: var(--accent-tint-2);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
}
.site-chip::before { content: ""; flex: none; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.site-chip[data-tone="warn"]  { color: var(--warn-ink); background: var(--warn-tint); border-color: color-mix(in srgb, var(--warn) 26%, transparent); }
.site-chip[data-tone="good"]  { color: var(--good-ink); background: var(--good-tint); border-color: color-mix(in srgb, var(--good) 26%, transparent); }
.site-chip[data-tone="muted"] { color: var(--faint); background: var(--panel-2); border-color: var(--line); }
.site-avatar {
  flex: none; width: 23px; height: 23px; border-radius: 50%; display: grid; place-items: center;
  font-size: 10px; font-weight: 700; color: var(--accent-ink);
  background: var(--accent-tint); border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
}

/* BOARD — una colonna per ogni option REALE di stato; le card hanno columnId. */
.site-mock-board { display: grid; grid-template-columns: repeat(var(--mock-n, 3), minmax(0, 1fr)); gap: 10px; padding: 14px; background: var(--bg-2); align-items: start; }
.site-mock-col { display: grid; gap: 8px; align-content: start; padding: 10px; border-radius: 11px; background: var(--panel-2); border: 1px solid var(--line); min-width: 0; }
.site-mock-colh { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 2px 2px 4px; font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--faint); }
.site-mock-colh span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.site-mock-colh u { text-decoration: none; font-size: 10px; font-weight: 700; color: var(--muted); background: var(--panel); border: 1px solid var(--line); border-radius: 999px; padding: 1px 7px; flex: none; }
.site-mock-card { display: grid; gap: 8px; padding: 10px; border-radius: 9px; background: var(--panel); border: 1px solid var(--line); box-shadow: var(--shadow-1), var(--site-hi); font-size: 12px; font-weight: 600; color: var(--text); line-height: 1.35; min-width: 0; }
.site-mock-card::before { content: ""; height: 3px; width: 26px; border-radius: 999px; background: var(--accent); opacity: .5; }
.site-mock-card[data-tone="warn"]::before { background: var(--warn); }
.site-mock-card[data-tone="good"]::before { background: var(--good); }
.site-mock-card footer { display: flex; align-items: center; gap: 7px; font-size: 11px; font-weight: 500; color: var(--faint); min-width: 0; }
.site-mock-card footer span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* FORM — i campi VERI del form, incluso il CAMPO COMUNICANTE (visibleWhen): è la
   feature più distintiva del motore e il sito non la diceva a nessuno.
   La nota è un TrText reso nel DOM (.site-mock-note), MAI un `content:` CSS — il
   prodotto è a 5 lingue e `content:` non passa dall'estrattore traduzioni. */
.site-mock-form { display: grid; gap: 12px; padding: 18px; }
.site-mock-field { display: grid; gap: 5px; min-width: 0; }
.site-mock-field > span { font-size: 11px; font-weight: 650; color: var(--muted); }
.site-mock-field > b { font-weight: 500; font-size: 12px; color: var(--text); padding: 9px 11px; border-radius: 9px; background: var(--bg-2); border: 1px solid var(--line-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.site-mock-field.is-focus > b { border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus-ring); }
.site-mock-field.is-cond > b { border-style: dashed; border-color: color-mix(in srgb, var(--accent) 45%, transparent); background: var(--accent-quiet); color: var(--accent-ink); }
.site-mock-note { font-size: 11px; font-weight: 600; color: var(--accent-ink); opacity: .9; }
.site-mock-submit { justify-self: start; margin-top: 2px; padding: 9px 17px; border-radius: 9px; font-size: 12px; font-weight: 650; color: var(--on-accent); background: var(--accent); box-shadow: var(--shadow-accent); }

/* CHART — a catalogo ma NON nel sito di default: è l'unico mock il cui contenuto
   (numeri nel tempo) è intrinsecamente un CLAIM. Assi + label etichettate: è
   esattamente ciò che separa un grafico da sette barre anonime. */
.site-mock-chart { display: grid; grid-template-columns: auto minmax(0,1fr); grid-template-rows: minmax(0,1fr) auto; gap: 8px 10px; padding: 16px 16px 12px; }
.site-mock-yaxis { display: grid; align-content: space-between; text-align: right; font-size: 10px; color: var(--faint); font-variant-numeric: tabular-nums; }
.site-mock-plot { position: relative; display: flex; align-items: flex-end; gap: 8px; min-height: 140px; padding-top: 6px; background-image: repeating-linear-gradient(to top, var(--line) 0 1px, transparent 1px 25%); }
.site-mock-plot > i { flex: 1; min-width: 0; height: max(4%, var(--h, 50%)); border-radius: 5px 5px 2px 2px; background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent-far) 60%, var(--accent))); }
.site-mock-plot > i[data-peak] { background: linear-gradient(180deg, var(--accent-far), var(--accent)); box-shadow: 0 -8px 22px -6px var(--site-glow-a); }
.site-mock-xaxis { grid-column: 2; display: flex; gap: 8px; }
.site-mock-xaxis span { flex: 1; min-width: 0; text-align: center; font-size: 10px; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@container site (max-width: 620px) {
  .site-mock-app { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .site-mock-side { display: none; }
  .site-mock-board { grid-template-columns: minmax(0, 1fr); }
}

/* ---- SHOWCASE — LA rottura del ritmo centrato -------------------------------
   Prima l'UNICA griglia a due colonne del sito era il form contatti. `order` per
   l'inversione (non `direction: rtl`, che sorprende su scrollbar e select interni).
   Glow a z-index 0 e mock a z-index 1: nessun z-index NEGATIVO → nessuno stacking
   context da creare, nessun glow che finisce dietro un fondo opaco. */
.site-showcase-grid { display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: clamp(32px, 5cqw, 72px); align-items: center; }
.site-showcase-copy { display: grid; gap: 16px; justify-items: start; align-content: center; min-width: 0; }
.site-showcase-copy .site-section-title { font-size: clamp(26px, 3.4cqw, 42px); max-width: 15ch; text-align: left; }
.site-showcase-body { margin: 0; max-width: 46ch; font-size: 16px; font-weight: 400; line-height: 1.62; color: var(--muted); }
.site-showcase-bullets { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 11px; }
.site-showcase-bullets li { display: grid; grid-template-columns: auto minmax(0,1fr); gap: 11px; align-items: start; font-size: 14px; line-height: 1.5; color: var(--text); }
.site-showcase-bullets li::before {
  content: "\2713"; width: 20px; height: 20px; margin-top: 1px; border-radius: var(--radius-2xs); display: grid; place-items: center;
  font-size: 11px; font-weight: 800; color: var(--accent-ink); background: var(--accent-tint-2);
  border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
}
.saas-ui .site-canvas a.site-showcase-link { display: inline-flex; align-items: center; gap: 7px; margin-top: 4px; font-size: 14px; font-weight: 650; color: var(--accent-ink); text-decoration: none; }
.saas-ui .site-canvas a.site-showcase-link::after { content: "\2192"; transition: transform .18s ease; }
.saas-ui .site-canvas a.site-showcase-link:hover::after { transform: translateX(3px); }
.site-showcase-visual { position: relative; min-width: 0; }
.site-showcase-visual::before {
  content: ""; position: absolute; inset: -8% -8%; z-index: 0; pointer-events: none;
  border-radius: 50%; filter: blur(44px); opacity: .5;
  background: radial-gradient(55% 55% at 50% 50%, var(--site-glow-b), transparent 72%);
}
.site-showcase-visual > .site-mock { position: relative; z-index: 1; }
.site-showcase.is-reverse .site-showcase-copy { order: 2; }
@container site (max-width: 900px) {
  .site-showcase-grid { grid-template-columns: minmax(0, 1fr); gap: 34px; }
  .site-showcase.is-reverse .site-showcase-copy { order: 0; }   /* su mobile il testo SEMPRE prima */
}

/* ---- FEATURES: bento -------------------------------------------------------
   Prima `repeat(auto-fit, minmax(240px,1fr))` con 3 item dava sempre 3 colonne
   uguali: nessuna feature più importante di un'altra = zero gerarchia.
   ATTENZIONE — griglia UNICA + `.lead { grid-column: 1/-1 }` NON funziona: auto-fit
   non collassa le tracce che il lead occupa → con 3 unit le celle riempiono ~75%
   della riga e resta un orfano. Servono DUE contenitori: il lead a tutta larghezza
   e `.site-feature-rest` come griglia auto-fit SEPARATA. */
.site-feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 20px; }
.site-feature-bento { display: grid; gap: 16px; }
.site-feature-rest { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; }
.site-feature-card {
  position: relative; overflow: hidden; display: grid; gap: 12px; align-content: start; padding: 26px 24px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1), var(--site-hi);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
/* La schermata di prodotto dentro una feature card (layout grid): contenuta e con un filo di stacco. */
/* `--radius-md` non è mai esistita: nessun blocco la dichiara (c'è `.site-radius-md`, che è
   un'altra cosa — una CLASSE che imposta `--site-radius`). Una var() non risolta rende la
   dichiarazione non valida al calcolo, quindi il raggio cadeva a 0: la schermata dentro la
   card aveva gli angoli VIVI mentre la card che la contiene è arrotondata a 16px.
   `--radius` è il gradino intermedio giusto e segue il preset di raggio scelto per il sito. */
.site-feature-card-visual { margin-top: 4px; border-radius: var(--radius); overflow: hidden; border: 1px solid var(--line); background: var(--panel-2); }
.site-feature-card-visual > * { max-width: 100%; }
.site-feature-card::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 2px; background: linear-gradient(90deg, var(--accent), var(--accent-far)); transform: scaleX(0); transform-origin: left; transition: transform .25s ease; }
.site-feature-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-2); border-color: color-mix(in srgb, var(--accent) 32%, var(--line-2)); }
.site-feature-card:hover::before { transform: scaleX(1); }
.site-feature-lead {
  position: relative; overflow: hidden; display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); align-items: center;
  gap: clamp(24px, 3cqw, 48px); padding: clamp(26px, 3cqw, 40px);
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1), var(--site-hi);
}
.site-feature-lead .site-feature-copy { display: grid; gap: 12px; align-content: center; min-width: 0; }
.site-feature-lead .site-feature-name { font-size: clamp(20px, 2.2cqw, 26px); }
.site-feature-lead .site-mock { box-shadow: var(--shadow-2), var(--site-hi); }
/* ICONA: SVG inline monocromatico che eredita currentColor → segue il brand. Prima
   il tile da 46px ospitava un'EMOJI (◆/⚡/🔒, e la STESSA ◆ su tutte e tre le card
   di ogni SaaS del percorso reference): una glyph di piattaforma a colori FISSI,
   che su un brand teal lascia il lucchetto giallo. */
.site-feature-icon {
  display: grid; place-items: center; width: 42px; height: 42px; border-radius: var(--radius); font-size: 0; line-height: 1;
  color: var(--accent-ink); background: linear-gradient(160deg, var(--accent-tint-2), var(--accent-tint));
  border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
}
.site-feature-icon svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.site-feature-name { margin: 2px 0 0; font-size: 17px; font-weight: 650; color: var(--text); }
.site-feature-body { margin: 0; font-size: 14px; color: var(--muted); line-height: 1.6; }
@container site (max-width: 820px) { .site-feature-lead { grid-template-columns: minmax(0, 1fr); } }

/* ---- TRUST (riprova sociale, wordmark testuali senza immagini) ------------- */
.site-trust { padding-block: clamp(34px, 5cqw, 52px); }
.site-trust-inner { display: grid; gap: 20px; justify-items: center; }
.site-trust-label { font-size: 12px; font-weight: 650; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
.site-trust-logos { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: clamp(22px, 5cqw, 54px); }
.site-trust-logos span { font-size: clamp(16px, 2.4cqw, 22px); font-weight: 700; letter-spacing: -0.02em; color: var(--muted); opacity: .68; filter: grayscale(1); transition: .2s; }
.site-trust-logos span:hover { opacity: 1; color: var(--accent-ink); filter: grayscale(0); }

/* ---- STATS (metriche) ------------------------------------------------------
   VIA il testo-gradiente: `-webkit-background-clip:text` + `color: transparent`
   fa SPARIRE il numero se il clip fallisce (stampa, engine vecchi). Fail-invisible. */
.site-stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 28px; max-width: 940px; margin: 0 auto; }
.site-stat { position: relative; display: grid; gap: 6px; text-align: center; padding-top: 14px; }
.site-stat::before { content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%); width: 26px; height: 3px; border-radius: 999px; background: linear-gradient(90deg, var(--accent), var(--accent-far)); }
.site-stat-num { font-size: clamp(34px, 5cqw, 52px); font-weight: 800; letter-spacing: -0.03em; line-height: 1; color: var(--text); font-variant-numeric: tabular-nums; }
.site-stat-label { font-size: 14px; color: var(--muted); }

/* ---- SEZIONE LIBERA (block.content): mattoncini impilati, allineamento opzionale ---- */
.site-content-body { display: flex; flex-direction: column; gap: 14px; max-width: 760px; }
.site-attached-content { display: flex; flex-direction: column; gap: 18px; max-width: 760px; margin: 34px auto 0; padding-top: 28px; border-top: 1px solid var(--line-2); }
.site-attached-content .site-section-head { margin: 0; }
.site-content-center { text-align: center; }
.site-content-center .site-content-body { margin-inline: auto; align-items: center; }
.site-content-heading { font-size: 22px; font-weight: 700; color: var(--text); margin: 6px 0 0; }
.site-content-text { color: var(--muted); line-height: 1.6; margin: 0; }
.site-content-img { max-width: 100%; height: auto; border-radius: 14px; }
.site-content-card { display: flex; flex-direction: column; gap: 14px; width: 100%; box-sizing: border-box; padding: clamp(18px, 3cqw, 28px); background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-1), var(--site-hi); }
.site-content-card .site-content-img { width: 100%; max-height: 460px; object-fit: cover; }
.site-content-list { color: var(--muted); line-height: 1.7; margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 4px; }
.site-content-center .site-content-list { text-align: left; }
.site-content-divider { border: none; border-top: 1px solid var(--line-2); width: 100%; margin: 8px 0; }
/* Colonne affiancate (item kind "columns"): griglia responsiva, una colonna su mobile. */
.site-content-cols { display: grid; gap: 24px; align-items: start; width: 100%; }
.site-content-cols[data-cols="2"] { grid-template-columns: repeat(2, 1fr); }
.site-content-cols[data-cols="3"] { grid-template-columns: repeat(3, 1fr); }
.site-content-cols[data-cols="4"] { grid-template-columns: repeat(4, 1fr); }
.site-content-col { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
@container site (max-width: 720px) { .site-content-cols[data-cols] { grid-template-columns: 1fr; } }
/* Citazione, callout, spaziatore. */
.site-content-quote { margin: 0; padding: 6px 0 6px 18px; border-left: 3px solid var(--accent); font-size: 18px; line-height: 1.5; color: var(--text); font-style: italic; }
.site-content-quote-author { display: block; margin-top: 8px; font-size: 13px; font-style: normal; color: var(--muted); }
.site-content-quote-author::before { content: "— "; }
.site-content-callout { padding: 14px 16px; border-radius: 12px; border: 1px solid var(--line-2); background: var(--panel-2); color: var(--text); line-height: 1.55; }
.site-content-callout.tone-info { border-color: color-mix(in srgb, var(--accent) 40%, var(--line-2)); background: var(--accent-tint); }
.site-content-callout.tone-warn { border-color: color-mix(in srgb, #d97706 45%, var(--line-2)); background: color-mix(in srgb, #d97706 10%, transparent); }
.site-content-callout.tone-success { border-color: color-mix(in srgb, #16a34a 45%, var(--line-2)); background: color-mix(in srgb, #16a34a 10%, transparent); }
.site-content-spacer.size-sm { height: 12px; }
.site-content-spacer.size-md { height: 28px; }
.site-content-spacer.size-lg { height: 56px; }

/* ---- STEPS — un rail, non la TERZA fila di rettangoli bianchi ---------------
   .site-step aveva valori IDENTICI a .site-feature-card (stesso padding, stesso
   --panel/--line/--radius-lg/--shadow-1): tre sezioni consecutive erano tre file
   dello stesso rettangolo. Qui cambia la FORMA: le card spariscono. */
.site-steps-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(24px, 3cqw, 44px); max-width: var(--site-content); margin: 0 auto; position: relative; }
.site-steps-grid::before {
  content: ""; position: absolute; top: 21px; left: 12%; right: 12%; height: 2px; z-index: 0;
  background: linear-gradient(90deg, transparent, var(--accent), var(--accent-far), transparent); opacity: .4;
}
.site-step { position: relative; z-index: 1; display: grid; gap: 10px; align-content: start; padding: 0; background: none; border: 0; box-shadow: none; }
.site-step-n {
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  font-size: 15px; font-weight: 800; color: var(--on-accent);
  background: linear-gradient(145deg, var(--accent), var(--accent-far));
  box-shadow: var(--shadow-accent), 0 0 0 6px var(--bg), var(--site-hi);
}
.site-block[data-tone="quiet"] .site-step-n { box-shadow: var(--shadow-accent), 0 0 0 6px var(--accent-quiet), var(--site-hi); }
.site-block[data-tone="raised"] .site-step-n { box-shadow: var(--shadow-accent), 0 0 0 6px var(--bg-2), var(--site-hi); }
.site-step-title { margin: 6px 0 0; font-size: 17px; font-weight: 700; color: var(--text); }
.site-step-body { margin: 0; max-width: 34ch; font-size: 14px; font-weight: 400; color: var(--muted); line-height: 1.6; }
@container site (max-width: 780px) {
  .site-steps-grid { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .site-steps-grid::before { display: none; }
}

/* ---- PREZZI — il centraggio ridiventa un GESTO ----------------------------- */
.site-pricing-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 20px; max-width: 1080px; margin: 0 auto; align-items: stretch; }
.site-price-card { position: relative; display: grid; gap: 16px; align-content: start; padding: 30px 26px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-1), var(--site-hi); }
.site-price-card.is-featured { transform: translateY(-8px); border-color: color-mix(in srgb, var(--accent) 50%, transparent); box-shadow: var(--shadow-3), var(--shadow-accent), var(--site-hi); }
.site-price-card.is-featured::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 4px; border-radius: var(--radius-lg) var(--radius-lg) 0 0; background: linear-gradient(90deg, var(--accent), var(--accent-far)); }
.site-price-name { margin: 0; font-size: 13px; font-weight: 650; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
.site-price-amount { font-size: 40px; font-weight: 800; letter-spacing: -0.03em; line-height: 1; color: var(--text); }
.site-price-period { font-size: 14px; font-weight: 500; color: var(--muted); letter-spacing: 0; }
.site-price-feats { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 10px; font-size: 14px; color: var(--muted); }
.site-price-feats li { display: flex; gap: 9px; align-items: flex-start; line-height: 1.45; }
.site-price-feats li::before { content: "\2713"; color: var(--accent); font-weight: 800; }
.saas-ui .site-canvas a.site-price-cta { width: 100%; box-sizing: border-box; justify-content: center; margin-top: 6px; padding: 11px 18px; border-radius: 10px; font-weight: 650; text-decoration: none; background: transparent; color: var(--accent-ink); border: 1px solid color-mix(in srgb, var(--accent) 38%, var(--line-2)); }
.saas-ui .site-canvas a.site-price-cta:hover { background: var(--accent-tint); border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
.saas-ui .site-canvas a.site-price-cta.primary { background: var(--accent); color: var(--on-accent); border-color: transparent; box-shadow: var(--shadow-accent); }
.saas-ui .site-canvas a.site-price-cta.primary:hover { background: var(--accent-hover); color: var(--on-accent); }
@container site (max-width: 860px) { .site-price-card.is-featured { transform: none; } }

/* ---- TESTIMONIANZE (fuori dal default: un SaaS nuovo non ha clienti) ------- */
.site-testi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; max-width: 1000px; margin: 0 auto; }
.site-testi-card { position: relative; overflow: hidden; display: grid; gap: 16px; padding: 28px 26px 24px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-1), var(--site-hi); }
.site-testi-card::before { content: "\201C"; position: absolute; top: -6px; right: 18px; font-family: var(--font-serif); font-size: 92px; line-height: 1; color: var(--accent); opacity: .16; pointer-events: none; }
.site-testi-quote { position: relative; margin: 0; font-size: 16px; line-height: 1.65; color: var(--text); }
.site-testi-author { font-size: 13px; color: var(--muted); display: flex; align-items: center; gap: 10px; }
.site-testi-author[data-initial]::before { content: attr(data-initial); flex: none; width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; font-size: 13px; font-weight: 700; color: var(--accent-ink); background: var(--accent-tint); border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent); }
.site-testi-author b { color: var(--text); font-weight: 650; }
.site-testi-role { color: var(--muted); }

/* ---- FAQ — split sticky (la terza forma). Il <details> nativo INVARIATO ----- */
.site-faq-split { display: grid; grid-template-columns: minmax(240px, 340px) minmax(0, 1fr); gap: clamp(28px, 5cqw, 72px); align-items: start; }
.site-faq-split .site-section-head { position: sticky; top: 88px; margin-bottom: 0; }
.site-faq-list { display: grid; gap: 12px; min-width: 0; }
.site-faq-item { border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); box-shadow: var(--shadow-1), var(--site-hi); overflow: hidden; transition: border-color .15s; }
.site-faq-item[open] { border-color: color-mix(in srgb, var(--accent) 32%, var(--line-2)); }
.site-faq-q { list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 20px; font-weight: 600; font-size: 15px; color: var(--text); }
.site-faq-q::-webkit-details-marker { display: none; }
.site-faq-q::after { content: ""; flex: none; width: 9px; height: 9px; margin-top: -3px; border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted); transform: rotate(45deg); transition: transform .2s ease, border-color .2s; }
.site-faq-item[open] .site-faq-q::after { transform: rotate(225deg); border-color: var(--accent); }
.site-faq-a { margin: 0; padding: 0 20px 18px; font-size: 14px; color: var(--muted); line-height: 1.65; }
@container site (max-width: 880px) {
  .site-faq-split { grid-template-columns: minmax(0, 1fr); }
  .site-faq-split .site-section-head { position: static; margin-bottom: 8px; }
}

/* ---- CTA BAND (il momento accent, full-bleed nel canvas) -------------------
   IL WASH BIANCO RESTA COM'È. Derivarlo da --on-accent sarebbe una REGRESSIONE:
   il wash bianco abbassa il contrasto solo dove --on-accent È bianco e lo ALZA
   dove è scuro. E comunque la GEOMETRIA lo tiene fuori dal testo: centro a
   (18%,-40%) con `transparent 60%`, titolo a distanza normalizzata ~1.05 > 0.6 →
   wash ZERO sotto il testo. È un bagliore d'angolo. */
.site-cta-band { overflow: hidden; border-block: 1px solid var(--line); color: var(--on-accent); background: radial-gradient(720px 300px at 18% -40%, color-mix(in srgb, #fff 24%, transparent), transparent 60%), linear-gradient(135deg, var(--accent), var(--accent-far)); }
/* La dot-grid SÌ va derivata: `rgba(255,255,255,.5)` è quasi invisibile su una
   banda chiara. Derivarla da --on-accent è un miglioramento STRETTO (identica dove
   --on-accent è bianco, più visibile altrove) e 1px ogni 24px ≈ 0.5% di copertura:
   zero impatto sul contrasto del testo. */
.site-cta-band::before { content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none; background-image: radial-gradient(color-mix(in srgb, var(--on-accent) 55%, transparent) 1px, transparent 1.4px); background-size: 24px 24px; -webkit-mask-image: radial-gradient(600px 300px at 50% 120%, #000, transparent 70%); mask-image: radial-gradient(600px 300px at 50% 120%, #000, transparent 70%); opacity: .2; }
.site-cta-inner { position: relative; z-index: 1; max-width: 780px; margin: 0 auto; display: grid; gap: 16px; justify-items: center; text-align: center; }
.site-cta-title { margin: 0; font-size: clamp(30px, 4.4cqw, 52px); font-weight: 800; line-height: 1.04; color: var(--on-accent); text-wrap: balance; }
/* Contrasto: NON smorzare --on-accent con l'alpha. Su fondo accent pieno, 82% fa
   fallire WCAG AA a 5 brand su 6 della palette (rosa 3.50, teal 3.81, blu 4.01,
   arancio 4.07, verde 4.25); a 100% passano tutti e sei (4.60-6.29). --on-accent è
   già calcolato per-brand per essere leggibile: usarlo pieno. */
.site-cta-sub { margin: 0; max-width: 56ch; font-size: 16px; line-height: 1.55; color: var(--on-accent); }
/* Pulsante INVERSO sulla banda: fondo BIANCO fisso (non --on-accent, che per un accent
   chiaro diventa scuro → testo scuro su fondo scuro). Bianco + --accent-strong resta
   leggibile con ogni accent della palette, sia in chiaro sia in scuro. */
.saas-ui .site-canvas .site-cta-band a.site-cta { background: #fff; color: var(--accent-strong); border-color: transparent; box-shadow: 0 10px 26px -10px rgba(0,0,0,.4); }
.saas-ui .site-canvas .site-cta-band a.site-cta:hover { background: color-mix(in srgb, #fff 92%, var(--accent)); color: var(--accent-strong); }

/* ---- CONTATTI -------------------------------------------------------------- */
.site-contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 44px; align-items: start; max-width: 1000px; margin: 0 auto; }
@container site (max-width: 720px) { .site-contact-grid { grid-template-columns: minmax(0, 1fr); gap: 28px; } }
.site-contact-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 20px; }
.site-contact-list li { display: grid; gap: 5px; }
.site-contact-list a { color: var(--accent-ink); font-weight: 550; text-decoration: none; }
.site-contact-list a:hover { text-decoration: underline; text-underline-offset: 3px; }
.site-contact-k { font-size: 11px; font-weight: 650; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); }
.site-contact-form { display: grid; gap: 14px; padding: 24px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-1), var(--site-hi); }
.site-field { display: grid; gap: 5px; }
.site-field-label { font-size: 12px; font-weight: 600; color: var(--muted); }
.saas-ui .site-canvas .site-contact-form input, .saas-ui .site-canvas .site-contact-form textarea, .saas-ui .site-canvas .site-contact-form .input {
  width: 100%; box-sizing: border-box; padding: 11px 13px; border-radius: var(--radius);
  background: var(--bg-2); color: var(--text); border: 1px solid var(--line-2); font: inherit; font-size: 15px;
  transition: border-color .15s, box-shadow .15s;
}
.saas-ui .site-canvas .site-contact-form input:focus, .saas-ui .site-canvas .site-contact-form textarea:focus, .saas-ui .site-canvas .site-contact-form .input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus-ring); }
.site-ro { display: flex; align-items: center; color: var(--muted); opacity: .7; }

/* ---- FOOTER a COLONNE (+ barra di pagina) ----------------------------------
   Prima il default non includeva block.footer: .site-footer-block era codice morto
   e la pagina finiva su .site-footer, una barra flex da 22px.
   NB: `1.4fr repeat(auto-fit, minmax(130px,1fr))` è INVALIDO (un auto-repeat non
   può coesistere con tracce flessibili nella stessa track list) → l'INTERA
   dichiarazione verrebbe scartata e resterebbe UNA traccia. Due livelli: la track
   list esterna è fissa, l'auto-fit vive nel suo contenitore. */
.site-footer-block { background: var(--bg-2); border-top: 1px solid var(--line); }
.site-footer-inner { display: grid; grid-template-columns: minmax(240px, 1.2fr) minmax(0, 2fr); gap: clamp(28px, 4cqw, 64px); max-width: var(--site-content); margin: 0 auto; align-items: start; }
.site-footer-inner.is-legacy { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; max-width: 1000px; }
.site-footer-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: clamp(20px, 3cqw, 40px); }
.site-footer-brand { display: grid; gap: 12px; align-content: start; min-width: 0; }
.site-footer-word { display: flex; align-items: center; gap: 11px; font-size: clamp(22px, 2.6cqw, 30px); font-weight: 800; letter-spacing: -0.035em; line-height: 1; color: var(--text); }
.site-footer-word::before { content: ""; flex: none; width: 26px; height: 26px; border-radius: 8px; background: linear-gradient(145deg, var(--accent), var(--accent-far)); box-shadow: var(--shadow-accent), var(--site-hi); }
.site-footer-text { margin: 0; max-width: 36ch; font-size: 13px; font-weight: 400; line-height: 1.6; color: var(--muted); }
.site-footer-col { display: grid; gap: 10px; align-content: start; min-width: 0; }
.site-footer-col > b { font-size: 11px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase; color: var(--muted); }
.site-footer-col a { font-size: 13px; font-weight: 400; color: var(--muted); text-decoration: none; }
.site-footer-col a:hover { color: var(--accent-ink); }
.site-footer-links { display: flex; gap: 18px; flex-wrap: wrap; }
.site-footer-links a { font-size: 13px; color: var(--accent-ink); text-decoration: none; }
.site-footer-links a:hover { color: var(--accent); }
.site-footer { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px; padding: 20px var(--site-pad-x); border-top: 1px solid var(--line); font-size: 12px; color: var(--muted); background: var(--bg-2); }
@container site (max-width: 780px) { .site-footer-inner { grid-template-columns: minmax(0, 1fr); gap: 32px; } }

/* ---- Ritocchi dark-mode (i token si adattano; alziamo la luminanza accent) -- */
.saas-ui[data-theme="dark"] .site-hero {
  background:
    radial-gradient(1240px 540px at 50% -8%, var(--site-glow-a), transparent 60%),
    radial-gradient(780px 400px at 84% 2%,  var(--site-glow-b), transparent 58%),
    radial-gradient(900px 500px at 50% -20%, color-mix(in srgb, #fff 4%, transparent), transparent 62%),
    var(--bg);
}
.saas-ui[data-theme="dark"] .site-hero::before { opacity: .7; }
.saas-ui[data-theme="dark"] .site-feature-card:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--line-2)); }
.saas-ui[data-theme="dark"] .site-header { background: color-mix(in srgb, var(--panel) 72%, transparent); }
.saas-ui[data-theme="dark"] .site-mock { border-color: color-mix(in srgb, var(--accent) 18%, var(--line-2)); }
.saas-ui[data-theme="dark"] .site-hero-stage::before,
.saas-ui[data-theme="dark"] .site-showcase-visual::before { opacity: .68; }
.saas-ui[data-theme="dark"] .site-footer-word::before { box-shadow: var(--shadow-accent); }
.saas-ui[data-theme="dark"] .site-block[data-tone="raised"] { background: color-mix(in srgb, var(--panel-2) 60%, var(--bg)); }

/* Qui c'era un blocco reduced-motion che azzerava i `transform` di questi hover. Esisteva SOLO
   come conseguenza della regola globale: quella spegneva la `transition` e lasciava il `transform`
   a scattare di colpo, quindi bisognava azzerarlo. Tolta la globale, la transizione torna a
   funzionare e l'hover si muove come deve — tenere questi reset avrebbe congelato l'hover proprio
   a chi la decisione voleva far animare. Le due cose erano legate, e vanno via insieme. */

/* ---- GUARDIE ANTI-OVERFLOW (360 → 1400) ------------------------------------
   Ogni traccia usa minmax(0,…) e ogni testo del mock ha l'ellissi: nessuna
   superficie nuova può spingere il canvas oltre la sua larghezza. */
.site-canvas :where(.site-showcase-grid, .site-feature-bento, .site-feature-rest,
                    .site-mock-app, .site-faq-split, .site-footer-inner) > * { min-width: 0; }
.site-mock, .site-mock-work, .site-mock-table { max-width: 100%; }
.site-mock svg { max-width: 100%; }

/* ---------- Workspace SaaS (shell navigabile, §7.3-7.4) ---------- */
/* Layout con MENU ORIZZONTALE in alto (non sidebar). */
/* `font-family` va RI-APPLICATA qui: il brand ridefinisce --font-sans su questo elemento,
   ma `body{font-family:var(--font-sans)}` l'ha già RISOLTA (col valore di root) e i figli
   ereditano il font calcolato, non la variabile. Senza questa riga i titoli prendono la
   coppia del brand (regola su h1-h6) e il testo no: tipografia mista. Stessa cosa fa .saas-ui. */
.saas-shell { position: relative; z-index: 1; display: flex; flex-direction: column; min-height: 100vh; font-family: var(--font-sans); }
/* Il workspace applica l'accent del BRAND inline su .saas-shell. Le custom property
   sostituiscono i var() NEL PUNTO IN CUI SONO DICHIARATE: --accent-ink di :root resterebbe
   quindi legato all'accent di PIATTAFORMA e il brand andrebbe perso. Ri-dichiarandolo qui
   si ricalcola sull'accent del brand → testo di brand E leggibile (qualsiasi colore). */
.saas-shell { --accent-ink: color-mix(in srgb, var(--accent) 58%, var(--text)); }
:root[data-theme="dark"] .saas-shell { --accent-ink: color-mix(in srgb, var(--accent) 60%, #fff); }

/* Integrazioni: UNA PER RIGA con icona (leggibilità > densità: il titolo e la descrizione
   hanno spazio, l'occhio scorre una colonna sola). Icona = SVG inline che segue l'accent
   del brand via currentColor — nessun asset, nessuna CDN. --accent-tint non esiste nella
   chrome (è un token di .saas-ui) → color-mix esplicito. */
/* ---------- SELETTORI D'ASPETTO: colori e caratteri (wizard «Aspetto» + Impostazioni → Brand)
   UNA famiglia di classi per DUE selettori, non due copie. Prima `.fontpair-*` e `.pal-*` erano
   regole IDENTICHE scritte due volte — stessa griglia, stesso padding, stesso hover, stesso
   is-active — con sopra il commento «stessa forma, di proposito». Due copie di una decisione
   sono due occasioni di cambiarne una sola: la prova è che erano già divergenti nei figli
   (`.fontpair-sample` 40×40 contro `.pal-chips` alte 28/34/40). Sono la stessa domanda («che
   aspetto ha il mio SaaS?»); adesso sono anche lo stesso oggetto.

   La spunta non è solo il colore del bordo (daltonismo): sfondo tint + anello interno. */
.pick-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(272px, 1fr)); gap: 8px; }
/* flex-start, NON center. Con `center` ogni card centrava il proprio testo sulla propria altezza:
   nella stessa riga «Foresta» (mood su 3 righe) portava il titolo 8px più in basso di «Indaco»
   (mood su 4) — misurato, non stimato. Titoli che ballano di 8px dentro una riga sono la ragione
   per cui la griglia sembrava sciatta. Ancorati in alto, restano allineati comunque vada a capo
   il testo. La griglia pareggia già le altezze delle card di una riga (stretch). */
.pick {
  display: flex; align-items: flex-start; gap: 12px; text-align: left; padding: 11px 12px;
  border: 1px solid var(--line-2); border-radius: var(--radius, 12px); background: var(--panel);
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.pick:hover { border-color: color-mix(in srgb, var(--accent) 35%, var(--line-2)); }
.pick.is-active {
  border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.pick:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring, color-mix(in srgb, var(--accent) 35%, transparent)); }
.pick-meta { min-width: 0; display: grid; gap: 2px; }
.pick-meta b { font-size: 13px; }
.pick-mood { font-size: 11px; color: var(--muted); line-height: 1.35; }
/* LA FIGURA: stessa impronta 40×40 per l'«Aa» e per la palette, così le due griglie hanno la
   stessa colonna sinistra e si leggono come due varianti della stessa cosa. */
.pick-fig { flex: none; width: 40px; height: 40px; border-radius: var(--radius-sm); }
.pick-aa {
  display: grid; place-items: center; font-size: 19px; line-height: 1;
  color: var(--accent-ink, var(--accent));
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 16%, transparent);
}
/* La palette come UN blocco diviso in tre, non tre chip sparse. Prima erano tre rettangoli
   sovrapposti di altezza 40/34/28: la gerarchia c'era ma il risultato era un bordo superiore
   frastagliato, che è esattamente ciò che si nota per primo in una griglia di otto. La gerarchia
   resta — passa dall'altezza alla LARGHEZZA: il primario, il colore che l'utente clicca, occupa
   metà del campione. Un solo raggio, un solo bordo, niente seghettatura. */
.pick-pal { display: flex; overflow: hidden; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12); }
.pick-pal i { display: block; height: 100%; }

/* ---------- ANTEPRIMA D'ASPETTO (passo «Aspetto») ----------
   Vedi components/brand-preview.tsx per il PERCHÉ. Qui vale solo una regola: dentro `.bpv` NON
   si scrive un colore. Ogni valore è un token di `.saas-ui`, che il brand scelto ricalcola da
   sé — se qui comparisse un hex, l'anteprima potrebbe dire una cosa e il pannello farne
   un'altra, ed è precisamente il difetto che questo componente esiste per non avere. */
.bpv {
  display: flex; overflow: hidden; height: 172px; user-select: none;
  border: 1px solid var(--line-2); border-radius: var(--radius, 12px); background: var(--bg);
}
.bpv-side {
  flex: none; width: 152px; padding: 12px 10px; display: grid; gap: 3px; align-content: start;
  background: var(--sidebar); border-right: 1px solid var(--line);
}
.bpv-brand { display: flex; align-items: center; gap: 8px; min-width: 0; margin-bottom: 9px; }
.bpv-brand b { min-width: 0; font-size: 12px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bpv-mark {
  flex: none; width: 22px; height: 22px; border-radius: var(--radius-2xs); display: grid; place-items: center;
  background: linear-gradient(150deg, var(--accent), var(--accent-2));
  color: var(--on-accent, #fff); font-size: 11px; font-weight: 800; line-height: 1;
}
.bpv-nav { font-size: 11px; color: var(--muted); padding: 5px 8px; border-radius: 7px; }
.bpv-nav.is-on { background: var(--accent-tint); color: var(--accent-ink); font-weight: 600; }
.bpv-main { flex: 1; min-width: 0; padding: 12px 14px; display: grid; gap: 7px; align-content: start; }
.bpv-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.bpv-head h4 { margin: 0; font-size: 14px; color: var(--text); }
.bpv-btn {
  flex: none; font-size: 11px; font-weight: 600; padding: 5px 10px; border-radius: var(--radius-sm);
  background: linear-gradient(135deg, var(--accent), var(--accent-2)); color: var(--on-accent, #fff);
}
.bpv-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 9px 10px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.bpv-row i { display: block; height: 6px; border-radius: 3px; background: var(--line-2); }
.bpv-tag {
  flex: none; font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: var(--pill);
  background: color-mix(in srgb, var(--brand-accent, var(--accent)) 15%, transparent);
  color: color-mix(in srgb, var(--brand-accent, var(--accent)) 62%, var(--text));
}
.bpv-tag.is-alt {
  background: color-mix(in srgb, var(--brand-secondary, var(--accent-2)) 15%, transparent);
  color: color-mix(in srgb, var(--brand-secondary, var(--accent-2)) 62%, var(--text));
}
/* Su schermo stretto la sidebar va RISTRETTA, non lasciata com'è. A 375px l'anteprima è larga
   316: una sidebar da 152 se ne prende il 48%, contro il 21% che occupa nel pannello vero a
   larghezza piena — misurato. Le proporzioni sono metà di ciò che un'anteprima comunica, e una
   mezza-mezza non somiglia a nessun gestionale esistente: mostrerebbe fedelmente i colori giusti
   dentro un layout che non esiste. Ristretta a 104 torna un terzo, che a questa larghezza è
   quanto di più vicino al vero si possa stare restando leggibili. */
@media (max-width: 560px) {
  .bpv { height: 154px; }
  .bpv-side { width: 104px; padding: 10px 7px; }
  .bpv-brand { margin-bottom: 7px; }
  .bpv-brand b { font-size: 11px; }
  .bpv-nav { font-size: 11px; padding: 4px 6px; }
  .bpv-main { padding: 10px 11px; }
}

/* ---------- Attesa della GENERAZIONE ----------
   Le fasi arrivano dal server (`saas_project.gen_phase`): qui si anima ciò che è già vero, non si
   inventa un avanzamento. Per questo non esiste nessuna @keyframes che «avanza»: il tempismo è
   del worker. Le animazioni servono all'ENTRATA di una riga e al respiro di quella in corso —
   cioè a rendere leggibile un cambiamento, non a simularlo. */
/* La scena d'attesa a pagina intera: un pannello solo, largo quanto basta a leggere le fasi
   senza andare a capo. */
.genx-stage { max-width: 460px; width: 100%; padding: 22px 24px; }
.genx-head { display: flex; align-items: center; gap: 12px; }
.genx-head > div { display: grid; gap: 2px; }
.genx-head b { font-size: 15px; }
.genx-sub { font-size: 12px; }
.genx { margin-top: 18px; }
.genx-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; position: relative; }
/* Il filo che unisce i passi: si vede che è UN percorso, non sei righe. Parte dal primo pallino e
   arriva all'ultimo — da qui i 15px di offset e l'altezza calcolata sugli estremi. */
.genx-list::before {
  content: ""; position: absolute; left: 11px; top: 15px; bottom: 15px; width: 2px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 45%, transparent), var(--line));
  border-radius: var(--radius-2xs);
}
.genx-item {
  display: flex; align-items: flex-start; gap: 12px; padding: 6px 0; position: relative;
  /* --i è l'indice della fase: le righe entrano a cascata, una dopo l'altra. È l'unico ritardo
     a tempo di tutta la schermata, e non racconta nulla — è solo l'apparire della LISTA. */
  animation: genx-in 0.45s var(--ease, cubic-bezier(0.22, 1, 0.36, 1)) both;
  animation-delay: calc(var(--i, 0) * 70ms);
}
.genx-dot {
  flex: none; width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
  font-size: 12px; font-weight: 700; background: var(--panel); color: var(--faint);
  border: 2px solid var(--line-2); z-index: 1; position: relative;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.genx-text { min-width: 0; display: grid; gap: 2px; padding-top: 2px; }
.genx-text b { font-size: 13px; font-weight: 600; color: var(--faint); transition: color var(--dur) var(--ease); }
.genx-hint { font-size: 11px; color: var(--muted); line-height: 1.4; animation: genx-in 0.35s var(--ease) both; }

/* FATTA: il worker è andato oltre, quindi è finita per davvero. */
.genx-item.is-done .genx-dot { background: var(--good); border-color: var(--good); color: #fff; }
.genx-item.is-done .genx-text b { color: var(--muted); }
/* IN CORSO: l'unica che respira, e l'unica che si spiega.
   L'anello era ACCESO ma FERMO, e l'unico moto era il puntino da 8px che si dilatava di 3px ogni
   1,6s: tecnicamente animato, in pratica indistinguibile da una riga selezionata. Ma «in corso» e
   «selezionato» sono due cose diverse, e questa schermata esiste apposta per dire la prima.
   Il pieno lo prende l'ARCO che gira — il segno che chiunque legge come «sta lavorando» — e per
   farlo vedere il bordo del pallino torna a fare da BINARIO (accent tenue): un anello pieno
   d'accent nasconderebbe l'arco che ci corre sopra. Il puntino continua a respirare sotto.
   Nessuna delle due animazioni misura alcunché: girano a velocità fissa perché non sappiamo
   quanto manca, e fingere di saperlo sarebbe la barra di avanzamento finta che questa schermata
   ha deliberatamente evitato. Dicono «sono vivo», non «sono all'80%». */
.genx-item.is-now .genx-dot {
  border-color: color-mix(in srgb, var(--accent) 26%, transparent); background: var(--panel);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 12%, transparent);
}
.genx-item.is-now .genx-dot::after {
  content: ""; position: absolute; inset: -2px; border-radius: 50%;
  border: 2px solid transparent; border-top-color: var(--accent); border-right-color: var(--accent);
  animation: genx-spin 0.9s linear infinite;
}
.genx-item.is-now .genx-text b { color: var(--text); }
.genx-pulse {
  width: 8px; height: 8px; border-radius: 50%;
  background: linear-gradient(150deg, var(--accent), var(--accent-2));
  animation: genx-breathe 1.6s var(--ease, ease-in-out) infinite;
}
/* NON ANCORA: presente ma spenta. Sapere cosa manca fa parte del capire dove sei. */
.genx-item.is-wait { opacity: 0.5; }
.genx-foot { font-size: 12px; margin: 14px 0 0; text-align: center; }

@keyframes genx-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes genx-breathe {
  0%, 100% { transform: scale(1); opacity: 0.85; }
  50% { transform: scale(1.45); opacity: 1; }
}
@keyframes genx-spin { to { transform: rotate(360deg); } }
/* Qui c'era la variante a movimento ridotto di questa scena: l'arco spariva e il bordo del pallino
   tornava accent pieno, così la riga in corso restava riconoscibile senza girare. Tolta con la
   regola globale (vedi §~483): l'arco ora gira per tutti.
   Nota per chi legge: era proprio questa scena a far sembrare rotta l'attesa — nel browser di
   verifica `prefers-reduced-motion` è forzato, quindi l'arco non partiva mai e il bullet sembrava
   fermo. La variante funzionava; era il contesto a nasconderla. */

.integ-list { display: grid; gap: 8px; }
.integ-row {
  display: flex; align-items: flex-start; gap: 12px; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--radius, 12px); background: var(--panel);
  transition: border-color 0.15s;
}
.integ-row:hover { border-color: color-mix(in srgb, var(--accent) 28%, var(--line-2)); }
.integ-ic {
  flex: none; width: 38px; height: 38px; border-radius: var(--radius); display: grid; place-items: center;
  color: var(--accent-ink, var(--accent));
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
}
.integ-main { min-width: 0; display: grid; gap: 4px; }
.integ-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.integ-title { font-size: 13px; font-weight: 600; }
.integ-sum { margin: 0; font-size: 12px; line-height: 1.45; color: var(--muted); }
.saas-top {
  display: flex; align-items: center; gap: 20px; padding: 0 24px; height: 56px;
  border-bottom: 1px solid var(--line); background: color-mix(in srgb, var(--bg-2) 82%, transparent);
  position: sticky; top: 0; z-index: 5; backdrop-filter: blur(10px);
}
@media (max-width: 820px) { .saas-top { flex-wrap: wrap; height: auto; padding: 10px 16px; gap: 10px; } }
.saas-brand { display: flex; align-items: center; gap: 10px; font-size: 15px; letter-spacing: var(--tracking-title); }
.saas-brand .mark { width: 22px; height: 22px; }
.saas-brand-env { margin-left: 4px; }
.saas-menu { display: flex; gap: 2px; flex: 1; overflow-x: auto; }
.saas-tab {
  padding: 8px 14px; font-size: 13px; color: var(--muted); white-space: nowrap;
  border-bottom: 2px solid transparent; transition: color 0.15s, border-color 0.15s;
}
.saas-tab:hover { color: var(--text); }
.saas-tab.is-active { color: var(--text); border-bottom-color: var(--accent); }
.saas-top-right { display: flex; align-items: center; gap: 12px; }
.saas-top-right a:hover { color: var(--accent); }
/* Switch DOPPIO RUOLO (account unificati): passaggio rapido dashboard costruttore ↔ console admin. */
.role-switch { white-space: nowrap; }
.saas-stage { flex: 1; width: 100%; max-width: 1300px; margin: 0 auto; padding: 22px 24px 48px; min-width: 0; }
@media (max-width: 560px) { .saas-stage { padding: 16px 14px 32px; } }

/* Chat + canvas (§7.4 VINCOLO): chat a sinistra, tela viva (iframe) a destra.
   FULLWIDTH edge-to-edge (split-pane), a tutta altezza sotto la topbar (56px). */
.cc { display: grid; grid-template-columns: 380px minmax(0, 1fr); gap: 0; align-items: stretch; height: calc(100vh - 56px); min-height: 0; }
@media (max-width: 900px) { .cc { grid-template-columns: minmax(0, 1fr); height: auto; } }
.cc-chat {
  border: 0; border-right: 1px solid var(--line); background: var(--panel);
  display: flex; flex-direction: column; overflow: hidden; height: 100%;
}
@media (max-width: 900px) { .cc-chat { height: 60vh; border-right: 0; border-bottom: 1px solid var(--line); } }
.cc-chat-head { padding: 12px 14px; border-bottom: 1px solid var(--line); }
/* `min-height: 0` NON è ornamentale: senza, un figlio flex non scende sotto la propria altezza
   naturale e la conversazione, invece di scorrere, spinge fuori la barra di scrittura. */
.cc-msgs { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }

/* IL PANNELLO DI STRUTTURA — regione sua, non un pezzo della barra di scrittura.
   Stava dentro `.cc-compose`, cioè nell'ultimo elemento della colonna, che si dimensiona sul
   contenuto e non ha tetto: un editor alto la faceva crescere senza limite, la conversazione veniva
   schiacciata e la casella finiva a metà schermo. Qui ha un massimo e scorre da sé, così cresce fin
   dove può e poi si ferma invece di spingere via il resto. */
/* COMPONENTI DELLA SUPERFICIE — una finestra, non un pezzo della colonna della chat.
   È stato prima dentro la barra di scrittura (che non ha tetto e cresceva schiacciando la
   conversazione), poi in una regione propria: ma un editor denso dentro 380px resta illeggibile —
   caselle mandate a capo, etichette troncate. Non era dove metterlo: è che non ci sta.
   `<dialog>` porta scrim, inertizzazione ed Esc dal browser; qui c'è solo la forma. */
.cc-structure { width: min(760px, calc(100vw - 32px)); max-height: min(84vh, 760px);
  padding: 0; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); color: var(--text); box-shadow: var(--shadow-3); overflow: hidden; }
.cc-structure::backdrop { background: rgba(9, 12, 18, .5); }
.cc-structure-head { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 13px 16px; border-bottom: 1px solid var(--line); font-size: 14px; }
/* Il corpo è l'unica cosa che scorre: l'intestazione resta ferma, così il pulsante di chiusura
   non scappa via mentre si scorre un elenco lungo di zone. */
.cc-structure-body { overflow-y: auto; max-height: calc(min(84vh, 760px) - 48px); padding: 4px 0 8px; }
.cc-msg { display: flex; gap: 8px; align-items: flex-start; }
.cc-user { justify-content: flex-end; }
.cc-msg-tag {
  font-size: 10px; font-weight: 700; letter-spacing: 0.05em; color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); border-radius: var(--radius-2xs); padding: 2px 5px; margin-top: 3px;
}
/* GLI A CAPO SONO PARTE DELLA RISPOSTA. Senza `pre-wrap` ogni messaggio strutturato — la mappa
   delle capacità, i componenti proposti dopo un rifiuto, la diagnosi grafica — si appiattiva in un
   paragrafo unico: gli elenchi puntati diventavano una colata di testo con dei «•» in mezzo. Il
   server scriveva la struttura e il CSS la buttava via. `pre-wrap` e non `pre` perché le righe
   lunghe devono comunque andare a capo da sole nella colonna stretta della chat. */
.cc-bubble { padding: 9px 12px; border-radius: var(--radius); font-size: 13px; line-height: 1.5; border: 1px solid var(--line-2); background: var(--panel-2); max-width: 82%; white-space: pre-wrap; overflow-wrap: anywhere; }

/* SCELTE CLICCABILI. Pulsanti larghi e impilati, non chip in fila: l'etichetta porta con sé una
   riga di spiegazione, e in una colonna da ~380px una fila orizzontale le taglierebbe entrambe. */
.cc-choices { display: grid; gap: 6px; margin-top: 8px; max-width: 82%; }
.cc-choices-q { font-size: 11px; font-weight: 650; color: var(--faint); text-transform: uppercase; letter-spacing: .04em; }
.cc-choice { display: grid; gap: 2px; justify-items: start; text-align: left; width: 100%; padding: 8px 11px; border-radius: var(--radius); border: 1px solid var(--line-2); background: var(--panel); color: var(--text); font: inherit; font-size: 12.5px; cursor: pointer; transition: border-color .12s ease, background .12s ease; }
.cc-choice:hover:not(:disabled) { border-color: color-mix(in srgb, var(--accent) 45%, var(--line-2)); background: var(--accent-quiet); }
.cc-choice:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
.cc-choice:disabled { opacity: .55; cursor: default; }
.cc-choice b { font-weight: 600; }
.cc-choice-hint { font-size: 11px; color: var(--faint); line-height: 1.4; }
.cc-user .cc-bubble { background: color-mix(in srgb, var(--accent) 14%, transparent); border-color: color-mix(in srgb, var(--accent) 34%, transparent); }
.cc-typing { font-style: italic; color: var(--faint); animation: cc-pulse 1.3s ease-in-out infinite; }
/* LE FASI della modifica in corso: cosa sta facendo il server, non cosa crede di fare il modello.
   Il passo aperto pulsa, quelli conclusi restano con la spunta — così si vede sia dove si è, sia
   cosa è già stato fatto se la richiesta si ferma a metà. */
.cc-phases { display: grid; gap: 5px; }
.cc-phase { display: grid; grid-template-columns: 14px 1fr; gap: 7px; align-items: start;
  font-size: 12px; color: var(--muted); }
.cc-phase-mark { color: var(--faint); line-height: 1.45; }
.cc-phase:not(.is-done) { animation: cc-pulse 1.3s ease-in-out infinite; }
.cc-phase.is-done { color: var(--text); }
.cc-phase.is-done .cc-phase-mark { color: var(--good); }
.cc-phase-detail { display: block; color: var(--faint); font-size: 11.5px; margin-top: 1px; }
@keyframes cc-pulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
/* LA COLONNA DELLA CHAT NON LA DECIDE IL SUO CONTENUTO.
   `display: grid` senza `grid-template-columns` crea una colonna implicita la cui funzione di
   dimensionamento MINIMO è `auto`: il pavimento diventa il min-content del figlio più largo, senza
   alcun tetto legato alla larghezza della colonna. Bastava quindi una riga non spezzabile — il
   selettore dell'elemento puntato, in monospace e `white-space: nowrap` — perché l'intera chat si
   allargasse spingendo via la tela. `minmax(0, 1fr)` rimette il tetto: la traccia può stringersi
   sotto il contenuto, e a troncare pensano i figli. */
.cc-compose { padding: 12px; border-top: 1px solid var(--line); display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; }
.cc-compose-box { border: 1px solid var(--line-2); border-radius: var(--radius); background: var(--panel-2); overflow: hidden; transition: border-color 0.15s; }
.cc-compose-box:focus-within { border-color: color-mix(in srgb, var(--accent) 45%, var(--line-2)); }
.cc-textarea {
  width: 100%; border: 0; background: transparent; color: var(--text); font: inherit;
  padding: 12px 13px; resize: none; min-height: 78px; line-height: 1.5; display: block;
}
.cc-textarea::placeholder { color: var(--faint); }
.cc-textarea:disabled { cursor: not-allowed; }
.cc-compose-bar { display: flex; align-items: center; justify-content: space-between; padding: 6px 8px 8px; }
.cc-compose-tools { display: flex; gap: 4px; }
.cc-icon-btn { width: 32px; height: 32px; border-radius: var(--radius-sm); display: grid; place-items: center; color: var(--muted); border: 1px solid transparent; }
.cc-icon-btn:hover:not(:disabled) { background: var(--panel); border-color: var(--line-2); color: var(--text); }
.cc-icon-btn:disabled { opacity: var(--disabled-opacity); cursor: not-allowed; }
/* PUNTA UN ELEMENTO (§ I5): il pulsante acceso, la barra dell'elemento scelto, e la tela in
   modalità puntamento. Il mirino nella tela lo disegna la tela stessa (`[data-picking]`), perché
   il cursore e l'evidenziazione devono stare dove sta il contenuto. */
.cc-icon-btn.is-on { background: var(--accent-tint); border-color: var(--accent); color: var(--accent-ink, var(--accent)); }
/* `min-width: 0` sul contenitore flex: senza, il suo minimo automatico è il min-content dei figli
   e la riga rifiuta di stringersi — la seconda metà della stessa causa. */
.cc-anchor { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; padding: 6px 8px; min-width: 0;
  border: 1px solid var(--line-2); border-radius: var(--radius-sm); background: var(--panel-2); }
/* L'etichetta prende lo spazio che era del selettore, e si tronca da sé invece di spingere. */
.cc-anchor > .chip { flex: 1; min-width: 0; }
/* La barra della selezione in corso: sta sopra la tela perché è lì che avviene il gesto. */
.cc-picking-bar { display: flex; align-items: center; gap: 8px; padding: 8px 12px;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line-2));
  background: var(--accent-tint); color: var(--text); }
.cc-frame.is-picking { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Dentro la tela: cursore a mirino e contorno su ciò che si sta per scegliere. */
body[data-picking] { cursor: crosshair; }
body[data-picking] [data-pick-hover] { outline: 2px solid var(--accent, #4f46e5); outline-offset: 1px;
  background: color-mix(in srgb, var(--accent, #4f46e5) 8%, transparent); }
.pick-hint { position: fixed; left: 50%; bottom: 14px; transform: translateX(-50%); z-index: 60;
  padding: 6px 12px; border-radius: 999px; font-size: 12px; background: var(--text); color: var(--panel);
  box-shadow: 0 4px 14px rgba(0,0,0,.18); pointer-events: none; }
.cc-compose-hint { font-size: 11px; text-align: center; }
.cc-canvas { border: 0; overflow: hidden; background: var(--panel-2); min-width: 0; display: flex; flex-direction: column; height: 100%; }
.cc-toolbar { display: flex; align-items: center; gap: 10px; padding: 10px 16px; border-bottom: 1px solid var(--line); background: var(--panel); flex: none; }
.cc-toolbar-right { margin-left: auto; display: flex; gap: 8px; }
/* Tela = iframe read-only del runtime draft (§7.4), riempie la tela */
.cc-frame { flex: 1; width: 100%; border: 0; display: block; background: var(--bg); }
/* Bottone Pubblica + esito inline */
.pub-wrap { display: inline-flex; align-items: center; gap: 10px; }
.pub-msg { font-size: 12px; font-weight: 600; white-space: nowrap; }
.pub-msg.ok { color: var(--good); }
.pub-msg.err { color: var(--crit); }
/* Storico versioni (Impostazioni) */
.wk-versions { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.wk-versions li { display: flex; align-items: center; gap: 12px; font-size: 12px; flex-wrap: wrap; }

/* Fullwidth EDGE-TO-EDGE per le viste chat+canvas: nessun margine esterno. */
.saas-stage.is-full { max-width: none; padding: 0; }
.rt-embed { padding: 20px 22px; display: grid; gap: 14px; max-width: 1160px; margin: 0 auto; }
@media (max-width: 560px) { .rt-embed { padding: 12px; } }
.site-embed { min-height: 100vh; }
.site-embed .site-canvas { border: 0; border-radius: 0; box-shadow: none; }

/* ── Editing INLINE della tela del sito (§7.4) ────────────────────────────────
   TUTTO scoped sotto `.site-editing`, che esiste SOLO nella tela del rivenditore
   proprietario: il sito pubblico non deve prendersi nemmeno un cursore diverso.
   `.site-t` (il marcatore di `trText`) è per il resto INVISIBILE: uno <span> dentro
   un <h1> non deve cambiare di una virgola come il testo si vede o si manda a capo. */
.site-t { display: inline; }
.site-editing [data-tr-key] {
  cursor: text; border-radius: var(--radius-2xs);
  /* `box-shadow` e non `outline`/`border`: non occupa spazio e non sposta di 1px il
     testo quando compare — su un titolo si vedrebbe "saltare" al passaggio del mouse. */
  box-shadow: 0 0 0 1.5px transparent;
  transition: box-shadow .12s ease, background-color .12s ease;
}
.site-editing [data-tr-key]:hover {
  box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
.site-editing [data-tr-key][contenteditable] {
  outline: none;
  box-shadow: 0 0 0 2px var(--accent);
  background: color-mix(in srgb, var(--accent) 9%, transparent);
}
/* Indicazione + esito, ancorata alla tela e fuori dal flusso: non deve spostare il sito. */
.site-edit-hud {
  position: fixed; left: 50%; bottom: 14px; transform: translateX(-50%);
  z-index: 30; pointer-events: none; max-width: min(92vw, 560px); text-align: center;
}
.site-edit-hud .chip { box-shadow: var(--shadow-2, 0 6px 20px rgb(0 0 0 / .18)); }
/* (Qui c'era `.site-editing [data-tr-key] { transition: none; }` per il movimento ridotto: via
   con gli altri — l'evidenziazione dei testi modificabili sfuma per tutti.) */

/* Tab "in arrivo" del menu piattaforma */
.saas-tab.is-soon { color: var(--faint); cursor: default; }
.saas-tab.is-soon:hover { color: var(--faint); border-bottom-color: transparent; }

/* Sezione SaaS (§7.2): elenco card + quadrettone */
.saas-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; }
.saas-card {
  border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 16px; background: var(--panel);
  display: flex; flex-direction: column; gap: 12px; transition: border-color 0.2s, transform 0.2s, box-shadow 0.2s;
}
a.saas-card:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--line-2)); transform: translateY(-2px); box-shadow: var(--shadow); }
.saas-card-top { display: flex; align-items: center; gap: 10px; }
.saas-card-mark { width: 26px; height: 26px; border-radius: var(--radius-sm); flex: none; box-shadow: inset 0 0 0 1px color-mix(in srgb, #fff 12%, transparent); }
.saas-card-mark.spin { background: conic-gradient(from 0deg, var(--accent), transparent 70%); animation: spin 1.1s linear infinite; }
.saas-card-name { font-weight: 600; flex: 1; letter-spacing: var(--tracking-title); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.saas-card-meta { font-size: 12px; color: var(--muted); }
.saas-card-foot { display: flex; justify-content: space-between; align-items: center; font-size: 12px; margin-top: auto; }
.saas-card-open { color: var(--accent); font-weight: 600; }
.saas-card.is-generating { border-style: solid; }
.saas-card.is-empty {
  align-items: center; justify-content: center; text-align: center; gap: 6px; min-height: 168px;
  border-style: dashed; color: var(--muted);
}
a.saas-card.is-empty:hover { color: var(--accent); }
.saas-empty-plus { font-size: 30px; line-height: 1; color: var(--accent); font-weight: 300; }
/* Guadagni ultimi 30 giorni sulla card */
.saas-card-earn { display: flex; align-items: baseline; gap: 8px; padding: 10px 0; border-top: 1px solid var(--line); margin-top: 2px; }
.saas-card-earn-val { font-size: 18px; font-weight: 700; color: var(--good); font-variant-numeric: tabular-nums; letter-spacing: var(--tracking-title); }
.saas-card-earn .faint { font-size: 11px; }

/* Sezione "I tuoi SaaS" nella dashboard unificata */
.wk-saas-section { margin-top: 22px; scroll-margin-top: 72px; }
.wk-section-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 14px; }
.wk-section-head h2 { font-size: 18px; }

/* `@keyframes spin` era ridefinita qui, identica a quella di riga ~427. Due definizioni con lo
   stesso nome non convivono: vince l'ultima, quindi la prima era già morta — e il giorno in cui
   qualcuno avesse modificato quella, non sarebbe successo niente. Ne resta una sola. */

.wk-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-bottom: 16px; }
@media (max-width: 720px) { .wk-kpis { grid-template-columns: 1fr; } }
.wk-kpi { padding: 16px 18px; }
.wk-kpi-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--faint); }
/* Il numerone di una card KPI ha UNA misura sola: `.adm-kpi .k-val` (console) lo scrive a 22px,
   questo (workspace) lo scriveva a 26 — stesso ruolo, due dimensioni, per nessun motivo. */
.wk-kpi-value { font-size: 22px; font-weight: 700; letter-spacing: var(--tracking-title); margin-top: 6px; font-variant-numeric: tabular-nums; }
.wk-kpi-hint { font-size: 12px; color: var(--muted); margin-top: 2px; }
.wk-steps { margin: 0; padding-left: 18px; display: grid; gap: 8px; font-size: 13px; color: var(--muted); }
.wk-steps li::marker { color: var(--accent); }
/* Pannello Domini del SaaS (§7.3): sottodomini + domini custom (CNAME/verifica DNS) */
.dom-rows { display: grid; gap: 10px; }
.dom-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.dom-k { font-size: 12px; color: var(--muted); min-width: 64px; }
.dom-list { list-style: none; margin: 0 0 14px; padding: 0; display: grid; gap: 10px; }
.dom-item { border: 1px solid var(--line); border-radius: var(--radius); padding: 12px 14px; background: var(--panel-2); display: grid; gap: 10px; }
.dom-item-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.dom-item-actions { margin-left: auto; display: inline-flex; gap: 8px; }
.dom-dns { display: grid; gap: 6px; font-size: 12px; }
.dom-dns-rec { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 10px; border-radius: var(--radius-sm); background: var(--panel); border: 1px solid var(--line); }
.dom-add { display: flex; gap: 8px; align-items: center; }
.dom-add .input { flex: 1; min-width: 180px; }
@media (max-width: 560px) { .dom-item-actions { margin-left: 0; } }
.wk-subnav { display: flex; gap: 6px; margin-bottom: 14px; flex-wrap: wrap; }
.wk-subnav-item {
  padding: 6px 12px; border-radius: var(--radius-sm); font-size: 12px; color: var(--muted);
  border: 1px solid var(--line); background: var(--panel-2);
}
.wk-subnav-item.is-active { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); background: color-mix(in srgb, var(--accent) 10%, transparent); }

/* Anteprima sito in cornice "browser" */
.wk-site-frame { border: 1px solid var(--line-2); border-radius: 12px; overflow: hidden; box-shadow: var(--shadow); }
.wk-site-chrome {
  display: flex; align-items: center; gap: 7px; padding: 9px 14px;
  background: var(--panel-2); border-bottom: 1px solid var(--line);
}
.wk-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--line-2); }
.wk-url { margin-left: 10px; font-size: 12px; color: var(--muted); }
.wk-site-body { max-height: 620px; overflow: auto; background: var(--panel); }
/* Nel frame il sito non deve avere angoli/margini propri: neutralizza il canvas */
.wk-site-body .site-block { padding: 40px 32px; }

/* Impostazioni: swatch dei colori del brand */
.wk-swatches { display: flex; gap: 18px; flex-wrap: wrap; }
.wk-swatch { display: flex; align-items: center; gap: 8px; }
.wk-swatch-chip { width: 26px; height: 26px; border-radius: var(--radius-xs); border: 1px solid var(--line-2); }
/* Niente `capitalize`: serviva a far sembrare presentabile la CHIAVE grezza («secondary» →
   «Secondary»). Ora l'etichetta è scritta in italiano, e capitalize la storpierebbe in
   «Colore Secondario». */
.wk-swatch-k { font-size: 12px; }
.wk-soon .sub { max-width: 68ch; }

/* ============================================================================
   GENERATED SaaS THEME — "Crisp SaaS Admin" (light, professional, gestionale).
   Scope: everything is under .saas-ui, so the dark builder console (outside
   .saas-ui) is untouched. Applied by wrapping the generated panel/site in
   <div class="saas-ui" style="--accent:#..;--accent-2:#..">. Opt-in dark:
   data-theme="dark"; opt-in serif site headings: data-site-serif.
   Prodotto da un panel di design (workflow), scelta "Crisp SaaS Admin".
   ========================================================================== */
.saas-ui{
  /* — DESIGN SYSTEM "Refined Enterprise" — tutto derivato da UN input di brand (--accent) —
     via color-mix, così ogni SaaS resta coeso col proprio colore. Chiaro default + dark opt-in. */
  --accent:#4f56d3; --accent-2:#6366f1;
  --on-accent:#fff;
  --accent-strong: color-mix(in srgb, var(--accent) 88%, #000);
  --accent-hover:  color-mix(in srgb, var(--accent) 90%, #000);
  --accent-tint:   color-mix(in srgb, var(--accent) 9%, transparent);
  --accent-tint-2: color-mix(in srgb, var(--accent) 15%, transparent);
  --accent-quiet:  color-mix(in srgb, var(--accent) 5%, transparent);
  /* INK: testo accent AA-safe SU tint. Da usare per ogni testo-su-tint (chiude i bug di contrasto). */
  --accent-ink:    color-mix(in srgb, var(--accent) 70%, var(--text));
  --focus-ring:    color-mix(in srgb, var(--accent) 30%, transparent);

  /* neutri con leggera tinta di brand → coesione senza spostare il contrasto */
  --bg:      color-mix(in srgb, var(--accent) 3%, #f6f8fb);
  --bg-2:    #ffffff;
  --panel:   #ffffff;
  --panel-2: color-mix(in srgb, var(--accent) 5%, #eef1f7);
  --sidebar: color-mix(in srgb, var(--accent) 7%, #edeff5);
  --line:    color-mix(in srgb, var(--accent) 7%, #e6e9f0);
  --line-2:  color-mix(in srgb, var(--accent) 11%, #d5dae4);
  /* --faint era #7b8493 = 3,77:1 su bianco, SOTTO il minimo AA di 4,5. Non era un dettaglio:
     il tema chiaro è il default (direttiva di Luigi), e --faint veste le intestazioni di OGNI
     tabella, le etichette dei KPI e le targhette di stato — tutte a 10-11px, cioè proprio dove
     un contrasto debole si paga di più. #6b7280 = 4,83:1, con margine.
     La gerarchia regge e resta leggibile a ogni gradino: text (#1a2130) > muted (5,98:1) >
     faint. Un token usato per il TESTO dev'essere leggibile; se serve qualcosa di più
     tenue, quello è decorazione, non testo.

     SECONDO GIRO (misurato, non a occhio): #6b7280 passava — ma SOLO su `--panel`, che è
     bianco. Le altre due superfici del pannello sono TINTE e più scure: `--panel-2` e
     `--sidebar`. Lì lo stesso grigio scendeva a 3,90-4,05:1, e non su decorazione: sulle
     etichette dei gruppi in sidebar, sulle intestazioni di colonna del mock e sul
     suggerimento «⌘K». Un token va tarato sulla superficie PIÙ SCURA su cui viene scritto,
     non sulla più chiara: #5e6875 tiene 5,66:1 sul bianco e 4,53:1 sulle tinte.
     Lo scuro aveva lo stesso difetto (4,04:1 su `--panel-2`): #8b93a5 lo porta a 4,89:1. */
  --text:#1a2130; --muted:#5b6472; --faint:#5e6875;

  /* semantici + tint + ink standardizzati (auto-adattano in dark) */
  --good:#0f7a43; --warn:#b45c09; --crit:#cf2f2f; --info:#2563eb;
  --good-tint:color-mix(in srgb,var(--good) 13%,transparent); --good-ink:color-mix(in srgb,var(--good) 68%,var(--text));
  --warn-tint:color-mix(in srgb,var(--warn) 15%,transparent); --warn-ink:color-mix(in srgb,var(--warn) 70%,var(--text));
  --crit-tint:color-mix(in srgb,var(--crit) 13%,transparent); --crit-ink:color-mix(in srgb,var(--crit) 70%,var(--text));
  --info-tint:color-mix(in srgb,var(--info) 13%,transparent); --info-ink:color-mix(in srgb,var(--info) 68%,var(--text));

  /* scala spaziatura */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px; --sp-6:24px; --sp-8:32px; --sp-10:40px;
  --radius:12px; --radius-sm:8px; --radius-lg:16px; --pill:999px;

  /* elevazioni: il bordo fa il lavoro, l'ombra è di supporto (enterprise, non flashy) */
  --shadow-1:0 1px 2px rgba(18,24,38,.05);
  --shadow-2:0 1px 2px rgba(18,24,38,.05), 0 6px 16px -8px rgba(18,24,38,.12);
  --shadow-3:0 2px 4px rgba(18,24,38,.06), 0 18px 44px -14px rgba(18,24,38,.22);
  --shadow-accent:0 8px 22px -10px color-mix(in srgb, var(--accent) 45%, transparent);
  --shadow: var(--shadow-2); /* alias legacy */

  /* densità regolabile (comfortable | compact) */
  --row-py:11px; --ctrl-h:36px; --ctrl-h-sm:32px; --topbar-h:56px;

  /* Poppins SELF-HOSTED via next/font (zero rete a runtime: i file sono serviti dal nostro
     dominio). --font-poppins è dichiarata sull'<html> dal layout; qui la si eredita. */
  --font-sans:var(--font-poppins), system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono:ui-monospace, "SF Mono", "Cascadia Code", "Roboto Mono", Menlo, Consolas, monospace;
  --font-serif:Georgia,"Times New Roman","Iowan Old Style","Palatino Linotype",serif;

  --panel-hi:0 0 transparent; --accent-glow:transparent; --good-glow:transparent; --crit-glow:transparent;
  --glow-a:transparent; --glow-b:transparent; --grid-dot:transparent;
  color:var(--text); background:var(--bg); font-family:var(--font-sans);
  letter-spacing:normal; -webkit-font-smoothing:antialiased;
}
.saas-ui[data-density="compact"]{ --row-py:7px; --ctrl-h:30px; --ctrl-h-sm:28px; --topbar-h:48px; }
/* Il verso opposto: «piu arioso» e la richiesta piu comune di un restyling, e non era esprimibile.
   `comfortable` e il default (nessun attributo), quindi serviva un terzo gradino vero. */
.saas-ui[data-density="relaxed"]{ --row-py:16px; --ctrl-h:44px; --ctrl-h-sm:38px; --topbar-h:68px; }
.saas-ui .bg-fx{ display:none; }

.saas-ui[data-theme="dark"]{
  --on-accent:#fff;
  --accent-tint:   color-mix(in srgb, var(--accent) 24%, transparent);
  --accent-tint-2: color-mix(in srgb, var(--accent) 32%, transparent);
  --accent-quiet:  color-mix(in srgb, var(--accent) 16%, transparent);
  --accent-ink:    color-mix(in srgb, var(--accent) 55%, #fff);
  --focus-ring:    color-mix(in srgb, var(--accent) 45%, transparent);
  --bg:#0f1319; --bg-2:#151a22; --panel:#161c25; --panel-2:#1e2530;
  --sidebar:#12171f; --line:#252d38; --line-2:#333d4b;
  --text:#e7ecf4; --muted:#9aa6b8; --faint:#8b93a5;
  --good:#37c98d; --warn:#e0a545; --crit:#f26571; --info:#5a9bff;
  /* ink schiariti verso il bianco per AA su tint scuri */
  --good-ink:color-mix(in srgb,var(--good) 45%,#fff); --warn-ink:color-mix(in srgb,var(--warn) 50%,#fff);
  --crit-ink:color-mix(in srgb,var(--crit) 55%,#fff); --info-ink:color-mix(in srgb,var(--info) 45%,#fff);
  --shadow-1:0 1px 2px rgba(0,0,0,.40);
  --shadow-2:0 1px 2px rgba(0,0,0,.40), 0 8px 24px -8px rgba(0,0,0,.55);
  --shadow-3:0 2px 6px rgba(0,0,0,.5), 0 24px 60px -16px rgba(0,0,0,.7);
  --shadow: var(--shadow-2);
  color:var(--text); background:var(--bg);
}

/* SIDEBAR indipendente dal tema globale (token `brand.theme.sidebar` → data-sidebar). Ridefinisce i
   neutri/testo SOLO dentro `.pnl-side`: le custom-properties cascano ai figli (voci, icone, footer),
   che leggono già --text/--muted/--line/--accent-*. Così «sidebar scura + contenuto chiaro» (layout
   tipo Notion/Linear) è possibile senza toccare il tema globale. `light` fa l'inverso. */
.saas-ui[data-sidebar="dark"] .pnl-side{
  --sidebar:#12171f; --panel:#161c25; --panel-2:#1e2530; --line:#252d38; --line-2:#333d4b;
  --text:#e7ecf4; --muted:#9aa6b8; --faint:#8b93a5;
  --accent-tint:color-mix(in srgb, var(--accent) 24%, transparent);
  --accent-quiet:color-mix(in srgb, var(--accent) 16%, transparent);
  --accent-ink:color-mix(in srgb, var(--accent) 55%, #fff);
  color:var(--text);
}
.saas-ui[data-sidebar="light"] .pnl-side{
  --sidebar:color-mix(in srgb, var(--accent) 7%, #edeff5); --panel:#ffffff;
  --panel-2:color-mix(in srgb, var(--accent) 5%, #eef1f7);
  --line:color-mix(in srgb, var(--accent) 7%, #e6e9f0); --line-2:color-mix(in srgb, var(--accent) 11%, #d5dae4);
  --text:#1a2130; --muted:#5b6472; --faint:#6b7280;
  --accent-tint:color-mix(in srgb, var(--accent) 9%, transparent);
  --accent-quiet:color-mix(in srgb, var(--accent) 5%, transparent);
  --accent-ink:color-mix(in srgb, var(--accent) 70%, var(--text));
  color:var(--text);
}

/* TEMPERATURA di superficie (token `brand.theme.surface` → data-surface): sposta i neutri CHIARI
   verso il caldo/freddo. GATED su :not([data-theme="dark"]) di proposito — il tema scuro ha i suoi
   neutri; se questi fossero var inline vincerebbero sul foglio scuro (inline batte lo stylesheet),
   dando una base chiara sotto testo chiaro = illeggibile. Come attributo il gate reagisce anche al
   toggle CLIENT del tema (data-theme si accende su .saas-ui), senza dipendere dal timing dell'SSR. */
.saas-ui[data-surface="warm"]:not([data-theme="dark"]){
  --bg:#faf7f2; --panel-2:#f3ede3; --sidebar:#f1ebe0; --line:#eae1d3; --line-2:#ddd2c0;
}
.saas-ui[data-surface="cool"]:not([data-theme="dark"]){
  --bg:#f2f5fb; --panel-2:#e9eef7; --sidebar:#e7edf7; --line:#dfe6f1; --line-2:#ccd6e6;
}

/* ⚠️ SU UN SAAS SCURO LA TEMPERATURA ERA INERTE, e l'operazione diceva comunque «Fatto».
   Il gate `:not([data-theme="dark"])` qui sopra è motivato — i neutri CHIARI caldi sotto testo
   chiaro sono illeggibili — ma la risposta giusta a quel vincolo è pubblicare la variante SCURA,
   non negare la leva in silenzio: chi mette il pannello scuro e poi chiede «toni più caldi»
   riceveva «Aggiornato la temperatura dei neutri» su uno schermo identico, e il censimento gli
   raccontava pure che il suo SaaS era caldo. Quattro righe trasformano un falso successo in una
   capacità. I valori restano scuri: si sposta la TINTA dei neutri, non la loro luminosità. */
.saas-ui[data-theme="dark"][data-surface="warm"]{
  --bg:#14110d; --panel:#1b1713; --panel-2:#221d17; --sidebar:#1a1611; --line:#2b2419; --line-2:#3a3124;
}
.saas-ui[data-theme="dark"][data-surface="cool"]{
  --bg:#0d1017; --panel:#131822; --panel-2:#18202c; --sidebar:#111722; --line:#1f2836; --line-2:#2a3547;
}

/* PANEL: sidebar shell */
.saas-ui .pnl-shell{ display:grid; grid-template-columns:240px minmax(0,1fr); min-height:100vh; }
.saas-ui .pnl-side{ background:var(--sidebar); border-right:1px solid var(--line);
  padding:14px 12px; display:flex; flex-direction:column; gap:18px; min-width:0; }
/* FINESTRA MODALE dichiarativa (`RenderNode` t:"modal"). Usa `<dialog>` nativo: scrim, inertizzazione
   del resto della pagina e chiusura con Esc arrivano dal browser, non da JavaScript generato.
   `::backdrop` è lo sfondo nativo — non un div nostro, quindi non può restare orfano se il dialog
   viene rimosso dal DOM mentre è aperto. */
.saas-ui .rn-modal{ border:1px solid var(--line); border-radius:var(--radius); background:var(--panel);
  color:var(--text); padding:0; max-width:min(720px, calc(100vw - 32px)); width:100%;
  max-height:calc(100vh - 64px); box-shadow:var(--shadow-3); }
.saas-ui .rn-modal::backdrop{ background:rgba(9,12,18,.55); }
.saas-ui .rn-modal-head{ display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:14px 16px; border-bottom:1px solid var(--line); }
.saas-ui .rn-modal-head h2{ margin:0; font-size:16px; font-weight:700; }
.saas-ui .rn-modal-body{ padding:16px; overflow:auto; max-height:calc(100vh - 160px); }

/* Integrazioni: più pannelli affiancati verticalmente, spaziati dal CONTENITORE. Il `gap` stava
   inline nel componente insieme a un `maxWidth: 1040` che nessun'altra sezione condivideva; la
   larghezza ora è dello stage (come Dashboard e Impostazioni) e qui resta solo la spaziatura.
   `.greet` azzera il suo margine: dentro un grid sommerebbe due spaziature diverse. */
.wk-integrazioni { display: grid; gap: 14px; align-content: start; }
.wk-integrazioni > .greet { margin-bottom: 0; }
/* «Già collegate»: il blocco operativo, chiuso di default. È un `<details>`, quindi l'apertura è
   nativa e accessibile — nessuno stato React per una piega. Chiuso deve sembrare una riga, non un
   pannello vuoto: il padding del corpo vive nel corpo, non nel contenitore. */
.wk-integrazioni-ops { padding: 0; }
.wk-integrazioni-ops > summary { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 14px 18px; cursor: pointer; font-size: 13px; }
.wk-integrazioni-ops > summary::-webkit-details-marker { display: none; }
.wk-integrazioni-ops > summary > .faint { font-size: 12px; }
.wk-integrazioni-ops[open] > summary { border-bottom: 1px solid var(--line); }
.wk-integrazioni-ops[open] > summary > .faint { visibility: hidden; }
.wk-integrazioni-ops-body { display: grid; gap: 14px; padding: 16px 18px 18px; }
/* Dentro il corpo lo spazio è del contenitore: il margine fra pannelli lo raddoppierebbe. */
.wk-integrazioni-ops-body > .panel + .panel { margin-top: 0; }

/* ANTEPRIMA dei token d'aspetto (Impostazioni → Brand → «Aspetto — dettagli»).
   Vive dentro `.saas-ui` con gli STESSI data-attribute del runtime, così tono della barra,
   temperatura, densità e raggio arrivano dai blocchi qui sopra invece di essere riscritti a mano:
   una miniatura che si dipinge da sola divergerebbe dal pannello vero al primo cambio di token.
   Queste regole aggiustano solo la SCALA; i colori non li tocca nessuna di esse.
   Stanno DOPO `.pnl-side` di proposito: stessa specificità, quindi vince l'ultima. */
.saas-ui.brand-preview{ display:grid; grid-template-columns:96px minmax(0,1fr);
  border:1px solid var(--line); border-radius:var(--radius); overflow:hidden;
  background:var(--bg); max-width:440px; min-height:150px; font-size:10px; margin-top:2px; }
.saas-ui.brand-preview[data-nav="top"]{ grid-template-columns:minmax(0,1fr); grid-template-rows:auto minmax(0,1fr); }
.saas-ui .brand-preview-side{ padding:9px 8px; gap:9px; }
.saas-ui.brand-preview[data-nav="top"] .brand-preview-side{ flex-direction:row; align-items:center; gap:10px;
  border-right:0; border-bottom:1px solid var(--line); padding:7px 9px; }
.saas-ui .brand-preview-mark{ width:16px; height:16px; flex:0 0 auto; border-radius:var(--radius-xs);
  background:linear-gradient(135deg,var(--accent),var(--accent-2,var(--accent))); }
.saas-ui .brand-preview-navs{ display:flex; flex-direction:column; gap:3px; min-width:0; }
.saas-ui.brand-preview[data-nav="top"] .brand-preview-navs{ flex-direction:row; }
.saas-ui .brand-preview-nav{ padding:calc(var(--row-py) * .42) 6px; border-radius:var(--radius-sm);
  color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.saas-ui .brand-preview-nav.is-active{ background:var(--accent-quiet); color:var(--accent-ink); font-weight:600; }
.saas-ui .brand-preview-main{ display:flex; flex-direction:column; gap:9px; padding:10px; min-width:0; }
.saas-ui .brand-preview-top{ display:flex; align-items:center; justify-content:space-between; gap:8px; }
.saas-ui .brand-preview-title{ font-weight:600; color:var(--text); }
.saas-ui .brand-preview-btn{ background:var(--accent); color:var(--on-accent);
  padding:calc(var(--row-py) * .34) 8px; border-radius:var(--radius-sm); font-weight:600; }
.saas-ui .brand-preview-card{ background:var(--panel); border:1px solid var(--line);
  border-radius:var(--radius); padding:10px; display:flex; flex-direction:column;
  gap:calc(var(--row-py) * .55); flex:1; box-shadow:var(--shadow-1); }
.saas-ui .brand-preview-line{ height:5px; border-radius:var(--radius-2xs); background:var(--panel-2); }
.saas-ui .brand-preview-line.is-strong{ background:var(--line-2); width:55%; }
.saas-ui .brand-preview-line.is-short{ width:35%; }
@media (max-width:560px){ .saas-ui.brand-preview{ max-width:none; } }

.saas-ui .pnl-brand{ display:flex; align-items:center; gap:10px; padding:2px 6px 4px; }
.saas-ui .pnl-brand b{ font-size:14px; font-weight:600; letter-spacing:-.01em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.saas-ui .pnl-brand .mark{ box-shadow:none; }
.saas-ui .pnl-group{ display:flex; flex-direction:column; gap:2px; }
.saas-ui .pnl-navlabel{ font-size:11px; font-weight:600; letter-spacing:.04em;
  color:var(--faint); padding:2px 8px; margin-bottom:2px; }
.saas-ui .pnl-nav{ display:flex; flex-direction:column; gap:2px; }
.saas-ui .pnl-navitem{ position:relative; display:flex; align-items:center; gap:10px;
  padding:8px 10px; border-radius: var(--radius-sm); font-size: 13px; font-weight:500;
  color:var(--muted); cursor:pointer; width:100%; text-align:left; background:none;
  transition:background 120ms ease, color 120ms ease; }
.saas-ui .pnl-navitem .pnl-ic{ width:16px; height:16px; flex:none; color:var(--faint);
  display:grid; place-items:center; }
.saas-ui .pnl-navitem:hover{ background:var(--panel-2); color:var(--text); }
.saas-ui .pnl-navitem.is-active{ background:var(--accent-tint); color:var(--text); font-weight:550; }
.saas-ui .pnl-navitem.is-active .pnl-ic{ color:var(--accent); }
.saas-ui .pnl-navitem.is-active::before{ content:""; position:absolute; left:0; top:6px; bottom:6px;
  width:3px; border-radius:0 3px 3px 0; background:var(--accent); }
.saas-ui .pnl-badge{ margin-left:auto; font-size:10px; font-weight:600; color:var(--faint);
  background:var(--panel-2); border:1px solid var(--line); border-radius: var(--pill); padding:1px 7px; }
.saas-ui .pnl-side-foot{ margin-top:auto; display:flex; align-items:center; gap:10px;
  padding:10px; border:1px solid var(--line); border-radius: var(--radius); background:var(--panel); font-size: 12px; color:var(--muted); }
.saas-ui .pnl-main{ display:flex; flex-direction:column; min-width:0; }
.saas-ui .pnl-topbar{ height:var(--topbar-h,56px); display:flex; align-items:center; gap:14px; padding:0 20px;
  background:var(--panel); border-bottom:1px solid var(--line); position:sticky; top:0; z-index:5; }
/* Zona 1 — Breadcrumb (dove sei): sostituisce il titolo monolitico. */
.saas-ui .pnl-crumbs{ display:flex; align-items:center; gap:7px; min-width:0; font-size:13px; }
.saas-ui .pnl-crumb{ color:var(--muted); text-decoration:none; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:240px; }
.saas-ui a.pnl-crumb:hover{ color:var(--text); }
.saas-ui .pnl-crumb.is-current{ color:var(--text); font-weight:600; }
.saas-ui .pnl-crumb-sep{ color:var(--faint); flex:none; }
/* Zona 3 — cluster destro ridotto: crediti / assistenza / notifiche / account. */
.saas-ui .pnl-top-right{ margin-left:auto; display:flex; align-items:center; gap:8px; }
.saas-ui .pnl-standards{ display:inline-flex; align-items:center; gap:8px; }
.saas-ui .pnl-iconbtn{ width:34px; height:34px; border-radius:10px; display:inline-grid; place-items:center;
  border:1px solid var(--line); background:var(--panel); color:var(--muted); font-size:15px; text-decoration:none;
  cursor:pointer; transition:background 120ms ease, border-color 120ms ease, color 120ms ease; }
.saas-ui .pnl-iconbtn:hover{ background:var(--panel-2); border-color:var(--line-2); color:var(--text); }
.saas-ui .pnl-credits{ display:inline-flex; align-items:center; gap:6px; height:34px; padding:0 12px;
  border-radius:var(--pill,999px); border:1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  background:var(--accent-tint); color:var(--accent-ink); font-size:13px; font-weight:600; text-decoration:none;
  font-variant-numeric:tabular-nums; }
.saas-ui .pnl-credits:hover{ background:var(--accent-tint-2); }
/* Account: bottone + popover (menu che assorbe Impostazioni/tema/densità). */
.saas-ui .pnl-usermenu{ position:relative; }
.saas-ui .pnl-acct{ display:inline-flex; align-items:center; gap:7px; height:34px; padding:0 10px 0 3px;
  border:1px solid var(--line); border-radius:var(--pill,999px); background:var(--panel); font-size: 12px;
  color:var(--text); cursor:pointer; transition:background 120ms ease, border-color 120ms ease; }
.saas-ui .pnl-acct:hover{ background:var(--panel-2); border-color:var(--line-2); }
.saas-ui .pnl-acct-av{ width:26px; height:26px; border-radius:50%; display:inline-grid; place-items:center; background:var(--accent); color:var(--on-accent,#fff); font-size: 11px; font-weight:800; letter-spacing:.02em; }
.saas-ui .pnl-acct-av.lg{ width:34px; height:34px; font-size:13px; }
.saas-ui .pnl-acct-name{ font-weight:600; }
.saas-ui .pnl-acct-caret{ color:var(--faint); }
.saas-ui .pnl-menu{ position:absolute; top:calc(100% + 8px); right:0; width:248px; z-index:30;
  background:var(--panel); border:1px solid var(--line); border-radius:var(--radius,12px); box-shadow:var(--shadow-3);
  padding:8px; display:flex; flex-direction:column; gap:2px; }
.saas-ui .pnl-menu-id{ display:flex; align-items:center; gap:10px; padding:6px 8px 8px; }
.saas-ui .pnl-menu-id-txt{ display:flex; flex-direction:column; min-width:0; }
.saas-ui .pnl-menu-id-txt b{ font-size: 13px; font-weight:600; }
.saas-ui .pnl-menu-id-txt span{ font-size:12px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.saas-ui .pnl-menu-role{ color:var(--faint) !important; }
.saas-ui .pnl-menu-sep{ height:1px; background:var(--line); margin:6px 0; }
.saas-ui .pnl-menu-label{ font-size:11px; font-weight:600; letter-spacing:.03em; color:var(--faint); padding:2px 8px; }
.saas-ui .pnl-seg{ display:grid; grid-template-columns:1fr 1fr; gap:4px; padding:2px 4px 6px; }
.saas-ui .pnl-seg-btn{ height:30px; border:1px solid var(--line); background:var(--panel); color:var(--muted);
  border-radius: var(--radius-sm); font-size: 12px; font-weight:600; cursor:pointer; transition:all 120ms ease; }
.saas-ui .pnl-seg-btn:hover{ border-color:var(--line-2); color:var(--text); }
.saas-ui .pnl-seg-btn.on{ background:var(--accent-tint); border-color:color-mix(in srgb, var(--accent) 30%, var(--line)); color:var(--accent-ink); }
.saas-ui .pnl-menu-item{ display:flex; align-items:center; gap:9px; padding:8px; border-radius:8px;
  font-size:13px; color:var(--text); text-decoration:none; cursor:pointer; }
.saas-ui .pnl-menu-item:hover{ background:var(--panel-2); }
.saas-ui .pnl-menu-item svg{ color:var(--muted); flex:none; }
.saas-ui .pnl-content{ background:var(--bg); padding:22px 26px 40px; min-width:0; }
/* Barra di anteprima del rivenditore (fuori dalla topbar del cliente). */
.saas-ui .saas-previewbar{ display:flex; align-items:center; gap:12px; height:34px; padding:0 16px;
  font-size:12px; color:var(--muted); background:var(--panel-2); border-bottom:1px dashed var(--line-2);
  overflow-x:auto; white-space:nowrap; }
.saas-ui .saas-previewbar .pvb-tag{ color:var(--accent); font-size:13px; }
.saas-ui .saas-previewbar .pvb-tag-txt{ font-weight:600; color:var(--muted); }
.saas-ui .saas-previewbar .pvb-group,
.saas-ui .saas-previewbar .pvb-chip{ display:inline-flex; align-items:center; gap:6px; }
.saas-ui .saas-previewbar .pvb-k{ font-size: 11px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--faint); }
.saas-ui .saas-previewbar .pvb-group .chip{ text-decoration:none; cursor:pointer; padding:1px 7px; font-size:11px; }
.saas-ui .saas-previewbar .pvb-chip{ font-weight:600; color:var(--text); }
.saas-ui .saas-previewbar .pvb-badge{ margin-left:2px; }
.saas-ui .saas-previewbar .pvb-link{ margin-left:auto; color:var(--accent-ink); font-weight:600; text-decoration:none; white-space:nowrap; }
.saas-ui .saas-previewbar .pvb-link:hover{ text-decoration:underline; }

@media (max-width:860px){
  .saas-ui .pnl-shell{ grid-template-columns:60px minmax(0,1fr); }
  .saas-ui .pnl-navitem span,
  .saas-ui .pnl-navlabel,
  .saas-ui .pnl-brand b,
  .saas-ui .pnl-side-foot{ display:none; }
  .saas-ui .pnl-navitem{ justify-content:center; padding:10px; }
  .saas-ui .pnl-content{ padding:18px 18px 32px; }
}
/* NAVIGAZIONE SOPRA (`brand.theme.navPosition:"top"`) — la barra laterale diventa una fascia
   orizzontale sopra il contenuto. NON è un layout nuovo: è lo stesso che il pannello usa già sotto i
   560px, promosso da ripiego per telefoni a scelta esplicita. Riusarlo invece di scriverne un altro
   significa che le due strade non possono divergere — e che questa disposizione ha già mesi di uso
   alle spalle.

   Il selettore sta PRIMA della media query dei 560px di proposito: su un telefono deve vincere il
   layout impilato comunque, e una scelta di gusto non deve poter rendere il pannello inutilizzabile
   su schermo stretto. Stessa ragione per cui non tocca il collasso a icone degli 860px. */
.saas-ui[data-nav="top"] .pnl-shell{ grid-template-columns:minmax(0,1fr); }
.saas-ui[data-nav="top"] .pnl-side{ flex-direction:row; align-items:center; gap:6px; overflow-x:auto;
  padding:8px 12px; border-right:0; border-bottom:1px solid var(--line); }
.saas-ui[data-nav="top"] .pnl-group{ flex-direction:row; align-items:center; gap:6px; }
.saas-ui[data-nav="top"] .pnl-nav{ flex-direction:row; }
.saas-ui[data-nav="top"] .pnl-navitem{ white-space:nowrap; }
/* L'etichetta di gruppo («Moduli», «Strumenti») è una intestazione di COLONNA: in orizzontale
   diventa una parola che galleggia in mezzo alle voci, quindi sparisce. Le voci restano. */
.saas-ui[data-nav="top"] .pnl-navlabel{ display:none; }
/* L'indicatore di voce attiva passa da barretta a SINISTRA a sottolineatura SOTTO: in orizzontale
   una barra verticale accanto alla voce non si legge come «sei qui». */
.saas-ui[data-nav="top"] .pnl-navitem.is-active::before{ left:8px; right:8px; top:auto; bottom:0;
  width:auto; height:2px; border-radius: var(--radius-2xs) 2px 0 0; }
/* Il piede della barra (avatar + «Area riservata») in orizzontale va spinto a destra, non lasciato
   in mezzo alle voci. */
.saas-ui[data-nav="top"] .pnl-side-foot{ margin-left:auto; margin-top:0; }

@media (max-width:560px){
  .saas-ui .pnl-shell{ grid-template-columns:minmax(0,1fr); }
  .saas-ui .pnl-side{ flex-direction:row; align-items:center; gap:6px; overflow-x:auto;
    padding:8px 10px; border-right:0; border-bottom:1px solid var(--line); }
  .saas-ui .pnl-group{ flex-direction:row; }
  .saas-ui .pnl-nav{ flex-direction:row; }
  .saas-ui .pnl-navitem{ white-space:nowrap; }
  .saas-ui .pnl-navitem.is-active::before{ left:8px; right:8px; top:auto; bottom:0;
    width:auto; height:2px; border-radius: var(--radius-2xs) 2px 0 0; }
}

/* Table (render-model) */
.saas-ui .rt-table th{ font-family:var(--font-sans); text-transform:none; letter-spacing:.01em;
  font-size: 11px; font-weight:600; color:var(--muted);
  border-bottom:1px solid var(--line-2); padding:10px 12px; }
.saas-ui .rt-table td{ border-bottom:1px solid var(--line); padding:var(--row-py,11px) 12px; color:var(--text); white-space:nowrap; vertical-align:middle; }
.saas-ui .rt-table tbody tr:hover td{ background:var(--panel-2); }
.saas-ui .rt-num{ font-family:var(--font-mono); font-variant-numeric:tabular-nums; }
/* Tabella di revisione traduzioni (WP2.5c). */
.saas-ui .tr-review td{ vertical-align:middle; padding:8px 12px; }
.saas-ui .tr-key{ font-family:var(--font-mono); font-size: 11px; color:var(--faint); max-width:280px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.saas-ui .input.sm{ padding:5px 8px; font-size: 12px; min-width:160px; }
.saas-ui .mono{ font-family:var(--font-mono); }
.saas-ui .rt-title-cell{ font-weight:600; }

/* Form (render-model) */
.saas-ui .field-label{ font-family:var(--font-sans); text-transform:none; letter-spacing:0;
  font-size:12px; font-weight:550; color:var(--muted); margin-bottom:6px; }
.saas-ui .rt-section-title{ font-family:var(--font-sans); text-transform:none; letter-spacing:-.01em;
  font-size:13px; font-weight:600; color:var(--text); margin:0 0 12px;
  padding-bottom:8px; border-bottom:1px solid var(--line); }
.saas-ui .input.rt-ro,
.saas-ui .rt-ro{ background:transparent; border:0; padding:2px 0 0; opacity:1;
  color:var(--text); font-family:var(--font-sans); font-size:14px; min-height:0; }
.saas-ui .input{ font-family:var(--font-sans); background:var(--panel); color:var(--text);
  border:1px solid var(--line-2); border-radius:var(--radius-sm); padding:9px 11px; font-size: 13px; }
.saas-ui .input::placeholder{ color:var(--faint); font-family:var(--font-sans); }
.saas-ui .input:focus{ outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--focus-ring); }
.saas-ui select.input{ font-family:var(--font-sans); cursor:pointer; }
.saas-ui .textarea{ font-family:var(--font-sans); }
/* Campi OBBLIGATORI + errori di validazione (feedback UX Luigi): asterisco ROSSO, bordo + anello
   focus rossi sul campo in errore; il messaggio inline (.field-err) sta subito sotto il campo. */
.saas-ui .req-star{ color:var(--crit); font-weight:600; }
.saas-ui .input[aria-invalid="true"]{ border-color:var(--crit); }
.saas-ui .input[aria-invalid="true"]:focus{ border-color:var(--crit); box-shadow:0 0 0 3px color-mix(in srgb, var(--crit) 22%, transparent); }
.saas-ui .field-err{ margin-top:5px; }

/* Buttons */
.saas-ui .btn{ border-radius:8px; }
.saas-ui .btn:hover{ background:var(--panel-2); border-color:var(--line-2); color:var(--text); }
.saas-ui .btn.primary{ background:var(--accent); color:var(--on-accent,#fff); border-color:transparent;
  box-shadow:0 1px 2px rgba(18,24,38,.10); }
.saas-ui .btn.primary:hover{ background:var(--accent-hover); color:var(--on-accent,#fff); filter:none; }
.saas-ui .btn.ghost{ background:transparent; border-color:transparent; color:var(--muted); }
.saas-ui .btn.ghost:hover{ background:var(--panel-2); border-color:transparent; color:var(--text); }
.saas-ui .btn.danger{ background:transparent; color:var(--crit);
  border-color:color-mix(in srgb, var(--crit) 38%, transparent); }
.saas-ui .btn.danger:hover{ background:color-mix(in srgb, var(--crit) 10%, transparent);
  border-color:var(--crit); color:var(--crit); }

/* Chips / status */
.saas-ui .chip{ font-family:var(--font-sans); font-size: 11px; font-weight:600;
  letter-spacing:0; padding:3px 9px; border:1px solid var(--line-2); color:var(--muted); }
/* Il TESTO di una targhetta sta sopra un tint, e per quel caso il design system ha già i token
   `-ink` («da usare per ogni testo-su-tint, chiude i bug di contrasto»). Qui erano rimasti i
   colori pieni: «Bozza» in ambra su tint ambra misurava 3,93:1, sotto il minimo AA di 4,5.
   Il tint e il bordo restano quelli — cambia solo l'inchiostro, che è ciò che si legge. */
.saas-ui .chip.ok{ color:var(--good-ink); background:color-mix(in srgb, var(--good) 12%, transparent);
  border-color:color-mix(in srgb, var(--good) 24%, transparent); }
.saas-ui .chip.warn{ color:var(--warn-ink); background:color-mix(in srgb, var(--warn) 12%, transparent);
  border-color:color-mix(in srgb, var(--warn) 24%, transparent); }
.saas-ui .chip.crit{ color:var(--crit-ink); background:color-mix(in srgb, var(--crit) 12%, transparent);
  border-color:color-mix(in srgb, var(--crit) 24%, transparent); }
/* Stessa regola delle altre: sopra un tint si scrive con l'INK, non col colore pieno.
   L'accent pieno sul proprio tint misurava 3,74:1 — e questa è la targhetta più usata
   del pannello (tipo di report, filtro attivo, valore selezionato). */
.saas-ui .chip.accent{ color:var(--accent-ink); background:var(--accent-tint);
  border-color:color-mix(in srgb, var(--accent) 24%, transparent); }
.saas-ui .chip.idle{ color:var(--muted); background:var(--panel-2); border-color:var(--line-2); }
/* Dark: --good/--warn/--crit hanno varianti schiarite, ma --accent resta il colore
   di brand → il testo accent sul suo tint (22%) scendeva a ~2.7:1 (sotto AA). In
   scuro schiariamo SOLO il testo del chip accent verso il bianco (~5.4:1). WS8.3. */
.saas-ui[data-theme="dark"] .chip.accent{ color:color-mix(in srgb, var(--accent) 58%, #fff); }

/* Cards / KPI */
.saas-ui .panel{ background:var(--panel); border:1px solid var(--line); border-radius:var(--radius);
  box-shadow:var(--shadow); }
/* Dentro un SaaS `is-primary` NON vuol dire «scheda in evidenza»: è la classe di LAYOUT della
   card che ospita la tabella (vedi sotto: padding 0, raggi sul wrapper). Niente bordo d'accento
   e niente filetto — che è ciò che diceva anche il `::after{display:none}` che stava qui, ora
   rimosso perché quello pseudo-elemento non esiste più.
   `box-shadow` esplicito e non lasciato all'ordine delle regole: la ereditava per il solo fatto
   che `.saas-ui .panel` viene DOPO nel file. Chi riordinasse il foglio si troverebbe il filetto
   della console spuntare dentro i gestionali dei clienti, senza capire perché. */
.saas-ui .panel.is-primary{ border-color:var(--line); box-shadow:var(--shadow); }
/* Padding interno delle card: dopo la nuova struttura (niente .rt-head) il
   selettore globale `.panel:has(> .rt-head)` non copre più il pannello primario né
   l'empty-state → contenuto a filo del bordo. Ripristinato qui. */
.saas-ui .panel.is-primary{ padding:0; overflow:visible; border-radius:var(--radius); }
/* Il wrapper della tabella eredita l'arrotondamento in basso (l'overflow del pannello ora è
   visible per far uscire il dropdown dei filtri; gli angoli restano puliti perché il contenuto
   non ha sfondo agli spigoli). */
.saas-ui .panel.is-primary > .rt-table-wrap{ border-bottom-left-radius:var(--radius); border-bottom-right-radius:var(--radius); }
.saas-ui .panel.is-primary > .rt-toolbar{ margin:0; padding:13px 16px; border-bottom:1px solid var(--line); }
.saas-ui .panel.is-primary .rt-table th{ padding-left:16px; padding-right:16px; }
.saas-ui .panel.is-primary .rt-table td{ padding-left:16px; padding-right:16px; }
/* Il piede paginazione allineato col contenuto della tabella (16px), non appiccicato al bordo:
   la base `.rt-pager` ha solo 2px orizzontali (va bene nelle liste correlate senza inset). */
.saas-ui .panel.is-primary .rt-pager{ padding-left:16px; padding-right:16px; }
.saas-ui .panel:not(.is-primary):not(:has(> .rt-head)){ padding:16px 18px; }
/* Avatar/mark: usa il colore testo calcolato sull'accent (leggibile con ogni brand). */
.saas-ui .avatar{ background:var(--accent); color:var(--on-accent, #fff); }
.saas-ui .wk-kpi-label{ font-size:11px; font-weight:600; letter-spacing:.06em; color:var(--faint); }
.saas-ui .wk-kpi-value{ font-family:var(--font-mono); font-variant-numeric:tabular-nums;
  font-weight:600; letter-spacing:-.01em; }

/* Toggle */
.saas-ui .toggle.on .track{ background:var(--accent); border-color:transparent; }


/* SITE (public) — le regole di tema del sito vivono ORA nel blocco "Aurora" (in alto):
   token-driven, chiaro+scuro, guidate dall'accent del brand. Il vecchio override
   `.saas-ui .site-*` è stato rimosso perché, a specificità (0,2,0), vinceva sul nuovo
   design (0,1,0) e ne annullava hero/header/card. Resta solo la variante serif opt-in. */
.saas-ui[data-site-serif] .site-hero-title,
.saas-ui[data-site-serif] .site-section-title,
.saas-ui[data-site-serif] .site-feature-name{ font-family:var(--font-serif); letter-spacing:-.01em; }

/* ============================================================================
   DESIGN SYSTEM v2 (WS1) — rifinitura moderna/professionale/comoda UNIFICATA.
   Sovrascrive il tema base per un look da gestionale commerciale premium. Ogni
   SaaS generato la riceve di default; il rivenditore può poi cambiare tutto via chat.
   ========================================================================== */
.saas-ui{
  --radius:12px; --radius-sm:8px;
  --shadow:0 1px 2px rgba(18,24,38,.05), 0 6px 16px -8px rgba(18,24,38,.10);
  --shadow-lg:0 2px 4px rgba(18,24,38,.06), 0 18px 44px -14px rgba(18,24,38,.20);
  --info:#2563eb;
}
.saas-ui[data-theme="dark"]{
  --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px -10px rgba(0,0,0,.55);
  --shadow-lg:0 2px 6px rgba(0,0,0,.5), 0 26px 64px -18px rgba(0,0,0,.72);
  --info:#60a5fa;
}

/* Topbar: filo netto per profondità */
.saas-ui .pnl-topbar{ box-shadow:0 1px 0 var(--line); }
.saas-ui .pnl-content{ padding:26px 30px 48px; }

/* Header di pagina unificato (titolo + sottotitolo + azioni). */
.saas-ui .rt-pagehead{ display:flex; flex-direction:column; gap:14px; margin-bottom:20px; }
.saas-ui .rt-pagehead .rt-pagehead-row{ display:flex; align-items:flex-start; gap:16px; }
.saas-ui .rt-pagehead .rt-pagehead-main{ min-width:0; }
.saas-ui .rt-pagehead h2{ margin:0; font-size:19px; font-weight:700; }
.saas-ui .rt-pagehead .sub{ margin:4px 0 0; color:var(--muted); font-size:13px; max-width:70ch; }
.saas-ui .rt-pagehead .rt-pagehead-actions{ margin-left:auto; display:flex; gap:8px; align-items:center; flex:none; }
.saas-ui .rt-pagehead .rt-meta{ display:flex; flex-wrap:wrap; gap:6px; margin-top:10px; align-items:center; }
.saas-ui .rt-pagehead .rt-meta .chip{ font-variant-numeric:tabular-nums; }
/* Tab-bar delle VISTE (Tabella/Bacheca/…): underline accent, unifica gli switch sparsi. */
.saas-ui .rt-tabs{ display:flex; gap:2px; border-bottom:1px solid var(--line); overflow-x:auto; }
.saas-ui .rt-tab{ position:relative; padding:9px 14px; font-size:13px; font-weight:600; color:var(--muted);
  text-decoration:none; white-space:nowrap; border-bottom:2px solid transparent; margin-bottom:-1px;
  transition:color 120ms ease, border-color 120ms ease; }
.saas-ui .rt-tab:hover{ color:var(--text); }
.saas-ui .rt-tab.is-active{ color:var(--accent-ink); border-bottom-color:var(--accent); }
/* Tab-bar autonoma (scheda record): stesso stile, con margini propri. */
.saas-ui .rt-tabs-standalone{ margin:16px 0 18px; }

/* ── SCHEDA RECORD: highlights + griglia dettaglio (fase 5) ─────────────────── */
.saas-ui .rt-back{ margin-bottom:12px; }
.saas-ui .rt-highlights{ display:flex; align-items:flex-start; gap:16px; padding:18px 20px; }
.saas-ui .rt-hl-icon{ width:52px; height:52px; flex:none; border-radius:14px; display:grid; place-items:center;
  background:var(--accent-tint); color:var(--accent); border:1px solid color-mix(in srgb, var(--accent) 16%, var(--line)); }
.saas-ui .rt-hl-main{ min-width:0; flex:1; }
.saas-ui .rt-hl-titlerow{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.saas-ui .rt-hl-title{ margin:0; font-size:22px; font-weight:700; letter-spacing:-.02em; }
.saas-ui .rt-hl-type{ font-size:11px; }
.saas-ui .rt-hl-fields{ display:flex; flex-wrap:wrap; gap:10px 28px; margin:14px 0 0; }
.saas-ui .rt-hl-field{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.saas-ui .rt-hl-k{ font-size:11px; font-weight:600; letter-spacing:.03em; text-transform:uppercase; color:var(--faint); }
.saas-ui .rt-hl-v{ margin:0; font-size:15px; font-weight:600; color:var(--text); font-variant-numeric:tabular-nums; }
.saas-ui .rt-hl-actions{ display:flex; gap:8px; align-items:center; flex:none; }
/* Corpo tab Dettagli: main + rail metadati (2 colonne ≥1100px). */
.saas-ui .rt-detail-grid{ display:grid; grid-template-columns:1fr; gap:18px; }
@media (min-width:1100px){ .saas-ui .rt-detail-grid{ grid-template-columns:minmax(0,1fr) 280px; align-items:start; } }
.saas-ui .rt-meta-card{ padding:16px 18px; }
.saas-ui .rt-meta-card-title{ font-size:13px; font-weight:600; margin:0 0 10px; }
.saas-ui .rt-meta-list{ margin:0; display:flex; flex-direction:column; gap:10px; }
.saas-ui .rt-meta-list > div{ display:flex; flex-direction:column; gap:2px; }
.saas-ui .rt-meta-list dt{ font-size:11px; font-weight:600; letter-spacing:.03em; text-transform:uppercase; color:var(--faint); }
.saas-ui .rt-meta-list dd{ margin:0; font-size:13px; color:var(--text); font-variant-numeric:tabular-nums; word-break:break-all; }
.saas-ui .rt-meta-list dd.mono{ font-family:var(--font-mono); font-size:12px; }
@media (max-width:640px){ .saas-ui .rt-highlights{ flex-wrap:wrap; } .saas-ui .rt-hl-actions{ width:100%; } }
/* Tab «Correlati»: un blocco per ogni entità figlia (Fatture, Preventivi…), ciascuno con
   intestazione icona+titolo+conteggio e la sua tabella di record collegati. */
.saas-ui .rt-related{ display:flex; flex-direction:column; gap:16px; }
.saas-ui .rt-related-block{ padding:0; overflow:hidden; }
.saas-ui .rt-related-head{ display:flex; align-items:center; gap:10px; padding:13px 16px; border-bottom:1px solid var(--line); }
.saas-ui .rt-related-icon{ display:inline-grid; place-items:center; width:28px; height:28px; flex:none; border-radius:8px; background:var(--accent-tint); color:var(--accent-ink); }
.saas-ui .rt-related-title{ margin:0; font-size: 13px; font-weight:650; letter-spacing:-.01em; }
.saas-ui .rt-related-count{ margin-left:auto; min-width:22px; height:22px; padding:0 7px; border-radius:11px; display:inline-grid; place-items:center;
  background:var(--panel-2); color:var(--muted); font-size: 11px; font-weight:700; font-variant-numeric:tabular-nums; }
.saas-ui .rt-related-block > .rt-table-wrap{ border:0; }
.saas-ui .rt-related-block > p.sub{ padding:16px; }

/* Selettore relazioni N-N (std.relation-picker): collegati (chip con ×) + candidati (chip +). */
.saas-ui .rt-relpick{ padding:14px 16px; display:flex; flex-direction:column; gap:12px; }
.saas-ui .rt-relpick-links{ display:flex; flex-wrap:wrap; gap:7px; align-items:center; }
.saas-ui .rt-relpick-add{ display:flex; flex-wrap:wrap; gap:7px; align-items:center; padding-top:10px; border-top:1px dashed var(--line); }
.saas-ui .rt-relpick-chip{ display:inline-flex; align-items:center; gap:5px; }
.saas-ui .rt-relpick-x{ border:0; background:transparent; color:currentColor; cursor:pointer; padding:0; margin:0 -2px 0 1px;
  font-size:15px; line-height:1; opacity:.65; border-radius: var(--radius-2xs); width:15px; height:15px; display:inline-grid; place-items:center; }
.saas-ui .rt-relpick-x:hover:not(:disabled){ opacity:1; }
.saas-ui .rt-relpick-cand{ cursor:pointer; border-style:dashed; }
.saas-ui .rt-relpick-cand:hover:not(:disabled){ border-color:var(--accent); color:var(--accent-ink); }
.saas-ui .rt-relpick button:disabled{ opacity:.5; cursor:default; }

/* KPI card moderna (cruscotto/home). */
.saas-ui .kpi{ background:var(--panel); border:1px solid var(--line); border-radius:14px;
  padding:15px 17px; display:grid; gap:10px; box-shadow:var(--shadow); text-decoration:none; color:inherit;
  transition:transform .16s ease, box-shadow .16s ease, border-color .16s ease; }
.saas-ui a.kpi:hover{ transform:translateY(-2px); box-shadow:var(--shadow-lg);
  border-color:color-mix(in srgb, var(--accent) 32%, var(--line)); }
.saas-ui .kpi-top{ display:flex; align-items:center; justify-content:space-between; gap:10px; }
.saas-ui .kpi-ic{ width:34px; height:34px; border-radius:10px; display:grid; place-items:center; flex:none;
  background:var(--accent-tint); color:var(--accent); font-size:14px; font-weight:800; letter-spacing:.02em; }
.saas-ui .kpi-label{ font-size:12px; font-weight:600; color:var(--muted);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.saas-ui .kpi-value{ font-size:28px; font-weight:800; letter-spacing:-.02em; line-height:1;
  font-variant-numeric:tabular-nums; color:var(--text); }
.saas-ui .kpi-hint{ font-size: 11px; color:var(--faint); }
.saas-ui .kpi-ic svg{ width:18px; height:18px; }
.saas-ui .kpi-spark{ display:block; width:100%; height:26px; margin-top:2px; }
/* Home/dashboard: griglia KPI + layout 2 colonne (grafici + laterale). */
.saas-ui .home-kpis{ display:grid; grid-template-columns:repeat(auto-fill, minmax(190px, 1fr)); gap:14px; }
.saas-ui .home-grid{ display:grid; grid-template-columns:1fr; gap:16px; }
@media (min-width:960px){ .saas-ui .home-grid{ grid-template-columns:2fr 1fr; align-items:start; } }
.saas-ui .home-workspace{ display:grid; grid-template-columns:minmax(0, 1fr); gap:16px; }
.saas-ui .home-region{ min-width:0; }
.saas-ui .home-region-full{ grid-column:1 / -1; }
@media (min-width:960px){
  .saas-ui .home-workspace-columns-2{ grid-template-columns:minmax(0, 2fr) minmax(260px, 1fr); align-items:start; }
  .saas-ui .home-region-main{ grid-column:1; }
  .saas-ui .home-region-aside{ grid-column:2; }
}
.saas-ui .home-main{ display:grid; grid-template-columns:1fr; gap:14px; min-width:0; }
@media (min-width:1280px){ .saas-ui .home-main:has(> section + section){ grid-template-columns:1fr 1fr; } }
.saas-ui .home-side{ display:grid; gap:14px; align-content:start; min-width:0; }
.saas-ui .home-quick-list{ display:grid; gap:2px; margin-top:4px; }
.saas-ui .home-quick-item{ display:flex; align-items:center; gap:10px; padding:9px 10px; border-radius:9px;
  font-size:13px; font-weight:500; color:var(--text); text-decoration:none; transition:background .12s ease; }
.saas-ui .home-quick-item:hover{ background:var(--accent-quiet); color:var(--accent-ink); }
.saas-ui .home-quick-ic{ width:28px; height:28px; flex:none; border-radius:8px; display:grid; place-items:center;
  background:var(--accent-tint); color:var(--accent); }

/* Tabella: header sticky, hover d'accento, densità comoda, angoli morbidi. */
.saas-ui .rt-table{ border-collapse:separate; border-spacing:0; }
.saas-ui .rt-table thead th{ position:sticky; top:0; z-index:1;
  background:color-mix(in srgb, var(--panel) 90%, var(--bg)); }
.saas-ui .rt-table tbody tr{ transition:background .12s ease; }
.saas-ui .rt-table tbody tr:hover td{ background:var(--accent-quiet); }
.saas-ui .rt-table tbody tr:last-child td{ border-bottom:0; }
.saas-ui .rt-title-cell{ font-weight:650; letter-spacing:-.01em; }
/* Cella titolo ricca: link + eventuale avatar-iniziale (entità persona/azienda). */
.saas-ui .rt-title-cell{ max-width:320px; }
.saas-ui .rt-title-cell .rt-row-open{ display:inline-flex; align-items:center; gap:9px; color:inherit; text-decoration:none; font-weight:650; min-width:0; max-width:100%; }
/* Nome su UNA riga, troncato con ellissi (righe di altezza uniforme; nome completo nel tooltip). */
.saas-ui .rt-cell-text{ display:block; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Contenitori stretti (embed/finestre piccole): comprimi padding e nome così la tabella entra
   meglio prima di ricorrere allo scroll orizzontale. */
@media (max-width:900px){
  .saas-ui .rt-table th, .saas-ui .rt-table td{ padding-left:10px; padding-right:10px; }
  .saas-ui .rt-title-cell{ max-width:190px; }
}
.saas-ui a.rt-row-open:hover{ color:var(--accent-ink); }
.saas-ui a.rt-row-open:hover .rt-cell-text{ text-decoration:underline; text-underline-offset:2px; }
.saas-ui .rt-avatar{ width:26px; height:26px; flex:none; border-radius:50%; display:inline-grid; place-items:center;
  background:var(--accent-tint); color:var(--accent-ink); font-size:11px; font-weight:800; letter-spacing:.02em; }
/* Azioni riga: rivelate all'hover (sempre visibili al focus da tastiera e su touch). */
.saas-ui .rt-actions-col .rec-actions{ opacity:0; transition:opacity .12s ease; }
.saas-ui .rt-table tbody tr:hover .rec-actions,
.saas-ui .rt-table tbody tr:focus-within .rec-actions{ opacity:1; }
@media (hover:none){ .saas-ui .rt-actions-col .rec-actions{ opacity:1; } }
/* Template UNIFICATO per gli stati "senza contenuto": liste vuote, moduli senza
   dati e (is-locked) feature non incluse nel piano — <EmptyState> di @saas/ui. */
/* Stato VUOTO illustrato (`<EmptyState>` di @saas/ui). Non è scopato a `.saas-ui` per la
   stessa ragione della modale: è un componente di prodotto, non del tema del SaaS generato.
   Con lo scope la console poteva solo scrivere «Nessun movimento» in grigio, mentre il
   gestionale del cliente mostrava icona, titolo e invito all'azione. */
.rt-empty-state{ text-align:center; padding:44px 24px; display:grid; justify-items:center; }
/* VARIANTE COMPATTA — per un vuoto che è una RIGA DI ESITO, non una pagina intera.
   Il cerchio da 64px e i 44px di padding sono giusti quando il vuoto È la schermata; dentro una
   colonna affiancata diventano un muro bianco alto 320px che dice due parole. Sta qui e non in un
   selettore di pagina perché è una variante del COMPONENTE: riusabile ovunque serva, invece di un
   override che entra dentro `.rt-empty-*` da fuori (vedi il confine dichiarato a riga 788). */
.rt-empty-state.is-compact{ padding:22px 20px; }
.rt-empty-state.is-compact .rt-empty-state-illus{ width:44px; height:44px; margin-bottom:10px; }
.rt-empty-state.is-compact .rt-empty-state-illus svg{ width:21px; height:21px; }
.rt-empty-state-illus{ width:64px; height:64px; margin:0 auto 14px; border-radius:50%; display:grid; place-items:center;
  background:var(--accent-tint); color:var(--accent); border:1px solid color-mix(in srgb, var(--accent) 18%, var(--line));
  font-size:27px; line-height:1; }
.rt-empty-state-illus svg{ width:30px; height:30px; }
.rt-empty-state.is-locked .rt-empty-state-illus{ background:color-mix(in srgb, var(--warn) 13%, transparent); color:var(--warn);
  border-color:color-mix(in srgb, var(--warn) 26%, var(--line)); }
.rt-empty-state-title{ font-size:15px; font-weight:650; letter-spacing:-.01em; color:var(--text); margin-bottom:5px; }
.rt-empty-state-sub{ font-size: 12px; color:var(--muted); max-width:48ch; margin:0 auto; line-height:1.5; }
.rt-empty-state-action{ margin-top:18px; display:flex; justify-content:center; gap:8px; flex-wrap:wrap; }
/* Chip dei filtri attivi, rimovibili. */
.saas-ui .rt-filterchips{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; padding:10px 16px 0; }
.saas-ui .rt-filterchip{ display:inline-flex; align-items:center; gap:6px; padding:3px 6px 3px 10px; border-radius:var(--pill,999px);
  font-size:12px; font-weight:600; text-decoration:none; background:var(--accent-tint); color:var(--accent-ink);
  border:1px solid color-mix(in srgb, var(--accent) 22%, var(--line)); }
.saas-ui .rt-filterchip:hover{ background:var(--accent-tint-2); }
.saas-ui .rt-filterchip-x{ display:inline-grid; place-items:center; width:15px; height:15px; border-radius:50%;
  background:color-mix(in srgb, var(--accent) 18%, transparent); font-size: 10px; }
.saas-ui .rt-filterchip-clear{ font-size:12px; color:var(--muted); text-decoration:none; margin-left:2px; }
.saas-ui .rt-filterchip-clear:hover{ color:var(--text); text-decoration:underline; }
/* Selettore righe per pagina nel pager. */
.saas-ui .rt-pager-size{ display:inline-flex; align-items:center; gap:4px; }
.saas-ui .rt-pager-size-k{ font-size:12px; color:var(--faint); margin-right:2px; }
.saas-ui .rt-pager-size-opt{ min-width:26px; text-align:center; padding:2px 6px; border-radius:7px; font-size:12px; font-weight:600;
  color:var(--muted); text-decoration:none; border:1px solid transparent; font-variant-numeric:tabular-nums; }
.saas-ui .rt-pager-size-opt:hover{ background:var(--panel-2); color:var(--text); }
.saas-ui .rt-pager-size-opt.is-active{ background:var(--accent-tint); color:var(--accent-ink); border-color:color-mix(in srgb, var(--accent) 26%, var(--line)); }
/* Skeleton di caricamento (shimmer). NON scopato a `.saas-ui`: serve anche alla console e alla
   dashboard, che con l'App Router non avevano NESSUN `loading.tsx` — quindi cambiando sezione la
   pagina vecchia restava congelata per tutto il render lato server, senza un segno che il clic
   fosse arrivato. Lo scheletro è quel segno. */
.rt-skel-row{ display:flex; gap:12px; padding:var(--row-py,11px) 16px; border-bottom:1px solid var(--line); }
.rt-skel{ height:12px; border-radius:var(--radius-2xs); flex:1;
  background:linear-gradient(90deg, var(--panel-2) 25%, color-mix(in srgb, var(--line-2) 60%, var(--panel-2)) 37%, var(--panel-2) 63%);
  background-size:400% 100%; animation:rt-shimmer 1.4s ease infinite; }
@keyframes rt-shimmer{ 0%{ background-position:100% 0; } 100%{ background-position:0 0; } }
/* ⚠ ECCEZIONE ALLA DECISIONE DI LUIGI DEL 2026-07-17 (vedi il commento lungo accanto a
   `@keyframes reveal`): lì è stabilito che le animazioni girano SEMPRE, anche per chi ha
   chiesto al sistema di ridurre il movimento, e che quella scelta non va "corretta" alla cieca.
   Questa riga la contraddice per UN solo elemento — lo scheletro di caricamento — introdotto
   dopo quella decisione. La motivazione: lo shimmer è un movimento CONTINUO e a ciclo infinito,
   cioè il tipo che dà disturbo a chi ha problemi vestibolari, e lo scheletro dice già «sto
   caricando» con la sua sola forma: fermarlo non toglie informazione.
   È comunque una decisione di prodotto, non tecnica: se Luigi la vuole allineata al resto,
   basta togliere questa riga. */
@media (prefers-reduced-motion: reduce){ .rt-skel{ animation:none; } }

/* Scheletro di PAGINA: ricalca la testata + i KPI + una scheda, così il salto al contenuto
   vero non sposta nulla di quello che l'occhio ha già agganciato. */
.pg-skel{ display:grid; gap:14px; }
.pg-skel .rt-skel.is-title{ height:26px; max-width:340px; }
.pg-skel .rt-skel.is-sub{ height:13px; max-width:520px; }
.pg-skel-kpis{ display:grid; grid-template-columns:repeat(auto-fit, minmax(160px, 1fr)); gap:12px; }
.pg-skel-card{ border:1px solid var(--line); background:var(--panel); border-radius:var(--radius);
  padding:14px 16px; display:grid; gap:10px; }

/* Chip: pill piena + soft coerente. */
.saas-ui .chip{ border-radius:999px; padding:3px 10px; }

/* Bottoni: transizione + focus visibile. */
.saas-ui .btn{ transition:background .14s ease, border-color .14s ease, color .14s ease, box-shadow .14s ease; }
.saas-ui .btn:focus-visible{ outline:none; box-shadow:0 0 0 3px var(--focus-ring); }
.saas-ui .btn.primary:hover{ box-shadow:0 2px 8px -1px color-mix(in srgb, var(--accent) 45%, transparent); }
/* Focus da tastiera coerente anche su navigazione/link/righe del SaaS generato
   (WS8.3): finora solo .btn e .input avevano l'anello brandizzato. */
.saas-ui .pnl-navitem:focus-visible,
.saas-ui .site-nav-link:focus-visible,
.saas-ui .rt-recent-item:focus-visible,
.saas-ui .saas-tab:focus-visible{ outline:none; box-shadow:0 0 0 3px var(--focus-ring); }

/* Lista "aggiunti di recente" (home). */
.saas-ui .rt-recent-item{ color:var(--text); border:1px solid transparent; transition:background .12s ease, border-color .12s ease; }
.saas-ui .rt-recent-item:hover{ background:var(--accent-tint); border-color:color-mix(in srgb, var(--accent) 20%, transparent); color:var(--text); }

/* Empty state più curato. */
.saas-ui .rt-empty{ padding:0; }

/* Card ancorabile (link) con hover coerente. */
.saas-ui a.panel{ transition:transform .16s ease, box-shadow .16s ease, border-color .16s ease; }
.saas-ui a.panel:hover{ transform:translateY(-2px); box-shadow:var(--shadow-lg);
  border-color:color-mix(in srgb, var(--accent) 28%, var(--line)); }

/* ── SELEZIONE MULTIPLA + AZIONI MASSIVE (WS7 #61) ───────────────────────────── */
/* Colonna di spunta iniziale della tabella (checkbox riga + "seleziona tutte"). */
.saas-ui .rt-sel-col{ width:34px; text-align:center; padding-left:10px; padding-right:0; }
.saas-ui .rt-sel-col input[type="checkbox"]{ width:16px; height:16px; cursor:pointer; accent-color:var(--accent); vertical-align:middle; }
.saas-ui .rt-table tbody tr:has(.rt-select:checked){ background:var(--accent-tint); }

/* Barra fluttuante delle azioni massive: appare in basso quando c'è una selezione. */
.saas-ui .rt-bulkbar{ position:sticky; bottom:16px; z-index:5; margin:16px auto 16px; width:max-content; max-width:calc(100% - 24px);
  display:flex; align-items:center; gap:10px; padding:9px 14px; border-radius: var(--pill);
  background:var(--panel); border:1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  box-shadow:var(--shadow-lg); }
.saas-ui .rt-bulkbar-count{ font-weight:700; font-size:13px; color:var(--accent); padding:0 4px; white-space:nowrap; }
.saas-ui .rt-bulkbar-spacer{ width:1px; height:22px; background:var(--line); }
.saas-ui .rt-bulkbar-status{ display:flex; align-items:center; gap:6px; }
.saas-ui .rt-bulkbar .input.sm{ padding:5px 8px; font-size:13px; }
.saas-ui .btn.danger{ background:var(--danger, #dc2626); color:#fff; border-color:transparent; }
.saas-ui .btn.danger:hover{ filter:brightness(.94); }

/* ── RICERCA GLOBALE cross-entità in topbar (WS5) ────────────────────────────── */
/* Ricerca globale (zona 2 della topbar): una sola affordance forte, al centro. */
.saas-ui .pnl-search{ position:relative; display:flex; align-items:center; flex:1 1 auto; min-width:0; max-width:520px; }
.saas-ui .pnl-search-ic{ position:absolute; left:11px; font-size:14px; color:var(--faint); pointer-events:none; }
.saas-ui .pnl-search .input{ width:100%; height:var(--ctrl-h,36px); padding:0 48px 0 30px; font-size:13px; border-radius:var(--pill,999px); }
.saas-ui .pnl-search .input:focus{ border-color:color-mix(in srgb, var(--accent) 45%, var(--line)); }
@media (max-width: 720px){ .saas-ui .pnl-search{ display:none; } }

/* ── Stelle di soddisfazione ticket (WS5.1) ──────────────────────────────────── */
.saas-ui .rate-star{ border:none; background:none; cursor:pointer; font-size:20px; line-height:1; padding:0 2px;
  color:var(--line-2, #d1d5db); transition:color .12s ease, transform .12s ease; }
.saas-ui .rate-star:hover{ transform:scale(1.15); }
/* Hover progressivo: passando su una stella si accendono lei e le precedenti. */
.saas-ui .rate-star:hover, .saas-ui .rate-star:has(~ .rate-star:hover){ color:var(--warn); }
.saas-ui .rate-star:disabled{ cursor:default; opacity:.6; }

/* ═══ FASE 7 — MODALE / TOAST / COMMAND PALETTE (Refined Enterprise) ═══════════ */

/* Hint tastiera nella ricerca globale (topbar). */
/* Il suggerimento «⌘K» sta su `--panel-2`, non su bianco: con `--faint` misurava 4,05:1. */
.saas-ui .pnl-search-kbd{ position:absolute; right:9px; font-size: 11px; font-weight:600; color:var(--muted);
  border:1px solid var(--line-2); border-radius: var(--radius-2xs); padding:1px 5px; background:var(--panel-2); pointer-events:none; }
@media (max-width:720px){ .saas-ui .pnl-search-kbd{ display:none; } }

/* Modale di conferma: scrim + card centrata, focus-trap gestito in JS.
   NON scopata a `.saas-ui`: `useConfirm`/`usePrompt` vivono anche nella console SuperAdmin e
   nella dashboard rivenditore (gestione account, piani, ruoli, cestino…). Con lo scope la
   modale ci arrivava senza `position:fixed` e compariva IN LINEA in mezzo alla scheda,
   spingendo il contenuto in giù. Vale lo stesso per toast e palette qui sotto. */
.saas-modal-scrim{ position:fixed; inset:0; z-index:60; display:grid; place-items:center; padding:20px;
  background:rgba(18,24,38,.45); backdrop-filter:blur(2px); animation:saas-fade .14s ease; }
.saas-modal{ width:100%; max-width:440px; background:var(--panel); border:1px solid var(--line);
  border-radius:var(--radius-lg,16px); box-shadow:var(--shadow-3); overflow:hidden; animation:saas-pop .16s ease; }
.saas-modal-head{ padding:18px 20px 0; }
.saas-modal-head h2{ margin:0; font-size:17px; font-weight:700; }
.saas-modal-body{ padding:10px 20px 18px; font-size: 13px; line-height:1.5; color:var(--muted); }
.saas-modal-foot{ display:flex; justify-content:flex-end; gap:8px; padding:14px 20px;
  background:var(--panel-2); border-top:1px solid var(--line); }
@keyframes saas-fade{ from{ opacity:0; } to{ opacity:1; } }
@keyframes saas-pop{ from{ opacity:0; transform:translateY(6px) scale(.98); } to{ opacity:1; transform:none; } }

/* MediaPicker: l'archivio unico dei file, con cartelle e miniature.
   NON scopato a `.saas-ui`, per la stessa ragione della modale qui sopra: lo stesso componente si
   apre nella console del rivenditore E dentro il SaaS (allegato di un record), e con lo scope
   arriverebbe senza `position:fixed` in uno dei due mondi. Il prefisso `mp-` è scelto apposta fuori
   dalla famiglia `pnl-|rt-|site-|saas-`: quelli sono un vocabolario dichiarato in `UI_CLASSES`, e
   una classe nuova lì dentro rompe la build finché non viene registrata. */
.mp-card{ width:100%; max-width:860px; max-height:min(680px, 88vh); display:flex; flex-direction:column;
  background:var(--panel); border:1px solid var(--line); border-radius:var(--radius-lg,16px);
  box-shadow:var(--shadow-3); overflow:hidden; animation:saas-pop .16s ease; }
.mp-head{ display:flex; align-items:center; gap:10px; padding:14px 16px; border-bottom:1px solid var(--line); }
.mp-head strong{ font-size:15px; }
.mp-search{ margin-left:auto; max-width:260px; }
.mp-path{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; padding:10px 16px;
  border-bottom:1px solid var(--line); background:var(--panel-2); font-size:12px; }
.mp-crumb{ background:none; border:0; padding:2px 4px; border-radius:6px; color:var(--accent);
  cursor:pointer; font:inherit; }
.mp-crumb:disabled{ color:var(--text); cursor:default; font-weight:600; }
.mp-sep{ color:var(--muted); margin:0 2px; }
.mp-spacer{ flex:1; }
.mp-error{ margin:10px 16px 0; padding:8px 10px; border-radius:8px; font-size:12px;
  background:color-mix(in srgb, var(--crit) 10%, transparent); border:1px solid var(--crit); color:var(--text); }
.mp-body{ flex:1; overflow:auto; padding:14px 16px; display:grid; gap:12px;
  grid-template-columns:repeat(auto-fill, minmax(148px, 1fr)); align-content:start; }
/* Il bersaglio del trascinamento è TUTTA l'area, non una striscia: chi trascina punta al centro. */
.mp-body.is-over{ outline:2px dashed var(--accent); outline-offset:-8px; background:color-mix(in srgb, var(--accent) 5%, transparent); }
.mp-item{ display:flex; flex-direction:column; gap:4px; padding:8px; text-align:left; cursor:pointer;
  background:var(--panel-2); border:1px solid var(--line); border-radius:var(--radius,12px); font:inherit; color:var(--text); }
.mp-item:hover{ border-color:var(--accent); }
.mp-item.is-busy{ opacity:.7; cursor:default; }
.mp-thumb{ aspect-ratio:4/3; display:grid; place-items:center; overflow:hidden; border-radius:8px;
  background:var(--panel); border:1px solid var(--line); }
.mp-thumb img{ width:100%; height:100%; object-fit:cover; }
.mp-doc{ font-size:26px; color:var(--muted); }
.mp-folder .mp-thumb{ background:color-mix(in srgb, var(--accent) 8%, var(--panel)); }
/* Il nome sta su una riga sola: un nome lungo che manda a capo fa saltare l'allineamento della
   griglia, e a quel punto le schede non si scorrono più con l'occhio. Il titolo completo resta nel
   `title` dell'elemento. */
.mp-name{ font-size:12px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.mp-meta{ font-size:11px; color:var(--muted); }
.mp-empty{ grid-column:1/-1; margin:24px 0; text-align:center; color:var(--muted); font-size:13px; }
@media (max-width:560px){
  .mp-body{ grid-template-columns:repeat(auto-fill, minmax(112px, 1fr)); }
  .mp-search{ max-width:none; }
}

/* Toast: stack in basso a destra, bordo-sinistro semantico, auto-dismiss. */
.saas-toast-stack{ position:fixed; right:18px; bottom:18px; z-index:70; display:flex; flex-direction:column;
  gap:8px; align-items:flex-end; pointer-events:none; max-width:min(360px, calc(100vw - 36px)); }
.saas-toast{ pointer-events:auto; background:var(--panel); border:1px solid var(--line);
  border-left:3px solid var(--muted); border-radius: var(--radius); box-shadow:var(--shadow-2); padding:11px 14px;
  font-size:13px; color:var(--text); opacity:0; transform:translateY(8px); transition:opacity .2s ease, transform .2s ease; max-width:100%; }
.saas-toast.in{ opacity:1; transform:none; }
.saas-toast.tone-ok{ border-left-color:var(--good); }
.saas-toast.tone-warn{ border-left-color:var(--warn); }
.saas-toast.tone-crit{ border-left-color:var(--crit); }
.saas-toast.tone-info{ border-left-color:var(--accent); }

/* Command palette ⌘K: overlay in alto-centro + card risultati. */
.saas-cmdk-scrim{ position:fixed; inset:0; z-index:65; display:flex; justify-content:center; align-items:flex-start;
  padding:12vh 20px 20px; background:rgba(18,24,38,.45); backdrop-filter:blur(2px); animation:saas-fade .14s ease; }
.saas-cmdk{ width:100%; max-width:560px; background:var(--panel); border:1px solid var(--line);
  border-radius:var(--radius-lg,16px); box-shadow:var(--shadow-3); overflow:hidden; animation:saas-pop .16s ease; }
.saas-cmdk-inputrow{ display:flex; align-items:center; gap:10px; padding:14px 16px; border-bottom:1px solid var(--line); }
.saas-cmdk-ic{ font-size:16px; color:var(--faint); }
.saas-cmdk-input{ flex:1; border:0; outline:none; background:transparent; font-size:15px; color:var(--text); font-family:inherit; }
.saas-cmdk-kbd{ font-size: 11px; font-weight:600; color:var(--faint); border:1px solid var(--line-2);
  border-radius: var(--radius-2xs); padding:1px 6px; background:var(--panel-2); }
.saas-cmdk-list{ max-height:min(52vh, 420px); overflow-y:auto; padding:6px; }
.saas-cmdk-empty{ padding:24px 12px; text-align:center; color:var(--faint); font-size:13px; }
.saas-cmdk-item{ display:flex; align-items:center; gap:10px; padding:9px 11px; border-radius: var(--radius-sm);
  color:var(--text); text-decoration:none; font-size: 13px; }
.saas-cmdk-item.is-active{ background:var(--accent-tint); }
.saas-cmdk-kind{ font-size: 11px; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
  color:var(--accent-ink); background:color-mix(in srgb, var(--accent) 12%, transparent); border-radius: var(--radius-2xs); padding:2px 6px; flex:none; }
.saas-cmdk-label{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.saas-cmdk-enter{ margin-left:auto; color:var(--faint); font-size:13px; }
.saas-cmdk-foot{ padding:9px 16px; border-top:1px solid var(--line); background:var(--panel-2);
  font-size: 11px; color:var(--faint); }

/* Preferenze notifiche: righe con interruttore (switch) accessibile. */
.np-row{ display:flex; align-items:center; justify-content:space-between; gap:14px; padding:9px 12px;
  border:1px solid var(--line); border-radius: var(--radius); background:var(--panel); font-size: 13px; }
.np-switch{ position:relative; width:40px; height:22px; flex:none; border-radius: var(--pill); border:1px solid var(--line-2);
  background:var(--panel-2); cursor:pointer; transition:background .16s ease, border-color .16s ease; padding:0; }
.np-switch.on{ background:var(--accent); border-color:var(--accent); }
.np-switch .np-knob{ position:absolute; top:1px; left:1px; width:18px; height:18px; border-radius:50%;
  background:#fff; box-shadow:0 1px 2px rgba(18,24,38,.25); transition:transform .16s ease; }
.np-switch.on .np-knob{ transform:translateX(18px); }
.np-switch:focus-visible{ outline:none; box-shadow:0 0 0 3px var(--focus-ring); }
.np-switch:disabled{ opacity:var(--disabled-opacity); cursor:not-allowed; }

/* Editor email a blocchi — regole GLOBALI (usato sia nel runtime .saas-ui sia nella console admin). */
.eb-block{ border:1px solid var(--line); border-radius: var(--radius); padding:10px 12px; background:var(--panel); display:grid; gap:8px; }
.eb-block-head{ display:flex; align-items:center; justify-content:space-between; gap:8px; }
.eb-block-actions{ display:inline-flex; gap:4px; }
.eb-block textarea.input{ resize:vertical; min-height:38px; font-family:inherit; line-height:1.5; }
.eb-block-actions .btn{ padding:5px 7px; display:inline-flex; align-items:center; justify-content:center; line-height:0; }
/* Editor email a due colonne: form a sinistra, anteprima LIVE (sticky) a destra. */
.eb-live{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:18px; align-items:start; }
.eb-form{ display:grid; gap:12px; min-width:0; }
.eb-preview{ display:grid; gap:8px; min-width:0; position:sticky; top:12px; }
.eb-preview-head{ display:flex; align-items:center; justify-content:space-between; font-size:12px; font-weight:600; color:var(--faint); text-transform:uppercase; letter-spacing:.04em; }
.eb-preview-frame{ width:100%; height:520px; border:1px solid var(--line); border-radius: var(--radius); background:#fff; display:block; }
@media (max-width: 900px){
  .eb-live{ grid-template-columns:1fr; }
  .eb-preview{ position:static; }
  .eb-preview-frame{ height:420px; }
}

/* Conferma di CANCELLAZIONE (`DeleteSaasButton`). Riquadro in tono critico: l'unica azione del
   prodotto che non torna indietro merita di sembrare diversa da un bottone qualunque. Colori
   derivati da `--crit` con color-mix, come tutto il resto — nessun rosso scritto a mano. */
.del-confirm { border: 1px solid color-mix(in srgb, var(--crit) 45%, transparent); background: color-mix(in srgb, var(--crit) 7%, transparent); border-radius: var(--radius); padding: 14px; }
.del-warn { margin: 0 0 6px; font-size: 13px; line-height: 1.5; color: var(--text); }
.del-warn b { color: var(--crit); }
.del-err { margin: 10px 0 0; font-size: 12px; line-height: 1.5; color: var(--crit); background: color-mix(in srgb, var(--crit) 10%, transparent); border: 1px solid color-mix(in srgb, var(--crit) 30%, transparent); border-radius: var(--radius-sm); padding: 8px 10px; }
.del-confirm .input { max-width: 320px; }

/* RenderNode grid/galleria: colonne dichiarate, responsivita' fidata. I valori arrivano solo
   dai domini 1..4 del validatore: mai CSS o media-query dal bundle del tenant. */
.render-responsive-grid { display:grid; grid-template-columns:repeat(var(--render-grid-columns, 1), minmax(0, 1fr)); min-width:0; }
.render-responsive-row { flex-direction:row; min-width:0; }
@media (max-width: 680px){
  .render-responsive-grid { grid-template-columns:repeat(var(--render-grid-mobile-columns, 1), minmax(0, 1fr)); }
  .render-responsive-row[data-render-row-mobile-direction="column"] { flex-direction:column; align-items:stretch !important; }
}

/* ══ ASSISTENZA (ticket) ══════════════════════════════════════════════════════
   Le due console dell'Assistenza — la coda della piattaforma e le richieste del costruttore —
   fanno lo stesso mestiere e adesso hanno la stessa forma: ELENCO a sinistra, CONVERSAZIONE a
   destra. Prima erano una tabella a otto colonne col thread renderizzato sotto (piattaforma) e
   un modulo fisso in cima con l'elenco sotto e il thread sotto ancora (costruttore): entrambe
   costringevano a scorrere su e giù per fare una cosa sola, rispondere.
   Gli stili stanno QUI e non nel JSX: erano una trentina di `style={{…}}` sparsi fra tre file,
   ed è il motivo per cui le due schermate erano divergenti sul serio (bolle, bordi e spaziature
   diverse a parità di significato). */
.tk-wrap { display: grid; grid-template-columns: minmax(260px, 360px) minmax(0, 1fr); gap: 16px; align-items: start; }
/* Sotto i 1000px la colonna del thread finirebbe a ~300px: sotto quella soglia si torna a una
   colonna sola, e l'elenco si accorcia perché il contenuto scelto viene PRIMA — su schermo
   stretto la conversazione è ciò che si è chiesto di vedere. */
@media (max-width: 1000px) {
  .tk-wrap { grid-template-columns: minmax(0, 1fr); }
  .tk-wrap > .tk-col:first-child { order: 2; max-height: 420px; overflow-y: auto; }
  .tk-wrap > .tk-col:last-child { order: 1; }
}
.tk-col { min-width: 0; }

/* Filtri: una riga sola di chip. Stato e categoria erano su due righe con un'etichetta
   «Categoria:» davanti; il separatore verticale dice la stessa cosa senza occupare una riga. */
.tk-filters { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.tk-filters-sep { width: 1px; align-self: stretch; min-height: 18px; background: var(--line-2); margin: 0 2px; }
.tk-search { display: flex; gap: 6px; align-items: center; }
.tk-search .input { min-width: 220px; }
@media (max-width: 560px) { .tk-search { width: 100%; } .tk-search .input { min-width: 0; flex: 1; } }

/* L'elenco */
.tk-list { display: grid; gap: 6px; align-content: start; }
.tk-item {
  display: grid; gap: 5px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel);
  text-decoration: none; color: inherit;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.tk-item:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--line-2)); }
/* Selezionato = bordo accent + anello interno, non solo il bordo: su un elenco di venti righe
   un cambio di bordo da solo si perde (ed è invisibile a chi non distingue i colori). */
.tk-item.is-active {
  border-color: var(--accent); background: color-mix(in srgb, var(--accent) 7%, var(--panel));
  box-shadow: inset 0 0 0 1px var(--accent);
}
.tk-item-top { display: flex; align-items: center; gap: 8px; }
.tk-item-subject { flex: 1; min-width: 0; font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tk-item-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 11px; color: var(--faint); }
.tk-item-who { color: var(--muted); font-weight: 500; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tk-item-assignee { font-family: var(--font-mono); }
.tk-item-flag { font-size: 10px; padding: 1px 6px; }
/* Il riquadro paga il padding UNA volta sola. Sommava i suoi 22px ai 44px dell'EmptyState, e con
   il cerchio da 64px il risultato era un muro bianco di 872x320 che diceva «Scegli una richiesta»:
   è il «brutto» che il proprietario ha visto per primo. Il visual resta del componente (variante
   `is-compact`), qui si azzera solo la cella — lo stesso confine già scritto a riga 788. */
.tk-empty { padding: 0; display: grid; place-items: center; }
/* Zero richieste: non ha senso affiancare «non ce n'è nessuna» a «scegline una». Erano DUE stati
   vuoti uno accanto all'altro, e nella coda della piattaforma è lo stato del primo giorno. */
.tk-wrap.is-empty { grid-template-columns: minmax(0, 1fr); }

/* La conversazione */
.tk-thread { padding: 16px 18px; display: grid; gap: 12px; }
.tk-thread-head { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.tk-thread-title { min-width: 0; flex: 1 1 260px; }
.tk-thread-title h2 { margin: 0; font-size: 16px; font-weight: 700; line-height: 1.35; }
.tk-thread-meta { display: flex; gap: 6px; flex-wrap: wrap; font-size: 11px; color: var(--faint); margin-top: 4px; }
.tk-thread-meta > span + span::before { content: "· "; }
.tk-thread-chips { display: flex; gap: 6px; flex-wrap: wrap; }

/* I messaggi scorrono DENTRO il riquadro: un thread lungo non deve spingere fuori schermo la
   casella per rispondere, che è il motivo per cui si è aperta la pagina. */
/* `46vh` era una frazione scelta a occhio: su un monitor alto lasciava mezza pagina inutilizzata
   sotto la conversazione. Il tetto sale, ma con un massimo assoluto — una colonna di messaggi più
   alta di così non si legge, si scorre. Sui portatili bassi non cambia nulla: è un tetto, non
   un'altezza, e sotto i 1000px resta rimosso del tutto. */
.tk-msgs { display: grid; gap: 10px; max-height: min(58vh, 620px); overflow-y: auto; padding: 2px 4px 2px 2px; }
@media (max-width: 1000px) { .tk-msgs { max-height: none; } }
.tk-msg { max-width: 86%; border: 1px solid var(--line); border-radius: var(--radius); padding: 9px 12px; background: var(--panel-2); }
.tk-msg.is-mine { justify-self: end; background: color-mix(in srgb, var(--accent) 8%, transparent); border-color: color-mix(in srgb, var(--accent) 30%, var(--line)); }
.tk-msg.is-internal { max-width: 100%; border-style: dashed; border-color: var(--warn); background: color-mix(in srgb, var(--warn) 8%, transparent); }
.tk-msg.is-internal .tk-msg-head b { color: var(--warn); }
.tk-msg-head { display: flex; gap: 6px; flex-wrap: wrap; align-items: baseline; font-size: 11px; color: var(--faint); margin-bottom: 4px; }
.tk-msg-head b { color: var(--text); font-weight: 600; }
.tk-msg.is-mine .tk-msg-head b { color: var(--accent-ink, var(--accent)); }
/* `anywhere` e non `break-word`: nei ticket si incollano URL e stack trace, che senza questo
   allargano la bolla oltre la colonna e fanno scorrere l'intera pagina in orizzontale. */
.tk-msg-body { font-size: 13px; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }

/* Gli attrezzi della piattaforma */
.tk-tools { display: grid; gap: 8px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel-2); }
.tk-tool-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.tk-tool-label { font-size: 11px; color: var(--faint); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 700; min-width: 62px; }
.tk-tool-select { width: auto; }
.tk-tool-assignee { width: 200px; }
.tk-canned { padding: 0 2px; }

/* La casella per rispondere */
.tk-compose { display: grid; gap: 8px; }
.tk-compose-note { margin: 0; font-size: 12px; color: var(--muted); }
.tk-compose-box { min-height: 84px; }
.tk-compose-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* Il pulsante di invio va a destra qualunque cosa ci sia alla sua sinistra (toggle nota interna,
   chiudi/riapri): `margin-left: auto` sull'ULTIMO figlio, non una regola per ogni combinazione. */
.tk-compose-bar > :last-child { margin-left: auto; }

/* Apertura di una richiesta */
.tk-new { padding: 18px 20px; }
.tk-new-sub { margin: 6px 0 14px; }
.tk-form { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 12px; }
@media (max-width: 620px) { .tk-form { grid-template-columns: 1fr; } }
.tk-field { display: grid; gap: 5px; min-width: 0; }
.tk-field-wide { grid-column: 1 / -1; }
.tk-field > span { font-size: 12px; font-weight: 600; color: var(--muted); }
.tk-field-hint { font-size: 11px; color: var(--faint); font-weight: 400; }
.tk-new-body { min-height: 120px; }
.tk-new-actions { display: flex; gap: 10px; align-items: center; margin-top: 14px; }
