@charset "UTF-8";
/* ============================================================================
   theme/core.css -- Foundation + Layout consolidato
   Generato 2026-05-05 da merge di:
     fonts.css + tokens.css + base.css + icons.css + layout.css + layouts.css
   Ordine cascade preservato. Vedi docs/superpowers/specs/2026-05-04-theme-css-restructure-design.md
   ============================================================================ */


/* ============================================================================
   === SECTION 1: FONTS (@font-face Inter + IBM Plex Mono + Anybody) ============
   Originale: theme/fonts.css
   ============================================================================ */

/* ============================================================================
   theme/fonts.css -- Font self-hosted (GDPR compliant, no Google Fonts CDN)
   ----------------------------------------------------------------------------
   Sostituisce i `@font-face` precedentemente in `dashboard/css/tokens-overrides.css`
   spostandoli nel theme come unica fonte. Il dashboard PHP eredita
   automaticamente caricando theme.css (cascade order in header.php).

   Fonts inclusi:
   - Inter Variable (100-900 weight) per UI body/heading
   - IBM Plex Mono Regular (400) + Medium (500) per devEUI hex, log feed,
     code snippets

   File woff2 in theme/fonts/{inter,ibm-plex-mono}/. Migration 2026-05-02
   da dashboard/fonts/ -- vedi `dashboard_to_theme_migration_plan.md`.

   Apache mod_negotiation: nome file NON deve contenere `.var.` (causa 500).
   Workaround in theme/fonts/.htaccess: `Options -MultiViews` + `AddType font/woff2 .woff2`.
   ============================================================================ */

/* === Inter (variable, romano 100-900) ===================================== */
@font-face {
    font-family: 'Inter';
    src: url('./fonts/inter/Inter-roman.woff2') format('woff2-variations'),
         url('./fonts/inter/Inter-roman.woff2') format('woff2');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

/* === IBM Plex Mono -- Regular (400) ======================================== */
@font-face {
    font-family: 'IBM Plex Mono';
    src: url('./fonts/ibm-plex-mono/IBMPlexMono-Regular.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

/* === IBM Plex Mono -- Medium (500) ========================================= */
@font-face {
    font-family: 'IBM Plex Mono';
    src: url('./fonts/ibm-plex-mono/IBMPlexMono-Medium.woff2') format('woff2');
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

/* === Anybody (display font per KPI value, geometric variable 500-700) ===== */
/* 2026-05-02 (Step 2 migration): self-hosted da Google Fonts CDN.
   Era previously caricato via `<link href="https://fonts.googleapis.com/css2?family=Anybody...">`
   nelle 36 pagine demo theme. Ora inline self-hosted = GDPR + offline + no
   round-trip CDN. File scaricati con curl UA Chrome moderno per ottenere woff2.
   2 file unicode-range (latin + latin-ext) coprono IT/EN dashboard. Vietnamese
   range skipped (non utilizzato). Variable font: stessi 2 file gestiscono
   weights 500/600/700 (1 @font-face per range con `font-weight: 500 700`). */
@font-face {
    font-family: 'Anybody';
    src: url('./fonts/anybody/Anybody-latin.woff2') format('woff2');
    font-weight: 500 700;
    font-style: normal;
    font-stretch: 100%;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Anybody';
    src: url('./fonts/anybody/Anybody-latin-ext.woff2') format('woff2');
    font-weight: 500 700;
    font-style: normal;
    font-stretch: 100%;
    font-display: swap;
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* ============================================================================
   === SECTION 2: DESIGN TOKENS (primitive + semantic, OKLCH P3) ===============
   Originale: theme/tokens.css
   ============================================================================ */

/* ============================================================================
   tokens.css -- Design tokens (enterprise refined)
   Sistema a 2 livelli: PRIMITIVE (scale colori grezzi) + SEMANTIC (alias).
   Per cambiare il look globale: modifica i SEMANTIC token.
   Per cambiare i colori brand: modifica i PRIMITIVE.

   Ispirazione 2026: zinc-tinted neutrals (Linear/Vercel/shadcn), OKLCH per
   varianti chroma uniformi, font Inter/Geist optical sized, typography che
   "fa heavy lifting" con micro-spacing curato.
   ============================================================================ */


/* === PRIMITIVE -- Scale colori grezze (50->950, stile Tailwind/Radix) ====== */
:root {
    /* Zinc (cool neutrals con sottile undertone blu -- standard 2026) */
    --zinc-50:  #FAFAFA;
    --zinc-100: #F4F4F5;
    --zinc-200: #E4E4E7;
    --zinc-300: #D4D4D8;
    --zinc-400: #A1A1AA;
    --zinc-500: #71717A;
    --zinc-600: #52525B;
    --zinc-700: #3F3F46;
    --zinc-800: #27272A;
    --zinc-900: #18181B;
    --zinc-950: #09090B;

    /* Slate (alternative neutrals con piu' blu, per data-heavy UI) */
    --slate-50:  #F8FAFC;
    --slate-100: #F1F5F9;
    --slate-200: #E2E8F0;
    --slate-300: #CBD5E1;
    --slate-400: #94A3B8;
    --slate-500: #64748B;
    --slate-600: #475569;
    --slate-700: #334155;
    --slate-800: #1E293B;
    --slate-900: #0F172A;
    --slate-950: #020617;

    /* Brand teal (deep teal-cerulean, water infrastructure) */
    --teal-50:  #ECFEFF;
    --teal-100: #CFFAFE;
    --teal-200: #A5F3FC;
    --teal-300: #67E8F9;
    --teal-400: #22D3EE;
    --teal-500: #06B6D4;
    --teal-600: #0891B2;
    --teal-700: #0E7490;
    --teal-800: #155E75;
    --teal-900: #164E63;
    --teal-950: #083344;

    /* Verde success (emerald) */
    --green-50:  #ECFDF5;
    --green-100: #D1FAE5;
    --green-500: #10B981;
    --green-600: #059669;
    --green-700: #047857;
    --green-900: #064E3B;

    /* Ambra warning */
    --amber-50:  #FFFBEB;
    --amber-100: #FEF3C7;
    --amber-500: #F59E0B;
    --amber-600: #D97706;
    --amber-700: #B45309;
    --amber-900: #78350F;

    /* Rosso danger */
    --red-50:  #FEF2F2;
    --red-100: #FEE2E2;
    --red-500: #EF4444;
    --red-600: #DC2626;
    --red-700: #B91C1C;
    --red-900: #7F1D1D;

    /* Blu info */
    --blue-50:  #EFF6FF;
    --blue-100: #DBEAFE;
    --blue-500: #3B82F6;
    --blue-600: #2563EB;
    --blue-700: #1D4ED8;
    --blue-900: #1E3A8A;

    /* Viola accent */
    --violet-50:  #F5F3FF;
    --violet-100: #EDE9FE;
    --violet-500: #8B5CF6;
    --violet-600: #7C3AED;
    --violet-700: #6D28D9;
    --violet-900: #4C1D95;
}


/* === OKLCH P3 wide-gamut upgrade ============================================
   Browser support 2026: Chrome 111+, Safari 15.4+, Firefox 113+ (~96% global).
   I valori HEX di sopra rimangono fallback per browser legacy.
   OKLCH offre:
   - Chroma percettivamente uniforme (L=0.55 = stessa luminanza percepita per tutti hue)
   - P3 wide gamut ~30% piu' colore su Mac/iOS recenti
   - Generazione varianti coerente via color-mix in oklch
   ============================================================================ */
@supports (color: oklch(0.5 0.1 200)) {
    :root {
        /* Zinc (neutrale freddo) */
        --zinc-50:  oklch(0.985 0.001 286);
        --zinc-100: oklch(0.967 0.002 286);
        --zinc-200: oklch(0.920 0.004 286);
        --zinc-300: oklch(0.871 0.006 286);
        --zinc-400: oklch(0.706 0.009 286);
        --zinc-500: oklch(0.553 0.013 286);
        --zinc-600: oklch(0.442 0.014 285);
        --zinc-700: oklch(0.370 0.013 286);
        --zinc-800: oklch(0.274 0.014 286);
        --zinc-900: oklch(0.210 0.012 286);
        --zinc-950: oklch(0.141 0.012 286);

        /* Teal (brand) -- chroma piu' rich su display P3 */
        --teal-50:  oklch(0.984 0.020 196);
        --teal-100: oklch(0.953 0.054 195);
        --teal-200: oklch(0.901 0.090 195);
        --teal-300: oklch(0.829 0.120 196);
        --teal-400: oklch(0.732 0.142 200);
        --teal-500: oklch(0.661 0.142 207);
        --teal-600: oklch(0.578 0.118 215);
        --teal-700: oklch(0.502 0.106 207);
        --teal-800: oklch(0.430 0.090 207);
        --teal-900: oklch(0.378 0.075 207);
        --teal-950: oklch(0.275 0.060 215);

        /* Verde success -- green-700 abbassato a 0.470 per WCAG AA su surface bianco
           (era 0.547 con contrast 4.44; ora ~5.5+) */
        --green-500: oklch(0.731 0.165 156);
        --green-600: oklch(0.642 0.140 158);
        --green-700: oklch(0.470 0.118 158);

        /* Ambra warning -- amber-700 abbassato a 0.530 per WCAG AA su surface bianco
           (era 0.605 con contrast 3.88; ora ~4.6) */
        --amber-500: oklch(0.793 0.151 70);
        --amber-600: oklch(0.706 0.150 60);
        --amber-700: oklch(0.530 0.150 50);

        /* Rosso danger */
        --red-500:  oklch(0.668 0.224 22);
        --red-600:  oklch(0.595 0.220 26);
        --red-700:  oklch(0.527 0.205 28);

        /* Blu info */
        --blue-500: oklch(0.661 0.180 252);
        --blue-600: oklch(0.581 0.180 256);
        --blue-700: oklch(0.501 0.180 258);

        /* Viola */
        --violet-500: oklch(0.661 0.221 290);
        --violet-600: oklch(0.575 0.221 290);
        --violet-700: oklch(0.500 0.221 290);
    }
}


/* === SEMANTIC -- Alias significativi (modifica qui per cambiare brand) === */
:root {
    color-scheme: light;

    /* Brand */
    --primary:        var(--teal-700);
    --primary-dark:   var(--teal-800);
    --primary-light:  var(--teal-500);
    /* color-mix in srgb come baseline. Browser con OKLCH support useranno
       i token primitive in oklch automaticamente, mantenendo chroma P3. */
    --primary-soft:   color-mix(in srgb, var(--teal-700) 8%, transparent);
    --primary-border: color-mix(in srgb, var(--teal-700) 20%, transparent);
    --secondary:      var(--teal-600);

    /* Stati */
    --success:        var(--green-600);
    --success-dark:   var(--green-700);
    --success-soft:   color-mix(in srgb, var(--green-600) 10%, transparent);
    --success-border: color-mix(in srgb, var(--green-600) 22%, transparent);

    --warning:        var(--amber-600);
    --warning-dark:   var(--amber-700);
    --warning-soft:   color-mix(in srgb, var(--amber-600) 10%, transparent);
    --warning-border: color-mix(in srgb, var(--amber-600) 22%, transparent);

    --danger:         var(--red-600);
    --danger-dark:    var(--red-700);
    --danger-soft:    color-mix(in srgb, var(--red-600) 10%, transparent);
    --danger-border:  color-mix(in srgb, var(--red-600) 22%, transparent);

    --info:           var(--blue-600);
    --info-dark:      var(--blue-700);
    --info-soft:      color-mix(in srgb, var(--blue-600) 10%, transparent);
    --info-border:    color-mix(in srgb, var(--blue-600) 22%, transparent);

    --purple:         var(--violet-600);
    --purple-dark:    var(--violet-700);
    --purple-soft:    color-mix(in srgb, var(--violet-600) 10%, transparent);
    --purple-border:  color-mix(in srgb, var(--violet-600) 22%, transparent);

    /* Superfici (light mode = quasi bianco zinc-tinted, no pure white) */
    --background:     var(--zinc-50);          /* sfondo body */
    --surface:        #FFFFFF;                 /* card, modal */
    --surface-hover:  color-mix(in srgb, var(--primary) 5%, var(--surface));  /* row/card hover */
    --surface-2:      var(--zinc-50);          /* livello secondario tra surface e sunken */
    --surface-elevated: var(--surface);        /* alias compat (== surface in light, == raised in dark) */
    --raised:         #FFFFFF;                 /* elementi rialzati */
    --sunken:         var(--zinc-100);         /* input bg, sezioni infossate */
    --border:         var(--zinc-200);         /* bordi standard */
    --border-light:   var(--zinc-100);         /* divider sottili */
    --border-strong:  var(--zinc-300);         /* hover/focus ring */

    /* Testo (gerarchia 3 livelli + on-primary)
       WCAG AA contrast verificato vs --surface (#FFFFFF) e --sunken (--zinc-100):
       primary 16.4:1, secondary 7.2:1, muted 4.5:1, disabled 2.9:1 (decorativo). */
    --text-primary:   var(--zinc-900);         /* heading + body principale */
    --text-secondary: var(--zinc-600);         /* sub-text, label */
    --text-muted:     var(--zinc-600);         /* helper/meta -- bumped da zinc-500 a zinc-600 per WCAG AA su sunken */
    --text-disabled:  var(--zinc-400);
    --text-on-primary: #FFFFFF;

    /* Disabled */
    --disabled-bg:     var(--zinc-100);
    --disabled-text:   var(--zinc-400);
    --disabled-border: var(--zinc-200);
    --inactive-opacity: 0.5;             /* refined Linear/Vercel premium subtlety */

    /* Tabelle */
    --table-row-even-bg: var(--zinc-50);
    --table-row-hover-bg: color-mix(in srgb, var(--primary) 5%, transparent);

    /* Ombre (refinement: piu' sottili, low-spread, tinted) */
    --shadow-xs:  0 1px 1px 0 rgba(15, 23, 42, 0.04);
    --shadow-sm:  0 1px 2px 0 rgba(15, 23, 42, 0.05), 0 1px 3px 0 rgba(15, 23, 42, 0.06);
    --shadow-md:  0 4px 6px -2px rgba(15, 23, 42, 0.06), 0 2px 4px -2px rgba(15, 23, 42, 0.05);
    --shadow-lg:  0 10px 15px -4px rgba(15, 23, 42, 0.08), 0 4px 6px -2px rgba(15, 23, 42, 0.04);
    --shadow-xl:  0 20px 25px -5px rgba(15, 23, 42, 0.10), 0 8px 10px -6px rgba(15, 23, 42, 0.04);
    --shadow-premium: 0 24px 48px -12px rgba(15, 23, 42, 0.18);
    --shadow-focus-ring: 0 0 0 3px color-mix(in srgb, var(--primary) 25%, transparent);
    --shadow-glow: 0 0 20px color-mix(in srgb, var(--primary) 30%, transparent);  /* hover glow su button/icon-tile */

    /* Glass / Overlay */
    --glass:       rgba(255, 255, 255, 0.85);
    --overlay-bg:  rgba(15, 23, 42, 0.55);

    /* Spaziature (4px base -- scala 4/8/12/16/20/24/32/40/48/64)
       Naming conventions: due alias paralleli (semantico xs/sm/md/lg/xl + numerico 1/2/3/.../12)
       Stesso valore, diversa familiarita': enhancements.css usa numerico, components.css usa semantico. */
    --space-px:  1px;
    --space-1:   4px;    /* alias di --space-xs */
    --space-xs:  4px;
    --space-2:   8px;    /* alias di --space-sm */
    --space-sm:  8px;
    --space-3:   12px;
    --space-4:   16px;   /* alias di --space-md */
    --space-md:  16px;
    --space-5:   20px;
    --space-6:   24px;   /* alias di --space-lg */
    --space-lg:  24px;
    --space-8:   32px;   /* alias di --space-xl */
    --space-xl:  32px;
    --space-2xl: 48px;
    --space-12:  48px;   /* alias di --space-2xl */
    --space-3xl: 64px;
    --space-16:  64px;   /* alias di --space-3xl */
    --space-4xl: 96px;

    /* Raggi (refinement: piu' contenuti, no over-rounded) */
    --radius-xs: 3px;
    --radius-sm: 5px;
    --radius-md: 8px;
    --radius-lg: 12px;
    --radius-xl: 16px;
    --radius-2xl: 24px;
    --radius-pill: 999px;
    --radius-circle: 50%;

    /* === Tipografia -- Inter Variable + IBM Plex Mono ============================
       Inter e' lo standard 2026 per dashboard (leggibilita' a tutte le size).
       Fallback su system-ui per performance se non caricata. */
    --font-mono:    'IBM Plex Mono', 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
    --font-display: 'Anybody', 'Inter', system-ui, sans-serif;
    --font-body:    'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

    /* Type scale (modular 1.125 -- refined, 11.5px -> 36px) */
    --text-2xs:  0.6875rem;  /* 11px -- caption, badge xs, meta */
    --text-xs:   0.75rem;    /* 12px -- small label, tag */
    --text-sm:   0.8125rem;  /* 13px -- table cell, form helper */
    --text-md:   0.875rem;   /* 14px -- body default [star] */
    --text-base: 0.9375rem;  /* 15px -- body comfortable */
    --text-lg:   1rem;       /* 16px -- section content */
    --text-xl:   1.125rem;   /* 18px -- card heading */
    --text-2xl:  1.375rem;   /* 22px -- page subhead */
    --text-3xl:  1.75rem;    /* 28px -- page title */
    --text-4xl:  2.25rem;    /* 36px -- hero */
    --text-5xl:  3rem;       /* 48px -- display */

    /* Line-height (refined: heading tight, body comfortable) */
    --leading-tight:    1.15;     /* heading */
    --leading-snug:     1.35;     /* sub-heading */
    --leading-normal:   1.5;      /* body [star] */
    --leading-relaxed:  1.65;     /* paragrafi lunghi */

    /* Letter-spacing (uppercase needs tracking +) */
    --tracking-tighter: -0.02em;
    --tracking-tight:   -0.01em;
    --tracking-normal:  0em;
    --tracking-wide:    0.025em;
    --tracking-wider:   0.05em;
    --tracking-widest:  0.1em;

    /* Font weights (Inter ha 9 pesi, usiamone 4 selezionati) */
    --weight-regular:  400;
    --weight-medium:   500;
    --weight-semibold: 600;
    --weight-bold:     700;

    /* Layout */
    --header-height:   60px;     /* refinement: 72->60 piu' compatto */
    --topbar-height:   56px;
    --sidebar-width:   240px;
    --sidebar-width-collapsed: 72px;   /* min: >=72 per evitare overflow footer (avatar 36 + border 2*2 = 40, theme-btn 32 stacked column) */
    --drawer-width:    300px;
    --container-max:   1280px;

    /* === DURATE STANDARDIZZATE 2026 (calm UI, Linear/Material 3) === */
    --dur-instant:    80ms;   /* hover/focus ring */
    --dur-quick:      150ms;  /* button press, toggle */
    --dur-normal:     220ms;  /* dropdown, popover, toast */
    --dur-medium:     320ms;  /* modal, drawer */
    --dur-slow:       520ms;  /* page transition, complex move */
    --dur-spring:     650ms;  /* spring overshoot, larger range */

    /* === EASING CURVES 2026 === */
    --ease-out-quart:    cubic-bezier(0.25, 1, 0.5, 1);          /* dropdown, list item */
    --ease-out-expo:     cubic-bezier(0.16, 1, 0.3, 1);          /* enter elementi */
    --ease-emphasized:   cubic-bezier(0.2, 0, 0, 1);             /* Material 3 emphasized */
    --ease-decel:        cubic-bezier(0, 0, 0.2, 1);             /* enter only */
    --ease-accel:        cubic-bezier(0.4, 0, 1, 1);             /* exit only */
    --ease-in-out-smooth: cubic-bezier(0.4, 0, 0.2, 1);          /* default */
    --ease-spring:       cubic-bezier(0.5, 1.4, 0.5, 1);         /* legacy spring */

    /* CSS linear() native spring (Chrome 113+, Firefox 112+, Safari 17.6+) */
    --ease-spring-soft:  linear(0,.005,.02 2.4%,.082 5.5%,.32,.554,.733,.852,
                         .921,.96,.978 22%,.99,1.001,1.005,1.005 33%,1.002,
                         1 47.7%,1);
    --ease-spring-snap:  linear(0,.18,.464 7.6%,.798,1.025,1.146,1.183,
                         1.169,1.121,1.05 30%,.997,.974,.972 36%,.987,1.001,
                         1.005 50%,1);
    --ease-bounce:       linear(0,.004,.016,.035,.063,.098,.141 13.4%,
                         .32,.555 26.5%,.7,.852,1 50%,.96,.93,.91,.91 75%,.94,1);

    /* Transizioni shortcut (use durate token) */
    --transition:        all var(--dur-normal) var(--ease-in-out-smooth);
    --transition-fast:   all var(--dur-quick) var(--ease-in-out-smooth);
    --transition-slow:   all var(--dur-medium) var(--ease-in-out-smooth);

    /* Z-index */
    --z-dropdown:     50;
    --z-sticky:       99;
    --z-header:       100;
    --z-fab:          200;
    --z-banner:       1040;
    --z-notification: 1050;
    /* Toast sopra tutto: .map-fullscreen (9999, dashboard-legacy.css) e
       flyout (--z-overlay + 1 = 10000) non devono coprire il feedback. */
    --z-toast:        10001;
    --z-overlay:      9999;
    --z-modal:        10000;
    --z-modal-top:    10100;

    /* Breakpoint (riferimento JS) */
    --bp-2xl: 1536px;
    --bp-xl:  1280px;
    --bp-lg:  1024px;
    --bp-md:  768px;
    --bp-sm:  640px;

    /* Form controls accent */
    accent-color: var(--primary);

    /* Chart palette categoricale (scelti per WCAG AA contrasto) */
    --chart-grid: var(--zinc-200);
    --chart-1: var(--teal-600);
    --chart-2: var(--green-600);
    --chart-3: var(--amber-500);
    --chart-4: var(--red-500);
    --chart-5: var(--violet-500);
    --chart-6: var(--blue-500);
    --chart-7: #EC4899;   /* pink */
    --chart-8: #84CC16;   /* lime */

    /* Dynamic placeholder (sovrascritto inline da JS) */
    --badge-color: inherit;
    --badge-bg: transparent;
    --broker-color: var(--primary);
    --dot-color: var(--text-muted);
}


/* ============================================================================
   DARK MODE -- Tokens override
   "Soft dark" basato su zinc-950 (no pure black). Preserva contrasto WCAG AA.
   ============================================================================ */
[data-theme="dark"] {
    color-scheme: dark;

    /* Brand: leggermente piu' luminoso per contrasto su sfondo scuro */
    --primary:        var(--teal-400);
    --primary-dark:   var(--teal-500);
    --primary-light:  var(--teal-300);
    --primary-soft:   color-mix(in srgb, var(--teal-400) 12%, transparent);
    --primary-border: color-mix(in srgb, var(--teal-400) 25%, transparent);
    --secondary:      var(--teal-500);

    /* Stati piu' luminosi.
       Nota: --*-dark in dark mode mantiene la STESSA shade di --* (500),
       NON va piu' scuro perche' quello peggiorerebbe il contrasto su surface
       scuro. Per text/icon su soft bg con tint dark, --*-dark = --* funziona
       per WCAG AA (~5+ contrast). Vedi audit 2026-04-28. */
    --success:        var(--green-500);
    --success-dark:   var(--green-500);
    --warning:        var(--amber-500);
    --warning-dark:   var(--amber-500);
    --danger:         var(--red-500);
    --danger-dark:    var(--red-500);
    --info:           var(--blue-500);
    --info-dark:      var(--blue-500);
    --purple:         var(--violet-500);
    --purple-dark:    var(--violet-500);

    /* Superfici (zinc-tinted soft dark) */
    --background:     var(--zinc-950);
    --surface:        var(--zinc-900);
    --surface-hover:  color-mix(in srgb, var(--primary) 12%, var(--surface));  /* row/card hover */
    --surface-2:      var(--zinc-850, color-mix(in srgb, var(--zinc-900) 70%, var(--zinc-800) 30%));
    --surface-elevated: var(--raised);        /* alias compat */
    --raised:         var(--zinc-800);
    --sunken:         #0A0A0D;                /* leggermente piu' nero del background */
    --border:         var(--zinc-800);
    --border-light:   color-mix(in srgb, var(--zinc-700) 50%, transparent);
    --border-strong:  var(--zinc-700);

    /* Testo (WCAG AA contrast verificato vs --surface zinc-900: primary 19.5:1,
       secondary 7.6:1, muted 5.0:1, disabled 3.1:1 decorativo). */
    --text-primary:   var(--zinc-50);
    --text-secondary: var(--zinc-300);         /* bumped da zinc-400 per WCAG AA dark */
    --text-muted:     var(--zinc-400);         /* bumped da zinc-500 per WCAG AA dark */
    --text-disabled:  var(--zinc-600);

    /* Disabled */
    --disabled-bg:     var(--zinc-800);
    --disabled-text:   var(--zinc-600);
    --disabled-border: var(--zinc-700);

    /* Tabelle */
    --table-row-even-bg: color-mix(in srgb, var(--zinc-800) 50%, transparent);
    --table-row-hover-bg: color-mix(in srgb, var(--primary) 12%, transparent);

    /* Ombre piu' marcate per separazione */
    --shadow-xs:  0 1px 1px 0 rgba(0, 0, 0, 0.3);
    --shadow-sm:  0 1px 2px 0 rgba(0, 0, 0, 0.35), 0 1px 3px 0 rgba(0, 0, 0, 0.4);
    --shadow-md:  0 4px 6px -2px rgba(0, 0, 0, 0.4), 0 2px 4px -2px rgba(0, 0, 0, 0.3);
    --shadow-lg:  0 10px 15px -4px rgba(0, 0, 0, 0.5);
    --shadow-xl:  0 20px 25px -5px rgba(0, 0, 0, 0.6);
    --shadow-glow: 0 0 24px color-mix(in srgb, var(--primary) 40%, transparent);  /* dark più intenso */

    --glass:      rgba(24, 24, 27, 0.85);
    --overlay-bg: rgba(0, 0, 0, 0.7);

    --chart-grid: color-mix(in srgb, var(--zinc-700) 50%, transparent);
    --chart-1: var(--teal-400);
    --chart-2: #34D399;
    --chart-3: var(--amber-500);
    --chart-4: #F87171;
    --chart-5: #A78BFA;
    --chart-6: #60A5FA;
    --chart-7: #F472B6;
    --chart-8: #A3E635;
}


/* ============================================================================
   Palette categoricale alternativa -- Okabe-Ito (color-blind safe + WCAG AA)
   ----------------------------------------------------------------------------
   Apply via <html data-palette="okabe-ito"> per usarla al posto della default.
   Dashboard LoRaWAN AMAG la attiva via header.php (accessibility-critical).
   Caricato DOPO [data-theme="dark"]: vince in light & dark mode (specificity
   [data-palette] == [data-theme], ordine in stylesheet decide).
   La palette Okabe-Ito (Masataka Okabe & Kei Ito 2008, "Color Universal Design")
   e' lo standard de-facto color-blind safe in scientific publishing.
   ============================================================================ */
[data-palette="okabe-ito"] {
    --chart-1: #0072B2;             /* blu */
    --chart-2: #E69F00;             /* arancio */
    --chart-3: #009E73;             /* verde */
    --chart-4: #CC79A7;             /* rosa */
    --chart-5: #56B4E9;             /* azzurro chiaro */
    --chart-6: #D55E00;             /* rosso-arancio */
    --chart-7: #F0E442;             /* giallo */
    --chart-8: var(--violet-500);   /* fallback ottava serie (off-Okabe) */
}


/* ============================================================================
   Auto dark mode via prefers-color-scheme (se [data-theme] non e' settato)
   ============================================================================ */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) {
        color-scheme: dark;
        --primary: var(--teal-400);
        --primary-soft: color-mix(in srgb, var(--teal-400) 12%, transparent);
        --background: var(--zinc-950);
        --surface: var(--zinc-900);
        --raised: var(--zinc-800);
        --border: var(--zinc-800);
        --text-primary: var(--zinc-50);
        --text-secondary: var(--zinc-300);     /* WCAG AA bumped */
        --text-muted: var(--zinc-400);         /* WCAG AA bumped */
    }
}


/* ============================================================================
   High Contrast / Accessibility overrides
   ============================================================================ */
@media (forced-colors: active) {
    :root {
        --primary: LinkText;
        --text-primary: CanvasText;
        --background: Canvas;
        --border: ButtonText;
    }
}

@media (prefers-contrast: more) {
    :root {
        --text-muted: var(--zinc-700);
        --border: var(--zinc-400);
        --border-light: var(--zinc-300);
    }
    [data-theme="dark"] {
        --text-muted: var(--zinc-300);
        --border: var(--zinc-600);
    }
}


/* ============================================================================
   === SECTION 3-5: BASE (reset + typography + accessibility) ==================
   Originale: theme/base.css
   ============================================================================ */

/* ============================================================================
   base.css -- Reset, regole globali, tipografia, scrollbar.
   Dipende da tokens.css.
   ============================================================================ */

/* === Reset minimo === */
*,
*::before,
*::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    -webkit-font-smoothing: antialiased;
}

/* Attribute [hidden] universale.
   Necessario perche' molti componenti del theme usano display:flex/grid/block
   che hanno specificity superiore al default user-agent `[hidden] { display:none }`.
   Senza questo, `el.hidden = true` non funziona su .table-toolbar, .alert, .config-form-row, ecc. */
[hidden] {
    display: none !important;
}

html {
    scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }
    /* Override globale per garantire WCAG 2.1 SC 2.3.3: riduce drasticamente
       animation e transition per chi soffre di motion sickness/vestibolari.
       I componenti possono comunque opt-out tramite max-specificity, ma
       il default e' "praticamente istantaneo" per tutto. */
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

body {
    background-color: var(--background);
    color: var(--text-primary);
    font-family: var(--font-body);
    font-size: var(--text-md);              /* refined: 14px default (prima 15px) */
    line-height: var(--leading-normal);
    letter-spacing: var(--tracking-tight);  /* tighter feel da SaaS moderno */
    font-weight: var(--weight-regular);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    font-feature-settings: 'cv11', 'ss01', 'ss03';   /* Inter optical features */
}

/* === Headings (refined: tighter tracking, weight 600 default) === */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-body);          /* Inter, no display font per defaults */
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-tighter);
    line-height: var(--leading-tight);
    color: var(--text-primary);
}

h1 { font-size: var(--text-4xl); font-weight: var(--weight-bold); }
h2 { font-size: var(--text-3xl); font-weight: var(--weight-bold); }
h3 { font-size: var(--text-xl); }
h4 { font-size: var(--text-lg); }
h5 { font-size: var(--text-md); }
h6 { font-size: var(--text-sm); text-transform: uppercase; letter-spacing: var(--tracking-wider); color: var(--text-secondary); }

/* Display heading: usa font-display per branding (KPI, hero) */
.heading-display {
    font-family: var(--font-display);
    letter-spacing: var(--tracking-tight);
}

/* === Link === */
a {
    color: var(--primary);
    text-decoration: none;
    transition: var(--transition);
}

a:hover {
    color: var(--primary-dark);
    text-decoration: underline;
}

/* === Code / pre === */
code,
kbd,
samp,
pre {
    font-family: var(--font-mono);
    font-size: 0.95em;
}

code {
    padding: 2px 6px;
    background: var(--primary-soft);
    color: var(--primary-dark);
    border-radius: var(--radius-xs);
}

pre {
    padding: var(--space-md);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow-x: auto;
}

pre code {
    padding: 0;
    background: none;
    color: inherit;
}

/* === HR === */
hr {
    border: 0;
    border-top: 1px solid var(--border);
    margin: var(--space-lg) 0;
}

/* === Selezione testo === */
::selection {
    background: var(--primary-soft);
    color: var(--primary-dark);
}

/* === Focus visible (accessibilita') === */
*:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* === Scrollbar custom (WebKit) === */
::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: var(--surface);
}

::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: var(--radius-sm);
}

::-webkit-scrollbar-thumb:hover {
    background: var(--text-muted);
}

/* === Skip link (a11y) === */
.skip-link {
    position: absolute;
    top: -40px;
    left: 0;
    background: var(--primary);
    color: var(--text-on-primary);
    padding: var(--space-sm) var(--space-md);
    text-decoration: none;
    z-index: var(--z-modal);
    border-radius: 0 0 var(--radius-sm) 0;
    font-weight: var(--weight-semibold);
    transition: top var(--dur-normal) var(--ease-out-expo);
}

.skip-link:focus,
.skip-link:focus-visible {
    top: 0;
    outline: 3px solid var(--primary);
    outline-offset: 2px;
}

/* === Screen reader only === */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
}

/* === High contrast mode (Windows forced-colors) ===
   Garantisce visibilita' degli elementi UI quando l'utente forza colori OS.
   Aggiunge bordi espliciti su elementi che dipendono solo da background. */
@media (forced-colors: active) {
    .badge,
    .status-badge,
    .tag-badge,
    .health-badge,
    .health-badge-grade,
    .conn-badge,
    .freshness-badge,
    .dr-pill,
    .btn,
    button,
    .card,
    .alert,
    .banner,
    .form-control,
    select,
    .tab,
    .pagination-btn,
    .ghost-cat,
    .audit-stat,
    .live-indicator,
    .map-toolbar-btn {
        border: 1px solid ButtonText;
    }
    /* Status dots / live dots forced-colors fallback (color schemes ignorati) */
    .status-dot,
    .status-light-dot,
    .live-dot {
        outline: 1px solid ButtonText;
    }
    /* Selected/active states usano il colore di sistema */
    .btn-primary,
    .nav-link.active,
    .sidebar-link.active,
    .pagination-btn.active,
    .density-btn.active,
    .segment.active,
    .tile-opt.active {
        background: Highlight !important;
        color: HighlightText !important;
        border-color: Highlight !important;
    }
}

/* prefers-contrast: more -- gestito autoritativamente in tokens.css con
   --text-muted: var(--zinc-700) e --border: var(--zinc-500). Evita duplicazione. */


/* ============================================================================
   === SECTION 6: ICONS (Lucide SVG inline styling) ============================
   Originale: theme/icons.css
   ============================================================================ */

/* ============================================================================
   icons.css -- Utility per le icone SVG generate da js/icons.js
   Le icone usano stroke="currentColor" quindi ereditano automaticamente
   il colore del padre (text, link, button, badge).
   ============================================================================ */

/* === Base === */
.icon {
    display: inline-block;
    vertical-align: middle;
    flex-shrink: 0;
    transition: color var(--dur-quick) ease, transform var(--dur-quick) ease;
}

/* === Sizes === */
.icon-2xs { width: 12px; height: 12px; stroke-width: 2.25; }
.icon-xs  { width: 14px; height: 14px; stroke-width: 2; }
.icon-sm  { width: 16px; height: 16px; stroke-width: 2; }
.icon-md  { width: 20px; height: 20px; stroke-width: 1.75; }
.icon-lg  { width: 24px; height: 24px; stroke-width: 1.75; }
.icon-xl  { width: 32px; height: 32px; stroke-width: 1.5; }
.icon-2xl { width: 40px; height: 40px; stroke-width: 1.5; }
.icon-3xl { width: 56px; height: 56px; stroke-width: 1.25; }

/* === Inline auto-spacing nei testi === */
.icon-inline {
    margin-right: var(--space-xs);
    vertical-align: -3px;
}

/* === Color modifiers (override del currentColor) === */
.icon-primary { color: var(--primary); }
.icon-success { color: var(--success); }
.icon-warning { color: var(--warning); }
.icon-danger  { color: var(--danger); }
.icon-info    { color: var(--info); }
.icon-purple  { color: var(--purple); }
.icon-muted   { color: var(--text-muted); }

/* === Stroke weight variants ===
   SVG `stroke-width` come presentation attribute ha specificity zero,
   qualsiasi class CSS lo sovrascrive senza !important. */
.icon-thin     { stroke-width: 1.25; }
.icon-regular  { stroke-width: 1.75; }
.icon-bold     { stroke-width: 2.25; }

/* === Animations === */
.icon-spin {
    animation: iconSpin 1.5s linear infinite;
    transform-origin: 50% 50%;
}

.icon-pulse {
    animation: iconPulse 2s ease-in-out infinite;
}

@keyframes iconSpin {
    to { transform: rotate(360deg); }
}

@keyframes iconPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}

@media (prefers-reduced-motion: reduce) {
    .icon-spin, .icon-pulse { animation: none; }
}

/* === Icon button (wrap circolare per icone interattive) === */
.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    background: transparent;
    border: 1px solid transparent;
    color: var(--text-secondary);
    cursor: pointer;
    transition: var(--transition);
}

.icon-btn:hover {
    background: var(--primary-soft);
    color: var(--primary);
}

.icon-btn:active { transform: scale(0.95); }

.icon-btn-sm { width: 28px; height: 28px; }
.icon-btn-lg { width: 44px; height: 44px; }

/* === Icon container colorato (per KPI, banner, header) === */
.icon-tile {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    background: var(--primary-soft);
    color: var(--primary);
    flex-shrink: 0;
}

.icon-tile.icon-tile-lg { width: 48px; height: 48px; border-radius: var(--radius-lg); }
.icon-tile.icon-tile-xl { width: 64px; height: 64px; border-radius: var(--radius-lg); }

.icon-tile.tile-success { background: var(--success-soft); color: var(--success); }
.icon-tile.tile-warning { background: var(--warning-soft); color: var(--warning); }
.icon-tile.tile-danger  { background: var(--danger-soft); color: var(--danger); }
.icon-tile.tile-info    { background: var(--info-soft); color: var(--info); }
.icon-tile.tile-purple  { background: var(--purple-soft); color: var(--purple); }

/* === Override per legacy: kpi-icon e altri usano .icon-tile-style === */
/* 2026-05-02 (rev 3): 18 -> 20px per rapport 50% con kpi-icon container 40px. */
.kpi-icon .icon { width: 20px; height: 20px; stroke-width: 1.75; }
.sidebar-icon .icon { width: 18px; height: 18px; stroke-width: 1.75; }
.btn .icon { width: 16px; height: 16px; stroke-width: 2; }
.btn-sm .icon { width: 14px; height: 14px; stroke-width: 2; }
.banner-icon .icon { width: 20px; height: 20px; stroke-width: 1.75; }
.empty-state-icon .icon { width: 28px; height: 28px; stroke-width: 1.5; }
.toast .icon { width: 18px; height: 18px; }
.health-badge .icon { width: 12px; height: 12px; stroke-width: 2.25; }


/* === .lucide-spin (promosso 2026-05-02 Step 14) =============================
   Spinner rotation per icone Lucide inline. Sostituisce .fa-spin di FontAwesome
   (rimosso nella migrazione FA->Lucide 2026-04-29). Applica al wrapper <i>:
     <i data-icon="loader" class="lucide-spin"></i> */
.lucide-spin svg { animation: lucide-spin 1s linear infinite; }
@keyframes lucide-spin { from { transform: rotate(0); } to { transform: rotate(360deg); } }


/* ============================================================================
   === SECTION 7: LAYOUT PRIMITIVES (.container, .app-header, .nav, .grid-*) ===
   Originale: theme/layout.css
   ============================================================================ */

/* ============================================================================
   layout.css -- Header, container, griglie, footer.
   Dipende da tokens.css.
   ============================================================================ */

/* === Container === */
.container {
    max-width: 100%;
    margin: 0 auto;
    padding: 0 var(--space-xl);
}

@media (max-width: 768px) {
    .container {
        padding: 0 var(--space-md);
    }
}

/* === Header (sticky) === */
.app-header {
    height: var(--header-height);
    position: sticky;
    top: 0;
    z-index: var(--z-header);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

.app-header-content {
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

/* === Logo (esempio brand-agnostico) === */
.brand {
    display: flex;
    flex-direction: column;
    text-decoration: none;
    transition: var(--transition);
}

.brand:hover {
    opacity: 0.8;
    text-decoration: none;
}

.brand-name {
    font-size: var(--text-2xl);
    font-family: var(--font-display);
    font-weight: var(--weight-bold);
    letter-spacing: -0.02em;
    background: linear-gradient(to right, var(--primary), var(--info));
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

.brand-subtitle {
    font-size: var(--text-2xs);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: var(--tracking-widest);
    font-weight: var(--weight-semibold);
    margin-top: -2px;
}

/* === Navigation principale === */
.nav {
    display: flex;
    gap: var(--space-sm);
}

.nav-link {
    padding: var(--space-sm) var(--space-md);
    border-radius: var(--radius-md);
    text-decoration: none;
    color: var(--text-secondary);
    font-weight: var(--weight-medium);
    font-size: var(--text-base);
    transition: var(--transition);
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

.nav-link:hover {
    background: var(--primary-soft);
    color: var(--primary);
    text-decoration: none;
}

.nav-link.active {
    background: var(--primary-soft);
    color: var(--primary);
    font-weight: var(--weight-semibold);
}

/* === Sub-navigation sticky (sotto header) === */
.sec-nav {
    position: sticky;
    top: var(--header-height);
    z-index: var(--z-sticky);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
}

.sec-nav-inner {
    display: flex;
    gap: var(--space-sm);
    padding: var(--space-sm) var(--space-md);
    white-space: nowrap;
}

.sec-nav-inner::-webkit-scrollbar {
    display: none;
}

.sec-nav-link {
    padding: var(--space-xs) var(--space-md);
    border-radius: var(--radius-sm);
    text-decoration: none;
    color: var(--text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    transition: var(--transition);
}

.sec-nav-link:hover,
.sec-nav-link.active {
    background: var(--primary-soft);
    color: var(--primary);
    text-decoration: none;
}

.sec-nav-link:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* === Main content === */
.main-content {
    padding: var(--space-lg) 0 var(--space-2xl);
    flex-grow: 1;
}

/* === Page header === */
.page-header {
    margin-bottom: var(--space-lg);
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.page-header h2 {
    font-size: var(--text-3xl);
    color: var(--text-primary);
    font-family: var(--font-display);
}

/* === Footer === */
.app-footer {
    padding: var(--space-lg) 0;
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    font-size: var(--text-sm);
    text-align: center;
}

/* === Griglie === */
.grid-2 {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-lg);
}

.grid-3 {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-lg);
}

.grid-4 {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-lg);
}

.grid-auto-fit {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: var(--space-lg);
}

@media (max-width: 1024px) {
    .grid-3,
    .grid-4 {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 768px) {
    .grid-2,
    .grid-3,
    .grid-4 {
        grid-template-columns: 1fr;
        gap: var(--space-md);
    }
}

/* === Stats grid (KPI cards layout) === */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--space-md);
    margin-bottom: var(--space-lg);
}

/* === Mobile nav toggle === */
.nav-toggle {
    display: none;
    background: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-xs) var(--space-sm);
    cursor: pointer;
    color: var(--text-secondary);
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    transition: var(--transition);
}

.nav-toggle:hover {
    background: var(--primary-soft);
    color: var(--primary);
}

@media (max-width: 1200px) {
    .nav-toggle {
        display: flex;
    }
    .nav {
        display: none;
        position: absolute;
        top: var(--header-height);
        left: 0;
        right: 0;
        background: var(--surface);
        border-bottom: 1px solid var(--border);
        flex-direction: column;
        padding: var(--space-md);
        gap: var(--space-xs);
        z-index: var(--z-dropdown);
    }
    .nav.nav-open {
        display: flex;
    }
}


/* ============================================================================
   === SECTION 8: APP SHELL (sidebar + topbar + main) ==========================
   Originale: theme/layouts.css
   ============================================================================ */

/* ============================================================================
   layouts.css -- Pattern layout alternativi.
   Importare DOPO layout.css. Contiene 3 varianti:

     1. SIDEBAR layout       (sidebar fissa a sinistra, collapsable icon-only)
     2. DRAWER layout        (off-canvas, hamburger trigger, overlay)
     3. MIXED layout         (sidebar + topbar globale per search/profile)

   Markup base (selezionabile via class su <body>):
     <body class="layout-sidebar">    -> sidebar fissa
     <body class="layout-drawer">     -> drawer off-canvas
     <body class="layout-mixed">      -> sidebar + topbar
     <body>                           -> default (header in alto, gia' in layout.css)

   Tutti i layout sono RESPONSIVE: <=768px collassano sempre in drawer.
   ============================================================================ */


/* === Variabili layout =====================================================
   I token --sidebar-width / --sidebar-width-collapsed / --topbar-height /
   --drawer-width sono definiti in tokens.css (authoritative). Qui non si
   ridefiniscono: il consumer puo' override-ar-li tramite :root nel proprio
   stylesheet senza patcharare layouts.css.
   ============================================================================ */


/* ============================================================================
   1. LAYOUT SIDEBAR (fissa, collapsable)
   ============================================================================ */

.layout-sidebar {
    display: grid;
    grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
    grid-template-rows: 1fr;
    min-height: 100vh;
    /* Animazione collapse: anima sia il grid sia le proprietà del sidebar
       che cambiano (padding/transform) per UX coerente. */
    transition: grid-template-columns var(--dur-medium) var(--ease-out-expo);
}

.layout-sidebar.sidebar-collapsed {
    grid-template-columns: var(--sidebar-width-collapsed) minmax(0, 1fr);
}

/* === Sidebar container === */
.app-sidebar {
    grid-column: 1;
    grid-row: 1;
    background: var(--surface);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow: hidden;          /* clip container, scroll solo nel nav interno */
    height: 100vh;
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    /* min-width safety: anche su viewport molto stretti, il sidebar non
       viene compresso sotto la sua --sidebar-width-collapsed. Il grid
       template column è già coercitivo, ma fail-safe se un wrapper esterno
       forzasse il restringimento. */
    min-width: var(--sidebar-width-collapsed);
}

.app-sidebar-header {
    padding: var(--space-md);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    min-height: var(--topbar-height);
    flex-shrink: 0;
}

/* Override del .brand globale (definito in layout.css come flex-direction:column).
   Nel sidebar-header vogliamo logo a sinistra + testo accanto (row), con il
   testo che a sua volta impila brand-name sopra brand-subtitle (column). */
.app-sidebar-header .brand {
    overflow: hidden;
    flex: 1;
    min-width: 0;
    flex-direction: row;
    align-items: center;
    gap: var(--space-sm);
}

.app-sidebar-header .brand .brand-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow: hidden;
    line-height: 1.1;
}

.app-sidebar-header .brand .brand-name {
    font-size: var(--text-xl);
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    display: block;
}

/* Toggle collapse (in fondo o nell'header) */
.sidebar-collapse-btn {
    background: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    width: 32px;
    height: 32px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: var(--transition);
}

.sidebar-collapse-btn:hover {
    background: var(--primary-soft);
    color: var(--primary);
    border-color: var(--primary-border);
}

/* === Sidebar nav === */
.app-sidebar-nav {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;       /* mai scroll orizzontale */
    /* scrollbar-gutter: stable riserva sempre lo spazio scrollbar (Chrome 94+,
       Firefox 97+, Safari 18.2+) -> la posizione dei link rimane stabile con e
       senza scrollbar, evitando shift dell'active marker quando il nav è
       più corto/lungo del viewport. */
    scrollbar-gutter: stable;
    padding: var(--space-sm);
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;             /* permette restringimento senza forzare scroll */
}

/* Scrollbar sottile e discreta nel nav */
.app-sidebar-nav::-webkit-scrollbar { width: 6px; }
.app-sidebar-nav::-webkit-scrollbar-track { background: transparent; }
.app-sidebar-nav::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: 3px;
}
.app-sidebar-nav::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }

.sidebar-section {
    margin-top: var(--space-md);
}

.sidebar-section-title {
    padding: var(--space-xs) var(--space-sm);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    color: var(--text-muted);
    letter-spacing: var(--tracking-wider); /* 0.05em è il token più vicino a 0.08em entro la scala */
    font-weight: var(--weight-bold);
    white-space: nowrap;
    overflow: hidden;
}

.sidebar-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    text-decoration: none;
    color: var(--text-secondary);
    font-size: var(--text-md);
    font-weight: var(--weight-medium);
    letter-spacing: var(--tracking-tight);
    transition: var(--transition-fast);
    white-space: nowrap;
    position: relative;
}

.sidebar-link:hover {
    background: var(--sunken);
    color: var(--text-primary);
    text-decoration: none;
}

.sidebar-link.active {
    background: var(--primary-soft);
    color: var(--primary-dark);
    font-weight: var(--weight-semibold);
}

[data-theme="dark"] .sidebar-link.active {
    color: var(--primary-light);
}

.sidebar-link.active::before {
    content: '';
    position: absolute;
    left: -10px;
    top: 4px;
    bottom: 4px;
    width: 2px;
    border-radius: 0 2px 2px 0;
    background: var(--primary);
}

.sidebar-link .sidebar-icon {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.1em;
}

.sidebar-link .sidebar-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar-link .sidebar-badge {
    background: var(--danger);
    color: var(--text-on-primary);
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    padding: 1px 6px;
    border-radius: var(--radius-pill);
    margin-left: auto;
    flex-shrink: 0;
    min-width: 18px;
    text-align: center;
}

/* Sidebar footer (profilo utente / azioni) */
.app-sidebar-footer {
    padding: var(--space-sm);
    border-top: 1px solid var(--border);
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

.app-sidebar-footer .avatar {
    border-color: var(--surface);
    flex-shrink: 0;
}

.app-sidebar-footer .user-info-block {
    flex: 1;
    min-width: 0;
    overflow: hidden;
}

.user-info-block .user-name {
    font-weight: var(--weight-semibold);
    color: var(--text-primary);
    font-size: var(--text-sm);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-info-block .user-meta {
    font-size: var(--text-xs);
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Main content (a destra della sidebar) */
.layout-sidebar > .main-content,
.layout-sidebar > .app-main {
    grid-column: 2;
    grid-row: 1;
    padding: var(--space-lg);
    overflow-x: hidden;
}

/* === SIDEBAR COLLAPSED state (icon-only) ==============================
   Strategia: nascondi ogni elemento testuale che non sta in 72px.
   Label/brand/badge/user-info usano opacity+visibility per fade smooth
   durante la transizione del grid column (era display:none instant flash).
   Header e footer diventano colonna verticale per spread su piu' righe.
   ====================================================================== */

/* Animazione labels: fade smooth durante collapse/expand. Le proprietà
   width/visibility coordinano la rimozione dal layout DOPO il fade. */
.app-sidebar .brand-name,
.app-sidebar .brand-subtitle,
.app-sidebar .sidebar-label,
.app-sidebar .sidebar-section-title,
.app-sidebar .sidebar-badge,
.app-sidebar .user-info-block {
    transition: opacity var(--dur-quick) ease;
}

.layout-sidebar.sidebar-collapsed .brand-name,
.layout-sidebar.sidebar-collapsed .brand-subtitle,
.layout-sidebar.sidebar-collapsed .sidebar-label,
.layout-sidebar.sidebar-collapsed .sidebar-section-title,
.layout-sidebar.sidebar-collapsed .sidebar-badge,
.layout-sidebar.sidebar-collapsed .user-info-block,
.layout-mixed.sidebar-collapsed .brand-name,
.layout-mixed.sidebar-collapsed .brand-subtitle,
.layout-mixed.sidebar-collapsed .sidebar-label,
.layout-mixed.sidebar-collapsed .sidebar-section-title,
.layout-mixed.sidebar-collapsed .sidebar-badge,
.layout-mixed.sidebar-collapsed .user-info-block {
    /* display: none rimuove dal layout (necessario per shrink width).
       Per il fade smooth durante la transizione, usiamo anche opacity:0
       come fallback per casi dove display:none viene posticipato */
    display: none;
    opacity: 0;
}

/* Header: brand vuoto sparisce, mantieni solo collapse-btn centrato.
   Padding animato per match del transition del grid column. */
.app-sidebar-header {
    transition: padding var(--dur-medium) var(--ease-out-expo),
                justify-content var(--dur-medium) var(--ease-out-expo);
}
.layout-sidebar.sidebar-collapsed .app-sidebar-header,
.layout-mixed.sidebar-collapsed .app-sidebar-header {
    justify-content: center;
    padding: var(--space-sm);
    gap: 0;
}
.layout-sidebar.sidebar-collapsed .app-sidebar-header .brand,
.layout-mixed.sidebar-collapsed .app-sidebar-header .brand {
    display: none;
}

/* Nav: padding ridotto e link centrato icon-only */
.layout-sidebar.sidebar-collapsed .app-sidebar-nav,
.layout-mixed.sidebar-collapsed .app-sidebar-nav {
    padding: var(--space-xs);
    align-items: center;
}

.layout-sidebar.sidebar-collapsed .sidebar-link,
.layout-mixed.sidebar-collapsed .sidebar-link {
    justify-content: center;
    padding: 10px;
    width: 40px;          /* quadrato fisso, niente flex elastico */
    gap: 0;
}

/* Active marker quando collapsed: il `::before` (offset da link centrato)
   risulta clipped da .app-sidebar { overflow: hidden } perché scrollbar-gutter
   reserva 6px che spostano il link verso il centro, facendo finire il marker
   fuori dell'edge sinistro del sidebar.
   Soluzione: nasconde il `::before` in collapsed e usa `box-shadow: inset`
   sul link stesso per un indicatore active sempre visibile (3px barretta
   verticale al left edge del link, centrata visivamente nel sidebar). */
.layout-sidebar.sidebar-collapsed .sidebar-link.active::before,
.layout-mixed.sidebar-collapsed .sidebar-link.active::before {
    display: none;
}
.layout-sidebar.sidebar-collapsed .sidebar-link.active,
.layout-mixed.sidebar-collapsed .sidebar-link.active {
    box-shadow: inset 3px 0 0 var(--primary);
}

/* Footer: layout colonna, ogni elemento centrato */
.layout-sidebar.sidebar-collapsed .app-sidebar-footer,
.layout-mixed.sidebar-collapsed .app-sidebar-footer {
    flex-direction: column;
    gap: var(--space-xs);
    padding: var(--space-sm) var(--space-xs);
    align-items: center;
}

/* Tooltip sui link collassati: ora gestito via JS (lorawan-bridge.js) come
   real DOM element invece di pseudo-element ::after, perche' la combinazione
   sticky-sidebar + grid-body + overflow-hidden crea un context dove il
   pseudo-element non veniva renderizzato anche con z-index e overflow visible.
   Stile applicato alla classe .sidebar-floating-tooltip definita sotto.

   Sopprimiamo il tooltip CSS globale [data-tooltip]::after definito in
   components.css per i sidebar-link: altrimenti su hover apparirebbero DUE
   etichette (lo pseudo-element CSS sopra il link + il floating JS a destra).
   `content: none` annulla la generazione del pseudo-element. */
.app-sidebar .sidebar-link[data-tooltip]::after,
.app-sidebar .sidebar-link[data-tooltip]:hover::after {
    content: none;
}

.layout-sidebar.sidebar-collapsed .sidebar-link[data-tooltip],
.layout-mixed.sidebar-collapsed .sidebar-link[data-tooltip] {
    position: relative;
}
.sidebar-floating-tooltip {
    position: fixed;
    background: var(--text-primary);
    color: var(--surface);
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    white-space: nowrap;
    z-index: var(--z-toast);
    pointer-events: none;
    box-shadow: var(--shadow-md);
    opacity: 0;
    transform: translateY(-50%) translateX(-4px);
    transition: opacity .12s ease, transform .12s cubic-bezier(0.22, 1, 0.36, 1);
}
.sidebar-floating-tooltip.show {
    opacity: 1;
    transform: translateY(-50%) translateX(0);
}

/* IMPORTANTE: l'.app-sidebar ha `overflow: hidden` per clippare label/badge
   durante la transizione di collapse. Quando collapsed, pero', i tooltip
   .sidebar-link[data-tooltip]::after sono pseudo-elementi posizionati
   `left: calc(100% + 12px)` che escono dalla sidebar (72px wide) e finiscono
   nel canvas main-content. Senza override l'overflow:hidden li clippa.
   Soluzione: quando collapsed, overflow visibile sull'app-sidebar e sul nav.
   Lo scroll verticale del nav e' rinunciato (con 13 voci in 5 gruppi sta in
   altezza tipica >=600px). */
.layout-sidebar.sidebar-collapsed .app-sidebar,
.layout-mixed.sidebar-collapsed .app-sidebar {
    overflow: visible;
}
.layout-sidebar.sidebar-collapsed .app-sidebar-nav,
.layout-mixed.sidebar-collapsed .app-sidebar-nav {
    overflow: visible;
}

/* Stacking: durante hover, alza z-index del link cosi' il tooltip emerge
   sopra .app-main (che e' display: static ma in column 2 del grid body
   genera un suo painting context indirettamente). Senza questo override
   il tooltip resta dietro le card della main, anche se il pseudo-element
   ha z-index alto. Il fix sposta l'intera stacking context del link
   sopra il main quando hovered. */
.layout-sidebar.sidebar-collapsed .sidebar-link[data-tooltip]:hover,
.layout-mixed.sidebar-collapsed .sidebar-link[data-tooltip]:hover {
    z-index: var(--z-toast);
}

/* Topbar mobile dentro .layout-sidebar > .app-main: nascosta su desktop,
   visibile solo <=768px. Sostituisce il vecchio inline style="display:none"
   gestito da JS resize listener (era source di "state stuck" al breakpoint). */
.layout-sidebar .app-topbar-minimal {
    display: none;
}
.mobile-topbar-inset {
    /* Estende edge-to-edge "rompendo" il padding del .app-main.
       margin-top -space-md per allinearsi al ridotto padding-top mobile
       (vedi @media in fondo: padding-top: var(--space-md) invece dei
       72px legacy che lasciavano 48px di gap fantasma sopra la topbar). */
    margin: calc(var(--space-md) * -1) calc(var(--space-lg) * -1) var(--space-lg);
}

/* Anti-flicker durante resize: il modulo Sidebar in ui.js aggiunge
   .is-resizing su <html> per ~120ms al resize. Disabilita transition sul
   sidebar/backdrop per evitare animazione delle property CSS che cambiano
   al cross-breakpoint (transform/position/grid-template-columns). */
html.is-resizing .app-sidebar,
html.is-resizing .drawer-backdrop,
html.is-resizing .layout-sidebar > .app-sidebar,
html.is-resizing .layout-mixed > .app-sidebar,
html.is-resizing .layout-drawer .app-drawer {
    transition: none !important;
}

/* Mobile: sidebar diventa drawer overlay
   Specificity 0,2,0 per battere .layout-sidebar.sidebar-collapsed senza !important */
@media (max-width: 768px) {
    .layout-sidebar,
    .layout-sidebar.sidebar-collapsed {
        grid-template-columns: 1fr;
    }
    .layout-sidebar > .app-sidebar {
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        width: var(--drawer-width);
        max-width: 85vw;
        transform: translateX(-100%);
        transition: transform var(--dur-medium) var(--ease-out-expo);
        z-index: var(--z-overlay);
        box-shadow: var(--shadow-lg);
        /* Quando in modalità drawer le label/brand-name sono SEMPRE visibili
           (anche se sidebar-collapsed era attivo da desktop): override stato collapsed */
    }
    .layout-sidebar.drawer-open > .app-sidebar {
        transform: translateX(0);
    }
    /* Override sidebar-collapsed in mobile drawer: mostra TUTTO (label/brand)
       perché il drawer è sempre full-width quando aperto su mobile.
       Reset esplicito di opacity (era 0 dal fade transition) e width/gap del link
       (erano fissati a 40px/0 per icon-only desktop). */
    .layout-sidebar.sidebar-collapsed > .app-sidebar .brand-name,
    .layout-sidebar.sidebar-collapsed > .app-sidebar .brand-subtitle,
    .layout-sidebar.sidebar-collapsed > .app-sidebar .sidebar-label,
    .layout-sidebar.sidebar-collapsed > .app-sidebar .sidebar-section-title,
    .layout-sidebar.sidebar-collapsed > .app-sidebar .sidebar-badge,
    .layout-sidebar.sidebar-collapsed > .app-sidebar .user-info-block {
        display: revert;
        opacity: 1;
    }
    .layout-sidebar.sidebar-collapsed > .app-sidebar .sidebar-link {
        justify-content: flex-start;
        padding: var(--space-sm) var(--space-md);
        width: auto;
        gap: 10px;
    }
    /* Reset header collapsed (era padding ridotto + brand display:none) */
    .layout-sidebar.sidebar-collapsed > .app-sidebar .app-sidebar-header {
        justify-content: space-between;
        padding: var(--space-md);
        gap: var(--space-sm);
    }
    .layout-sidebar.sidebar-collapsed > .app-sidebar .app-sidebar-header .brand {
        display: revert;
    }
    /* Reset footer (era column stack icon-only) */
    .layout-sidebar.sidebar-collapsed > .app-sidebar .app-sidebar-footer {
        flex-direction: row;
        gap: var(--space-sm);
        padding: var(--space-sm);
        align-items: center;
    }
    /* Reset nav (era padding ridotto + align center icon-only) */
    .layout-sidebar.sidebar-collapsed > .app-sidebar .app-sidebar-nav {
        padding: var(--space-sm);
        align-items: stretch;
    }
    /* Reset active marker collapsed (no più box-shadow inset, torna al `::before` espanso) */
    .layout-sidebar.sidebar-collapsed > .app-sidebar .sidebar-link.active {
        box-shadow: none;
    }
    .layout-sidebar.sidebar-collapsed > .app-sidebar .sidebar-link.active::before {
        display: block;
    }
    .layout-sidebar > .main-content,
    .layout-sidebar > .app-main {
        grid-column: 1;
        /* padding-top normale (era topbar-height + space-md = 72px che lasciava
           gap fantasma 48px sopra la topbar). La .mobile-topbar-inset usa
           margin-top: -space-md per andare all'edge senza gap. */
        padding-top: var(--space-md);
    }
    .layout-sidebar .app-topbar-minimal {
        display: flex;
    }
}


/* ============================================================================
   2. LAYOUT DRAWER (off-canvas, hamburger trigger)
   ============================================================================ */

.layout-drawer {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/* Topbar minimale: solo brand + hamburger (+ azioni dx) */
.app-topbar-minimal {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    height: var(--topbar-height);
    padding: 0 var(--space-md);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: var(--z-header);
}

.app-topbar-minimal .hamburger {
    background: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    width: 36px;
    height: 36px;
    cursor: pointer;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-xl);
    transition: var(--transition);
}

.app-topbar-minimal .hamburger:hover {
    background: var(--primary-soft);
    color: var(--primary);
}

.app-topbar-minimal .topbar-spacer { flex: 1; }

/* Drawer container */
.app-drawer {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: var(--drawer-width);
    max-width: 85vw;
    background: var(--surface);
    border-right: 1px solid var(--border);
    transform: translateX(-100%);
    transition: transform 0.25s var(--ease-out-expo);
    z-index: var(--z-overlay);
    display: flex;
    flex-direction: column;
    box-shadow: none;
}

.app-drawer.open {
    transform: translateX(0);
    box-shadow: var(--shadow-lg);
}

/* Variante drawer da destra */
.app-drawer.drawer-right {
    left: auto;
    right: 0;
    border-right: none;
    border-left: 1px solid var(--border);
    transform: translateX(100%);
}

.app-drawer.drawer-right.open { transform: translateX(0); }

/* Drawer header */
.app-drawer-header {
    padding: var(--space-md);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: var(--topbar-height);
}

.app-drawer-nav {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-sm);
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* Riusa .sidebar-link, .sidebar-section, .sidebar-section-title */

.app-drawer-footer {
    padding: var(--space-sm);
    border-top: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

/* Backdrop overlay */
.drawer-backdrop {
    position: fixed;
    inset: 0;
    background: var(--overlay-bg);
    z-index: calc(var(--z-overlay) - 1);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur-medium) ease;
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
}

.drawer-backdrop.open {
    opacity: 1;
    pointer-events: auto;
}

/* Main content (full width sotto la topbar) */
.layout-drawer .app-main {
    flex: 1;
    padding: var(--space-lg);
    overflow-x: hidden;
}


/* ============================================================================
   3. LAYOUT MIXED (sidebar + topbar globale)
   ============================================================================ */

.layout-mixed {
    display: grid;
    /* minmax(0, 1fr) evita overflow del main content quando il viewport
       è molto stretto: senza minmax, la colonna 1fr ha implicit min-width
       basata sul contenuto, che può forzare scroll orizzontale. */
    grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
    grid-template-rows: var(--topbar-height) 1fr;
    grid-template-areas:
        "sidebar topbar"
        "sidebar main";
    min-height: 100vh;
    transition: grid-template-columns var(--dur-medium) var(--ease-out-expo);
}

.layout-mixed.sidebar-collapsed {
    grid-template-columns: var(--sidebar-width-collapsed) minmax(0, 1fr);
}

.layout-mixed > .app-sidebar { grid-area: sidebar; }
.layout-mixed > .app-topbar { grid-area: topbar; }
.layout-mixed > .app-main { grid-area: main; }

/* Topbar globale (search + actions + profile) */
.app-topbar {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    padding: 0 var(--space-md);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: var(--z-header);
    height: var(--topbar-height);
}

.topbar-search {
    flex: 1;
    max-width: 480px;
    position: relative;
}

.topbar-search input,
.topbar-search-btn {
    width: 100%;
    padding: var(--space-xs) var(--space-md) var(--space-xs) var(--space-2xl);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--background);
    color: var(--text-primary);
    font-size: var(--text-sm);
    transition: var(--transition);
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
    min-height: 32px;
}

.topbar-search input:focus,
.topbar-search-btn:hover,
.topbar-search-btn:focus-visible {
    background: var(--surface);
    border-color: var(--primary);
    outline: none;
    box-shadow: 0 0 0 3px var(--primary-soft);
}

.topbar-search-placeholder {
    color: var(--text-muted);
    flex: 1;
    text-align: left;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.topbar-search-btn kbd {
    flex-shrink: 0;
}

/* SVG search icon inline (Lucide MIT). currentColor non funziona con
   background-image, quindi stroke esplicito a #6b7280 (text-muted equivalent).
   Per dark mode, override piu' avanti. */
.topbar-search::before {
    content: '';
    position: absolute;
    left: var(--space-sm);
    top: 50%;
    width: 16px;
    height: 16px;
    transform: translateY(-50%);
    pointer-events: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='8'/><path d='M21 21l-4.35-4.35'/></svg>");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 16px;
    opacity: 0.85;
}

[data-theme="dark"] .topbar-search::before {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='8'/><path d='M21 21l-4.35-4.35'/></svg>");
}

.topbar-search kbd {
    position: absolute;
    right: var(--space-sm);
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
}

.topbar-actions {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    margin-left: auto;
}

/* Divider verticale topbar (separazione visiva fra gruppi di action) */
.topbar-divider {
    display: inline-block;
    width: 1px;
    height: 24px;
    background: var(--border);
    margin: 0 var(--space-xs);
    flex-shrink: 0;
}

/* Topbar action button (notifiche, theme, ecc.) */
.topbar-btn {
    background: none;
    border: none;
    color: var(--text-secondary);
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: var(--transition);
    position: relative;
}

.topbar-btn:hover {
    background: var(--primary-soft);
    color: var(--primary);
}

.topbar-btn .topbar-badge {
    position: absolute;
    top: 4px;
    right: 4px;
    background: var(--danger);
    color: var(--text-on-primary);
    font-size: 9px;
    font-weight: var(--weight-bold);
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: var(--radius-pill);
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid var(--surface);
}

/* Profile menu trigger */
.profile-trigger {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    background: none;
    border: 1px solid transparent;
    padding: var(--space-xs) var(--space-sm);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: var(--transition);
    font: inherit;
    color: var(--text-primary);
}

.profile-trigger:hover {
    background: var(--primary-soft);
    border-color: var(--primary-border);
}

.profile-trigger .user-name {
    font-weight: var(--weight-semibold);
    font-size: var(--text-sm);
    line-height: 1.2;
}

.profile-trigger .user-role {
    color: var(--text-muted);
    font-size: var(--text-xs);
}

/* Mobile mixed: sidebar diventa drawer + topbar resta */
@media (max-width: 768px) {
    /* CRITICO: includere `.sidebar-collapsed` (specificity 0,2,0) per battere
       la regola desktop che sopravviverebbe nel cascade. Senza questo, una
       pagina che parte narrow CON localStorage 'sidebar-collapsed=1' (eredità
       sessione desktop) terrebbe `grid-template-columns: 72px minmax(0,1fr)`
       inconsistente con `grid-template-areas: "topbar"/"main"` (1 col),
       comprimendo il main content a ~75px. */
    .layout-mixed,
    .layout-mixed.sidebar-collapsed {
        grid-template-columns: 1fr;
        grid-template-areas:
            "topbar"
            "main";
    }
    .layout-mixed > .app-sidebar {
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        width: var(--drawer-width);
        max-width: 85vw;
        transform: translateX(-100%);
        transition: transform var(--dur-medium) var(--ease-out-expo);
        z-index: var(--z-overlay);
        box-shadow: var(--shadow-lg);
    }
    .layout-mixed.drawer-open > .app-sidebar {
        transform: translateX(0);
    }
    /* Override sidebar-collapsed in mobile drawer: mostra label/brand quando
       il drawer è aperto, anche se da desktop era stato collassato.
       Reset esplicito di opacity (era 0 dal fade transition) e width/gap del link
       (erano fissati a 40px/0 per icon-only desktop). */
    .layout-mixed.sidebar-collapsed > .app-sidebar .brand-name,
    .layout-mixed.sidebar-collapsed > .app-sidebar .brand-subtitle,
    .layout-mixed.sidebar-collapsed > .app-sidebar .sidebar-label,
    .layout-mixed.sidebar-collapsed > .app-sidebar .sidebar-section-title,
    .layout-mixed.sidebar-collapsed > .app-sidebar .sidebar-badge,
    .layout-mixed.sidebar-collapsed > .app-sidebar .user-info-block {
        display: revert;
        opacity: 1;
    }
    .layout-mixed.sidebar-collapsed > .app-sidebar .sidebar-link {
        justify-content: flex-start;
        padding: var(--space-sm) var(--space-md);
        width: auto;
        gap: 10px;
    }
    /* Reset header collapsed (era padding ridotto + brand display:none) */
    .layout-mixed.sidebar-collapsed > .app-sidebar .app-sidebar-header {
        justify-content: space-between;
        padding: var(--space-md);
        gap: var(--space-sm);
    }
    .layout-mixed.sidebar-collapsed > .app-sidebar .app-sidebar-header .brand {
        display: revert;
    }
    /* Reset footer (era column stack icon-only) */
    .layout-mixed.sidebar-collapsed > .app-sidebar .app-sidebar-footer {
        flex-direction: row;
        gap: var(--space-sm);
        padding: var(--space-sm);
        align-items: center;
    }
    /* Reset nav (era padding ridotto + align center icon-only) */
    .layout-mixed.sidebar-collapsed > .app-sidebar .app-sidebar-nav {
        padding: var(--space-sm);
        align-items: stretch;
    }
    /* Reset active marker collapsed (no più box-shadow inset, torna al `::before` espanso) */
    .layout-mixed.sidebar-collapsed > .app-sidebar .sidebar-link.active {
        box-shadow: none;
    }
    .layout-mixed.sidebar-collapsed > .app-sidebar .sidebar-link.active::before {
        display: block;
    }
    .topbar-search {
        max-width: 100%;
        min-width: 0;          /* permette al flex item di shrinkare sotto la content size */
    }
    .topbar-search-placeholder { display: none; }  /* solo icona + ⌘K visibili */
    .topbar-search kbd { display: none; }          /* nascondi ⌘K hint su mobile */
    .topbar-search-btn {
        padding-left: var(--space-xl);             /* compensa icona search */
        padding-right: var(--space-sm);
    }
    .profile-trigger .user-name,
    .profile-trigger .user-role { display: none; }
    /* Hide bottoni meno critici per fit completo, mantieni bell + theme + profile */
    .topbar-actions > .topbar-btn[aria-label="Nuovo"] { display: none; }
}

/* No padding outer: il consumer (dashboard) usa .container-fluid con utility
   .px-3 .px-lg-4 .py-3 di Bootstrap come primo child. Sommare un padding
   teme qui causerebbe ~40-48px totali (24 outer + 16-24 inner). Lasciamo
   gestire al container interno per fonte unica. overflow-x: hidden resta
   per evitare scroll orizzontale su tabelle/mappe oversize. */
.layout-mixed > .app-main {
    overflow-x: hidden;
    /* 2026-05-02 FIX (rev 3): padding UNICO gestito dal theme. Removed
       il reset `:has(> .container-fluid)` perche' il dashboard PHP reale
       ora usa `<div>` plain (rimosse le utility Bootstrap `px-3 px-lg-4
       py-3` da header.php). Stesso pattern coerente tra dashboard PHP
       e pagine demo theme. */
    padding: var(--space-lg);
}


/* ============================================================================
   COMUNI: hamburger button per topbar (drawer/mixed)
   ============================================================================ */
.topbar-hamburger {
    background: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    width: 36px;
    height: 36px;
    cursor: pointer;
    color: var(--text-secondary);
    display: none;
    align-items: center;
    justify-content: center;
    font-size: var(--text-xl);
    transition: var(--transition);
    flex-shrink: 0;
}

.topbar-hamburger:hover {
    background: var(--primary-soft);
    color: var(--primary);
}

@media (max-width: 768px) {
    .topbar-hamburger { display: inline-flex; }
}
