.cursor-pointer {
      cursor: pointer;
}

/* ============================================================
   Ricerca globale topbar (gc-search)
   ============================================================ */
#gc-search-wrap {
    width: 640px;
    max-width: 100%;
}
#gc-search-wrap .topbar-search {
    width: 640px;
    max-width: 100%;
    padding-right: 2.25rem;
}
.gc-search-panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 1080;
    width: 640px;
    max-width: calc(100vw - 32px);
    background: var(--bs-body-bg, #fff);
    border: 1px solid var(--bs-border-color, #e5e7eb);
    border-radius: .6rem;
    box-shadow: 0 14px 36px rgba(15, 23, 42, .14);
    max-height: 75vh;
    overflow-y: auto;
    padding: .35rem 0;
}
.gc-search-panel .gc-search-group-label {
    font-size: .7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--bs-secondary-color, #6b7280);
    padding: .55rem .95rem .3rem;
    display: flex;
    align-items: center;
    gap: .4rem;
    border-top: 1px solid var(--bs-border-color, #f1f3f5);
}
.gc-search-panel .gc-search-group-label:first-child {
    border-top: 0;
}
.gc-search-panel .gc-search-item {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: start;
    gap: .25rem 1rem;
    padding: .55rem .95rem;
    color: var(--bs-body-color, #111);
    text-decoration: none;
    cursor: pointer;
    line-height: 1.25;
}
.gc-search-panel .gc-search-item:hover {
    background: var(--app-surface-sunken);
}
.gc-search-panel .gc-search-item.is-active {
    background: var(--app-accent-subtle);
}
.gc-search-panel .gc-search-main {
    min-width: 0;
}
.gc-search-panel .gc-search-title {
    font-weight: 500;
    font-size: .9375rem;
    color: var(--bs-body-color, #111827);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gc-search-panel .gc-search-sub {
    font-size: .8125rem;
    color: var(--bs-secondary-color, #6b7280);
    margin-top: .1rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gc-search-panel .gc-search-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: .15rem;
    white-space: nowrap;
    padding-top: 1px;
}
.gc-search-panel .gc-search-chip {
    font-size: .72rem;
    font-weight: 600;
    line-height: 1;
    padding: .25rem .5rem;
    border-radius: 999px;
    letter-spacing: .01em;
    background: var(--bs-secondary-bg, #eef0f3);
    color: var(--bs-secondary-color, #4b5563);
    border: 1px solid transparent;
}
.gc-search-panel .gc-chip-primary {
    background: var(--app-accent-subtle);
    color: var(--app-accent-text);
}
.gc-search-panel .gc-chip-mono {
    background: transparent;
    color: var(--bs-body-color, #111827);
    border-color: var(--bs-border-color, #e5e7eb);
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
    font-weight: 600;
}
.gc-search-panel .gc-chip-neutral {
    background: var(--bs-tertiary-bg, #f3f4f6);
    color: var(--bs-secondary-color, #6b7280);
}
.gc-search-panel .gc-search-meta-sub {
    font-size: .75rem;
    color: var(--bs-secondary-color, #6b7280);
    font-variant-numeric: tabular-nums;
}
.gc-search-panel .gc-search-empty,
.gc-search-panel .gc-search-loading {
    padding: .95rem;
    text-align: center;
    color: var(--bs-secondary-color, #6b7280);
    font-size: .9rem;
}
.gc-search-panel mark {
    background: var(--app-accent-subtle);
    color: var(--app-accent-text);
    padding: 0 .1rem;
    border-radius: .15rem;
    font-weight: 600;
}

/* =========================================================
   Gestione Credito — stili modulo (portati da iliopesca)
   ========================================================= */

/* Cifre tabellari: niente shift orizzontale su colonne valuta/numeriche */
.tabular-nums,
table.gc-tabular td.text-end,
table.gc-tabular th.text-end,
table.gc-tabular .fw-bold,
table.gc-tabular .fw-semibold.text-end {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
}

/* Off-canvas dettaglio entità */
.gc-offcanvas-md { width: 600px; max-width: 100%; }

/* Timeline esposizione cliente: riempie la card e scrolla internamente */
.card-body:has(> .gc-timeline-scroll) { display: flex; flex-direction: column; min-height: 0; }
.gc-timeline-scroll { flex: 1 1 0; min-height: 0; overflow-y: auto; }

/* Iconcina info (componente gc/metric-info) */
.gc-info { display: inline-flex; align-items: center; cursor: help; opacity: .55; vertical-align: -.1em; }
.gc-info > svg, .gc-info > i { width: .9em; height: .9em; }
.gc-info:hover, .gc-info:focus-visible { opacity: .9; }

/* Righe e card cliccabili: si comportano come link (JS delegato in gc-helpers.js) */
.gc-tabular tbody tr[data-href],
tr[data-href] { cursor: pointer; transition: background-color .12s ease; }
.gc-tabular tbody tr[data-href]:hover,
tr[data-href]:hover { background-color: var(--bs-primary-bg-subtle, #e8ecfb) !important; }
.gc-tabular tbody tr[data-href]:focus-visible,
tr[data-href]:focus-visible { outline: 2px solid var(--app-accent); outline-offset: -2px; }
/* Chevron affordance a destra */
tr[data-href] td:last-child { position: relative; padding-right: 1.75rem !important; }
tr[data-href] td:last-child::after {
    content: "\203A"; position: absolute; right: .6rem; top: 50%; transform: translateY(-50%);
    color: var(--bs-secondary-color, #98a6ad); font-size: 1.25rem; font-weight: 600; line-height: 1;
    opacity: .45; transition: opacity .12s ease, transform .12s ease, color .12s ease; pointer-events: none;
}
tr[data-href]:hover td:last-child::after { opacity: 1; color: var(--app-accent); transform: translateY(-50%) translateX(3px); }
/* Celle con bottoni azione: niente chevron (evita overlap) */
tr[data-href] td.gc-actions-cell { padding-right: .5rem !important; }
tr[data-href] td.gc-actions-cell::after { content: none; }

/* Link nelle tabelle GC e link "promosso" */
.gc-tabular tbody a[href] { text-decoration: none; }
.gc-tabular tbody a[href]:hover,
.gc-tabular tbody a[href]:focus-visible,
.gc-tabular tbody tr[data-href]:hover a[href].fw-medium,
.gc-tabular tbody tr[data-href]:hover a[href].link-reset,
a.gc-link:hover, a.gc-link:focus-visible,
.gc-tabular tbody tr[data-href]:hover a.gc-link {
    color: var(--app-accent); text-decoration: underline; text-underline-offset: 2px;
}
a.gc-link, a.gc-link:visited { color: inherit; text-decoration: none; }

/* Card cliccabile (griglia clienti) */
.card[data-href] { cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease; }
.card[data-href]:hover { border-color: var(--app-accent); box-shadow: var(--app-shadow-hover); }
.card[data-href]:focus-visible { outline: 2px solid var(--app-accent); outline-offset: 2px; }

/* Leggibilità (DESIGN.md §3 — Regola del Corpo Minimo Leggibile): il tema rende
   fs-xxs/fs-xs ~10px. Floor GLOBALE a ≥13px + sizing delle tabelle dato, così ogni
   pagina (moduli, tools, ui-kit) rispetta il minimo senza wrapper dedicati. */
.fs-xxs { font-size: .8125rem !important; }
.fs-xs  { font-size: .8125rem !important; }
table.gc-tabular { font-size: .9375rem; }
table.gc-tabular thead th { font-size: .8125rem; }
/* Affinamenti specifici delle pagine Gestione Credito */
.gc-scope .fs-sm  { font-size: .875rem  !important; }
.gc-scope .fs-md  { font-size: 1rem     !important; }
.gc-scope .small, .gc-scope small { font-size: .8125rem !important; }
.gc-scope .small .small, .gc-scope small small { font-size: 1em !important; }
/* ============================================================
   Chip / badge — più respiro e un filo più grandi, icona inclusa.
   Globale (vedi DESIGN.md §5). La spaziatura icona↔testo la dà `gap`,
   non i margini: così le icone interne restano centrate e ariose.
   ============================================================ */
.badge {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    padding: .42em .72em;
    font-size: .8125rem;
    font-weight: 500;
    line-height: 1.1;
    vertical-align: middle;
}
.badge > svg,
.badge > i[data-lucide] {
    width: 1.05em;
    height: 1.05em;
    margin: 0;              /* spaziatura via gap, non via me-1 */
    flex: 0 0 auto;
}

/* ============================================================
   Bottoni con icona — stessa logica dei badge: la spaziatura
   icona↔testo la dà `gap`, mai margini manuali (me-*) né icona
   incollata al testo. Scoped con :has() sui soli .btn con icona
   figlia diretta: i bottoni solo testo e i controlli generati
   (DataTables, btn-close, ...) restano intatti.
   ============================================================ */
.btn:has(> svg),
.btn:has(> [data-lucide]) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .45em;
}
.btn > svg,
.btn > [data-lucide] {
    margin: 0;              /* spaziatura via gap, non via me-1 */
    flex: 0 0 auto;
}

/* Badge di stato cliccabile (gc/stato-badge editable): stesso aspetto del
   badge, affordance da cursore + chevron; la tendina la costruisce
   jwd/stato-inline.js appesa a <body>. */
button.gc-stato-badge { cursor: pointer; }
button.gc-stato-badge:focus-visible {
    outline: 2px solid var(--app-accent);
    outline-offset: 2px;
}
.gc-stato-menu { min-width: 11rem; }

/* Cella "nome" cliccabile nelle tabelle: link in ink pieno (interattivo =
   near-black, regola design system), underline solo su hover. */
a.gc-cell-link {
    color: var(--app-ink);
    font-weight: 600;
    text-decoration: none;
}
a.gc-cell-link:hover { text-decoration: underline; }

/* ============================================================
   Select (Tom Select) — bridge sul design system
   Ogni <select> è potenziata da jwd/select.js (regola DESIGN.md §5:
   ogni dropdown è una Tom Select). Qui il tema bootstrap5 del plugin
   viene riagganciato ai token --app-*: niente colori propri.
   ============================================================ */
.ts-wrapper .ts-control {
    border-color: var(--app-border-strong);
    border-radius: var(--app-radius-sm);
    color: var(--app-ink);
    transition: border-color .12s ease, box-shadow .12s ease;
}
/* Focus: bordo accent + anello tenue (near-black), come gli altri input.
   Le varianti is-valid/is-invalid mantengono i loro colori semantici. */
.ts-wrapper.focus:not(.is-valid):not(.is-invalid) .ts-control {
    border-color: var(--app-accent);
    box-shadow: 0 0 0 .2rem rgba(var(--app-accent-rgb), .12);
}
/* Il placeholder mostra anche il valore precedente all'apertura (fix UX
   in jwd/select.js): deve leggersi come suggerimento, tenue. */
.ts-control > input::placeholder { color: var(--app-ink-faint); }

/* Tendina: Overlay del design system (bordo pieno, ombra overlay) */
.ts-dropdown {
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-sm);
    box-shadow: var(--app-shadow-overlay);
    color: var(--app-ink);
    margin-top: .3rem;
    padding: .25rem;
}
.ts-dropdown .option {
    padding: .45rem .6rem;
    border-radius: calc(var(--app-radius-sm) - 2px);
}
.ts-dropdown .active {
    background: var(--app-accent-subtle);
    color: var(--app-ink);
}
.ts-dropdown .selected { font-weight: 600; }
.ts-dropdown .optgroup-header {
    font-size: .72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--app-ink-muted);
    background: transparent;
}
.ts-dropdown .no-results,
.ts-dropdown .create {
    padding: .45rem .6rem;
    color: var(--app-ink-muted);
}

/* Multipla: le voci scelte sono chip in stile badge (subtle + ink) */
.ts-wrapper.multi .ts-control > .item {
    background: var(--app-accent-subtle);
    color: var(--app-ink);
    border: 0;
    border-radius: var(--app-radius-sm);
    padding: .15rem .45rem;
    font-size: .8125rem;
    line-height: 1.4;
}
.ts-wrapper.multi .ts-control > .item .remove {
    border-left: 0;
    padding: 0 .3rem;
    color: var(--app-ink-muted);
}
.ts-wrapper.multi .ts-control > .item .remove:hover {
    background: transparent;
    color: var(--app-ink);
}
.ts-wrapper .clear-button { color: var(--app-ink-muted); }
.ts-wrapper .clear-button:hover { color: var(--app-ink); }

/* ============================================================
   Select "ricche" (Tom Select, data-ts-rich): thumbnail + sotto-riga
   (jwd/select.js + componente forms/select con 'rich' => true)
   ============================================================ */
.ts-rich { display: flex; align-items: center; gap: .55rem; min-width: 0; }
.ts-rich-img {
    width: 34px; height: 34px; flex: 0 0 auto; object-fit: cover;
    border-radius: var(--app-radius-sm); border: 1px solid var(--app-border);
    background: var(--app-surface-sunken);
}
.ts-rich-item .ts-rich-img { width: 22px; height: 22px; }
.ts-rich-body { min-width: 0; }
.ts-rich-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ts-rich-sub {
    font-size: .78rem; color: var(--app-ink-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ============================================================
   UI Kit / style-guide — helper condivisi (pagine /ui-kit/*)
   ============================================================ */
.uikit-eyebrow { font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--app-ink-muted); }
.uikit-section { margin-bottom: 2.5rem; }
.uikit-section > h4 { font-weight: 600; letter-spacing: -.005em; margin-bottom: .25rem; }
.uikit-lead { color: var(--app-ink-muted); font-size: .9rem; margin-bottom: 1rem; max-width: 72ch; }
.uikit-demo { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.uikit-swatch-label { font-size: .72rem; color: var(--app-ink-muted); font-family: ui-monospace, SFMono-Regular, monospace; }
/* ── Respiro verticale del contenuto ──────────────────────────────────────
   Il template lascia .content-page con padding-top:0 sotto la topbar sticky,
   così il primo elemento (es. titolo pagina) risulta incollato all'header.
   Diamo a OGNI pagina un respiro sopra (sotto la topbar) e sotto (prima del footer). */
.content-page > .container-fluid {
    padding-top: var(--app-space-5);    /* 1.5rem sotto la topbar */
    padding-bottom: var(--app-space-6); /* 2rem prima del footer */
}

/* ============================================================
   Feedback azioni — toast flottanti (GC.toast, jwd/toast.js)
   Convenzione: MAI banner di conferma/errore a tutta pagina.
   Stack fisso in basso a destra, superficie ink inversa.
   ============================================================ */
.gc-toast-stack {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1090; /* sopra modal (1055) e offcanvas */
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: .5rem;
    pointer-events: none;
    max-width: min(380px, calc(100vw - 2rem));
}

.gc-toast {
    pointer-events: auto;
    display: flex;
    align-items: flex-start;
    gap: .55rem;
    padding: .7rem .8rem;
    border-radius: var(--app-radius-lg);
    background: var(--app-ink);
    color: var(--app-surface);
    box-shadow: var(--app-shadow-panel);
    font-size: .8125rem;
    line-height: 1.4;
    animation: gc-toast-in .18s ease-out;
}

.gc-toast-icon { width: 16px; height: 16px; flex: 0 0 auto; margin-top: 1px; }
.gc-toast-success .gc-toast-icon { color: var(--app-success); }
.gc-toast-danger  .gc-toast-icon { color: var(--app-danger); }
.gc-toast-warning .gc-toast-icon { color: var(--app-warning); }
.gc-toast-info    .gc-toast-icon { color: var(--app-info); }

.gc-toast-msg { flex: 1 1 auto; }

.gc-toast-close {
    background: none;
    border: 0;
    padding: 0;
    margin-left: .15rem;
    color: inherit;
    opacity: .55;
    display: inline-flex;
    flex: 0 0 auto;
    cursor: pointer;
}
.gc-toast-close:hover { opacity: 1; }
.gc-toast-close i { width: 14px; height: 14px; }

.gc-toast-out { animation: gc-toast-out .22s ease-in forwards; }

@keyframes gc-toast-in  { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes gc-toast-out { to   { opacity: 0; transform: translateY(6px); } }

/* ============================================================
   Popover di conferma inline ([data-confirm], jwd/actions.js):
   sostituisce window.confirm per le azioni distruttive.
   ============================================================ */
.gc-confirm-pop {
    position: fixed;
    z-index: 1091;
    background: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-lg);
    box-shadow: var(--app-shadow-overlay);
    padding: .65rem .75rem;
    max-width: 280px;
}
.gc-confirm-msg { font-size: .8125rem; color: var(--app-ink); margin-bottom: .55rem; }
.gc-confirm-actions { display: flex; gap: .4rem; justify-content: flex-end; }

/* ============================================================
   Off-canvas larghi — la classe usata dal componente
   offcanvas/offcanvas-std, che il tema non definisce.
   ============================================================ */
.offcanvas-600,
.jwd-offcanvas { width: 600px; max-width: 100%; }

/* Form dentro un off-canvas: il corpo scrolla, la barra azioni resta in fondo. */
.jwd-offcanvas > form {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}
.jwd-offcanvas-actions { background: var(--app-surface); }

/* ============================================================
   Vetrina Progetti (/progetti)
   ============================================================ */
.jwd-min-w-0 { min-width: 0; }

/* Logo cliente: quadrato neutro, immagine contenuta (i loghi hanno
   proporzioni imprevedibili), iniziali come fallback. E' il primo elemento
   che si riconosce in una vetrina, quindi tiene la scena.
   Misura base = anteprima del form; sulla card vale .jwd-logo-xl. */
.jwd-logo {
    width: 96px;
    height: 96px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius);
    background: var(--app-media-light);
    transition: background-color var(--app-duration) var(--app-ease);
}
.jwd-logo img { width: 100%; height: 100%; object-fit: contain; padding: 8px; }
.jwd-logo > span {
    font-weight: 700;
    font-size: 1.75rem;
    color: var(--app-ink-secondary);
    letter-spacing: .02em;
}
.jwd-logo > svg, .jwd-logo > i[data-lucide] { width: 26px; height: 26px; }

/* Fondo scuro: e' una scelta del progetto (loghi bianchi o con contorni
   chiari), quindi il colore non segue il tema — resta quasi nero anche in
   tema chiaro. Le iniziali di fallback si schiariscono di conseguenza. */
.jwd-logo-scuro {
    background: var(--app-media-dark);
    border-color: var(--app-media-dark);
}
.jwd-logo-scuro > span { color: var(--app-media-dark-ink); }
.jwd-logo-scuro > svg, .jwd-logo-scuro > i[data-lucide] { color: var(--app-media-dark-ink); }

/* Sulla card il logo si prende quasi tutta la meta' sinistra: cresce con la
   card invece di essere una miniatura fissa. Formato 4:3, che e' quello in
   cui di solito sta un logo (piu' largo che alto). */
.jwd-logo-xl {
    flex: 0 0 36%;
    width: auto;
    height: auto;
    /* Il 4:3 resta la misura naturale — e' quella che il logo contribuisce
       all'altezza della testata — ma con align-self:stretch il riquadro poi
       si allunga fino in fondo. Cosi' il bordo basso dell'immagine e quello
       dei bottoni coincidono sempre, sia che a comandare l'altezza sia il
       logo, sia che sia il testo della colonna di destra.
       L'immagine dentro non si deforma: ha object-fit:contain, cresce solo
       il riquadro che le sta attorno. */
    aspect-ratio: 4 / 3;
    align-self: stretch;
}
.jwd-logo-xl > span { font-size: 2.5rem; }
.jwd-logo-xl > svg, .jwd-logo-xl > i[data-lucide] { width: 32px; height: 32px; }

/* Testata: logo a sinistra, tutto il resto incolonnato a destra
   (nome, data + stato, aree, descrizione). */
.jwd-card-head {
    display: flex;
    /* stretch, non flex-start: entrambe le colonne devono arrivare in fondo
       alla testata. A destra perche' altrimenti il margin-top:auto dei
       bottoni non ha nulla contro cui spingere e restano a mezz'aria; a
       sinistra perche' il riquadro del logo chiuda alla stessa altezza dei
       bottoni. L'immagine dentro il riquadro non si deforma comunque
       (object-fit: contain). */
    align-items: stretch;
    gap: var(--app-space-4);
}
.jwd-card-info {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: var(--app-space-2);
}
/* Testata della colonna di destra: nome a sinistra, stato e comandi a
   destra. Prima i comandi erano sovrapposti in posizione assoluta e il nome
   si teneva libero un padding fisso; col badge dentro la larghezza non e'
   piu' prevedibile ("Bozza" e "In produzione" non misurano uguale), quindi
   il posto se lo contendono in flex. */
.jwd-card-testa {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-3);
}
.jwd-card-nome {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 1.25rem;
    font-weight: 600;
    line-height: 1.25;
    margin: 0;
}
.jwd-card-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--app-space-2);
}
/* La data e' corta e non si tocca: lo spazio lo cede la sede. */
.jwd-card-meta > .text-muted:first-child { flex: 0 0 auto; }
.jwd-card-aree { display: flex; flex-wrap: wrap; gap: .25rem; }

/* Su schermo stretto il logo va sopra: a meta' larghezza di un telefono
   non resterebbe spazio ne' per le pill ne' per la descrizione. */
@media (max-width: 575.98px) {
    .jwd-card-head { flex-direction: column; }
    .jwd-logo-xl { flex: 0 0 auto; width: 140px; align-self: flex-start; }

    /* Su schermo stretto nome, stato e comandi non ci stanno su una riga:
       sotto le 12rem di nome il blocco di destra scende sotto invece di
       spremere il titolo in una colonna di due parole. */
    .jwd-card-testa { flex-wrap: wrap; }
    .jwd-card-nome { min-width: 12rem; }
}

.jwd-clamp-2, .jwd-clamp-3 {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
}
.jwd-clamp-2 { -webkit-line-clamp: 2; line-height: 1.3; }
.jwd-clamp-3 { -webkit-line-clamp: 3; }

/* Chip area: e' una categoria, non uno stato -> nessun colore semantico
   (regola design system: il colore lo prende solo il dato). */
.jwd-chip {
    display: inline-flex;
    align-items: center;
    padding: .3em .65em;
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-pill);
    background: var(--app-surface-sunken);
    color: var(--app-ink-secondary);
    font-size: .8125rem;
    line-height: 1.15;
}

/* I quattro collegamenti: quattro in fila dove c'e' spazio, altrimenti 2x2.
   Mai 3+1, che e' la spaziatura peggiore delle tre.
   Griglia e non flex-wrap: il wrap del flexbox riempie ogni riga finche' ci
   sta qualcosa, quindi alle larghezze intermedie produce proprio il 3+1. Con
   grid il numero di colonne lo decidiamo noi, e sono solo 2 o 4.
   La soglia guarda la larghezza della CARD e non quella della finestra: la
   card e' cio' che conta davvero, e resta il riferimento giusto anche se un
   domani la colonna dell'elenco cambia larghezza o torna a due card per riga.
   800px e' il punto in cui la colonna di destra arriva a 474px e le quattro
   colonne restano da 112 l'una: la geometria e' info = 0.64*(card-34) - 16,
   dove il 36% se lo prende il logo. */
.jwd-link-btns {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .5rem;
}
#jwd-grid .card { container-type: inline-size; }
@container (min-width: 800px) {
    .jwd-link-btns { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* L'etichetta piu' lunga e' "Produzione": 66px di testo + 22 di padding + 2
   di bordo + 14 d'icona + 6 di gap del .btn = 110px di bottone. Sopra la
   soglia le colonne non scendono sotto i 112, quindi il nowrap non sborda e
   nessuna etichetta va abbreviata o rimpicciolita. */
.jwd-link-btns > * { min-width: 0; white-space: nowrap; }
.jwd-link-btns .disabled { opacity: .38; cursor: not-allowed; }

/* I quattro ambienti chiudono la colonna di destra (prima erano in un
   card-footer a tutta larghezza). Perche' restino allineati fra card di
   contenuto diverso servono tre cose in fila: il body come colonna flex, la
   testata che si prende l'altezza avanzata, e i bottoni spinti in fondo. */
#jwd-grid .card-body { display: flex; flex-direction: column; }
#jwd-grid .jwd-card-head { flex: 1 1 auto; }
.jwd-card-info > .jwd-link-btns {
    margin-top: auto;
    /* Niente filo di separazione: a staccarli dal testo bastano lo spazio
       che il margin-top:auto lascia sopra e il fatto che siano bottoni. */
    padding-top: var(--app-space-3);
}

/* Stato + comandi, in fondo alla riga di testata. Non si stringono mai:
   e' il nome a cedere spazio (ha min-width:0 e va a capo su due righe). */
.jwd-card-azioni {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: var(--app-space-2);
}

/* A sbiadire sono i soli comandi, non il badge: lo stato e' un dato del
   progetto e si legge sempre, mentre matita e cestino emergono all'hover.
   Restano comunque raggiungibili da tastiera e sempre visibili su touch. */
.jwd-card-azioni .btn {
    opacity: .5;
    transition: opacity var(--app-duration) var(--app-ease);
}
.card:hover .jwd-card-azioni .btn,
.jwd-card-azioni:focus-within .btn { opacity: 1; }
@media (hover: none) { .jwd-card-azioni .btn { opacity: 1; } }

/* Empty state: dentro una .row con row-cols-* le colonne hanno larghezza
   fissa, quindi al placeholder va imposta l'intera riga. */
.jwd-empty { flex: 0 0 100%; width: 100%; }

/* Zona logo del form: ci si incolla (Ctrl/Cmd+V), ci si trascina un file o
   si sceglie dal disco. Il bordo tratteggiato dice "qui puoi lasciare
   qualcosa"; resta una superficie a riposo (nessuna ombra) finche' non c'e'
   davvero un trascinamento sopra. */
.jwd-dropzone {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-3);
    padding: var(--app-space-3);
    border: 1px dashed var(--app-border-strong);
    border-radius: var(--app-radius);
    background: var(--app-surface);
    cursor: pointer;
    transition: border-color var(--app-duration) var(--app-ease),
                background-color var(--app-duration) var(--app-ease);
}
.jwd-dropzone:hover { border-color: var(--app-accent); }
/* L'<input type="file"> e' visually-hidden: il focus da tastiera lo mostra la zona. */
.jwd-dropzone:focus-within { outline: 2px solid var(--app-accent); outline-offset: 2px; }
.jwd-dropzone.is-dragover {
    border-style: solid;
    border-color: var(--app-accent);
    background: var(--app-accent-subtle);
}
.jwd-dropzone:has(input.is-invalid) { border-color: var(--app-danger); }
.jwd-dropzone p { color: var(--app-ink-secondary); }

/* Tasti nella riga di istruzioni: chip leggeri, non il blocco pieno di
   Bootstrap (che invertirebbe ink e superficie in mezzo al testo). */
.jwd-kbd {
    display: inline-block;
    padding: .05rem .35rem;
    margin: 0 .1rem;
    border: 1px solid var(--app-border-strong);
    border-bottom-width: 2px;
    border-radius: var(--app-radius-sm);
    background: var(--app-surface-sunken);
    color: var(--app-ink-secondary);
    font-family: inherit;
    font-size: .875em;
    line-height: 1.35;
}

/* Card puntata dalla ricerca globale (/progetti?focus=ID).
   Outline e non border-color: il bordo della .card se lo gioca il tema,
   l'outline no — ed e' lo stesso segno che il design system usa per il focus. */
.jwd-card-focus {
    outline: 2px solid var(--app-accent);
    outline-offset: 2px;
    box-shadow: var(--app-shadow-hover) !important;
}

/* ============================================================================
   Vetrina Progetti — mappa delle sedi
   La colonna di destra di /progetti: Leaflet con le tile OpenStreetMap,
   ricondotto ai token del design system. Tutto quello che Leaflet colora da
   solo (controlli, tooltip, attribuzione) qui viene riagganciato a --app-*,
   altrimenti in mezzo alla pagina resterebbe un rettangolo con un'estetica sua.
   ========================================================================= */

/* La mappa resta ferma mentre l'elenco scorre. Il top tiene conto della
   topbar sticky: senza, il bordo alto finirebbe sotto di essa. */
.jwd-mappa-sticky {
    position: sticky;
    top: calc(var(--ins-topbar-height, 70px) + var(--app-space-4));
}

.jwd-mappa {
    height: clamp(420px, calc(100vh - var(--ins-topbar-height, 70px) - 13rem), 720px);
    border-radius: var(--app-radius-lg) var(--app-radius-lg) 0 0;
    background: var(--app-surface-sunken);
    overflow: hidden;
    z-index: 0;   /* sotto off-canvas, dropdown e toast */
}

/* Sotto xl la mappa sta sopra l'elenco e non e' piu' sticky: alta quanto
   basta per orientarsi, senza rubare tutta la prima schermata. */
@media (max-width: 1199.98px) {
    .jwd-mappa-sticky { position: static; }
    .jwd-mappa { height: 300px; }
}

/* Le tile sono a colori e litigherebbero col monocromo near-black del design
   system: in grigio restano una mappa leggibile e smettono di gridare.
   Il filtro sta sul solo tile pane, cosi' i puntini restano a colori pieni. */
.jwd-mappa .leaflet-tile-pane {
    filter: grayscale(1) contrast(0.92) brightness(1.04);
}

/* Al buio la mappa si inverte invece di restare un rettangolo bianco in
   mezzo alla pagina scura. L'inversione dopo il grigio da' una mappa scura
   con le strade chiare, che e' esattamente la resa che si vuole. */
[data-bs-theme="dark"] .jwd-mappa .leaflet-tile-pane {
    filter: grayscale(1) invert(1) brightness(0.92) contrast(0.9);
}

/* Il pin: dentro ci sta il logo del cliente, cosi' la mappa si legge come la
   griglia — si riconosce chi e' senza passarci sopra. Niente marker azzurro di
   Leaflet: la sagoma la costruiamo qui, con i token del design system.
   Struttura: .jwd-pin e' il contenitore (ci disegna la punta), .jwd-pin-media
   e' il tondo che ritaglia il logo. Due nodi perche' il tondo ha overflow
   nascosto e si mangerebbe la punta. */
.jwd-pin {
    position: relative;
    display: block;
    width: 34px;
    height: 34px;
    transform-origin: 50% 100%;   /* cresce dalla punta, che e' il punto vero */
    transition: transform var(--app-duration) var(--app-ease);
}

/* La punta: un quadrato ruotato che sbuca da sotto il tondo, nello stesso
   colore del suo bordo. Sta sotto (z-index) cosi' si vede solo lo spicchio
   che avanza, e il tondo gli fa da testa. */
.jwd-pin::after {
    content: '';
    position: absolute;
    left: 50%;
    bottom: -4px;
    width: 13px;
    height: 13px;
    margin-left: -6.5px;
    border-radius: 0 0 3px 0;
    background: var(--app-surface);
    transform: rotate(45deg);
    box-shadow: var(--app-shadow-overlay);
}

.jwd-pin-media {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: var(--app-radius-pill);
    border: 2px solid var(--app-surface);
    background: var(--app-media-light);
    box-shadow: var(--app-shadow-overlay);
    transition: background-color var(--app-duration) var(--app-ease);
}
.jwd-pin-media img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 3px;
}
.jwd-pin-media > span {
    font-weight: 700;
    font-size: .75rem;
    line-height: 1;
    color: var(--app-ink-secondary);
    letter-spacing: .01em;
}

/* Fondo scuro dietro al logo: e' la stessa scelta di progetto che vale sulla
   card (`progetti.logo_scuro`), non una preferenza di tema. Un logo bianco
   deve restare visibile anche dentro il pin. */
.jwd-pin-scuro .jwd-pin-media {
    background: var(--app-media-dark);
    border-color: var(--app-media-dark);
}
.jwd-pin-scuro .jwd-pin-media > span { color: var(--app-media-dark-ink); }
.jwd-pin-scuro::after { background: var(--app-media-dark); }

/* Acceso quando il mouse (o il fuoco da tastiera) e' sulla card corrispondente. */
.leaflet-marker-icon.is-attivo .jwd-pin,
.leaflet-marker-icon:hover .jwd-pin {
    transform: scale(1.2);
}
.leaflet-marker-icon.is-attivo .jwd-pin-media,
.leaflet-marker-icon:hover .jwd-pin-media {
    border-color: var(--app-accent);
    box-shadow: var(--app-shadow-hover);
}
/* La punta e' la continuazione del bordo, non un pezzo a se': se resta del
   colore a riposo mentre il tondo si accende, il pin sembra spezzato in due. */
.leaflet-marker-icon.is-attivo .jwd-pin::after,
.leaflet-marker-icon:hover .jwd-pin::after {
    background: var(--app-accent);
}

@media (prefers-reduced-motion: reduce) {
    .jwd-pin { transition: none; }
}

.leaflet-marker-icon:focus-visible {
    outline: 2px solid var(--app-accent);
    outline-offset: 3px;
    border-radius: var(--app-radius-pill);
}

/* Controlli, tooltip e attribuzione: superfici e bordi del design system. */
.jwd-mappa .leaflet-bar a {
    background: var(--app-surface);
    color: var(--app-ink);
    border-bottom-color: var(--app-border);
}
.jwd-mappa .leaflet-bar a:hover { background: var(--app-surface-sunken); color: var(--app-ink); }
.jwd-mappa .leaflet-bar a.leaflet-disabled { background: var(--app-surface-sunken); color: var(--app-ink-faint); }
.jwd-mappa .leaflet-bar { border: 1px solid var(--app-border); box-shadow: var(--app-shadow-float); }

.jwd-mappa .leaflet-tooltip {
    background: var(--app-surface);
    color: var(--app-ink);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-sm);
    box-shadow: var(--app-shadow-overlay);
    font: inherit;
    padding: .4rem .6rem;
}
.jwd-mappa .leaflet-tooltip-top::before { border-top-color: var(--app-border); }

.jwd-mappa .leaflet-control-attribution {
    background: color-mix(in oklab, var(--app-surface) 88%, transparent);
    color: var(--app-ink-muted);
    font-size: .6875rem;   /* nota di licenza, non testo d'interfaccia */
}
.jwd-mappa .leaflet-control-attribution a { color: var(--app-ink-secondary); }

/* Sede: sta accanto alla data, nella riga dei metadati. E' l'unica delle
   due che puo' essere lunga, quindi e' l'unica che si restringe (la data no,
   flex:0 0 auto) e quando non ci sta tronca con l'ellissi — l'indirizzo per
   esteso resta nel title. L'icona cambia se il puntino manca. */
.jwd-card-sede {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    gap: .35rem;
    min-width: 0;
    color: var(--app-ink-muted);
}
.jwd-card-sede > i, .jwd-card-sede > svg { flex: 0 0 auto; }
.jwd-card-sede-testo {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Esito del geocoding nella scheda. Quando e' vuoto sparisce da solo: il nodo
   resta nel DOM perche' una regione aria-live deve esistere prima che il
   contenuto cambi, o non viene annunciata. */
.jwd-geo-esito {
    display: flex;
    align-items: flex-start;
    gap: .4rem;
    padding: .5rem .65rem;
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-sm);
    background: var(--app-surface-sunken);
    color: var(--app-ink-secondary);
    word-break: break-word;
}
.jwd-geo-esito:empty { display: none; }
.jwd-geo-esito > i, .jwd-geo-esito > svg { flex: 0 0 auto; margin-top: .15rem; }

/* Trovato: e' un dato (la sede c'e' ed e' posizionata), quindi qui il colore
   semantico ci sta. Gli altri due stati non sono errori — il progetto si salva
   comunque — quindi restano neutri. */
.jwd-geo-esito.is-ok {
    border-color: color-mix(in oklab, var(--app-success) 35%, var(--app-border));
    background: var(--app-success-subtle);
    color: var(--app-success-text);
}
.jwd-geo-esito.is-attesa > i, .jwd-geo-esito.is-attesa > svg {
    animation: jwd-gira 1s linear infinite;
}
@keyframes jwd-gira { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .jwd-geo-esito.is-attesa > i, .jwd-geo-esito.is-attesa > svg { animation: none; }
}

.jwd-geo-coord {
    display: inline-block;
    margin-left: .35rem;
    font-variant-numeric: tabular-nums;
    color: var(--app-ink-muted);
    white-space: nowrap;
}

/* ============================================================
   Vetrina Progetti — vista kanban
   Una colonna per stato, nell'ordine di StatiProgetti::ORDINE_KANBAN:
   il flusso di promozione (staging -> test -> produzione) piu' le bozze
   in testa e la demo, che sta fuori dal flusso, in coda.
   Le card NON si trascinano: lo stato e' derivato dai link, non e' una
   colonna a database — una card si sposta aggiungendo un collegamento.
   Il markup lo genera progetti.js dentro lo stesso #jwd-grid della
   griglia, cambiandogli la classe: cosi' i gestori delegati (modifica,
   elimina, hover sul puntino della mappa) restano validi in entrambe
   le viste, senza doverli riagganciare.
   ============================================================ */

/* In kanban la board si prende tutta la riga e la mappa si ritira:
   cinque colonne in sette dodicesimi di schermo non ci stanno. La mappa
   torna appena si torna alla griglia (un click sull'interruttore). */
.jwd-vetrina.is-kanban > .jwd-col-mappa { display: none; }
.jwd-vetrina.is-kanban > .jwd-col-lista { flex: 0 0 100%; max-width: 100%; }

.jwd-board {
    display: flex;
    align-items: stretch;
    gap: var(--app-space-3);
    overflow-x: auto;
    padding-bottom: var(--app-space-2);
}

/* Colonne a pari larghezza finche' ci stanno: sotto i 260px la board
   scorre in orizzontale invece di spremerle. La soglia la detta il nome
   del cliente: tolti i 24 di padding, i 36 del logo e i 56 dei comandi,
   al nome restano ~128px, il minimo perche' "Salumificio" o "Iliopesca"
   non si spezzino a meta' parola. */
.jwd-colonna {
    flex: 1 1 0;
    min-width: 260px;
    display: flex;
    flex-direction: column;
    padding: var(--app-space-2);
    border-radius: var(--app-radius-lg);
    background: var(--app-surface-sunken);
}

/* Compressa: una striscia verticale. Non e' un dettaglio estetico — e'
   il motivo per cui una colonna di servizio (le bozze) puo' stare in
   board senza rubare un quinto della larghezza alle altre quattro. */
.jwd-colonna.is-chiusa { flex: 0 0 3.25rem; min-width: 0; }

.jwd-colonna-testa {
    display: flex;
    align-items: center;
    gap: var(--app-space-2);
    padding: var(--app-space-1) var(--app-space-1) var(--app-space-2);
}
.jwd-colonna-titolo {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--app-space-2);
    padding: 0;
    border: 0;
    background: none;
    text-align: left;
    cursor: pointer;
}
.jwd-colonna-titolo:focus-visible {
    outline: 2px solid var(--app-accent);
    outline-offset: 2px;
    border-radius: var(--app-radius-sm);
}
.jwd-colonna-freccia {
    flex: 0 0 auto;
    color: var(--app-ink-muted);
    transition: transform var(--app-duration) var(--app-ease);
}
.jwd-colonna-conta {
    flex: 0 0 auto;
    font-weight: 600;
    color: var(--app-ink-muted);
    font-variant-numeric: tabular-nums;
}

/* Colonna verticale: ruota il titolo (writing-mode gira anche il badge e
   la sua etichetta, che e' quello che vogliamo), ma non la freccia —
   quella torna orizzontale e punta a destra, dalla parte in cui la
   colonna si riaprira'. */
.jwd-colonna.is-chiusa .jwd-colonna-testa {
    flex-direction: column;
    height: 100%;
    padding: var(--app-space-1);
}
.jwd-colonna.is-chiusa .jwd-colonna-titolo {
    flex: 1 1 auto;
    flex-direction: column;
    justify-content: flex-start;
    writing-mode: vertical-rl;
}
.jwd-colonna.is-chiusa .jwd-colonna-freccia {
    writing-mode: horizontal-tb;
    transform: rotate(-90deg);
}

/* Il corpo scorre per conto suo: una colonna lunga non deve allungare la
   pagina, o le altre restano un francobollo in cima allo schermo. */
.jwd-colonna-corpo {
    display: flex;
    flex-direction: column;
    gap: var(--app-space-2);
    max-height: 70vh;
    overflow-y: auto;
    /* Un pixel di respiro: senza, l'overflow taglia il bordo della card e
       l'anello di :focus-visible dei bottoni dentro. */
    padding: 1px;
}

.jwd-colonna-vuota {
    padding: var(--app-space-5) var(--app-space-3);
    border: 1px dashed var(--app-border-strong);
    border-radius: var(--app-radius-lg);
    text-align: center;
    color: var(--app-ink-muted);
}
.jwd-colonna-vuota > i[data-lucide],
.jwd-colonna-vuota > svg { width: 22px; height: 22px; }
.jwd-colonna-vuota p { margin-top: var(--app-space-2); }

/* La card in colonna: la stessa .card del design system, in 240px.
   Il badge di stato non c'e' — lo dice la colonna, ripeterlo su ogni
   card sarebbe rumore — quindi a portare il colore restano i quattro
   ambienti in fondo. */
.jwd-kcard { margin-bottom: 0; }
.jwd-kcard .card-body {
    gap: var(--app-space-2);
    padding: var(--app-space-3);
}
.jwd-kcard-testa {
    display: flex;
    align-items: flex-start;
    gap: var(--app-space-2);
}
.jwd-kcard-nome {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.3;
}

/* Il logo scende a miniatura accanto al nome: in colonna stretta il
   riquadro grande della griglia si mangerebbe la card intera. */
.jwd-logo-sm {
    width: 36px;
    height: 36px;
    flex: 0 0 auto;
}
.jwd-logo-sm > span { font-size: .9375rem; }
.jwd-logo-sm img { padding: 4px; }
.jwd-logo-sm > svg, .jwd-logo-sm > i[data-lucide] { width: 16px; height: 16px; }

/* Matita e cestino qui sono piu' piccoli dei btn-sm della griglia: sulla
   riga del nome ogni pixel che prendono loro e' un pixel che il cliente
   perde, e a 260px di colonna la differenza e' fra un nome leggibile e
   uno spezzato a meta' parola. Restano comunque sopra i 24px di
   bersaglio, e su touch sono sempre visibili (regola globale). */
.jwd-kcard .jwd-card-azioni { gap: var(--app-space-1); }
.jwd-kcard .jwd-card-azioni .btn {
    width: 26px;
    height: 26px;
    padding: 0;
}
.jwd-kcard .jwd-card-azioni .btn > svg,
.jwd-kcard .jwd-card-azioni .btn > i[data-lucide] { width: 14px; height: 14px; }

/* I quattro ambienti a icona sola: a 240px l'etichetta non ci sta, ma
   colore, icona e ordine restano quelli della griglia. margin-top:auto
   li tiene in fondo anche fra card di altezza diversa. */
.jwd-link-icone {
    display: flex;
    gap: var(--app-space-2);
    margin-top: auto;
    padding-top: var(--app-space-1);
}
.jwd-link-icone .disabled { opacity: .38; cursor: not-allowed; }
