/* =============================================================================
   PASTICCERE PRO — DESIGN SYSTEM
   =============================================================================
   Loaded LAST, after Bootstrap, WowDash (style.css) and DataTables.
   Presentation only. No layout rule here changes a value, a string or a query.

   WHY NOT @layer
   --------------
   The brief suggested `@layer tokens, base, components, overrides`. That would
   be wrong here: unlayered CSS always wins over layered CSS regardless of
   source order, and every existing stylesheet in this app (Bootstrap, WowDash,
   DataTables) is unlayered. Putting the design system in a layer would make it
   lose every single conflict. Using the plain-sections alternative the brief
   allows instead.

   DIRECTION
     "Laboratorio" (Direction A), from the Claude Design project
     "Pasticcere Pro design system": Tokens, Shell, Recipes (table archetype),
     Form, Dashboard, Index. Ink on a grey-green ground, brass once per screen,
     Archivo, border-only panels. Previous token names are kept as aliases in
     §1 so page-level styles written against them still resolve.

   SECTIONS
     1. TOKENS            colour, type, space, radius, elevation, motion,
                          legacy aliases
     2. BASE              document, typography, figures, focus, accessibility
     3. SHELL             sidebar rail, topbar, page header, content container
     4. COMPONENTS        buttons, inputs, panels, KPI, tables, badges, alerts,
                          modals, toasts, empty + loading states, charts, auth,
                          state screens, shortcuts dialog
     5. VENDOR OVERRIDES  Bootstrap / WowDash / DataTables, and (5.8–5.14) the
                          legacy inline brand colours, Bootstrap forms and
                          utilities, the dashboard band, control geometry.
                          Each !important names what it beats.
   ========================================================================== */


/* =============================================================================
   1. TOKENS
   ========================================================================== */

/* Direction A, "Laboratorio" (Claude Design project "Pasticcere Pro design
   system", Tokens.dc.html). Every ratio below was measured by the design, in
   both themes, against WCAG AA — the numbers in the comments are the design's.

   Two chromatic scopes, separated by what the colour ATTACHES to:
     money  -> figures only (--gain / --loss). Never a fill, border or form.
     alert  -> surfaces only (--danger*). Never a numeral. Error MESSAGE text
               is --ink; the chroma sits on the field border and one glyph.
   Success is deliberately non-chromatic.
   Brass appears once per screen, on the primary action. The nav rail's
   "you are here" mark is --ink, because it is a state, not an action.       */
:root {
  /* --- surface and ink --------------------------------------------------- */
  --ground:        #F4F5F3;   /* page background                              */
  --panel:         #FFFFFF;   /* raised working surface                       */
  --panel-sunken:  #EDEFEC;   /* table heads, inset areas, the rail           */
  --ink:           #171B1F;   /* primary text and figures         17.3:1 AAA  */
  --ink-muted:     #5C666E;   /* labels, captions, nav icons       5.9:1 AA   */
  --ink-disabled:  #8B959B;   /* inert controls, on --panel only   3.1:1      */
  --ink-2:         #3C464D;   /* neutral chip ink, chart ramp step 2          */
  --line:          #DFE3E0;   /* rules, panel edges, dividers                 */
  --line-strong:   #C2C8C3;   /* sticky-column edge, outer bounds, controls   */
  --rail-hover-bg: #E4E7E3;   /* hover on a sunken surface                    */

  /* --- strong neutral fill: selected states (current page, checked box,
         segmented choice) and the secondary "apply" button. Inverts in dark. */
  --fill-strong:       #171B1F;
  --fill-strong-hover: #3C464D;
  --on-fill-strong:    #F4F5F3;

  /* --- brass: three jobs, three tokens ----------------------------------- */
  --brass:            #9A6F35;  /* rules and marks only — never text          */
  --brass-fill:       #835C26;  /* primary button surface      label 6.0:1 AA */
  --brass-fill-hover: #7E5A28;
  --on-brass:         #FFFFFF;
  --brass-ink:        #7E5A28;  /* accent as text; the focus ring   6.2:1 AA  */
  --brass-tint:       #F5EEE3;  /* quiet warm highlight behind --brass-ink    */
  --brass-soft:       #E5BE86;  /* legacy tan fill; carries --on-soft-brass   */
  --on-soft-brass:    #171B1F;

  /* --- scope one: money, attaches to figures ----------------------------- */
  --gain:           #1D6F4E;    /* 6.1:1 AA                                   */
  --loss:           #7E2B24;    /* oxblood, 9.3:1 AAA                         */
  --gain-chip-bg:   #DDEEE5;  --gain-chip-ink: #175C40;   /* 6.6:1 AA         */
  --loss-chip-bg:   #F7E4E1;  --loss-chip-ink: #6E2A24;   /* 8.5:1 AAA        */

  /* --- scope two: alert, attaches to surfaces ---------------------------- */
  --danger:          #C4451E;   /* destructive confirm fill, both themes      */
  --on-danger:       #FFFFFF;   /* 5.0:1 AA                                   */
  --danger-ink:      #9E3717;   /* invalid border + alert glyph   7.0:1 AAA   */
  --danger-surface:  #FDF1EC;   /* invalid field fill                         */
  --danger-line:     #E4C3B4;   /* divider inside an invalid control          */
  --caution-bg:      #F3E4BE;  --caution-ink: #6B4E0F;    /* 6.1:1 AA         */
  --neutral-chip-bg: #E7EAE7;  --neutral-chip-ink: #3C464D; /* 8.0:1 AAA      */

  /* --- charts: one ordered ramp, brass on the leading slice -------------- */
  --chart-1: #9A6F35;  --chart-2: #3C464D;  --chart-3: #5C666E;
  --chart-4: #737E85;  --chart-5: #8B959B;
  --chart-6: #2A3137;  /* past five a ring stops reading; a sixth only so a
                          legacy six-slice chart never repeats a neighbour   */
  --chart-grid: #DFE3E0;

  /* --- the ink band: the dashboard's headline strip and any page strip that
         used to be navy. Same values in both themes except its ground.     */
  --band:          #171B1F;
  --band-line:     #2C343B;
  --band-control:  #1C2228;
  --on-band:       #E6EAED;
  --on-band-muted: #93A0A8;
  --on-band-brass: #D2A465;   /* figures on the band use the dark-theme ramp */
  --on-band-gain:  #4CB489;
  --on-band-loss:  #E08A82;

  /* --- focus ------------------------------------------------------------- */
  --focus: #7E5A28;           /* 2px outline, 2px offset                      */

  /* --- typography: Archivo, eleven steps --------------------------------- */
  --font-sans: 'Archivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
               Helvetica, Arial, sans-serif;
  --font-serif: var(--font-sans);   /* one family — the brand serif is retired */

  --fs-figure-xl: 2.75rem;   --lh-figure-xl: 2.75rem;  /* 44/44/600          */
  --fs-display:   2.25rem;   --lh-display:   2.5rem;   /* 36/40/600          */
  --fs-figure-lg: 2rem;      --lh-figure-lg: 2.25rem;  /* 32/36/600          */
  --fs-h1:        1.75rem;   --lh-h1:        2.125rem; /* 28/34/600          */
  --fs-h2:        1.375rem;  --lh-h2:        1.75rem;  /* 22/28/600          */
  --fs-h3:        1.125rem;  --lh-h3:        1.5rem;   /* 18/24/500          */
  --fs-body:      .9375rem;  --lh-body:      1.375rem; /* 15/22/400          */
  --fs-ui:        .875rem;   --lh-ui:        1.25rem;  /* 14/20/500          */
  --fs-figure:    .9375rem;  --lh-figure:    1.25rem;  /* 15/20/500          */
  --fs-small:     .8125rem;  --lh-small:     1.125rem; /* 13/18/400          */
  --fs-caption:   .75rem;    --lh-caption:   1rem;     /* 12/16/500          */
  /* older names, kept so every existing rule resolves */
  --fs-body-sm:   var(--fs-ui);     --lh-body-sm: var(--lh-ui);
  --fs-label:     var(--fs-ui);     --lh-label:   var(--lh-ui);
  --fs-kpi:       var(--fs-figure-lg); --lh-kpi:  var(--lh-figure-lg);

  /* the reserved minus slot: the tabular advance width, so a sign never
     shifts the digits */
  --minus-slot: .62em;

  /* --- spacing: 4 to 72, nothing off-scale ------------------------------- */
  --sp-1: 4px;   --sp-2: 8px;   --sp-3: 12px;  --sp-4: 16px;  --sp-5: 20px;
  --sp-6: 24px;  --sp-8: 32px;  --sp-10: 40px; --sp-14: 56px; --sp-18: 72px;
  --sp-12: 48px; --sp-16: 64px;   /* previous scale, still referenced */

  /* --- radius signals hierarchy ------------------------------------------ */
  --r-input:  3px;    /* inputs, buttons   */
  --r-card:   6px;    /* panels, regions   */
  --r-modal:  10px;   /* modals            */
  --r-pill:   999px;  /* chips only        */

  /* --- elevation: a panel is border only; two raised levels -------------- */
  --e-flat:    none;
  --e-raised:  0 4px 12px rgba(23, 27, 31, .10), 0 1px 2px rgba(23, 27, 31, .06);  /* dropdown, toast */
  --e-overlay: 0 16px 48px rgba(23, 27, 31, .16);                                   /* modal          */

  /* --- motion ------------------------------------------------------------ */
  --ease: cubic-bezier(.2, 0, 0, 1);
  --t-fast: 150ms var(--ease);
  --t-panel: 200ms var(--ease);

  /* --- shell metrics ----------------------------------------------------- */
  --rail-w: 264px;
  --rail-w-collapsed: 72px;
  --topbar-h: 60px;
  --page-max: 1440px;
  --page-gutter: 32px;

  /* --- sidebar: a sunken rail, same in spirit in both themes ------------- */
  --rail-bg:         var(--panel-sunken);
  --rail-text:       var(--ink-muted);
  --rail-text-strong: var(--ink);
  --rail-text-muted: var(--ink-muted);
  --rail-hover:      var(--rail-hover-bg);
  --rail-active:     var(--panel);
  --rail-rule:       var(--line);
  --rail-indicator:  var(--ink);

  /* =======================================================================
     LEGACY ALIASES — the previous direction's names, re-pointed.
     Every existing rule and every page-level style that reads one of these
     now resolves to Laboratorio without being edited.
     ==================================================================== */
  --surface:       var(--panel);
  --surface-sunk:  var(--ground);
  --surface-alt:   var(--panel-sunken);
  --border:        var(--line);
  --border-strong: var(--line-strong);
  --text:          var(--ink);
  --text-muted:    var(--ink-muted);
  --text-subtle:   var(--ink-disabled);

  --ink-950: #171B1F;                   /* text on a tan fill — stays dark    */
  --ink-900: var(--fill-strong);
  --ink-800: var(--fill-strong-hover);
  --ink-700: var(--ink-2);
  --ink-600: var(--ink-muted);

  --brass-700: var(--brass-ink);        /* brass as text                      */
  --brass-600: var(--brass-ink);
  --brass-500: var(--brass);            /* marks and rules                    */
  --brass-400: var(--brass-soft);       /* tan fill, dark text on top         */
  --brass-100: var(--brass-tint);
  --brass-50:  var(--brass-tint);

  --success-600: var(--gain);           --success-50: var(--gain-chip-bg);
  --warning-600: var(--caution-ink);    --warning-50: var(--caution-bg);
  --danger-600:  var(--loss);           --danger-50:  var(--loss-chip-bg);
  --info-600:    var(--ink-muted);      --info-50:    var(--panel-sunken);

  /* The old ring was a box-shadow. Focus is an outline now; the token stays
     so any rule still reading it draws nothing rather than a stale halo. */
  --ring: 0 0 0 0 transparent;
}

/* --- dark theme: token overrides only ------------------------------------ */
:root[data-theme="dark"] {
  --ground:        #14181C;
  --panel:         #1C2228;
  --panel-sunken:  #101418;
  --ink:           #E6EAED;   /* 13.3:1 AAA */
  --ink-muted:     #93A0A8;   /*  6.0:1 AA  */
  --ink-disabled:  #6C787F;   /*  3.5:1     */
  --ink-2:         #AEBAC0;
  --line:          #2C343B;
  --line-strong:   #3D474E;
  --rail-hover-bg: #171C21;

  --fill-strong:       #E6EAED;
  --fill-strong-hover: #C9D0D4;
  --on-fill-strong:    #14181C;

  --brass:            #D2A465;
  --brass-fill:       #D2A465;  /* label 7.9:1 AAA */
  --brass-fill-hover: #E5BE86;
  --on-brass:         #14181C;
  --brass-ink:        #E5BE86;  /* 9.2:1 AAA */
  --brass-tint:       #2A241B;
  --brass-soft:       #D2A465;
  --on-soft-brass:    #14181C;

  --gain:           #4CB489;
  --loss:           #E08A82;
  --gain-chip-bg:   #14261F;  --gain-chip-ink: #63C79C;
  --loss-chip-bg:   #2E1D1B;  --loss-chip-ink: #E8968E;

  --danger-ink:      #F0805C;
  --danger-surface:  #2A1C18;
  --danger-line:     #5A3326;
  --caution-bg:      #33280F;  --caution-ink: #E8C77A;
  --neutral-chip-bg: #242C32;  --neutral-chip-ink: #AEBAC0;

  --chart-1: #D2A465;  --chart-2: #AEBAC0;  --chart-3: #8C9A9B;
  --chart-4: #6C787F;  --chart-5: #3D474E;
  --chart-6: #C9D0D4;
  --chart-grid: #2C343B;

  --focus: #E5BE86;

  --band:         #101418;
  --band-control: #1C2228;

  --ink-950: #14181C;

  /* dark expresses elevation with a border step plus a deeper shadow */
  --e-raised:  0 4px 12px rgba(0, 0, 0, .40), 0 1px 2px rgba(0, 0, 0, .30);
  --e-overlay: 0 16px 48px rgba(0, 0, 0, .55);
}


/* =============================================================================
   2. BASE
   ========================================================================== */

/* Radios, checkboxes, date pickers, dropdown lists and scrollbars are
   painted by the browser, not by this stylesheet, and they follow color-scheme
   rather than any custom property. Without this the whole app kept light
   browser chrome in dark mode: an unchecked radio rendered as a solid white
   disc - reading as the checked one - and the native date picker opened as a
   white panel on a dark page. The theme script always stamps data-theme before
   first paint, so there is no system-preference branch to cover here.         */
:root { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

body {
  background-color: var(--surface-sunk);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Every numeral aligns digit-for-digit, with lining figures. */
td, th,
.ds-kpi-value,
.ds-num,
input[type="number"],
input[inputmode="decimal"],
.ds-table td,
.ds-money,
.ds-figure {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

h1, h2, h3, h4, h5, h6 { font-family: var(--font-sans); color: var(--text); }

a { color: var(--brass-ink); text-underline-offset: 2px; }
a:hover { color: var(--ink); }
::selection { background: var(--brass-tint); color: var(--ink); }

.ds-display { font-size: var(--fs-display); line-height: var(--lh-display); font-weight: 600; letter-spacing: -.02em; }
.ds-h1      { font-size: var(--fs-h1);      line-height: var(--lh-h1);      font-weight: 600; letter-spacing: -.01em; }
.ds-h2      { font-size: var(--fs-h2);      line-height: var(--lh-h2);      font-weight: 600; }
.ds-h3      { font-size: var(--fs-h3);      line-height: var(--lh-h3);      font-weight: 500; }
.ds-body    { font-size: var(--fs-body);    line-height: var(--lh-body); }
.ds-body-sm { font-size: var(--fs-body-sm); line-height: var(--lh-body-sm); }
.ds-label   { font-size: var(--fs-label);   line-height: var(--lh-label);   font-weight: 500; }
.ds-small   { font-size: var(--fs-small);   line-height: var(--lh-small); }
.ds-caption { font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: 500; color: var(--text-muted); }
.ds-muted   { color: var(--text-muted); }
.ds-subtle  { color: var(--text-subtle); }

/* Figures. figure-xl exists because figure-lg cannot be the largest object on
   a page whose h1 is 28 — it is for the headline numbers only. */
.ds-figure    { font-size: var(--fs-figure);    line-height: var(--lh-figure);    font-weight: 500; }
.ds-figure-lg { font-size: var(--fs-figure-lg); line-height: var(--lh-figure-lg); font-weight: 600; letter-spacing: -.01em; }
.ds-figure-xl { font-size: var(--fs-figure-xl); line-height: var(--lh-figure-xl); font-weight: 600; letter-spacing: -.01em; }
.ds-gain { color: var(--gain); }
.ds-loss { color: var(--loss); }

/* The reserved minus slot. Put the sign (U+2212, or nothing) in a
   .ds-sign span in front of the digits and a column of figures keeps its
   decimal separators aligned whether or not a row is negative. */
.ds-sign { display: inline-block; width: var(--minus-slot); text-align: start; }

/* One family: the wordmark and the login headline are Archivo too. */
.ds-wordmark, .ds-brand-headline { font-family: var(--font-sans); font-weight: 600; letter-spacing: -.01em; }

/* --- icons --------------------------------------------------------------- */
.ds-i {
  width: 16px; height: 16px;
  flex: 0 0 auto;
  /* Lucide symbols are stroke-only. State it on the <svg> as well as the
     <symbol>, because fill/stroke do not always inherit through <use>. */
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -.15em;
}
.ds-i-20 { width: 20px; height: 20px; }
.ds-i-24 { width: 24px; height: 24px; }

/* --- focus: never removed without a replacement (§11) -------------------- */
/* Laboratorio: a 2px --focus ring, 2px outside the control, everywhere. */
.ds-focusable:focus-visible,
.ds-btn:focus-visible,
.ds-input:focus-visible,
.ds-select:focus-visible,
.ds-icon-btn:focus-visible,
.ds-nav-link:focus-visible,
.ds-tab:focus-visible,
.btn:focus-visible,
.form-control:focus-visible,
.form-select:focus-visible,
.dropdown-item:focus-visible,
a:focus-visible,
button:focus-visible,
[role="button"]:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  box-shadow: none;
}
/* the rail's items are full-bleed, so their ring sits inside */
.ds-nav-link:focus-visible { outline-offset: -2px; }

/* --- screen-reader only + skip link (§11) -------------------------------- */
.ds-sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.ds-skip-link {
  position: absolute; inset-inline-start: var(--sp-4); top: -100px;
  z-index: 2000;
  padding: var(--sp-3) var(--sp-4);
  background: var(--fill-strong); color: var(--on-fill-strong);
  border-radius: var(--r-input);
  font-size: var(--fs-label); font-weight: 500;
  text-decoration: none;
  transition: top var(--t-fast);
}
.ds-skip-link:focus { top: var(--sp-4); color: var(--on-fill-strong); }

/* --- reduced motion (§11) ------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;   /* beats vendor keyframes       */
    animation-iteration-count: 1 !important; /* beats WowDash .blink loop    */
    transition-duration: .001ms !important;  /* beats vendor transitions     */
    scroll-behavior: auto !important;      /* beats any smooth-scroll JS/CSS */
  }
}


/* =============================================================================
   3. SHELL
   ========================================================================== */

/* --- 3.1 sidebar rail ---------------------------------------------------- *
   Laboratorio: a sunken rail, not a dark slab. Items are full-bleed 36px rows
   with a 3px leading edge; "where you are" is a white row with an --ink edge,
   because the rail is a state and brass is reserved for the action.
   WowDash styles `.sidebar-menu li a` at (0,1,2), which outranks .ds-nav-link,
   so every item rule below is scoped `.sidebar .sidebar-menu` (0,3,2) and wins
   on source order without !important.                                       */
.sidebar {
  background-color: var(--rail-bg);
  background-image: none;              /* the food photograph stays gone (F5) */
  border-inline-end: 1px solid var(--line);
  color: var(--ink);
}
.sidebar::before { display: none; }

.ds-rail-logo {
  display: flex; align-items: center;
  height: var(--topbar-h);
  flex: 0 0 auto;
  padding-inline: var(--sp-5);
  border-bottom: 1px solid var(--rail-rule);
  overflow: hidden;
}
.ds-rail-logo .sidebar-logo {
  display: flex; align-items: center;
  max-width: 100%; height: auto;
  border: 0;                           /* beats WowDash .sidebar-logo borders  */
}
.ds-rail-logo img {
  max-height: 36px; max-width: 100%; width: auto; object-fit: contain;
  border-radius: 2px;
}

/* The close button is a mobile-drawer control. */
.sidebar-close-btn { color: var(--ink-muted); }
@media (min-width: 1200px) {
  .sidebar-close-btn { display: none !important; }   /* beats .ds-icon-btn display */
}

.sidebar .ds-rail-scroll,
.sidebar .sidebar-menu-area {
  flex: 1 1 auto;
  min-height: 0;
  padding: var(--sp-4) 0;              /* beats WowDash's 0 1rem               */
  overflow-y: auto;
  overscroll-behavior: contain;
  border: 0;
}

/* group headings, and a 20px gap between groups */
.ds-rail-group { margin-top: var(--sp-5); }
.ds-rail-group:first-child { margin-top: 0; }
.sidebar .sidebar-menu .ds-rail-group-label {
  margin: var(--sp-5) 0 0;
  padding: 0 var(--sp-5) var(--sp-2);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: 500;
  color: var(--rail-text-muted);
  letter-spacing: 0;                   /* sentence case, never tracked caps    */
}
.sidebar .sidebar-menu .ds-rail-group-label:first-child { margin-top: 0; }
.ds-rail-rule { height: 1px; background: var(--rail-rule); margin: var(--sp-3) var(--sp-5); border: 0; }

/* nav item */
.ds-nav-link,
.sidebar .sidebar-menu li a.ds-nav-link {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  min-height: 36px;
  margin: 0;
  padding: 8px 12px 8px 17px;
  border: 0;
  border-inline-start: 3px solid transparent;
  border-radius: 0;
  background: transparent;
  color: var(--rail-text);
  opacity: 1;
  font-size: var(--fs-ui);
  line-height: var(--lh-ui);
  font-weight: 500;
  text-decoration: none;
  transition: background-color var(--t-fast), color var(--t-fast);
}
.sidebar .sidebar-menu li a.ds-nav-link:hover {
  background-color: var(--rail-hover);
  color: var(--rail-text-strong);
}
.ds-nav-link .ds-i,
.sidebar .sidebar-menu li a.ds-nav-link .ds-i {
  width: 20px; height: 20px;
  margin: 0;                           /* beats WowDash .menu-icon margins     */
  color: currentColor;                 /* monochrome, follows the label        */
  font-size: inherit;
}

/* where you are */
.sidebar .sidebar-menu li a.ds-nav-link.is-active,
.sidebar .sidebar-menu li a.ds-nav-link.active-page,
.sidebar .sidebar-menu li.active-page > a.ds-nav-link {
  background-color: var(--rail-active);
  border-inline-start-color: var(--rail-indicator);
  color: var(--rail-text-strong);
}
.ds-nav-link.is-active::before,
.sidebar-menu a.ds-nav-link.active-page::before { content: none; }

/* an open group: its label turns ink, the row stays unfilled */
.sidebar .sidebar-menu li.dropdown.open > a.ds-nav-link,
.sidebar .sidebar-menu li.dropdown.dropdown-open > a.ds-nav-link,
.ds-nav-link.is-parent-active {
  background-color: transparent;
  color: var(--rail-text-strong);
}
.sidebar .sidebar-menu li.dropdown.open > a.ds-nav-link:hover,
.sidebar .sidebar-menu li.dropdown.dropdown-open > a.ds-nav-link:hover {
  background-color: var(--rail-hover);
}

.ds-nav-label { flex: 1 1 auto; min-width: 0; }        /* no nowrap: i18n    */

/* WowDash draws its own chevron with a ::after on dropdown links; the rail
   ships a real SVG chevron. Scoped to the EXPANDED rail on purpose: in
   collapsed mode that ::after carries the tooltip (5.6f). */
.sidebar:not(.active) .sidebar-menu .dropdown > a.ds-nav-link::after { content: none !important; }  /* beats WowDash chevron ::after */
.sidebar .sidebar-menu li a.ds-nav-link .ds-nav-chevron {
  width: 16px; height: 16px;
  transition: transform var(--t-panel);
}
.dropdown-open > .ds-nav-link .ds-nav-chevron,
.open > .ds-nav-link .ds-nav-chevron { transform: rotate(90deg); }

/* submenu: children sit on a guide line under the parent's icon */
.sidebar .sidebar-menu .sidebar-submenu.ds-subnav {
  position: relative;
  margin: 0;
  padding: 2px 0 var(--sp-2);          /* beats WowDash's 2.75rem inset         */
  list-style: none;
}
.ds-subnav::before {
  content: "";
  position: absolute;
  inset-inline-start: 29px; top: 2px; bottom: var(--sp-2);
  width: 1px; background: var(--line-strong);
}
.sidebar .sidebar-menu .ds-subnav li a.ds-nav-link {
  min-height: 32px;
  padding: 6px 12px 6px 45px;
  font-weight: 400;
}
.sidebar .sidebar-menu .ds-subnav li a.ds-nav-link .ds-i { width: 16px; height: 16px; }
/* a child is marked by weight and fill, not by a second leading edge */
.sidebar .sidebar-menu .ds-subnav li a.ds-nav-link.active-page,
.sidebar .sidebar-menu .ds-subnav li.active-page > a.ds-nav-link {
  border-inline-start-color: transparent;
  background-color: var(--rail-active);
  font-weight: 500;
}

/* academy link — an external link, so a quiet row with an outbound mark */
.sidebar .sidebar-menu li a.ds-nav-link.ds-rail-academy {
  margin-top: var(--sp-5);
  border-top: 1px solid var(--rail-rule);
  padding-top: 12px; padding-bottom: 12px;
  background: transparent;
  color: var(--brass-ink);
}
.sidebar .sidebar-menu li a.ds-nav-link.ds-rail-academy:hover {
  background: var(--rail-hover);
  color: var(--ink);
}

/* user chip at the foot of the rail */
.ds-rail-user-meta { flex: 1 1 auto; min-width: 0; }
.ds-rail-user-menu { --bs-dropdown-min-width: 220px; }
.ds-rail-user {
  margin-top: auto;
  padding: var(--sp-3) var(--sp-3);
  border-top: 1px solid var(--rail-rule);
}
.ds-rail-user-btn {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  padding: var(--sp-1) var(--sp-2);
  border: 0; border-radius: var(--r-input);
  background: transparent; color: var(--ink);
  text-align: start;
  transition: background-color var(--t-fast);
}
.ds-rail-user-btn:hover { background: var(--rail-hover); }
.ds-rail-user-name { font-size: var(--fs-ui); line-height: var(--lh-ui); font-weight: 500; color: var(--ink); }
.ds-rail-user-role { font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--ink-muted); }

/* avatar with initials fallback (fixes F3) — a neutral chip, never brass */
.ds-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  flex: 0 0 auto;
  border-radius: var(--r-pill);
  background: var(--neutral-chip-bg);
  color: var(--neutral-chip-ink);
  font-size: var(--fs-small); font-weight: 600;
  overflow: hidden;
}
.ds-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ds-avatar-40 { width: 40px; height: 40px; font-size: var(--fs-ui); }
.ds-avatar-72 { width: 72px; height: 72px; font-size: var(--fs-h2); }

/* --- 3.2 topbar ---------------------------------------------------------- *
   60px, on the page ground, ruled once. Controls are 32px bordered squares. */
.ds-topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: var(--sp-3);
  height: var(--topbar-h);
  padding: 0 var(--page-gutter);       /* beats WowDash .navbar-header padding */
  background: var(--ground);
  border-bottom: 1px solid var(--line);
}
.ds-topbar.is-scrolled { backdrop-filter: blur(12px) saturate(1.2); background: color-mix(in srgb, var(--ground) 92%, transparent); }
.ds-topbar-spacer { flex: 1 1 auto; }
.ds-topbar-actions { display: flex; align-items: center; gap: var(--sp-2); }

.ds-topbar .ds-icon-btn,
.ds-topbar .ds-icon-btn.ds-icon-btn-40 {
  width: 32px; height: 32px;
  border: 1px solid var(--line);
  border-radius: var(--r-input);
  background: var(--panel);
  color: var(--ink-muted);
}
.ds-topbar .ds-icon-btn:hover {
  border-color: var(--line-strong);
  background: var(--panel);
  color: var(--ink);
}
.ds-topbar .ds-icon-btn .ds-i { width: 18px; height: 18px; }
/* the rail toggle sits in the topbar but reads as a bare glyph */
.ds-topbar .sidebar-toggle.ds-icon-btn,
.ds-topbar .sidebar-mobile-toggle.ds-icon-btn { border-color: transparent; background: transparent; }
.ds-topbar .sidebar-toggle.ds-icon-btn:hover,
.ds-topbar .sidebar-mobile-toggle.ds-icon-btn:hover { border-color: var(--line); background: var(--panel); }

/* the profile trigger: square initials plus a chevron, in one bordered box */
.ds-topbar-profile {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  height: 32px;
  padding: 0 var(--sp-1) 0 0;
  border: 1px solid var(--line);
  border-radius: var(--r-input);
  background: var(--panel);
  color: var(--ink-muted);
  cursor: pointer;
  transition: border-color var(--t-fast);
}
.ds-topbar-profile:hover { border-color: var(--line-strong); color: var(--ink); }
.ds-topbar-profile .ds-avatar {
  width: 30px; height: 30px;
  border-radius: 2px;
  font-size: var(--fs-caption);
}
.ds-topbar-profile .ds-i { width: 14px; height: 14px; }

.ds-breadcrumb { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-ui); line-height: var(--lh-ui); color: var(--ink-muted); min-width: 0; }
.ds-breadcrumb a { color: var(--ink-muted); text-decoration: none; }
.ds-breadcrumb a:hover { color: var(--ink); }
.ds-breadcrumb [aria-current="page"] { color: var(--ink); font-weight: 500; }
.ds-breadcrumb-sep { color: var(--line-strong); }

/* notification count: ink on the ground, not an alarm */
.ds-count {
  position: absolute; top: -5px; inset-inline-end: -5px;
  min-width: 16px; height: 16px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-pill);
  background: var(--fill-strong); color: var(--on-fill-strong);
  font-size: 11px; font-weight: 600; line-height: 14px;
  font-variant-numeric: tabular-nums lining-nums;
}

/* the shortcuts trigger: a word plus its key */
.ds-topbar-shortcuts {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  height: 32px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: var(--r-input);
  background: var(--panel); color: var(--ink-muted);
  font-family: inherit; font-size: var(--fs-ui); line-height: var(--lh-ui);
  cursor: pointer;
  transition: border-color var(--t-fast), color var(--t-fast);
}
.ds-topbar-shortcuts:hover { border-color: var(--line-strong); color: var(--ink); }
@media (max-width: 767.98px) { .ds-topbar-shortcuts { display: none; } }

/* key cap */
.ds-kbd {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px;
  border: 1px solid var(--line); border-radius: var(--r-input);
  background: var(--panel-sunken); color: var(--ink-muted);
  font-family: inherit; font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: 500;
  box-shadow: none;
}
.ds-kbd-lg { min-width: 22px; height: 22px; padding: 0 6px; font-size: var(--fs-small); color: var(--ink); }
/* on a brass fill the cap goes quiet brass */
.ds-btn-accent .ds-kbd,
.btn-gold .ds-kbd { border-color: rgba(255, 255, 255, .35); background: transparent; color: inherit; opacity: .85; }

/* notifications + profile menus: level-1 elevation */
.ds-notif-menu { min-width: 380px; max-width: calc(100vw - var(--sp-8)); border-radius: var(--r-card); border: 1px solid var(--line-strong); box-shadow: var(--e-raised); overflow: hidden; background: var(--panel); }
.ds-notif-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); background: var(--panel); border-bottom: 1px solid var(--line); }
.ds-notif-list { max-height: 400px; overflow-y: auto; padding: 0; }
.ds-notif-item {
  position: relative;
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4) var(--sp-3) 28px;
  border-bottom: 1px solid var(--line);
  color: var(--ink); text-decoration: none;
  transition: background-color var(--t-fast);
}
/* unread dot — the one optical off-scale value, 7px */
.ds-notif-item::before {
  content: "";
  position: absolute; inset-inline-start: 13px; top: 19px;
  width: 7px; height: 7px; border-radius: var(--r-pill);
  background: var(--brass);
}
.ds-notif-item:hover { background: var(--ground); color: var(--ink); }
.ds-notif-item .ds-kpi-icon { display: none; }   /* the dot carries the state */
.ds-notif-text { flex: 1 1 auto; min-width: 0; }
.ds-notif-title { display: block; font-size: var(--fs-ui); line-height: var(--lh-ui); font-weight: 500; }
.ds-notif-body { display: block; font-size: var(--fs-small); line-height: var(--lh-small); color: var(--ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-notif-time { font-size: var(--fs-small); color: var(--ink-muted); white-space: nowrap; font-variant-numeric: tabular-nums lining-nums; }
.ds-notif-foot { padding: var(--sp-3) var(--sp-4); background: var(--panel-sunken); border-top: 1px solid var(--line); }

.ds-profile-menu { min-width: 260px; border-radius: var(--r-card); border: 1px solid var(--line-strong); box-shadow: var(--e-raised); padding: 0; background: var(--panel); }
.ds-profile-head { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-4); border-bottom: 1px solid var(--line); }
.ds-profile-meta { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ds-profile-name { font-size: var(--fs-ui); font-weight: 500; }
.ds-profile-menu .dropdown-item,
.ds-rail-user-menu .dropdown-item { border-radius: var(--r-input); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-ui); color: var(--ink); }
.ds-profile-menu .dropdown-item:hover,
.ds-rail-user-menu .dropdown-item:hover { background: var(--ground); color: var(--ink); }

/* --- 3.3 page header ----------------------------------------------------- *
   h1 28/34 and one line of context, actions aligned to its baseline.        */
.ds-page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--sp-5);
  flex-wrap: wrap;
  margin-bottom: var(--sp-6);
}
.ds-page-head-text { flex: 1 1 380px; min-width: 0; }
.ds-page-title { font-size: var(--fs-h1); line-height: var(--lh-h1); font-weight: 600; letter-spacing: -.01em; margin: 0; color: var(--ink); }
.ds-page-context { margin: var(--sp-2) 0 0; font-size: var(--fs-body); line-height: var(--lh-body); color: var(--ink-muted); }
.ds-page-actions { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }

/* --- 3.4 content container ----------------------------------------------- */
.ds-page {
  max-width: var(--page-max);
  margin-inline: auto;
  padding: var(--sp-8) var(--page-gutter) var(--sp-14);
}
/* Bootstrap's .container caps and re-pads .ds-page at every breakpoint; the
   page region is the full 1440 measure with the 32px gutter instead. */
.container.ds-page,
.container-fluid.ds-page {
  max-width: var(--page-max);
  padding-left: var(--page-gutter);
  padding-right: var(--page-gutter);
}
.ds-stack > * + * { margin-top: var(--sp-6); }

@media (max-width: 767.98px) {
  :root { --page-gutter: 16px; }
  .ds-page { padding-top: var(--sp-5); }
}

/* =============================================================================
   4. COMPONENTS
   ========================================================================== */

/* --- 4.1 buttons --------------------------------------------------------- *
   36px, 3px radius, 14/20/500. Four roles:
     accent     brass — the one action a screen exists for (Salva, Aggiungi)
     primary    strong neutral fill — apply / filter / confirm-a-view
     secondary  panel with a --line-strong edge
     ghost      no edge until hovered
   Danger is a secondary button outside a confirmation, and the one filled
   --danger surface inside it.                                               */
.ds-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: 36px;
  padding: 0 var(--sp-4);
  border: 1px solid transparent;
  border-radius: var(--r-input);
  font-family: var(--font-sans);
  font-size: var(--fs-ui); line-height: var(--lh-ui); font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  box-shadow: none;
  transition: background-color var(--t-fast), border-color var(--t-fast), color var(--t-fast);
  /* i18n: never a fixed width, never nowrap-with-truncation */
  white-space: normal;
}
.ds-btn .ds-i { width: 16px; height: 16px; }
.ds-btn:disabled, .ds-btn.is-disabled {
  cursor: not-allowed; opacity: 1;
  background: var(--panel-sunken); color: var(--ink-disabled); border-color: transparent;
}

.ds-btn-accent    { background: var(--brass-fill); color: var(--on-brass); border-color: var(--brass-fill); }
.ds-btn-accent:hover:not(:disabled) { background: var(--brass-fill-hover); border-color: var(--brass-fill-hover); color: var(--on-brass); }

.ds-btn-primary   { background: var(--fill-strong); color: var(--on-fill-strong); border-color: var(--fill-strong); }
.ds-btn-primary:hover:not(:disabled) { background: var(--fill-strong-hover); border-color: var(--fill-strong-hover); color: var(--on-fill-strong); }

.ds-btn-secondary { background: var(--panel); color: var(--ink); border-color: var(--line-strong); }
.ds-btn-secondary:hover:not(:disabled) { background: var(--panel-sunken); color: var(--ink); }

.ds-btn-ghost     { background: transparent; color: var(--ink); }
.ds-btn-ghost:hover:not(:disabled) { background: var(--panel-sunken); color: var(--ink); }

.ds-btn-danger    { background: var(--panel); color: var(--ink); border-color: var(--line-strong); }
.ds-btn-danger:hover:not(:disabled) { background: var(--danger-surface); color: var(--ink); border-color: var(--danger-ink); }
/* the confirm step: the word and the fill appear together, nowhere else */
.ds-modal .ds-btn-danger-solid,
.modal .ds-btn-danger-solid,
.modal-footer .ds-btn-danger,
.modal-footer .btn-danger { background: var(--danger); color: var(--on-danger); border-color: var(--danger); }
.ds-modal .ds-btn-danger-solid:hover,
.modal .ds-btn-danger-solid:hover,
.modal-footer .ds-btn-danger:hover:not(:disabled),
.modal-footer .btn-danger:hover { background: var(--danger-ink); color: var(--on-danger); border-color: var(--danger-ink); }

.ds-btn-sm { min-height: 32px; padding: 0 var(--sp-3); font-size: var(--fs-ui); }
.ds-btn-lg { min-height: 40px; padding: 0 var(--sp-5); font-size: var(--fs-ui); }
.ds-btn-block { width: 100%; }

.ds-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  padding: 0;
  border: 0; border-radius: var(--r-input);
  background: transparent; color: var(--ink-muted);
  cursor: pointer;
  transition: background-color var(--t-fast), color var(--t-fast);
}
.ds-icon-btn:hover { background: var(--panel-sunken); color: var(--ink); }
.ds-icon-btn-danger:hover { background: var(--panel-sunken); color: var(--ink); }
.ds-icon-btn-40 { width: 40px; height: 40px; }

/* --- 4.2 inputs ---------------------------------------------------------- *
   Label above (14/500), help below (13, muted), error below the help. Error
   message text is --ink; the chroma sits on the border and one glyph.      */
.ds-field { display: block; }
.ds-field + .ds-field { margin-top: var(--sp-5); }
.ds-field-label {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: var(--fs-ui); line-height: var(--lh-ui); font-weight: 500; color: var(--ink);
}
.ds-field-optional { font-weight: 400; color: var(--ink-muted); }
.ds-field-help { margin-top: var(--sp-2); font-size: var(--fs-small); line-height: var(--lh-small); color: var(--ink-muted); }
.ds-field-error {
  display: flex; align-items: flex-start; gap: var(--sp-2);
  margin-top: var(--sp-2);
  font-size: var(--fs-ui); line-height: var(--lh-ui); color: var(--ink);
  text-wrap: pretty;
}
.ds-field-error::before,
.ds-glyph-alert {
  content: "!";
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; margin-top: 2px;
  border-radius: var(--r-pill);
  background: var(--danger-ink); color: var(--panel);
  font-size: 12px; line-height: 16px; font-weight: 600;
}

.ds-input, .ds-select, .ds-textarea {
  display: block; width: 100%;
  min-height: 40px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-input);
  background-color: var(--panel);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-figure);
  box-shadow: none;
  transition: border-color var(--t-fast);
}
.ds-textarea, textarea.ds-input { padding: 10px var(--sp-3); min-height: 96px; line-height: var(--lh-body); resize: vertical; }
.ds-input::placeholder, .ds-textarea::placeholder { color: var(--ink-disabled); opacity: 1; }
.ds-input:hover, .ds-select:hover, .ds-textarea:hover { border-color: var(--line-strong); }
.ds-input:focus, .ds-select:focus, .ds-textarea:focus {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-color: var(--line-strong);
  box-shadow: none;
}
.ds-input:disabled, .ds-select:disabled, .ds-textarea:disabled,
.ds-input[readonly] {
  background-color: var(--ground);
  color: var(--ink-disabled);
  border-color: var(--line);
  opacity: 1;                            /* no opacity trick (§6)            */
}
.ds-input.is-invalid, .ds-select.is-invalid, .ds-textarea.is-invalid {
  border: 2px solid var(--danger-ink);
  background-color: var(--danger-surface);
  padding-inline: 11px;
}

.ds-select {
  appearance: none; -webkit-appearance: none;
  padding-inline-end: 36px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 20 20' fill='none' stroke='%235C666E' stroke-width='1.5'%3E%3Cpolyline points='5,8 10,13 15,8'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
  background-size: 14px 14px;
  cursor: pointer;
}
:root[data-theme="dark"] .ds-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 20 20' fill='none' stroke='%2393A0A8' stroke-width='1.5'%3E%3Cpolyline points='5,8 10,13 15,8'/%3E%3C/svg%3E");
}
[dir="rtl"] .ds-select { background-position: left var(--sp-3) center; }

/* date inputs: one width for every date field on a page (F8) */
.ds-input[type="date"], .ds-date {
  min-width: 160px;
  font-variant-numeric: tabular-nums lining-nums;
}
.ds-input[type="date"]::-webkit-calendar-picker-indicator { opacity: .6; cursor: pointer; }
.ds-input[type="date"]:hover::-webkit-calendar-picker-indicator { opacity: 1; }

/* affix group: € is an affix on its own ground, not a character in the value */
.ds-affix { display: flex; align-items: stretch; }
.ds-affix > .ds-input { border-radius: 0; min-width: 0; }
.ds-affix > .ds-input:first-child { border-start-start-radius: var(--r-input); border-end-start-radius: var(--r-input); }
.ds-affix > .ds-input:last-child  { border-start-end-radius: var(--r-input);   border-end-end-radius: var(--r-input); }
.ds-affix-text {
  display: inline-flex; align-items: center;
  padding-inline: var(--sp-3);
  border: 1px solid var(--line);
  background: var(--panel-sunken);
  color: var(--ink-muted);
  font-size: var(--fs-ui);
  white-space: nowrap;
}
.ds-affix-text:first-child { border-inline-end: 0; border-start-start-radius: var(--r-input); border-end-start-radius: var(--r-input); }
.ds-affix-text:last-child  { border-inline-start: 0; border-start-end-radius: var(--r-input); border-end-end-radius: var(--r-input); }
.ds-affix:focus-within { border-radius: var(--r-input); outline: 2px solid var(--focus); outline-offset: 2px; }
.ds-affix:focus-within .ds-input,
.ds-affix:focus-within .ds-affix-text { border-color: var(--line-strong); }
.ds-affix .ds-input:focus { outline: none; }
/* money reads right-aligned and tabular */
.ds-input-money { text-align: end; font-weight: 500; font-variant-numeric: tabular-nums lining-nums; }

.ds-check { display: inline-flex; align-items: center; gap: 10px; font-size: var(--fs-ui); line-height: var(--lh-ui); }
.ds-check input { width: 18px; height: 18px; accent-color: var(--fill-strong); }

/* segmented choice (kg / litro / pezzo) */
.ds-segmented { display: flex; height: 40px; border: 1px solid var(--line); border-radius: var(--r-input); overflow: hidden; }
.ds-segmented > * { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--ink-muted); font-size: var(--fs-ui); font-weight: 500; cursor: pointer; }
.ds-segmented > * + * { border-inline-start: 1px solid var(--line); }
.ds-segmented > .is-selected,
.ds-segmented > input:checked + label { background: var(--fill-strong); color: var(--on-fill-strong); }

/* --- 4.3 cards / panels -------------------------------------------------- *
   A panel sits on the ground with a border and no shadow. Sections inside a
   form are ruled bands inside one region, not stacked cards.               */
.ds-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--e-flat);
}
.ds-card-body { padding: var(--sp-5) var(--sp-6); }
.ds-card-head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line);
}
.ds-card-title { flex: 1 1 auto; min-width: 0; font-size: var(--fs-h3); line-height: var(--lh-h3); font-weight: 500; margin: 0; color: var(--ink); }
.ds-card-sub { margin: 2px 0 0; font-size: var(--fs-small); line-height: var(--lh-small); color: var(--ink-muted); }
.ds-card-actions { display: flex; align-items: center; gap: var(--sp-2); }

/* PANEL HEADERS — quiet. No fill, one hairline, the title in ink, icons muted,
   so the numbers are the loudest thing on the screen. */
.ds-card-head-light { background: var(--panel-sunken); }
.ds-card-head-light .ds-card-title { color: var(--ink); }

.ds-card-head-quiet {
  background: transparent;
  border-bottom: 1px solid var(--line);
  padding: var(--sp-4) var(--sp-5);
}
.ds-card-head-quiet .ds-card-title {
  color: var(--ink);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  font-weight: 500;
  letter-spacing: 0;
}
.ds-card-head-quiet .ds-i { color: var(--ink-muted); }
.ds-card-head-quiet .ds-caption { color: var(--ink-muted); }
.ds-card-head-quiet > i[class^="bi-"], .ds-card-head-quiet > i[class*=" bi-"] { color: var(--ink-muted); }

/* a ruled band inside one region */
.ds-band { padding: var(--sp-5) var(--sp-6); border-bottom: 1px solid var(--line); }
.ds-band:last-child { border-bottom: 0; }
.ds-band-title { margin: 0 0 4px; font-size: var(--fs-h3); line-height: var(--lh-h3); font-weight: 500; }
.ds-band-sub { margin: 0 0 var(--sp-5); font-size: var(--fs-small); line-height: var(--lh-small); color: var(--ink-muted); }

/* the action bar at the foot of a form region */
.ds-actionbar {
  display: flex; flex-wrap: wrap; gap: var(--sp-4); align-items: center; justify-content: space-between;
  padding: var(--sp-4) var(--sp-6);
  background: var(--panel-sunken);
  border-top: 1px solid var(--line);
}

/* --- 4.3b anything sitting on an ink band -------------------------------- *
   Several pages paint a dark strip and drop a heading in it. Put ds-on-dark
   on the strip and everything inside reads light (--band tokens, §1).      */
.ds-on-dark,
.ds-on-dark .ds-card-title,
.ds-on-dark h1, .ds-on-dark h2, .ds-on-dark h3,
.ds-on-dark h4, .ds-on-dark h5, .ds-on-dark h6,
.ds-on-dark .ds-i { color: var(--on-band); }
.ds-on-dark small,
.ds-on-dark .ds-caption,
.ds-on-dark .text-light { color: var(--on-band-muted); }

/* mode / status pills that used .bg-secondary and vanished on dark strips */
.ds-badge.bg-secondary { background-color: var(--neutral-chip-bg) !important; color: var(--neutral-chip-ink) !important; }  /* beats Bootstrap .bg-secondary */

/* --- 4.3c definition list (profile details) ------------------------------ */
.ds-dl { display: grid; grid-template-columns: minmax(120px, auto) 1fr; gap: var(--sp-3) var(--sp-5); margin: 0; }
.ds-dl dt { font-size: var(--fs-small); line-height: var(--lh-small); font-weight: 500; color: var(--ink-muted); margin: 0; }
.ds-dl dd { font-size: var(--fs-body); color: var(--ink); margin: 0; min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 479.98px) { .ds-dl { grid-template-columns: 1fr; gap: var(--sp-1) 0; } .ds-dl dd { margin-bottom: var(--sp-3); } }

/* --- 4.4 KPI tiles ------------------------------------------------------- *
   A caption over a figure. The figure carries money colour only when it IS
   money that went one way; the tile itself never takes a tint.             */
.ds-kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-5);
}
@media (max-width: 1023.98px) { .ds-kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 639.98px)  { .ds-kpi-grid { grid-template-columns: minmax(0, 1fr); } }

.ds-kpi {
  display: flex; flex-direction: column; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-6);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
}
.ds-kpi-top { display: flex; align-items: flex-start; gap: var(--sp-3); }
.ds-kpi-label { flex: 1 1 auto; min-width: 0; font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--ink-muted); font-weight: 500; }
.ds-kpi-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: 0 0 auto;
  border-radius: var(--r-input);
  background: var(--panel-sunken);
  color: var(--ink-muted);
}
.ds-kpi-value {
  font-size: var(--fs-figure-lg); line-height: var(--lh-figure-lg);
  font-weight: 600; letter-spacing: -.01em; color: var(--ink);
  font-variant-numeric: tabular-nums lining-nums;
}
.ds-kpi-delta {
  display: inline-flex; align-items: center; gap: 4px;
  align-self: flex-start;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  font-size: var(--fs-small); line-height: var(--lh-small); font-weight: 500;
  font-variant-numeric: tabular-nums lining-nums;
}
.ds-kpi-delta-up   { background: var(--gain-chip-bg); color: var(--gain-chip-ink); }
.ds-kpi-delta-down { background: var(--loss-chip-bg); color: var(--loss-chip-ink); }
.ds-kpi-delta-flat { background: var(--neutral-chip-bg); color: var(--neutral-chip-ink); }

/* semantic tints. Icon marks are monochrome in this system; a tint survives
   only where it states money or an expiry. */
.ds-tint-brass   { background: var(--panel-sunken); color: var(--ink-muted); }
.ds-tint-success { background: var(--panel-sunken); color: var(--ink-muted); }
.ds-tint-warning { background: var(--caution-bg);   color: var(--caution-ink); }
.ds-tint-danger  { background: var(--loss-chip-bg); color: var(--loss-chip-ink); }
.ds-tint-info    { background: var(--panel-sunken); color: var(--ink-muted); }
.ds-tint-neutral { background: var(--panel-sunken); color: var(--ink-muted); }

/* --- 4.5 tables ---------------------------------------------------------- *
   One region, three parts: control row, table, pagination — inside a single
   1px border. 36px head on --panel-sunken, 44px rows, hover is a background
   change only. Text reads left, figures read right.                        */
.ds-table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* the one gradient in the system: an edge shadow that says content
     continues. It rides the scroll container, so it disappears at the end. */
  background:
    linear-gradient(to right, var(--panel) 30%, transparent) left center,
    linear-gradient(to left,  var(--panel) 30%, transparent) right center,
    radial-gradient(farthest-side at 0 50%, rgba(23,27,31,.16), transparent) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(23,27,31,.16), transparent) right center;
  background-repeat: no-repeat;
  background-size: 32px 100%, 32px 100%, 12px 100%, 12px 100%;
  background-attachment: local, local, scroll, scroll;
}
:root[data-theme="dark"] .ds-table-scroll {
  background:
    linear-gradient(to right, var(--panel) 30%, transparent) left center,
    linear-gradient(to left,  var(--panel) 30%, transparent) right center,
    radial-gradient(farthest-side at 0 50%, rgba(0,0,0,.5), transparent) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.5), transparent) right center;
  background-repeat: no-repeat;
  background-size: 32px 100%, 32px 100%, 12px 100%, 12px 100%;
  background-attachment: local, local, scroll, scroll;
}
.ds-table { width: 100%; border-collapse: collapse; font-size: var(--fs-body); line-height: var(--lh-figure); }
.ds-table thead th {
  position: sticky; top: 0; z-index: 1;
  height: 36px;
  padding: 8px var(--sp-4);
  background: var(--panel-sunken);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: 500; color: var(--ink-muted);
  text-align: start;
  white-space: nowrap;
  vertical-align: middle;
}
.ds-table tbody td {
  padding: 10px var(--sp-4);
  height: 44px;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  vertical-align: middle;
}
.ds-table thead th:first-child,
.ds-table tbody td:first-child,
.ds-table tfoot td:first-child,
.ds-table tfoot th:first-child { padding-inline-start: var(--sp-5); }
.ds-table thead th:last-child,
.ds-table tbody td:last-child,
.ds-table tfoot td:last-child,
.ds-table tfoot th:last-child { padding-inline-end: var(--sp-5); }
.ds-table tbody tr { transition: background-color var(--t-fast); }
.ds-table tbody tr:hover { background: var(--ground); }   /* no zebra, no lift */
.ds-table .ds-num, .ds-table .ds-money, .ds-table th.ds-num, .ds-table td.ds-num { text-align: end; }
.ds-table td.ds-num, .ds-table td.ds-money { font-weight: 500; }
.ds-table .ds-date-cell { color: var(--ink-muted); white-space: nowrap; }
.ds-table-row-flag { box-shadow: inset 3px 0 0 var(--brass); }  /* not a bg flood */
.ds-table-actions { display: flex; align-items: center; gap: var(--sp-1); justify-content: flex-end; }
/* the name column is a link in ink, brass only on hover */
.ds-table tbody td a:not(.btn):not(.ds-btn):not(.ds-icon-btn) { color: var(--ink); font-weight: 500; text-decoration: none; }
.ds-table tbody td a:not(.btn):not(.ds-btn):not(.ds-icon-btn):hover { color: var(--brass-ink); text-decoration: underline; }

/* --- 4.6 filter bar ------------------------------------------------------ */
.ds-filterbar {
  display: flex; align-items: flex-end; gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-5);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
}
.ds-filterbar .ds-field { flex: 1 1 180px; min-width: 0; }
.ds-filterbar .ds-field + .ds-field { margin-top: 0; }
.ds-filterbar-actions { display: flex; gap: var(--sp-2); margin-inline-start: auto; }
@media (max-width: 767.98px) {
  .ds-filterbar { align-items: stretch; padding: var(--sp-3) var(--sp-4); }
  .ds-filterbar .ds-field { flex-basis: calc(50% - var(--sp-2)); }
  .ds-filterbar-actions { margin-inline-start: 0; width: 100%; }
  .ds-filterbar-actions .ds-btn { flex: 1 1 auto; }
}
@media (max-width: 479.98px) { .ds-filterbar .ds-field { flex-basis: 100%; } }

.ds-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }
.ds-chip {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 28px;
  padding: 0 var(--sp-2) 0 var(--sp-3);
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--panel);
  font-size: var(--fs-small); color: var(--ink);
}

/* --- 4.6b grouped-date accordion rows (Vendite) --------------------------- */
.ds-list-head {
  border-start-start-radius: var(--r-card);
  border-start-end-radius: var(--r-card);
}
.ds-table tbody tr.group-header,
table.ds-table tbody tr.group-header {
  cursor: pointer;
  background-color: var(--panel-sunken) !important;   /* beats .bg-light */
  border-top: 1px solid var(--line);
}
.ds-table tbody tr.group-header:hover,
table.ds-table tbody tr.group-header:hover {
  background-color: var(--rail-hover-bg) !important;  /* beats .bg-light */
}
.ds-table tbody tr.group-header td { font-weight: 500; }
.ds-table tbody tr.group-header .toggle-arrow { color: var(--ink-muted); width: 32px; }
.ds-table tbody tr.group-header .toggle-arrow i { transition: transform var(--t-fast); display: inline-block; }
.ds-table tbody tr.group-header.is-open .toggle-arrow i { transform: rotate(90deg); }

/* --- 4.7 badges and chips ------------------------------------------------ *
   Pills only here. States are distinguished by weight and wording first;
   colour exists for money (gain / loss) and expiry (caution) alone.       */
.ds-badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  font-size: var(--fs-small); line-height: var(--lh-small); font-weight: 500;
  background: var(--neutral-chip-bg); color: var(--neutral-chip-ink);
  white-space: nowrap;
}
.ds-badge-success { background: var(--gain-chip-bg);    color: var(--gain-chip-ink); }
.ds-badge-warning { background: var(--caution-bg);      color: var(--caution-ink); }
.ds-badge-danger  { background: var(--loss-chip-bg);    color: var(--loss-chip-ink); }
.ds-badge-info    { background: var(--neutral-chip-bg); color: var(--neutral-chip-ink); }
.ds-badge-brass   { background: var(--brass-tint);      color: var(--brass-ink); }
/* role badges: one neutral chip, five roles told apart by their word */
.ds-badge-role    { background: var(--neutral-chip-bg); color: var(--neutral-chip-ink); border: 0; }

/* --- 4.8 alerts ---------------------------------------------------------- *
   An alert attaches to a surface. Its text is always --ink.               */
.ds-alert {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-6);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background: var(--panel);
  color: var(--ink);
}
.ds-alert > .ds-i { width: 18px; height: 18px; margin-top: 1px; }
.ds-alert-title { font-size: var(--fs-ui); line-height: var(--lh-ui); font-weight: 500; margin: 0 0 2px; }
.ds-alert-body  { font-size: var(--fs-ui); line-height: var(--lh-ui); margin: 0; }
.ds-alert-content { flex: 1 1 auto; min-width: 0; }
.ds-alert-info    { background: var(--panel-sunken);   border-color: var(--line); }
.ds-alert-info > .ds-i { color: var(--ink-muted); }
.ds-alert-success { background: var(--panel);          border-color: var(--line-strong); }
.ds-alert-success > .ds-i { color: var(--ink); }
.ds-alert-warning { background: var(--caution-bg);     border-color: color-mix(in srgb, var(--caution-ink) 25%, transparent); }
.ds-alert-warning > .ds-i { color: var(--caution-ink); }
.ds-alert-danger  { background: var(--danger-surface); border-color: var(--danger-line); }
.ds-alert-danger > .ds-i { color: var(--danger-ink); }
.ds-alert .ds-alert-body, .ds-alert .ds-alert-title, .ds-alert li { color: var(--ink); }

/* inline stat pair, used by the BEP strip */
.ds-statpair { display: flex; flex-wrap: wrap; gap: var(--sp-6); }
.ds-stat-label { font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: 500; color: var(--ink-muted); }
.ds-stat-value { font-size: var(--fs-h3); font-weight: 600; font-variant-numeric: tabular-nums lining-nums; color: var(--ink); }

/* --- 4.9 modals ---------------------------------------------------------- *
   Level 2: 10px, a --line-strong edge, the deep shadow. The footer is a
   sunken band that carries the consequence and the actions.               */
.ds-modal-content,
.modal-content {
  border-radius: var(--r-modal);
  border: 1px solid var(--line-strong);
  background: var(--panel);
  color: var(--ink);
  box-shadow: var(--e-overlay);
  overflow: hidden;
}
.ds-modal-head { display: flex; align-items: flex-start; gap: var(--sp-6); padding: var(--sp-6) var(--sp-6) var(--sp-5); border-bottom: 1px solid var(--line); }
.ds-modal-title { flex: 1 1 auto; font-size: var(--fs-h2); line-height: var(--lh-h2); font-weight: 600; margin: 0; }
.ds-modal-body { padding: var(--sp-6); }
.ds-modal-foot {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-6);
  background: var(--panel-sunken);
  border-top: 1px solid var(--line-strong);
}
.modal-content > .modal-header { padding: var(--sp-5) var(--sp-6); border-bottom: 1px solid var(--line); }
.modal-content > .modal-header .modal-title { font-size: var(--fs-h2); line-height: var(--lh-h2); font-weight: 600; color: var(--ink); }
.modal-content > .modal-body { padding: var(--sp-6); }
.modal-content > .modal-footer { padding: var(--sp-4) var(--sp-6); background: var(--panel-sunken); border-top: 1px solid var(--line-strong); gap: var(--sp-3); }
.modal-content > .modal-footer > * { margin: 0; }
.modal-backdrop { --bs-backdrop-bg: #171B1F; --bs-backdrop-opacity: .44; }
:root[data-theme="dark"] .modal-backdrop { --bs-backdrop-bg: #000; --bs-backdrop-opacity: .6; }

/* --- 4.10 toasts --------------------------------------------------------- *
   Non-chromatic: the confirmation is the event, not a green tick.          */
.ds-toast-region {
  position: fixed; inset-block-end: var(--sp-6); inset-inline-end: var(--sp-6);
  z-index: 1080;
  display: flex; flex-direction: column; gap: var(--sp-2);
  max-width: min(400px, calc(100vw - var(--sp-8)));
}
.ds-toast {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-card);
  background: var(--panel);
  color: var(--ink);
  box-shadow: var(--e-raised);
  font-size: var(--fs-ui); line-height: var(--lh-ui); font-weight: 500;
  animation: ds-toast-in var(--t-panel);
}
@keyframes ds-toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.ds-toast-icon { display: inline-flex; flex: none; }
.ds-toast-success .ds-toast-icon { color: var(--ink); }
.ds-toast-danger  .ds-toast-icon { color: var(--danger-ink); }
.ds-toast-info    .ds-toast-icon { color: var(--ink-muted); }
.ds-toast .ds-icon-btn { width: 28px; height: 28px; }

/* --- 4.11 empty + loading states ----------------------------------------- *
   The empty state sits inside the region it describes, under a real header
   row, so the columns still say what the screen will hold.                 */
.ds-empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--sp-4);
  padding: var(--sp-18) var(--sp-10);
}
.ds-empty-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-card);
  background: var(--panel-sunken);
  color: var(--ink-muted);
}
.ds-empty-mark .ds-i { width: 22px; height: 22px; }
.ds-empty-title { font-size: var(--fs-h2); line-height: var(--lh-h2); font-weight: 600; margin: 0; color: var(--ink); }
.ds-empty-body { font-size: var(--fs-body); line-height: var(--lh-body); color: var(--ink-muted); margin: 0 auto; max-width: 52ch; text-wrap: pretty; }
.ds-empty-actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); justify-content: center; }

/* Empty cells inside a table — DataTables' emptyTable / zeroRecords text and
   Blade @forelse..@empty rows both land in a single <td>. Styling the cell
   gives every list a real empty state without touching one string or one line
   of markup. The two cases stay distinguishable because DataTables already
   uses different copy for "nothing here yet" and "no results". */
td.dt-empty,
td.dataTables_empty,
.ds-empty-cell {
  padding: var(--sp-14) var(--sp-10) !important;  /* beats the page `table td` rules */
  text-align: center !important;                  /* beats page `table td{text-align}` */
  color: var(--ink-muted) !important;             /* beats page `table td{color}`      */
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  background: var(--panel) !important;            /* beats striped-row backgrounds     */
}
td.dt-empty::before,
td.dataTables_empty::before,
.ds-empty-cell::before {
  content: "";
  display: block;
  width: 44px; height: 44px;
  margin: 0 auto var(--sp-4);
  box-sizing: border-box;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-card);
  background-color: var(--panel-sunken);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' viewBox='0 0 24 24' fill='none' stroke='%235C666E' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 12h-6l-2 3h-4l-2-3H2'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
}
:root[data-theme="dark"] td.dt-empty::before,
:root[data-theme="dark"] td.dataTables_empty::before,
:root[data-theme="dark"] .ds-empty-cell::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' viewBox='0 0 24 24' fill='none' stroke='%2393A0A8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 12h-6l-2 3h-4l-2-3H2'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E");
}
/* Empty cells are the one case that stays centred, so they have to outrank
   the left-alignment rule in 5.6d. */
.ds-table tbody td.ds-empty-cell,
table.ds-table tbody td.ds-empty-cell,
.ds-table tbody td.dt-empty,
table.ds-table tbody td.dt-empty,
.ds-table tbody td.dataTables_empty,
table.ds-table tbody td.dataTables_empty { text-align: center !important; }

/* DataTables does not support colspan in a DOM-sourced tbody: it pads the row
   out with empty cells of its own. Hiding the padding cells is presentation
   only - the row still spans the table because the real cell keeps its colspan. */
tr:has(> td.ds-empty-cell[colspan]) > td:not(.ds-empty-cell),
tr:has(> td.dt-empty[colspan]) > td:not(.dt-empty),
tr:has(> td.dataTables_empty[colspan]) > td:not(.dataTables_empty) { display: none; }

/* an action offered under an empty cell */
.ds-empty-cell .ds-btn { margin-top: var(--sp-4); }

/* Panels whose empty message is a bare <p> rather than a table cell. */
.ds-empty-note {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--sp-3);
  padding: var(--sp-10) var(--sp-6);
  color: var(--ink-muted);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}
.ds-empty-note::before {
  content: "";
  width: 44px; height: 44px;
  box-sizing: border-box;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-card);
  background-color: var(--panel-sunken);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' viewBox='0 0 24 24' fill='none' stroke='%235C666E' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 3v18h18'/%3E%3Cpath d='m19 9-5 5-4-4-3 3'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
}
:root[data-theme="dark"] .ds-empty-note::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' viewBox='0 0 24 24' fill='none' stroke='%2393A0A8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 3v18h18'/%3E%3Cpath d='m19 9-5 5-4-4-3 3'/%3E%3C/svg%3E");
}

/* Loading: geometry held, no shimmer. A placeholder is a flat sunken shape
   of exactly the size the real content will take. */
.ds-skeleton {
  background: var(--panel-sunken);
  border-radius: var(--r-input);
}
.ds-skeleton-line { height: 12px; margin-bottom: var(--sp-2); }
.ds-skeleton-kpi  { height: 104px; border-radius: var(--r-card); }

.ds-spinner {
  width: 12px; height: 12px;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%;
  animation: ds-spin .6s linear infinite;
}
@keyframes ds-spin { to { transform: rotate(360deg); } }

/* --- 4.12 charts (appearance only) --------------------------------------- *
   Every chart sits in an identical panel on one fixed geometry, drawn from
   one ordered ramp with brass on the leading series (--chart-1..5). The
   colours are handed to ApexCharts by assets/js/ds-charts.js.              */
.ds-chart { min-height: 240px; }
.ds-legend { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-3); }
.ds-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-small); line-height: var(--lh-small); color: var(--ink-muted); }
.ds-legend-dot { width: 10px; height: 10px; border-radius: 2px; flex: 0 0 auto; }

/* ApexCharts chrome, in tokens. Series fills are set in JS; everything the
   library draws around them follows the theme here. */
.apexcharts-canvas { font-family: var(--font-sans) !important; }       /* beats ApexCharts inline font-family */
.apexcharts-text,
.apexcharts-xaxis-label,
.apexcharts-yaxis-label { fill: var(--ink-muted); }
.apexcharts-title-text,
.apexcharts-datalabel-value { fill: var(--ink); }
.apexcharts-gridline,
.apexcharts-xaxis-tick,
.apexcharts-grid-borders line,
.apexcharts-xaxis line { stroke: var(--chart-grid); }
.apexcharts-legend-text { color: var(--ink-muted) !important; font-family: var(--font-sans) !important; font-size: var(--fs-small) !important; }  /* beats ApexCharts inline style */
.apexcharts-legend-marker { border-radius: 2px !important; }            /* beats the inline round marker */
.apexcharts-tooltip.apexcharts-theme-light {
  border: 1px solid var(--line-strong) !important;                      /* beats the library's own border */
  background: var(--panel) !important;
  color: var(--ink);
  box-shadow: var(--e-raised) !important;
  border-radius: var(--r-input);
}
.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {
  background: var(--panel-sunken) !important;
  border-bottom: 1px solid var(--line) !important;
  font-family: var(--font-sans) !important;
}
.apexcharts-tooltip-text, .apexcharts-tooltip-text-y-value { font-variant-numeric: tabular-nums lining-nums; }
.apexcharts-xaxistooltip, .apexcharts-yaxistooltip {
  background: var(--panel) !important; border-color: var(--line-strong) !important; color: var(--ink) !important;
}
.apexcharts-menu { background: var(--panel) !important; border-color: var(--line-strong) !important; border-radius: var(--r-input) !important; }
.apexcharts-menu-item:hover { background: var(--panel-sunken) !important; }
/* A donut's slices are drawn with no seam of page colour between them */
.apexcharts-pie-series path { stroke: var(--panel); }
.apexcharts-pie-label { fill: var(--on-band) !important; font-weight: 500 !important; }
.apexcharts-datalabels-group .apexcharts-datalabel-label { fill: var(--ink-muted) !important; }
.apexcharts-datalabels-group .apexcharts-datalabel-value { fill: var(--ink) !important; font-weight: 600 !important; }


/* --- 4.13 auth / login --------------------------------------------------- */
.ds-auth-body { background: var(--ground); }
.ds-auth { display: flex; min-height: 100vh; }

.ds-auth-brand {
  display: none;
  flex: 1 1 50%;
  background: var(--band);
  color: var(--on-band);
  padding: var(--sp-16) var(--sp-12);
  border-inline-end: 1px solid var(--band-line);
}
@media (min-width: 992px) { .ds-auth-brand { display: flex; align-items: center; } }
.ds-auth-brand-inner { max-width: 440px; margin-inline: auto; }
.ds-auth-logo { max-width: 280px; height: auto; margin-bottom: var(--sp-10); border-radius: var(--r-input); }
.ds-auth-tagline {
  font-size: var(--fs-h2); line-height: var(--lh-h2);
  font-weight: 500;
  color: var(--on-band);
  margin: 0;
  text-wrap: pretty;
}
.ds-auth-tagline::before {
  content: "";
  display: block;
  width: 32px; height: 3px;
  margin-bottom: var(--sp-5);
  background: var(--brass);
}

.ds-auth-panel {
  flex: 1 1 50%;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-8) var(--sp-6);
  background: var(--ground);
}
.ds-auth-form {
  width: 100%; max-width: 440px;
  padding: var(--sp-8);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
}
@media (max-width: 479.98px) { .ds-auth-form { padding: var(--sp-6) var(--sp-5); } }
.ds-auth-logo-mobile { display: block; margin-bottom: var(--sp-6); }
.ds-auth-logo-mobile img { max-height: 56px; width: auto; border-radius: var(--r-input); }
@media (min-width: 992px) { .ds-auth-logo-mobile { display: none; } }

.ds-auth-title { margin: 0 0 var(--sp-2); }
.ds-auth-sub { margin: 0 0 var(--sp-6); color: var(--ink-muted); font-size: var(--fs-body); line-height: var(--lh-body); }

.ds-input-lg { min-height: 44px; }
.ds-password-wrap { position: relative; }
.ds-password-toggle {
  position: absolute; inset-inline-end: var(--sp-3); top: 50%;
  transform: translateY(-50%);
  cursor: pointer; color: var(--ink-muted);
  line-height: 1;
}
.ds-password-toggle:hover { color: var(--ink); }
.ds-password-toggle:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: var(--r-input); }
.ds-password-wrap .ds-input { padding-inline-end: var(--sp-10); }

.ds-auth-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap;
  margin: var(--sp-5) 0 var(--sp-6);
}
.ds-auth-link { font-size: var(--fs-ui); color: var(--brass-ink); text-decoration: underline; text-underline-offset: 2px; }
.ds-auth-link:hover { color: var(--ink); }

/* --- 4.14 state screens: 403, no permission ------------------------------ */
.ds-state {
  display: flex; align-items: center; justify-content: center;
  min-height: calc(100vh - var(--topbar-h) - var(--sp-16));
  padding: var(--sp-8);
}
.ds-state-inner { max-width: 440px; text-align: center; }
.ds-state-inner .ds-empty-mark { margin: 0 auto; }
.ds-state-title { margin: var(--sp-5) 0 0; font-size: var(--fs-h2); line-height: var(--lh-h2); font-weight: 600; color: var(--ink); }
.ds-state-body { margin: var(--sp-3) 0 0; font-size: var(--fs-body); line-height: var(--lh-body); color: var(--ink-muted); text-wrap: pretty; }
.ds-state-actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); justify-content: center; margin-top: var(--sp-5); }

/* --- 4.15 keyboard shortcuts dialog -------------------------------------- */
.ds-shortcuts[hidden] { display: none; }
.ds-shortcuts {
  position: fixed; inset: 0; z-index: 1090;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-8);
  background: rgba(23, 27, 31, .44);
}
:root[data-theme="dark"] .ds-shortcuts { background: rgba(0, 0, 0, .6); }
.ds-shortcuts-dialog {
  width: 640px; max-width: 100%; max-height: 100%;
  overflow: auto;
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-modal);
  box-shadow: var(--e-overlay);
  color: var(--ink);
}
.ds-shortcuts-cols { display: flex; flex-wrap: wrap; }
.ds-shortcuts-col { flex: 1 1 300px; min-width: 0; }
.ds-shortcuts-col + .ds-shortcuts-col { border-inline-start: 1px solid var(--line); }
.ds-shortcuts-colhead {
  padding: var(--sp-3) var(--sp-6);
  background: var(--panel-sunken);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: 500; color: var(--ink-muted);
}
.ds-shortcuts-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  padding: 10px var(--sp-6);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-ui); line-height: var(--lh-ui);
}
.ds-shortcuts-keys { display: flex; gap: var(--sp-1); flex: none; }
.ds-shortcuts-pending {
  position: fixed; inset-block-end: var(--sp-6); inset-inline-start: 50%;
  transform: translateX(-50%);
  z-index: 1085;
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-input);
  box-shadow: var(--e-raised);
  font-size: var(--fs-small); color: var(--ink-muted);
}
.ds-shortcuts-pending[hidden] { display: none; }

/* =============================================================================
   5. VENDOR OVERRIDES
   -----------------------------------------------------------------------------
   Every !important in this file is below this line, and each names what it
   beats. Nothing here changes a value, a string, or a query.
   ========================================================================== */

/* --- 5.1 defect F1: horizontal clipping ---------------------------------- *
   Root cause (measured, not assumed): WowDash sets `overflow-x: hidden` on
   <body> at style.css:385. Wide content is therefore CLIPPED and unreachable
   rather than scrollable — e.g. /comparison renders a 615px-wide table inside
   a 390px viewport with 225px permanently cut off.
   Fix: let the page scroll instead of amputating content. Individual wide
   blocks still get their own .ds-table-scroll container.                     */
body {
  overflow-x: visible !important;        /* beats WowDash style.css:385       */
}

/* --- 5.2 typography ------------------------------------------------------ *
   `* { font-family: ... !important }` was removed from app.blade.php (D1).
   WowDash's own body rule already asks for Inter; these keep headings and
   controls on the system stack rather than the template's serif fallbacks.   */
body, .dashboard-main, .sidebar,
h1, h2, h3, h4, h5, h6,
.btn, .form-control, .form-select, table, input, select, textarea, button {
  font-family: var(--font-sans);
}

/* --- 5.2b heading scale -------------------------------------------------- *
   WowDash sets `h1..h6, .h1..h6 { font-size: var(--hN) !important }` where the
   custom properties are viewport-scaled clamps — --h1 reaches 4.5rem. Nothing
   short of !important can beat that, so the design-system heading classes
   restate their sizes here.
   Deliberately scoped to the ds- classes: bare <h1> on pages not yet
   redesigned keeps WowDash's sizing until that page is done, so this cannot
   silently restyle the whole app mid-project.                                */
.ds-display { font-size: var(--fs-display) !important; }
.ds-h1      { font-size: var(--fs-h1) !important; }
.ds-h2      { font-size: var(--fs-h2) !important; }
.ds-h3      { font-size: var(--fs-h3) !important; }
.ds-page-title  { font-size: var(--fs-h1) !important; }
.ds-card-title  { font-size: var(--fs-h3) !important; }
.ds-modal-title { font-size: var(--fs-h2) !important; }
.ds-kpi-value   { font-size: var(--fs-figure-lg) !important; }
.ds-empty-title { font-size: var(--fs-h2) !important; }
.ds-state-title { font-size: var(--fs-h2) !important; }
.ds-band-title  { font-size: var(--fs-h3) !important; }
.ds-stat-value  { font-size: var(--fs-h3) !important; }
.ds-figure-xl   { font-size: var(--fs-figure-xl) !important; }
.ds-figure-lg   { font-size: var(--fs-figure-lg) !important; }
.modal-content > .modal-header .modal-title { font-size: var(--fs-h2) !important; }

/* --- 5.2c checkboxes and radios ------------------------------------------ *
   The vendor reset strips the native control's appearance; restore a visible
   box so "Ricordati di me" and every table check are actually seeable.       */
.ds-check input[type="checkbox"],
.ds-check input[type="radio"] {
  appearance: auto !important;           /* beats the vendor appearance reset */
  -webkit-appearance: auto !important;
  width: 18px; height: 18px;
  margin: 0;
  accent-color: var(--fill-strong);      /* a checked box is ink in both themes */
  flex: 0 0 auto;
}

/* The views use Bootstrap's .form-check markup rather than .ds-check, so they
   never picked up the rule above. Bootstrap draws its own control with
   appearance:none and a background-image tick in its own blue, and it follows
   data-bs-theme, which this app does not set - so in dark mode a checked radio
   came out a pale blue square that read as neither on nor off. A native control
   with accent-color tracks the theme and stays legible in both.               */
.form-check-input,
.ds-check-input {
  appearance: auto;
  -webkit-appearance: auto;
  width: 18px; height: 18px;
  accent-color: var(--fill-strong);
  border: 0;
  box-shadow: none;
}
.form-check-input:focus-visible,
.ds-check-input:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* a switch keeps Bootstrap's drawn track, in ink */
.form-switch .form-check-input {
  appearance: none; -webkit-appearance: none;
  width: 2.25em; height: 1.25em;
  border: 1px solid var(--line-strong);
  background-color: var(--panel-sunken);
}
.form-switch .form-check-input:checked { background-color: var(--fill-strong); border-color: var(--fill-strong); }

/* --- 5.2d panel headers on Bootstrap cards ------------------------------- *
   WowDash styles `.card .card-header { background-color: var(--white) }` at
   specificity 0,2,0, which outranks a single .ds-card-head-* class. When a dark
   header lost that fight it kept the white background but still got the white
   title — invisible text, the same failure mode as defect #2. Matching the
   vendor's specificity instead of reaching for !important.                    */
.card > .card-header.ds-card-head,
.card .card-header.ds-card-head {
  padding: var(--sp-4) var(--sp-5);
  border-color: var(--border);
  color: var(--text);
}
.card > .card-header.ds-card-head-light,
.card .card-header.ds-card-head-light {
  background-color: var(--surface-alt);
  color: var(--text);
}
.card > .card-header.ds-card-head-quiet,
.card .card-header.ds-card-head-quiet {
  background-color: transparent;
  border-color: var(--line);
  color: var(--ink);
}

/* .ds-card on a Bootstrap .card: flatten the vendor shadow, keep our border. */
.card.ds-card {
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  box-shadow: none;
}

/* --- 5.3 Bootstrap buttons (fixes F4) ------------------------------------ *
   23 raw .btn-primary render as Bootstrap blue next to brand brass. Re-point
   Bootstrap's primary at the brand navy so one button system reads across the
   app without touching a single markup class.                                */
/* Laboratorio: Bootstrap primary is the strong neutral fill (apply / filter).
   Brass stays for the one action a page exists for. */
.btn-primary {
  background-color: var(--fill-strong) !important;   /* beats .btn-primary bg     */
  border-color: var(--fill-strong) !important;
  color: var(--on-fill-strong) !important;
}
/* hover/focus/active: same fight as above, beats Bootstrap .btn-primary */
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
  background-color: var(--fill-strong-hover) !important;
  border-color: var(--fill-strong-hover) !important;
  color: var(--on-fill-strong) !important;
}
/* beats Bootstrap's own blue focus ring */
.btn-primary:focus-visible { box-shadow: none !important; outline: 2px solid var(--focus); outline-offset: 2px; }

/* Every Bootstrap button shares the system's geometry. */
.btn {
  border-radius: var(--r-input);
  font-family: var(--font-sans);
  font-weight: 500;
}
.ds-page .btn:not(.btn-sm):not(.btn-lg):not(.btn-link) { min-height: 36px; }
.btn:focus { box-shadow: none; }

/* --- 5.3b page-level brass table headers --------------------------------- *
   Several views carry a page <style> block with
   `table th { background-color:#e2ae76 !important; color:#041930 !important }`,
   which paints every table header solid brass. §6 wants a quiet --surface-alt
   header with muted text so the data leads, not the chrome. Scoped to .ds-table
   so tables not yet migrated keep their current look until their page is done. */
.ds-table thead th,
table.ds-table thead th {
  background-color: var(--panel-sunken) !important;  /* beats page-level !important */
  color: var(--ink-muted) !important;
  font-size: var(--fs-caption) !important;           /* beats page `font-size` + `fw-bold` */
  line-height: var(--lh-caption);
  font-weight: 500 !important;
  border-bottom: 1px solid var(--line);
}
/* a page's own <thead style="background:#e2ae76"> sits under the cells */
.ds-table > thead,
table.ds-table > thead { background-color: var(--panel-sunken) !important; }  /* beats the inline thead background */
/* the same blocks centre every cell; numbers belong on the right */
.ds-table td.ds-num, .ds-table th.ds-num,
table.ds-table td.ds-num, table.ds-table th.ds-num {
  text-align: end !important;                       /* beats `table td{text-align:center}` */
}

/* --- 5.4 DataTables restyle (appearance only; no init option touched) ----- *
   Laboratorio's table archetype is ONE region with three parts — control
   row, table, pagination — inside a single border. The views already put
   each DataTable in a bordered card; these rules drop the card body's and
   the scroll wrapper's padding and let the DataTables rows pad and rule
   themselves, so the card edge becomes the region edge.                    */
.dataTables_wrapper, div.dt-container { font-size: var(--fs-ui); color: var(--ink); }

.card-body:has(> .ds-table-scroll:only-child > .dt-container),
.card-body:has(> .table-responsive:only-child > .dt-container),
.card-body:has(> .dt-container:only-child) {
  padding: 0 !important;                 /* beats .px-4 / .p-3 on the card body */
}
.card-body > .ds-table-scroll:only-child:has(> .dt-container),
.card-body > .table-responsive:only-child:has(> .dt-container) {
  padding: 0 !important;                 /* beats .p-3 on the scroll wrapper   */
  margin: 0 !important;
  background: none;                      /* the table cell scrolls, not this   */
}
/* the region look applies only where the table fills its card */
div.dt-container > div.dt-layout-row {
  margin: 0;
  padding: var(--sp-3) var(--sp-5);
  gap: var(--sp-3);
  flex-wrap: wrap;
}
div.dt-container > div.dt-layout-row.dt-layout-table { padding: 0; }
div.dt-container > div.dt-layout-row:first-child:not(.dt-layout-table) { border-bottom: 1px solid var(--line); }
div.dt-container > div.dt-layout-row:last-child:not(.dt-layout-table) { border-top: 1px solid var(--line); }
div.dt-container div.dt-layout-cell { padding: 0; }
/* the table owns its scroll inside the region */
div.dt-container .dt-layout-cell.dt-layout-table,
div.dt-container .dt-layout-row.dt-layout-table > .dt-layout-cell { overflow-x: auto; }
div.dt-container table.dataTable { margin: 0 !important; }   /* beats DT's own .5em margins */
/* the region border already bounds the table; no second frame */
table.ds-table.table-bordered > :not(caption) > *,
table.dataTable.table-bordered > :not(caption) > * { border-width: 0; }

/* toolbar: length select + search on one line */
div.dt-container .dt-length,
div.dt-container .dt-search,
.dataTables_length, .dataTables_filter { display: flex; align-items: center; gap: var(--sp-2); }
.dataTables_filter { justify-content: flex-end; }

div.dt-container select,
.dataTables_length select {
  min-height: 36px; padding: 0 var(--sp-6) 0 var(--sp-3);
  border: 1px solid var(--line); border-radius: var(--r-input);
  background-color: var(--panel); color: var(--ink);
  font-size: var(--fs-ui); font-weight: 500;
}
/* search: a leading magnifier drawn as a background image */
div.dt-container input[type="search"],
.dataTables_filter input {
  min-height: 36px;
  padding: 0 var(--sp-3) 0 36px;
  border: 1px solid var(--line); border-radius: var(--r-input);
  background-color: var(--panel); color: var(--ink);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 20 20' fill='none' stroke='%235C666E' stroke-width='1.5'%3E%3Ccircle cx='9' cy='9' r='6'/%3E%3Cline x1='13.5' y1='13.5' x2='17' y2='17'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: left var(--sp-3) center;
  background-size: 16px 16px;
  font-size: var(--fs-ui);
}
:root[data-theme="dark"] div.dt-container input[type="search"],
:root[data-theme="dark"] .dataTables_filter input {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 20 20' fill='none' stroke='%2393A0A8' stroke-width='1.5'%3E%3Ccircle cx='9' cy='9' r='6'/%3E%3Cline x1='13.5' y1='13.5' x2='17' y2='17'/%3E%3C/svg%3E");
}
div.dt-container input[type="search"]::placeholder { color: var(--ink-disabled); }
div.dt-container input[type="search"]:hover,
div.dt-container select:hover { border-color: var(--line-strong); }
div.dt-container input[type="search"]:focus,
.dataTables_filter input:focus {
  outline: 2px solid var(--focus); outline-offset: 2px; border-color: var(--line-strong); box-shadow: none;
}

/* table body */
table.dataTable { border-collapse: collapse !important; }  /* beats DT's separate-borders reset */
table.dataTable thead th {
  height: 36px;
  padding: 8px var(--sp-4);
  background: var(--panel-sunken);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: 500; color: var(--ink-muted);
}
table.dataTable tbody td {
  height: 44px;
  padding: 10px var(--sp-4);
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  font-size: var(--fs-body);
  line-height: var(--lh-figure);
  vertical-align: middle;
}
table.dataTable thead th:first-child,
table.dataTable tbody td:first-child { padding-inline-start: var(--sp-5); }
table.dataTable thead th:last-child,
table.dataTable tbody td:last-child { padding-inline-end: var(--sp-5); }
table.dataTable tbody tr { background-color: transparent; }
table.dataTable tbody tr:hover > * {
  box-shadow: none !important;           /* beats DT's inset row-hover shadow */
  background-color: var(--ground);
}
table.dataTable.stripe tbody tr.odd > *,
table.dataTable.display tbody tr.odd > * {
  box-shadow: none !important;           /* beats DT zebra striping (§6)      */
}
table.dataTable.table-hover > tbody > tr:hover > * {
  --bs-table-bg-state: var(--ground);    /* Bootstrap's hover rides this var  */
  --bs-table-accent-bg: transparent;
}

/* pagination: 32px squares, the current page in ink */
div.dt-container .dt-paging .dt-paging-button,
.dataTables_paginate .paginate_button {
  min-width: 32px; height: 32px;
  padding: 0 var(--sp-2) !important;     /* beats DT's .5em/1em padding       */
  margin: 0 !important;                  /* the flex gap spaces them          */
  border: 1px solid var(--line) !important;
  border-radius: var(--r-input) !important;
  background: var(--panel) !important;   /* beats DT's gradient background    */
  color: var(--ink) !important;          /* beats DT's !important colour      */
  font-size: var(--fs-ui);
  font-weight: 500;
  line-height: 30px;
}
/* hover state: beats the DataTables gradient background and its !important colour */
div.dt-container .dt-paging .dt-paging-button:hover,
.dataTables_paginate .paginate_button:hover {
  background: var(--ground) !important;
  border-color: var(--line-strong) !important;
  color: var(--ink) !important;
}
/* current page: beats the DataTables gradient background and its !important colour */
div.dt-container .dt-paging .dt-paging-button.current,
.dataTables_paginate .paginate_button.current,
div.dt-container .dt-paging .dt-paging-button.current:hover {
  background: var(--fill-strong) !important;
  border-color: var(--fill-strong) !important;
  color: var(--on-fill-strong) !important;
}
div.dt-container .dt-paging .dt-paging-button.disabled,
.dataTables_paginate .paginate_button.disabled,
div.dt-container .dt-paging .dt-paging-button.disabled:hover {
  background: var(--panel) !important;   /* beats the hover rule above        */
  border-color: var(--line) !important;
  color: var(--ink-disabled) !important;
  pointer-events: none;
  opacity: 1;
}
div.dt-container .dt-paging .ellipsis { color: var(--ink-disabled); padding: 0 var(--sp-1); }
div.dt-container .dt-info, .dataTables_info {
  font-size: var(--fs-small); line-height: var(--lh-small); color: var(--ink-muted);
  padding-top: 0;
  font-variant-numeric: tabular-nums lining-nums;
}

/* DataTables wrappers must scroll, not clip */
div.dt-container .dt-layout-cell.dt-layout-table { overflow-x: auto; }

/* --- 5.5 inline clip hacks in two views ---------------------------------- *
   ingredients/index.blade.php:83 and user-management/users/index.blade.php:72
   carry style="overflow-x: hidden" on .card-body, which hard-clips the
   DataTable with no scrollbar. The inline attribute can only be beaten by
   !important. Markup left untouched.                                         */
.card-body[style*="overflow-x"] {
  overflow-x: auto !important;           /* beats the inline style attribute  */
}

/* --- 5.6 Bootstrap surfaces in dark theme -------------------------------- */
:root[data-theme="dark"] .card,
:root[data-theme="dark"] .modal-content,
:root[data-theme="dark"] .dropdown-menu {
  background-color: var(--surface);
  border-color: var(--border);
  color: var(--text);
}
:root[data-theme="dark"] .table { --bs-table-color: var(--text); --bs-table-bg: transparent; }
:root[data-theme="dark"] .form-control,
:root[data-theme="dark"] .form-select {
  background-color: var(--surface);
  border-color: var(--border);
  color: var(--text);
}
:root[data-theme="dark"] .text-dark { color: var(--text) !important; }  /* beats Bootstrap utility */
:root[data-theme="dark"] .bg-white,
:root[data-theme="dark"] .bg-light { background-color: var(--surface-alt) !important; }  /* beats Bootstrap utility */

/* =============================================================================
   5.6b CONTRAST REMEDIATION (§11)
   -----------------------------------------------------------------------------
   Every rule below closes a pair measured under 4.5:1 (or 3:1 for large text)
   by a WCAG audit across 16 pages in both themes. Each carries its measured
   ratio so the finding can be re-checked.
   ========================================================================== */

/* 2.52:1 - sidebar nav labels. WowDash sets
   `.sidebar-menu li a { color: var(--text-secondary-light) }` at 0,1,3, which
   outranks .ds-nav-link (0,1,0). Only the ACTIVE item was legible, because
   .is-active/.active-page reach 0,2,0 and won. Matching the vendor's
   specificity instead of using !important. */
/* (The rail's item colours now live in 3.1 at the same (0,3,2) specificity:
   --ink-muted at rest measures 5.9:1 on the sunken rail, --ink on hover and
   on the active row.) */
.sidebar .sidebar-menu li a.ds-nav-link .ds-nav-label { color: inherit; }

/* 1.13 - 1.19:1 - a Bootstrap .card-header keeps WowDash's --white background
   in dark theme while its title takes the dark-theme --text, so the heading
   vanished. Give un-tokenised headers a dark-theme surface. */
:root[data-theme="dark"] .card .card-header:not(.ds-card-head-quiet) {
  background-color: var(--surface-alt);
  border-color: var(--border);
  color: var(--text);
}
:root[data-theme="dark"] .card .card-header:not(.ds-card-head-quiet) .ds-card-title { color: var(--text); }

/* 1.13:1 and 3.73:1 - Bootstrap's muted/secondary greys are light-theme values
   and are unreadable on dark surfaces. */
:root[data-theme="dark"] .text-muted,
:root[data-theme="dark"] .text-secondary,
:root[data-theme="dark"] .text-secondary-light,
:root[data-theme="dark"] small.text-muted,
:root[data-theme="dark"] .form-text { color: var(--text-muted) !important; }  /* beats .text-muted's own !important */

/* 3.56 - 3.86:1 - Bootstrap's success/danger are tuned for white and fall under
   4.5:1 on our dark surfaces. The tokens already carry lifted dark values. */
:root[data-theme="dark"] .text-success { color: var(--success-600) !important; }
:root[data-theme="dark"] .text-danger  { color: var(--danger-600) !important; }
:root[data-theme="dark"] .text-warning { color: var(--warning-600) !important; }
:root[data-theme="dark"] .text-info    { color: var(--info-600) !important; }
:root[data-theme="dark"] .text-dark,
:root[data-theme="dark"] .text-black   { color: var(--text) !important; }

/* 1.11:1 - Bootstrap subtle badges keep light-theme text on a light-theme fill. */
.badge.bg-primary-subtle, .badge.bg-info-subtle,
.badge.bg-success-subtle, .badge.bg-warning-subtle, .badge.bg-danger-subtle {
  background-color: var(--surface-alt) !important;   /* beats .bg-*-subtle */
  color: var(--text) !important;
  border: 1px solid var(--border);
}

/* 1.96:1 - Bootstrap ships .text-info as #0dcaf0, which is a cyan meant for
   dark surfaces. On white it is unreadable. The note under the "add this recipe
   as an ingredient" checkbox uses it. --info-600 is the same role at 4.9:1. */
.text-info { color: var(--ink-muted) !important; }   /* no info hue in Laboratorio */

/* A selected pill is a state, so it takes the strong neutral fill.
   Covers the month pills on the control dashboard. */
.month-tabs .nav-link.active,
.nav-pills .nav-link.active[style*="e2ae76"] { color: var(--on-fill-strong) !important; }  /* beats .nav-link.active */

/* 1.99:1 / 4.00:1 / 4.37:1 - page-level brand buttons using raw brass or pure
   red as TEXT.

   These three rules are retired. They fixed the text colour only, which was
   right while all three were transparent outline buttons, but 5.6m later gave
   the family a complete treatment - fill, text and border together - and the
   two collided: .btn-gold ended up with --brass-700 text on a --brass-400 fill
   at 2.83:1, because :not(:hover) makes these (0,2,0) and outranks 5.6m's
   `body .btn-gold` at (0,1,1). Colour and background have to be set by the
   same rule or they drift apart like this. See 5.6m. */
/* (The dark-theme .btn-deepblue override that stood here is retired: 5.6m's
   token-based secondary button reads correctly in both themes, and at
   (0,4,0) the override outranked the quiet row action in 5.6d, boxing the
   "view" icon in every dark table.) */

/* 1.01:1 - page-level headings that hardcode navy text and end up on a navy
   or dark-theme surface. */
:root[data-theme="dark"] .stat-title,
:root[data-theme="dark"] .card-mini-value,
:root[data-theme="dark"] .page-header-custom { color: var(--text) !important; }
/* 4.48:1 - #777 caption on white */
.card-mini-title { color: var(--text-muted) !important; }
/* 1.07:1 - month links hardcode #041930, invisible on the dark page ground */
:root[data-theme="dark"] .month-tabs .nav-link { color: var(--text) !important; }
:root[data-theme="dark"] .month-tabs .nav-link:hover { background: var(--surface-alt) !important; }

/* 1.20:1 - chart legend/label text is forced to #000 by a page rule, which
   disappears on dark. Appearance only; no series or value is touched. */
:root[data-theme="dark"] .apexcharts-legend-text,
:root[data-theme="dark"] .apexcharts-text,
:root[data-theme="dark"] .apexcharts-datalabel,
:root[data-theme="dark"] .apexcharts-datalabel-label,
:root[data-theme="dark"] .apexcharts-datalabel-value {
  fill: var(--text) !important;    /* beats the page's `fill:#000 !important` */
  color: var(--text) !important;
}

/* --- 5.6c surfaces that ignore the theme --------------------------------- *
   Second pass. The rules above corrected TEXT colour for dark mode, which then
   exposed the real problem underneath: a number of surfaces are hardcoded light
   (#fff, .bg-light, .bg-light-subtle) and never respond to the theme, so
   correct dark-theme text landed on a white panel. Fixing the SURFACE is the
   right layer - the text colour was already right.                            */
:root[data-theme="dark"] .bg-light,
:root[data-theme="dark"] .bg-light-subtle,
:root[data-theme="dark"] .bg-body-tertiary,
:root[data-theme="dark"] .bg-body-secondary,
:root[data-theme="dark"] .table-light,
:root[data-theme="dark"] thead.table-light {
  background-color: var(--surface-alt) !important;   /* beats the Bootstrap utility */
  color: var(--text) !important;
}
:root[data-theme="dark"] .card-mini,
:root[data-theme="dark"] .dashboard-container,
:root[data-theme="dark"] .filter-card,
:root[data-theme="dark"] .stat-card {
  background-color: var(--surface) !important;       /* beats page-level `background:#fff` */
  border-color: var(--border) !important;
  color: var(--text) !important;
}

/* 1.04:1 - an open submenu's active child gets a pale blue fill from the
   vendor, which our light rail text then sat on. Keep the rail's own palette. */
.sidebar .sidebar-menu .sidebar-submenu li a.ds-nav-link.active-page,
.sidebar .sidebar-menu .sidebar-submenu li a.ds-nav-link.is-active,
.sidebar .sidebar-menu li.active-page > a.ds-nav-link {
  background-color: var(--rail-active) !important;   /* beats the vendor's light fill */
  color: var(--rail-text-strong) !important;
}

/* The active month pill: a selected state, the strong neutral fill. The page
   block paints it tan, so this has to outrank it. */
.month-tabs .nav-link.active,
:root[data-theme="dark"] .month-tabs .nav-link.active,
.nav-pills .month-tabs .nav-link.active {
  background-color: var(--fill-strong) !important;
  color: var(--on-fill-strong) !important;
}

/* 2.34 - 2.40:1 - muted captions sitting on a panel that stays light. Use the
   light-theme muted token regardless of theme when the surface is light. */
.bg-light .text-muted, .bg-light .text-secondary,
.bg-light-subtle .text-muted, .bg-light-subtle .text-secondary,
.card-mini .card-mini-title,
:root[data-theme="dark"] .bg-light-subtle .text-muted,
:root[data-theme="dark"] .bg-light-subtle .text-secondary { color: #5C666E !important; }
:root[data-theme="dark"] .bg-light-subtle .text-muted,
:root[data-theme="dark"] .bg-light-subtle .text-secondary,
:root[data-theme="dark"] .card-mini .card-mini-title { color: var(--text-muted) !important; }

/* =============================================================================
   5.6d PREMIUM PASS
   -----------------------------------------------------------------------------
   Corrections found by reviewing the rendered result rather than the spec.
   Each one is a thing that still read as "admin template" after the token layer
   landed.
   ========================================================================== */

/* 1. Panel headers on Bootstrap cards. The matched-specificity rules further
      down still paint the old navy fill; point them at the quiet treatment so
      all 55 headers across 41 views follow. */
.card > .card-header.ds-card-head-quiet,
.card .card-header.ds-card-head-quiet {
  background-color: transparent;
  border-color: var(--border);
  color: var(--text);
}
.card .card-header.ds-card-head-quiet .ds-card-title { color: var(--text); }
.card .card-header.ds-card-head-quiet .ds-i { color: var(--text-subtle); }

/* 2. Table footers rendered as a WHITE bar in dark theme. `tfoot.table-light`
      and `.fw-bold.bg-light` totals rows were bleeding through and looked
      cheap. A totals row should read as part of the table, not a separate slab. */
.ds-table tfoot th, .ds-table tfoot td,
table.ds-table tfoot th, table.ds-table tfoot td,
.ds-table tr.fw-bold.bg-light td, .ds-table tr.fw-bold.bg-light th {
  background-color: var(--surface-alt) !important;   /* beats .table-light / .bg-light */
  color: var(--text) !important;
  border-top: 1px solid var(--border-strong);
  font-weight: 600;
}
:root[data-theme="dark"] .ds-table tfoot,
:root[data-theme="dark"] table.ds-table tfoot { background-color: var(--surface-alt); }

/* 3. Collapsed icon rail. WowDash hides the item labels at 86px but our GROUP
      headings kept rendering, so the rail showed "Panoramica / Economia /
      Produzione" as stray text above unlabelled icons. Hide them with the
      labels and centre the icons. */
.sidebar.active .ds-rail-user-meta,
.sidebar.active .ds-rail-academy .ds-nav-label { display: none; }
/* At 72 the group headings have nowhere to go: a divider replaces each. */
.sidebar.active .sidebar-menu .ds-rail-group-label {
  height: 1px;
  margin: var(--sp-3) var(--sp-4);
  padding: 0;
  overflow: hidden;
  font-size: 0;
  background: var(--line);
}
.sidebar.active .sidebar-menu .ds-rail-group-label:first-child { display: none; }
.sidebar.active .sidebar-menu li a.ds-nav-link { justify-content: center; padding-inline: 0; }
.sidebar.active .sidebar-menu li a.ds-nav-link.ds-rail-academy { margin-inline: 0; }
.sidebar.active .ds-nav-link .ds-nav-chevron { display: none; }
.sidebar.active .ds-rail-user { display: flex; justify-content: center; padding-inline: 0; }
.sidebar.active .ds-rail-user-btn { justify-content: center; width: auto; }
.sidebar.active .ds-rail-logo { padding-inline: var(--sp-2); justify-content: center; }

/* 4. Filter bars were stair-stepping: each field sized itself, so labels and
      inputs never shared a baseline. Give every control a common minimum and
      let them wrap as equal units. */
.ds-filterbar { align-items: end; row-gap: var(--sp-4); }
.ds-filterbar > .ds-field { flex: 1 1 200px; max-width: 100%; }
.ds-filterbar > .ds-field > .ds-field-label { min-height: 1.2em; }

/* 5. Vertical rhythm. The template left large dead gaps between blocks, which
      reads as unfinished rather than airy. Tighten the page stack and the
      default bottom margin Bootstrap puts under cards. */
.ds-page > .row + .row,
.ds-page > .ds-kpi-grid + .row { margin-top: var(--sp-2); }
.ds-page .card.ds-card { margin-bottom: var(--sp-5); }
.ds-page .mb-5 { margin-bottom: var(--sp-6) !important; }   /* beats Bootstrap's 3rem */
.ds-page .mb-4 { margin-bottom: var(--sp-5) !important; }   /* beats Bootstrap's 1.5rem */

/* 6. A KPI delta that is only a share of total ("100%", "0%") is not a delta.
      Present it as a quiet caption so it stops looking like a broken metric. */
.ds-kpi-delta.ds-kpi-delta-flat {
  background: transparent;
  padding-inline: 0;
  color: var(--text-muted);
}

/* 7. Tables were drawn as a GRID — every cell boxed by vertical rules, from
      Bootstrap's .table-bordered plus page-level `table td { border }`. A grid
      of boxes is the clearest "admin template" signal there is. Data tables in
      a finished product use horizontal hairlines only: the eye tracks along a
      row, and the columns are held by alignment, not by lines. */
.ds-table, table.ds-table,
.ds-table > :not(caption) > * > *,
table.ds-table > :not(caption) > * > * {
  border-left: 0 !important;      /* beats .table-bordered and page `table td` */
  border-right: 0 !important;
}
.ds-table tbody td, table.ds-table tbody td {
  border-top: 0 !important;       /* one hairline per row, on the bottom only */
  border-bottom: 1px solid var(--border) !important;
}
.ds-table tbody tr:last-child td { border-bottom: 0 !important; }

/* 8. Body text was centred in every column by page-level
      `table td { text-align: center }`. Centred text has no left edge to scan,
      so a column of names becomes a ragged blob. Text reads left, numbers read
      right; only genuinely narrow status columns stay centred. */
.ds-table tbody td, table.ds-table tbody td,
.ds-table thead th, table.ds-table thead th { text-align: start !important; }
.ds-table td.ds-num, .ds-table th.ds-num,
table.ds-table td.ds-num, table.ds-table th.ds-num,
.ds-table td.text-end, .ds-table th.text-end { text-align: end !important; }
.ds-table td.text-center, .ds-table th.text-center,
.ds-table .ds-table-actions { text-align: center !important; }
.ds-table .ds-table-actions { display: flex; justify-content: flex-end; }

/* 9. Cards carried Bootstrap contextual borders (.border-primary,
      .border-warning, .border-success) — a coloured outline round a whole
      panel is decoration with no meaning. One neutral hairline instead. */
.ds-card.border-primary, .ds-card.border-warning, .ds-card.border-success,
.ds-card.border-info, .ds-card.border-danger, .ds-card.border-secondary,
.card.ds-card[class*="border-"] { border-color: var(--border) !important; }

/* 10. Vendor shadows on cards. Elevation is border-first in this system; a
       drop shadow under every panel makes a page look like stacked paper. */
.ds-card.shadow-sm, .ds-card.shadow, .ds-card.shadow-lg { box-shadow: none !important; }

/* 11. Intro slabs. Several pages open with a filled navy strip repeating the
       page title and a line of instructions. A coloured slab whose only job is
       to say hello is the §4.6 ban-list item, and here it also states the title
       twice. Demoted to a quiet note that sits in the page rather than shouting
       above it. The strings are untouched — only the slab is gone. */
.ds-page .page-header.ds-on-dark,
.ds-page .page-header[style*="041930"] {
  background: transparent !important;        /* beats the inline background-color */
  border: 0;
  border-left: 2px solid var(--brass-500);
  border-radius: 0 !important;                 /* beats the inline border-radius and .rounded */
  padding: var(--sp-1) 0 var(--sp-1) var(--sp-4) !important;
  margin-bottom: var(--sp-5);
}
.ds-page .page-header.ds-on-dark .ds-card-title,
.ds-page .page-header[style*="041930"] .ds-card-title {
  color: var(--text) !important;             /* beats .ds-on-dark's white */
  font-size: .9375rem !important;
  font-weight: 600;
}
.ds-page .page-header.ds-on-dark small,
.ds-page .page-header.ds-on-dark .text-light,
.ds-page .page-header[style*="041930"] small,
.ds-page .page-header[style*="041930"] .text-light {
  color: var(--text-muted) !important;       /* beats .text-light */
  font-size: var(--fs-body-sm);
}
.ds-page .page-header.ds-on-dark .ds-i,
.ds-page .page-header[style*="041930"] .ds-i { color: var(--brass-600) !important; }

/* 12. Dashed and coloured ornament borders. A dashed outline reads as a
       placeholder, not a finished panel. */
.stat-card, .ds-page .stat-card {
  border: 1px solid var(--border) !important;   /* beats `1px dashed #e2ae76` */
  border-radius: var(--r-card);
  background: var(--surface);
  padding: var(--sp-4);
}
.stat-card .stat-title { color: var(--text-muted); font-weight: 500; font-size: var(--fs-label); }
.stat-card .stat-value { font-size: var(--fs-h2); font-weight: 600; font-variant-numeric: tabular-nums; color: var(--text); }

/* 13. Row actions were bordered boxes, several with inline brand colours and
       onmouseover handlers. §6 asks for 32px ghost icon buttons: quiet until
       you reach for them, and destructive only turns red on hover.

       This used a hard `width: 32px`, which is right for the icon-only actions
       on most index pages but wrong wherever the button also carries a label.
       On /labor-cost the "Modifica" and "Elimina" text overflowed its 32px box
       (overflow is visible) and the two labels printed on top of each other.
       min-width keeps the icon-only buttons square and lets a labelled one
       grow to fit, which also honours §6's rule against fixed-width buttons
       in a translated interface. */
.ds-table td .btn.btn-sm,
.ds-table td a.btn.btn-sm,
.ds-table td button.btn.btn-sm {
  min-width: 32px; height: 32px;
  padding: 0 var(--sp-2) !important;                  /* beats .btn-sm padding */
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--sp-2);
  white-space: nowrap;
  border: 1px solid transparent !important;           /* beats inline border */
  background: transparent !important;                 /* beats inline background */
  color: var(--text-muted) !important;                /* beats inline colour */
  border-radius: var(--r-input);
  box-shadow: none !important;
}
/* a labelled action reads as a control, not as bare text floating in the cell */
.ds-table td .btn.btn-sm:not(:has(> i:only-child)):not(:has(> svg:only-child)) {
  border-color: var(--border) !important;
}
/* the actions in one cell need to be separated; the markup puts a bare <form>
   between them, so the gap goes on the cell */
.ds-table td:has(> .btn.btn-sm + form),
.ds-table td:has(> form + .btn.btn-sm),
.ds-table td:has(> .btn.btn-sm + .btn.btn-sm) {
  white-space: nowrap;
}
.ds-table td .btn.btn-sm + .btn.btn-sm,
.ds-table td form + .btn.btn-sm,
.ds-table td .btn.btn-sm + form,
.ds-table td form + form { margin-inline-start: var(--sp-2); }
.ds-table td .btn.btn-sm:hover,
.ds-table td .btn.btn-sm:focus-visible { background: var(--panel-sunken) !important; color: var(--ink) !important; }
/* No chroma on any row of a healthy table: a delete icon lifts to ink like
   the others. The word "Elimina" and the danger fill belong to the confirm. */
.ds-table td .btn.btn-sm.btn-red:hover,
.ds-table td .btn.btn-sm.btn-outline-danger:hover,
.ds-table td .btn.btn-sm.btn-danger:hover,
.ds-table td .btn.btn-sm.ds-btn-danger:hover { background: var(--panel-sunken) !important; color: var(--ink) !important; }
.ds-table td .btn.btn-sm i { font-size: 16px; line-height: 1; }

/* =============================================================================
   5.6e SIDEBAR BREAKPOINT — the rail starts at 1024, not 1200
   -----------------------------------------------------------------------------
   WowDash only makes the sidebar persistent at >=1200px. Everything below that,
   including the whole 1024-1199 band, got the off-canvas DRAWER: the sidebar
   slid over the content instead of pushing it, and .dashboard-main kept
   margin-inline-start: 0, so the left edge of every page sat underneath it.
   1024-1199 is a desktop window (or a 1900px screen at 160% zoom, which is how
   this was reported), and §10 is explicit that the drawer is for below 1024.
   ========================================================================== */
@media (min-width: 1024px) {
  /* Laboratorio: one rail width, 264, at every desktop size — WowDash's 275
     and 312 widening steps are overridden too. The longer Italian labels
     ("Pareggio e manodopera", "Gestione Categorie") fit at 14px; §10 forbids
     truncating them, and they may wrap rather than clip. */
  .sidebar { inset-inline-start: 0; width: var(--rail-w); }
  .dashboard-main { margin-inline-start: var(--rail-w); }
  /* collapse buys the table 192px, which is what makes 1200–1399 usable */
  .sidebar.active { width: var(--rail-w-collapsed); }
  .dashboard-main.active { margin-inline-start: var(--rail-w-collapsed); }
  /* drawer-only controls have no job once the rail is persistent */
  .sidebar-close-btn,
  .sidebar-mobile-toggle { display: none !important; }   /* beats .ds-icon-btn display */
  body.overlay-active::after { display: none; }          /* no scrim behind a persistent rail */
}
@media (min-width: 1400px) {
  .sidebar { width: var(--rail-w); }
  .dashboard-main { margin-inline-start: var(--rail-w); }
  .sidebar.active { width: var(--rail-w-collapsed); }
  .dashboard-main.active { margin-inline-start: var(--rail-w-collapsed); }
}
@media (min-width: 1650px) {
  .sidebar { width: var(--rail-w); }
  .dashboard-main { margin-inline-start: var(--rail-w); }
  .sidebar.active { width: var(--rail-w-collapsed); }
  .dashboard-main.active { margin-inline-start: var(--rail-w-collapsed); }
}
/* Below 1024 it IS a drawer: make sure the scrim reads as one. */
@media (max-width: 1023.98px) {
  .sidebar { width: min(280px, 82vw); }
  body.overlay-active { overflow: hidden; }   /* stop the page scrolling behind it */
}

/* =============================================================================
   5.6f COLLAPSED RAIL — tooltips, not expand-on-hover
   -----------------------------------------------------------------------------
   WowDash expands the collapsed rail back to full width on hover
   (`.sidebar.active:hover { width: auto }`). Two things break:
     - the expanded panel is z-index 3 and the topbar is z-index 20, so the
       topbar's background painted over the top 203px of it;
     - the page content does not move, so a 275px panel appears over it and
       vanishes again as the pointer drifts.
   §5.1 asks for tooltips in rail mode instead, which is also the calmer
   pattern: the rail stays exactly 72px and never moves anything.
   ========================================================================== */
.sidebar.active,
.sidebar.active:hover { width: var(--rail-w-collapsed) !important; }  /* beats .sidebar.active:hover{width:auto} */
/* The rail is z-index 3 and the topbar is 20, so a tooltip escaping the rail
   was painted over by both the topbar and the page content. Lift the collapsed
   rail above them; it is only 72px wide and the topbar starts after it, so
   nothing is covered. */
.sidebar.active { z-index: 30; }

/* keep everything hidden on hover too - the vendor rules un-hide these */
.sidebar.active:hover .ds-rail-group-label,
.sidebar.active:hover .ds-rail-user-meta,
.sidebar.active:hover .ds-rail-academy .ds-nav-label,
.sidebar.active:hover .ds-nav-label,
.sidebar.active:hover .sidebar-menu li a span,
.sidebar.active:hover .ds-nav-chevron { display: none !important; }
.sidebar.active:hover .sidebar-menu li a.ds-nav-link { justify-content: center; padding-inline: 0; }
.sidebar.active:hover .sidebar-submenu { display: none !important; }
.sidebar.active .ds-rail-logo img { max-width: 52px; height: auto; object-fit: contain; }

/* CSS-only tooltip. data-tip is filled from the label text by the shell script,
   so no markup change and nothing to keep in sync. */
.sidebar.active .ds-nav-link { position: relative; }
.sidebar.active .ds-nav-link[data-tip]::after {
  content: attr(data-tip) !important;   /* beats the vendor chevron content */
  position: absolute;
  inset-inline-start: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%) translateX(-4px);
  z-index: 40;
  padding: 6px 10px;
  border-radius: var(--r-input);
  /* An ink chip in light; a raised panel with a --line-strong edge in dark. */
  background: var(--fill-strong);
  color: var(--on-fill-strong);
  border: 1px solid var(--fill-strong);
  font-size: var(--fs-caption);
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  /* Essential: the containing block is the 47px nav link, and with
     inset-inline-start: calc(100% + 10px) the space left for a shrink-to-fit
     box goes negative, so it collapsed to a single character (22px wide).
     max-content sizes it to the label instead. */
  width: max-content;
  box-shadow: var(--e-raised);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-fast), transform var(--t-fast);
}
.sidebar.active .ds-nav-link[data-tip]:hover::after,
.sidebar.active .ds-nav-link[data-tip]:focus-visible::after {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}
@media (prefers-reduced-motion: reduce) {
  .sidebar.active .ds-nav-link[data-tip]::after { transition: none; }
}
:root[data-theme="dark"] .sidebar.active .ds-nav-link[data-tip]::after {
  background: var(--surface-alt);
  border-color: var(--border-strong);
  color: var(--text);
}
/* the rail clips its own scroll area, so let a tooltip escape it */
.sidebar.active .ds-rail-scroll { overflow: visible; }
.sidebar.active .sidebar-menu { overflow: visible; }

/* =============================================================================
   5.6g ROW EDITORS — the line-item tables on the create/edit forms
   -----------------------------------------------------------------------------
   Measured across 75 pages: 40 CRUSHED controls, 8 CLIPPED and 2 OVERLAP, all
   in the same place. These tables put a recipe select, a chef select, quantity,
   time, an equipment picker and a currency field in ONE row and then force the
   table to the container width, so every control collapses — selects down to
   ~100px with their label wrapping onto two lines and clipping, number inputs
   down to 41px, and the "Aggiungi" button pushed underneath its select.
   A form control has a minimum usable size. Give the columns that minimum and
   let the table scroll inside .ds-table-scroll instead of crushing its contents.
   ========================================================================== */
.ds-row-editor { min-width: 980px; }
.ds-row-editor td, .ds-row-editor th { padding: var(--sp-3) var(--sp-2); vertical-align: middle; }

/* every control in a row editor keeps a workable width */
.ds-row-editor td select,
.ds-row-editor td .ds-select {
  min-width: 160px;
  /* A <select> reports its widest <option> as its min-content width, which no
     percentage can override in an auto table layout - one long recipe name
     alone pushed the column to 300px. Only a length cap actually binds. */
  width: 100%;
  max-width: 200px;
}
.ds-row-editor td input[type="text"],
.ds-row-editor td input[type="number"],
.ds-row-editor td .ds-input         { min-width: 104px; }
.ds-row-editor td input[type="date"] { min-width: 160px; }
/* the money column reads right and tabular */
.ds-row-editor td input[inputmode="decimal"],
.ds-row-editor td .ds-input-money   { text-align: end; font-variant-numeric: tabular-nums; }

/* selects must never wrap to a second line: one line, ellipsis if truly long */
.ds-row-editor td select,
.ds-select, select.form-select {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  height: 40px;                       /* a select is a single-line control */
  padding-block: 0;
}
.ds-row-editor td .input-group,
.ds-row-editor td .ds-affix { flex-wrap: nowrap; }
.ds-row-editor td .input-group > .form-control,
.ds-row-editor td .ds-affix > .ds-input { min-width: 88px; }

/* the add / remove control at the end of a row stays on the row */
.ds-row-editor td .btn { white-space: nowrap; }
.ds-row-editor td .ds-table-actions { justify-content: center; }

/* A seven-control row cannot fit 1067px of panel at 1440, so the table scrolls.
   Pinning the last cell was tried and reverted: a sticky column necessarily
   floats over whatever it is scrolled past, and at rest it covered the trailing
   10px of the right-aligned money input beside it. Hiding part of a field the
   user has to type into is worse than asking for a scroll to reach a delete
   button, so the row scrolls plainly and the scrollbar below carries the cue. */

/* Seven form controls genuinely do not fit the panel at 1440, so this table
   scrolls by design. Say so: keep the bar permanently visible rather than
   letting the row look arbitrarily cut off. scroll affordance                */
.ds-table-scroll:has(.ds-row-editor) {
  overflow-x: scroll;
  scrollbar-width: thin;
  scrollbar-color: var(--text-subtle) transparent;
  padding-bottom: var(--sp-1);
}
.ds-table-scroll:has(.ds-row-editor)::-webkit-scrollbar { height: 10px; }
.ds-table-scroll:has(.ds-row-editor)::-webkit-scrollbar-track {
  background: var(--surface-alt);
  border-radius: var(--r-pill);
}
.ds-table-scroll:has(.ds-row-editor)::-webkit-scrollbar-thumb {
  background: var(--text-subtle);
  border-radius: var(--r-pill);
}
.ds-table-scroll:has(.ds-row-editor)::-webkit-scrollbar-thumb:hover {
  background: var(--text-subtle);
}

/* header cells in a row editor follow the quiet table header */
.ds-row-editor thead th { white-space: nowrap; }

/* --- 5.6h DataTables 2.x control bar -------------------------------------
   dataTables.min.css ships `div.dt-container select.dt-input{padding:4px}` at
   (0,2,2), which outranked the `div.dt-container select` rule in 5.4 (0,1,2).
   The "Mostra [10] elementi" select was left 33px wide on 15 index pages, with
   the native chevron painted over the number. These selectors match (0,2,2)
   and win on source order. Appearance only; no init option is touched.       */
div.dt-container select.dt-input,
div.dt-container .dt-length select {
  appearance: none; -webkit-appearance: none;
  height: 36px;
  min-width: 76px;                        /* fits "100" plus the chevron      */
  padding: 0 var(--sp-8) 0 var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-input);
  background-color: var(--panel);
  color: var(--ink);
  font-size: var(--fs-ui);
  font-weight: 500;
  font-variant-numeric: tabular-nums lining-nums;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 20 20' fill='none' stroke='%235C666E' stroke-width='1.5'%3E%3Cpolyline points='5,8 10,13 15,8'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
  background-size: 14px 14px;
  cursor: pointer;
  transition: border-color 140ms var(--ease);
}
:root[data-theme="dark"] div.dt-container select.dt-input,
:root[data-theme="dark"] div.dt-container .dt-length select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 20 20' fill='none' stroke='%2393A0A8' stroke-width='1.5'%3E%3Cpolyline points='5,8 10,13 15,8'/%3E%3C/svg%3E");
}
div.dt-container select.dt-input:focus,
div.dt-container .dt-length select:focus {
  outline: 2px solid var(--focus); outline-offset: 2px; border-color: var(--line-strong); box-shadow: none;
}

/* the search field matches the length select's height so the bar reads as one
   row of controls rather than two mismatched boxes                           */
div.dt-container .dt-search input,
div.dt-container input[type="search"].dt-input {
  height: 36px;
  min-width: 260px;
  margin-left: 0;                         /* beats DT's 3px nudge (0,2,1)     */
}

/* "Mostra" / "elementi" sit on the control's centre line instead of wrapping  */
div.dt-container .dt-length label,
div.dt-container .dt-search label {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--fs-ui);
  white-space: nowrap;
}

/* pagination: the first/last buttons carry "<<" and ">>", which wrapped to two
   lines inside a 32px button with 8px side padding, rendering "<<" as a single
   visible "<" -- indistinguishable from the previous-page button.            */
div.dt-container .dt-paging {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--sp-1); flex-wrap: wrap;
}
div.dt-container .dt-paging .dt-paging-button,
.dataTables_paginate .paginate_button {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 32px; height: 32px;
  padding: 0 var(--sp-2) !important;      /* beats DT's .5em/1em padding      */
  margin: 0 !important;                   /* the flex gap spaces them now     */
  white-space: nowrap;                    /* the actual wrap fix              */
  line-height: 1;
  font-variant-numeric: tabular-nums;     /* page numbers keep a stable width */
}
/* the search field reads from its leading edge, after the magnifier, rather
   than centring its own placeholder */
div.dt-container .dt-search input,
div.dt-container input[type="search"].dt-input { text-align: start; }

/* The premium pass replaced the table grid with hairlines and dropped zebra
   striping, but only DataTables' own striping was covered. Twenty views also
   carry Bootstrap's .table-striped, which paints every other row through
   --bs-table-bg-type, so those tables kept a banding the rest of the product
   dropped. Three classes against Bootstrap's two, so no !important needed.    */
.ds-page .table-striped > tbody > tr > *,
.ds-page .table-striped > tbody > tr:nth-of-type(odd) > *,
.ds-table.table-striped > tbody > tr > *,
.ds-table.table-striped > tbody > tr:nth-of-type(odd) > * {
  --bs-table-accent-bg: transparent;
  --bs-table-bg-type: transparent;
  background-color: transparent;
  box-shadow: none;
}

/* =============================================================================
   5.6i TABLES — the actions column header
   ========================================================================== */
/* An actions column is right-aligned in the body but its header kept the
   default leading alignment, so "Azioni" floated 150px away from the buttons
   it labels. Only tables that actually put controls in the last column. */
.ds-table:has(tbody td:last-child .btn) thead th:last-child,
table.dataTable:has(tbody td:last-child .btn) thead th:last-child { text-align: end; }
.ds-table:has(tbody td:last-child .btn) tbody td:last-child,
table.dataTable:has(tbody td:last-child .btn) tbody td:last-child { text-align: end; }

/* Roughly 150 panel headers carry an inline `color:#e2ae76` on their Bootstrap
   icon. #e2ae76 on white measures about 2:1 — under the 3:1 that meaningful
   non-text content needs — so every one of those headers wore a washed-out gold
   mark, and it contradicted the premium pass, which had already quieted the
   same icons on the panels it touched. An inline style can only be beaten with
   !important.                                                                 */
.ds-card-head > i[class*="bi-"],
.card-header > i[class*="bi-"],
.page-header > i[class*="bi-"],
.ds-page-head > i[class*="bi-"],
.ds-card-title > i[class*="bi-"] { color: var(--ink-muted) !important; }

/* =============================================================================
   5.6j ICONS — iconify-icon sizing
   ========================================================================== */
/* iconify-icon sizes itself from the inherited font-size and has no baseline
   alignment of its own. In a panel or page header the surrounding div is body
   size, so the icon came out 15px next to an 18-20px title and read as a
   fragment rather than an icon. */
iconify-icon { vertical-align: -0.145em; }
.ds-card-head > iconify-icon,
.ds-page-head > iconify-icon,
.page-header > iconify-icon,
.card-header > iconify-icon { font-size: 1.25rem; flex: 0 0 auto; }

/* =============================================================================
   5.6k ACCORDION HEADERS — /external-supplies
   ========================================================================== */
/* /external-supplies grouped its rows under accordion headers filled solid
   navy or solid gold, alternating all the way down the page. That fill carried
   the only signal separating a supply from a return, so the distinction is
   kept - as a 3px leading edge on a quiet header, not a full-width colour bar.
   Bootstrap drives this button from --bs-accordion-btn-* custom properties and
   an inset box-shadow, which is why these need !important.                    */
.ds-accordion-head.accordion-button {
  background: var(--surface-alt) !important;
  color: var(--text) !important;
  border-radius: var(--r-input) !important;
  border-inline-start: 3px solid var(--border-strong);
  font-weight: 500;
  box-shadow: none !important;
}
.ds-accordion-head.accordion-button:not(.collapsed) {
  background: var(--surface-alt) !important;
  color: var(--text) !important;
}
.ds-accordion-head.accordion-button:hover { background: var(--surface) !important; }
.ds-accordion-head.accordion-button:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 2px; box-shadow: none !important;  /* beats Bootstrap's accordion focus shadow */
}
/* these have to match the .ds-accordion-head.accordion-button stack above */
.ds-accordion-head--supply.accordion-button { border-inline-start-color: var(--ink-900); }
.ds-accordion-head--return.accordion-button { border-inline-start-color: var(--brass-500); }

/* =============================================================================
   5.6l FORMS — measure
   ========================================================================== */
/* SINGLE-COLUMN FORMS - cap the measure.
   The simple create/edit forms stack every field at the full width of the
   panel, so a name, a title or a VAT number came out 1067px wide at 1440. A
   control should be about as wide as the data it holds; stretched to the panel
   edge it reads as unfinished. Forms that already lay themselves out on a grid
   are left alone - :has() excludes anything with a .row or a paired column. */
.ds-card-body > form:not(:has(.row)):not(:has([class*="col-md-"])):not(:has([class*="col-lg-"])),
.card-body > form:not(:has(.row)):not(:has([class*="col-md-"])):not(:has([class*="col-lg-"])) {
  max-width: 832px;   /* the 880 region less its 24px sides */
}

/* ...and cap the panel too, so it hugs the form instead of leaving 400px of
   empty card beside it.

   Note the shape of these two rules: :has() cannot be nested inside :has(),
   so the exclusion cannot ride along in the first selector the way it does on
   the form above. It is a second rule instead, and it wins on specificity
   because its :has() argument is the more specific one. */
.ds-page .ds-card:has(> .ds-card-body > form),
.ds-page .card:has(> .card-body > form) {
  max-width: 880px;   /* Laboratorio: past 880 a field row stops being scannable */
}
/* On a create/edit page (no table anywhere on it) the form region is 880
   whether or not it lays itself out on a grid. Where the page also lists
   records, a grid form keeps the full width so it lines up with the table. */
.ds-page:has(table) .ds-card:has(> .ds-card-body > form .row),
.ds-page:has(table) .ds-card:has(> .ds-card-body > form [class*="col-md-"]),
.ds-page:has(table) .ds-card:has(> .ds-card-body > form [class*="col-lg-"]),
.ds-page:has(table) .card:has(> .card-body > form .row),
.ds-page:has(table) .card:has(> .card-body > form [class*="col-md-"]),
.ds-page:has(table) .card:has(> .card-body > form [class*="col-lg-"]) {
  max-width: none;
}

/* a two-row textarea is not enough for an article body or an address */
.ds-card-body textarea.form-control,
.ds-card-body textarea.ds-input,
.card-body textarea.form-control { min-height: 120px; }

/* =============================================================================
   5.6m LEGACY BUTTON CLASSES — restated once, in tokens
   -----------------------------------------------------------------------------
   Forty-two views each paste the same block of hand-rolled button classes into
   their own <style>, every declaration marked !important:

     .btn-deepblue    navy border and navy TEXT on transparent
     .btn-gold        brass border and (depending on the copy) brass or navy text
     .btn-gold-filled tan fill, navy text
     .btn-gold-blue   tan fill, navy text
     .btn-gold-save   tan fill, navy text
     .btn-red         red border and red text on transparent

   The text colours fail in both directions. #e2ae76 as text on white measures
   about 2:1. #041930 on the dark theme's ground measures about 1.05:1 — the
   button disappears completely, which is what /production/create's "Aggiungi
   Riga" was doing. One copy of .btn-gold also carries
   "background-color: e2ae76" with the # missing, so the declaration is dropped
   and that button falls back to navy-on-transparent as well.

   Rewriting forty-two style blocks would be forty-two chances to miss one, so
   the family is restated here instead. The page blocks load after this file and
   mark everything !important, so matching that is not enough — these carry one
   extra element in the selector, which outranks a bare class at equal
   importance.
   ========================================================================== */
/* Laboratorio mapping:
     .btn-gold* (the save / add actions)  -> brass fill, white label
     .btn-deepblue, .btn-outline-*        -> secondary
     .btn-red, .btn-danger, outline-danger-> secondary; the danger surface only
                                             shows on hover, and the solid
                                             --danger fill only in a confirm   */
body .btn-deepblue,
body .btn-outline-deepblue,
body .btn-outline-gold,
body .btn-gold-outline,
body .btn-secondary,
body .btn-outline-secondary,
body .btn-outline-primary,
body .btn-outline-success,
body .btn-outline-info,
body .btn-outline-warning,
body .btn-outline-dark {
  background: var(--panel) !important;
  color: var(--ink) !important;
  border: 1px solid var(--line-strong) !important;
}
body .btn-deepblue:hover,
body .btn-outline-deepblue:hover,
body .btn-outline-gold:hover,
body .btn-gold-outline:hover,
body .btn-secondary:hover,
body .btn-outline-secondary:hover,
body .btn-outline-primary:hover,
body .btn-outline-success:hover,
body .btn-outline-info:hover,
body .btn-outline-warning:hover,
body .btn-outline-dark:hover {
  background: var(--panel-sunken) !important;
  color: var(--ink) !important;
  border-color: var(--line-strong) !important;
}

body .btn-gold-filled,
body .btn-gold-blue,
body .btn-gold-save,
body .btn-gold,
body .btn-success {
  background-color: var(--brass-fill) !important;
  color: var(--on-brass) !important;
  border: 1px solid var(--brass-fill) !important;
}
body .btn-gold-filled:hover,
body .btn-gold-blue:hover,
body .btn-gold-save:hover,
body .btn-gold:hover,
body .btn-success:hover {
  background-color: var(--brass-fill-hover) !important;
  color: var(--on-brass) !important;
  border-color: var(--brass-fill-hover) !important;
}
/* icons inside carry the label colour, whatever the page's inline style says */
body .btn-gold-filled i, body .btn-gold-blue i, body .btn-gold-save i,
body .btn-gold i, body .btn-success i { color: inherit !important; }  /* beats inline `color:#041930` */

body .btn-red,
body .btn-danger,
body .btn-outline-danger {
  background: var(--panel) !important;
  color: var(--ink) !important;
  border: 1px solid var(--line-strong) !important;
}
body .btn-red:hover,
body .btn-danger:hover,
body .btn-outline-danger:hover {
  background: var(--danger-surface) !important;
  color: var(--ink) !important;
  border-color: var(--danger-ink) !important;
}
/* the confirm step: the word and the fill appear together */
body .modal-footer .btn-red,
body .modal-footer .btn-danger,
body .modal-footer .btn-outline-danger,
body .modal-footer .ds-btn-danger {
  background: var(--danger) !important;
  color: var(--on-danger) !important;
  border-color: var(--danger) !important;
}
body .modal-footer .btn-red:hover,
body .modal-footer .btn-danger:hover,
body .modal-footer .btn-outline-danger:hover,
body .modal-footer .ds-btn-danger:hover {
  background: var(--danger-ink) !important;
  border-color: var(--danger-ink) !important;
  color: var(--on-danger) !important;
}

/* the one outline-light button (recipe form, "Nuovo Ingrediente") now sits on
   a light panel header, so it is an ordinary secondary button */
body .btn-outline-light {
  background: var(--panel) !important;
  color: var(--ink) !important;
  border: 1px solid var(--line-strong) !important;
}
body .btn-outline-light:hover {
  background: var(--panel-sunken) !important;
  color: var(--ink) !important;
}

/* the same block also animates a lift on hover; panels and buttons do not
   float in this design system (§4.5) */
body .btn-lift { transition: background-color var(--t-fast), border-color var(--t-fast) !important; }
body .btn-lift:hover { transform: none !important; box-shadow: none !important; }

/* =============================================================================
   5.6n CONTROL SIZING — one height per form
   ========================================================================== */
/* Bootstrap's -lg size modifiers are scattered through these forms without a
   pattern rather than as a deliberate hierarchy: /costs/create has four large
   controls and one default, /external-supplies/create has five of each, and
   /showcase/create had four controls in a single row at four different heights.
   One control height per form is the point of a design system, so the modifiers
   are levelled to it. .input-group-lg is deliberately not included - the recipe
   form uses it for the running total, where the larger type is the point.      */
.ds-page .form-control-lg,
.ds-page .form-select-lg {
  min-height: 40px;
  /* inline padding only: several selects carry .form-control-lg by mistake,
     and 8px of block padding inside a 40px select clipped their text */
  padding-inline: var(--sp-3);
  font-size: var(--fs-body);
  border-radius: var(--r-input);
}
.ds-page select.form-control-lg,
.ds-page select.form-select-lg { padding-block: 0; padding-inline-end: 36px; }
.ds-page textarea.form-control-lg { min-height: 120px; padding-block: 10px; }

div.dt-container .dt-info, .dataTables_info { padding-top: 0; }

/* Bootstrap's .w-auto shrinks a select to its option text, which does not
   account for the chevron the design system paints in the right padding.
   The year picker on /costs-comparison came out 46px wide.                   */
select.ds-select.w-auto,
select.form-select.w-auto { min-width: 96px; }

/* --- 5.7 flash messages rendered by the shell ---------------------------- */
/* Confirmations are toasts now; this only catches page-level .alert markup
   that individual views still render inline. */
.dashboard-main > .alert {
  max-width: var(--page-max);
  margin: var(--sp-4) auto 0;
  width: calc(100% - (var(--page-gutter) * 2));
  border-radius: var(--r-card);
}
/* the validation panel sits in its own .ds-page wrapper, so trim its bottom */
.dashboard-main > .ds-page > .ds-alert { margin-bottom: 0; }
.ds-toast { transition: opacity 160ms var(--ease); }
.ds-toast > div { flex: 1 1 auto; min-width: 0; }

/* =============================================================================
   5.8 LEGACY INLINE BRAND COLOURS — mapped to Laboratorio
   -----------------------------------------------------------------------------
   The views still carry the previous brand as inline style attributes
   (`style="color:#e2ae76"` on panel icons, tan fills on save buttons, navy
   fills on intro strips). An inline declaration can only be beaten with
   !important; each selector is matched on the colour it paints, so nothing
   without that colour is touched. Case-insensitive.
   ========================================================================== */

/* panel and heading icons: monochrome, --ink-muted */
i[style*="e2ae76" i], i[style*="041930" i],
iconify-icon[style*="041930" i], iconify-icon[style*="e2ae76" i] {
  color: var(--ink-muted) !important;          /* beats the inline brand colour */
}
/* the multi-tone illustration marks become one monochrome glyph */
svg[style*="e2ae76" i] { fill: var(--ink-muted) !important; width: 22px; height: 22px; }  /* beats inline fill */
svg[style*="e2ae76" i] path,
svg[style*="e2ae76" i] polygon,
svg[style*="e2ae76" i] circle,
svg[style*="e2ae76" i] rect { fill: var(--ink-muted) !important; }   /* beats the inline fill on each shape */
h1[style*="e2ae76" i], h2[style*="e2ae76" i], h3[style*="e2ae76" i],
h4[style*="e2ae76" i], h5[style*="e2ae76" i], h6[style*="e2ae76" i] {
  color: var(--ink) !important;                /* gold as text fails AA on white */
}
a[style*="color:#e2ae76" i]:not(.btn), a[style*="color: #e2ae76" i]:not(.btn) {
  color: var(--brass-ink) !important;          /* the only brass that is text  */
}

/* tan-filled buttons are the page's primary action: brass fill, white label */
.btn[style*="e2ae76" i],
button[style*="background-color:#e2ae76" i],
button[style*="background-color: #e2ae76" i] {
  background-color: var(--brass-fill) !important;   /* beats inline background */
  border-color: var(--brass-fill) !important;
  color: var(--on-brass) !important;
}
.btn[style*="e2ae76" i]:hover,
button[style*="background-color:#e2ae76" i]:hover,
button[style*="background-color: #e2ae76" i]:hover {
  background-color: var(--brass-fill-hover) !important;
  border-color: var(--brass-fill-hover) !important;
  color: var(--on-brass) !important;
}
.btn[style*="e2ae76" i] i,
.btn[style*="e2ae76" i] svg,
button[style*="e2ae76" i] i,
.btn i[style*="041930" i],
.btn i[style*="e2ae76" i] { color: inherit !important; }   /* beats inline icon colour */
/* navy-outlined link buttons outside a table are secondary */
.btn[style*="solid #041930" i] {
  background: var(--panel) !important;
  border-color: var(--line-strong) !important;
  color: var(--ink) !important;
}

/* tan table heads */
thead[style*="e2ae76" i] {
  background-color: var(--panel-sunken) !important;  /* beats inline background */
  color: var(--ink-muted) !important;
}

/* navy panel headers are quiet headers */
.card-header[style*="041930" i] {
  background: transparent !important;                 /* beats inline background */
  color: var(--ink) !important;
  border-bottom: 1px solid var(--line) !important;
}
.card-header[style*="041930" i] .ds-card-title,
.card-header[style*="041930" i] h1, .card-header[style*="041930" i] h2,
.card-header[style*="041930" i] h3, .card-header[style*="041930" i] h4,
.card-header[style*="041930" i] h5, .card-header[style*="041930" i] h6 { color: var(--ink) !important; }
.card-header[style*="041930" i] i,
.card-header[style*="041930" i] .ds-i { color: var(--ink-muted) !important; }
.card-header[style*="041930" i] small,
.card-header[style*="041930" i] .text-light { color: var(--ink-muted) !important; }

/* any other navy strip becomes the ink band */
div[style*="background-color:#041930" i]:not(.page-header):not(.card-header),
div[style*="background-color: #041930" i]:not(.page-header):not(.card-header),
div[style*="background:#041930" i]:not(.page-header):not(.card-header) {
  background: var(--band) !important;                 /* beats inline background */
  color: var(--on-band) !important;
  border-radius: var(--r-card) !important;
}

/* page-level `table.dataTable thead .sorting:after { color:#041930 }` */
body table.dataTable thead .sorting:after,
body table.dataTable thead .sorting_asc:after,
body table.dataTable thead .sorting_desc:after { color: var(--ink-muted) !important; }

/* =============================================================================
   5.9 BOOTSTRAP FORM CONTROLS AND VALIDATION
   -----------------------------------------------------------------------------
   Most fields carry both .form-control and .ds-input; some carry only the
   Bootstrap class. Both get one geometry: 40px, 3px, --line, 15px.
   Validation follows the two-scope law: an invalid field is a 2px
   --danger-ink border on --danger-surface; the message is --ink with a
   single glyph; a VALID field shows nothing — success is non-chromatic.
   ========================================================================== */
:root {
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 20 20' fill='none' stroke='%235C666E' stroke-width='1.5'%3E%3Cpolyline points='5,8 10,13 15,8'/%3E%3C/svg%3E");
}
:root[data-theme="dark"] {
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 20 20' fill='none' stroke='%2393A0A8' stroke-width='1.5'%3E%3Cpolyline points='5,8 10,13 15,8'/%3E%3C/svg%3E");
}
.form-control, .form-select {
  min-height: 40px;
  border: 1px solid var(--line);
  border-radius: var(--r-input);
  background-color: var(--panel);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  box-shadow: none;
}
textarea.form-control { line-height: var(--lh-body); }
.form-control::placeholder { color: var(--ink-disabled); opacity: 1; }
.form-control:hover, .form-select:hover { border-color: var(--line-strong); }
.form-control:focus, .form-select:focus {
  border-color: var(--line-strong);
  background-color: var(--panel);
  color: var(--ink);
  box-shadow: none;
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
.form-control:disabled, .form-select:disabled,
.form-control[readonly] {
  background-color: var(--ground);
  color: var(--ink-disabled);
  border-color: var(--line);
  opacity: 1;
}
.form-control-sm, .form-select-sm { min-height: 32px; font-size: var(--fs-ui); border-radius: var(--r-input); }
.form-select {
  background-image: var(--chevron);
  background-size: 14px 14px;
  background-position: right var(--sp-3) center;
  padding-right: 36px;
}
.form-label, label.form-label {
  margin-bottom: var(--sp-2);
  font-size: var(--fs-ui); line-height: var(--lh-ui); font-weight: 500; color: var(--ink);
}
.form-text { margin-top: var(--sp-2); font-size: var(--fs-small); color: var(--ink-muted); }

/* input groups: the affix sits on its own sunken ground */
.input-group-text {
  border: 1px solid var(--line);
  border-radius: var(--r-input);
  background-color: var(--panel-sunken);
  color: var(--ink-muted);
  font-size: var(--fs-ui);
}
.input-group > .form-control:focus,
.input-group > .form-select:focus { outline-offset: -1px; z-index: 5; }
.input-group-lg > .form-control,
.input-group-lg > .form-select,
.input-group-lg > .input-group-text { border-radius: var(--r-input); }

/* invalid */
.form-control.is-invalid, .form-select.is-invalid,
.was-validated .form-control:invalid, .was-validated .form-select:invalid {
  border: 2px solid var(--danger-ink);
  background-color: var(--danger-surface);
  background-image: none;
  padding-right: var(--sp-3);
}
.form-select.is-invalid,
.was-validated .form-select:invalid {
  background-image: var(--chevron);
  padding-right: 36px;
}
.form-control.is-invalid:focus, .form-select.is-invalid:focus,
.was-validated .form-control:invalid:focus, .was-validated .form-select:invalid:focus {
  border-color: var(--danger-ink);
  box-shadow: none;
}
.invalid-feedback {
  align-items: flex-start; gap: var(--sp-2);
  margin-top: var(--sp-2);
  font-size: var(--fs-ui); line-height: var(--lh-ui);
  color: var(--ink);
}
.invalid-feedback::before {
  content: "!";
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; margin-top: 2px;
  border-radius: var(--r-pill);
  background: var(--danger-ink); color: var(--panel);
  font-size: 12px; line-height: 16px; font-weight: 600;
}
.is-invalid ~ .invalid-feedback,
.was-validated :invalid ~ .invalid-feedback,
.was-validated .input-group:has(:invalid) > .invalid-feedback,
.input-group:has(.is-invalid) > .invalid-feedback { display: flex; }
/* valid: no green, no tick */
.form-control.is-valid, .form-select.is-valid,
.was-validated .form-control:valid, .was-validated .form-select:valid {
  border-color: var(--line);
  background-image: none;
  padding-right: var(--sp-3);
}
.was-validated .form-select:valid,
.form-select.is-valid {
  background-image: var(--chevron);
  padding-right: 36px;
}
.form-control.is-valid:focus, .was-validated .form-control:valid:focus { border-color: var(--line-strong); box-shadow: none; }
.valid-feedback { display: none !important; }        /* beats Bootstrap's :valid ~ reveal */
.was-validated .form-check-input:valid ~ .form-check-label,
.form-check-input.is-valid ~ .form-check-label { color: inherit; }
.was-validated .form-check-input:invalid ~ .form-check-label,
.form-check-input.is-invalid ~ .form-check-label { color: var(--ink); }

/* =============================================================================
   5.10 BOOTSTRAP UTILITIES, BADGES AND ALERTS
   -----------------------------------------------------------------------------
   In the views, .text-danger / .text-success colour a margin or a total —
   money — so they take the money scope (oxblood / green ink). Badges become
   pills from the chip set.
   ========================================================================== */
.text-danger  { color: var(--loss) !important; }         /* beats Bootstrap utility */
.text-success { color: var(--gain) !important; }         /* beats Bootstrap utility */
.text-warning { color: var(--caution-ink) !important; }  /* beats Bootstrap utility */
.text-primary { color: var(--ink) !important; }          /* beats Bootstrap utility */
:root[data-theme="dark"] .text-danger  { color: var(--loss) !important; }
:root[data-theme="dark"] .text-success { color: var(--gain) !important; }
:root[data-theme="dark"] .text-warning { color: var(--caution-ink) !important; }
.fw-bold { font-weight: 600 !important; }                /* Archivo's heaviest step here */

.badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  font-size: var(--fs-small); line-height: var(--lh-small); font-weight: 500;
  font-variant-numeric: tabular-nums lining-nums;
}
.badge.bg-primary, .badge.bg-secondary, .badge.bg-info,
.badge.bg-dark, .badge.bg-light, .badge.text-bg-primary,
.badge.text-bg-secondary, .badge.text-bg-info, .badge.text-bg-light {
  background-color: var(--neutral-chip-bg) !important;   /* beats the Bootstrap utility */
  color: var(--neutral-chip-ink) !important;
}
.badge.bg-success, .badge.text-bg-success {
  background-color: var(--gain-chip-bg) !important;      /* beats the Bootstrap utility */
  color: var(--gain-chip-ink) !important;
}
.badge.bg-danger, .badge.text-bg-danger {
  background-color: var(--loss-chip-bg) !important;      /* beats the Bootstrap utility */
  color: var(--loss-chip-ink) !important;
}
.badge.bg-warning, .badge.text-bg-warning {
  background-color: var(--caution-bg) !important;        /* beats the Bootstrap utility */
  color: var(--caution-ink) !important;
}

.alert {
  border-radius: var(--r-card);
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  font-size: var(--fs-ui);
}
.alert-success { background: var(--panel); border-color: var(--line-strong); color: var(--ink); }
.alert-info, .alert-secondary, .alert-primary, .alert-light { background: var(--panel-sunken); border-color: var(--line); color: var(--ink); }
.alert-warning { background: var(--caution-bg); border-color: color-mix(in srgb, var(--caution-ink) 25%, transparent); color: var(--ink); }
.alert-danger { background: var(--danger-surface); border-color: var(--danger-line); color: var(--ink); }
.alert .alert-link { color: var(--ink); }

/* dropdown menus: level 1 */
.dropdown-menu {
  border: 1px solid var(--line-strong);
  border-radius: var(--r-card);
  background-color: var(--panel);
  box-shadow: var(--e-raised);
  font-size: var(--fs-ui);
}
.dropdown-item { color: var(--ink); }
.dropdown-item:hover, .dropdown-item:focus { background-color: var(--ground); color: var(--ink); }
.dropdown-item.active, .dropdown-item:active { background-color: var(--fill-strong); color: var(--on-fill-strong); }

/* nav tabs / pills */
.nav-tabs { border-bottom-color: var(--line); }
.nav-tabs .nav-link { color: var(--ink-muted); border-radius: var(--r-input) var(--r-input) 0 0; font-weight: 500; }
.nav-tabs .nav-link:hover { color: var(--ink); border-color: transparent; }
.nav-tabs .nav-link.active { color: var(--ink); background: var(--panel); border-color: var(--line) var(--line) var(--panel); }
.nav-pills .nav-link { border-radius: var(--r-input); color: var(--ink-muted); font-weight: 500; }
.nav-pills .nav-link.active { background: var(--fill-strong); color: var(--on-fill-strong); }

/* progress bars read as a bar on a sunken track */
.progress { background-color: var(--panel-sunken); border-radius: 2px; }
.progress-bar { background-color: var(--ink-2); }

/* accordions */
.accordion-item { border-color: var(--line); background: var(--panel); }
.accordion-button { font-weight: 500; color: var(--ink); background: var(--panel); }
.accordion-button:not(.collapsed) { color: var(--ink); background: var(--panel-sunken); box-shadow: inset 0 -1px 0 var(--line); }
.accordion-button:focus { box-shadow: none; outline: 2px solid var(--focus); outline-offset: -2px; }

/* list groups */
.list-group-item { border-color: var(--line); background: var(--panel); color: var(--ink); }

/* =============================================================================
   5.11 PLAIN BOOTSTRAP TABLES INSIDE A PAGE
   -----------------------------------------------------------------------------
   A few tables (the dashboard's rankings, recipe-sales) never took .ds-table.
   Give them the same head and rule treatment without the region layout.
   ========================================================================== */
.ds-page .table:not(.ds-table) {
  --bs-table-bg: transparent;
  --bs-table-color: var(--ink);
  --bs-table-border-color: var(--line);
  --bs-table-hover-bg: var(--ground);
  --bs-table-striped-bg: transparent;
  font-size: var(--fs-ui);
  margin-bottom: 0;
}
.ds-page .table:not(.ds-table) > thead > tr > th {
  background: var(--panel-sunken);
  color: var(--ink-muted);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: 500;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.ds-page .table:not(.ds-table) > tbody > tr > td {
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}

/* =============================================================================
   5.12 DASHBOARD — many charts, no mess
   -----------------------------------------------------------------------------
   The headline figures sit on one ink band, ruled into cells; every other
   block is an identical border-only panel. Nothing floats, nothing lifts.
   ========================================================================== */
.ds-kpi-band {
  gap: 0;
  background: var(--band);
  border: 1px solid var(--band-line);
  border-radius: var(--r-card);
  overflow: hidden;
  color: var(--on-band);
}
/* each cell rules its own left and top edge; the ones on the band's outer
   edge fall outside and are clipped, so any number of tiles rules cleanly */
.ds-kpi-band > .ds-kpi {
  min-height: 148px;
  padding: var(--sp-6);
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: -1px 0 0 var(--band-line), 0 -1px 0 var(--band-line);
  container-type: inline-size;
  gap: var(--sp-5);
}
.ds-kpi-band .ds-kpi-icon { display: none; }
.ds-kpi-band .ds-kpi-label { color: var(--on-band-muted); }
.ds-kpi-band .ds-kpi-value {
  color: var(--on-band);
  font-size: clamp(1.75rem, 13cqi, var(--fs-figure-xl)) !important;  /* beats 5.2b's fixed figure size */
  line-height: 1.05;
  overflow-wrap: anywhere;
}
.ds-kpi-band .ds-kpi:has(.ds-tint-brass)   .ds-kpi-value { color: var(--on-band-brass); }
.ds-kpi-band .ds-kpi:has(.ds-tint-success) .ds-kpi-value { color: var(--on-band-gain); }
.ds-kpi-band .ds-kpi:has(.ds-tint-warning) .ds-kpi-value { color: var(--on-band-loss); }
.ds-kpi-band .ds-kpi-delta,
.ds-kpi-band .ds-kpi-delta.ds-kpi-delta-flat {
  padding: 0;
  background: transparent;
  color: var(--on-band-muted);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}
/* the empty sparkline slots take no room */
.ds-kpi-band .ds-kpi > div[id$="-chart"]:empty { display: none; }

/* every card on a page is a border-only panel (the dashboard's were
   borderless, shadowed and 16px round) */
.ds-page .card.border-0 { border: 1px solid var(--line) !important; }       /* beats .border-0 */
.ds-page .card.shadow-sm,
.ds-page .card.shadow { box-shadow: none !important; }                       /* beats Bootstrap shadow utilities */
.ds-page .card.rounded-3,
.ds-page .card.rounded-4 { border-radius: var(--r-card) !important; }       /* beats Bootstrap radius utilities */
.ds-page .card {
  background-color: var(--panel);
  border-color: var(--line);
  border-radius: var(--r-card);
}
.ds-page .card > .card-header {
  padding: var(--sp-4) var(--sp-5);
  background-color: var(--panel);
  border-bottom: 1px solid var(--line);
}
.ds-page .card > .card-header.bg-body-tertiary { background-color: var(--panel) !important; }  /* beats the utility */
.ds-page .card > .card-header .ds-card-title,
.ds-page .card h6.ds-card-title { font-size: var(--fs-h3) !important; line-height: var(--lh-h3); font-weight: 500 !important; }
/* the small date pickers in a panel header read as one control row */
.ds-page .card > .card-header .form-control-sm {
  min-width: 140px;
  min-height: 32px;
  font-size: var(--fs-ui);
}
.ds-page .card > .card-header .btn-sm { min-height: 32px; min-width: 32px; }

/* the three money chips above the monthly chart */
.kpi-chip {
  padding: var(--sp-3) var(--sp-4) !important;                  /* beats .p-2 */
  background: var(--panel) !important;                          /* beats .bg-light-subtle */
  border: 1px solid var(--line) !important;
  border-radius: var(--r-card) !important;                      /* beats .rounded-3 */
}
.kpi-chip small { font-size: var(--fs-caption); font-weight: 500; color: var(--ink-muted) !important; }
.kpi-chip .ds-card-title { font-size: var(--fs-figure-lg) !important; line-height: var(--lh-figure-lg); font-weight: 600 !important; font-variant-numeric: tabular-nums lining-nums; }

/* every chart box holds its height before the data arrives */
.chart-min-260 { min-height: 260px; }

/* =============================================================================
   5.13 SHELL DETAILS
   ========================================================================== */
/* The list search shows its key, as in the design ("/" focuses it). */
div.dt-container .dt-search { position: relative; }
div.dt-container .dt-search input[type="search"] { padding-right: 36px; }
div.dt-container .dt-search::after {
  content: "/";
  position: absolute; right: 8px; top: 50%;
  transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px;
  border: 1px solid var(--line); border-radius: var(--r-input);
  background: var(--panel-sunken); color: var(--ink-muted);
  font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: 500;
  pointer-events: none;
}
@media (max-width: 767.98px) { div.dt-container .dt-search::after { display: none; } }

/* Unread notifications: the count carries the state. The layout's yellow
   background flash on the bell is replaced by a brass ring on the control. */
.ds-topbar .ds-icon-btn.blink {
  animation: none;                        /* beats the layout's .blink flash */
  border-color: var(--brass);
}

/* =============================================================================
   5.14 CONTROL GEOMETRY — one height per control
   -----------------------------------------------------------------------------
   WowDash fixes .form-control at 44px, the -lg modifiers at 56px and -sm at
   40px, and pads .btn-lg to 52px with 600 weight. Laboratorio has three
   heights: 40 for a field, 36 for a button, 32 for the compact variants.
   ========================================================================== */
.form-control:not(textarea),
.form-select,
.form-control.form-control-lg:not(textarea),
.form-select.form-select-lg {
  height: 40px;
  min-height: 40px;
  padding-top: 0;
  padding-bottom: 0;
  padding-left: var(--sp-3);
  font-size: var(--fs-body);
  border-radius: var(--r-input);
}
.form-control.form-control-sm:not(textarea),
.form-select.form-select-sm {
  height: 32px;
  min-height: 32px;
  font-size: var(--fs-ui);
}
.form-control:not(textarea) { padding-right: var(--sp-3); }
textarea.form-control { padding: 10px var(--sp-3); }
.form-control::placeholder { font-size: inherit !important; }   /* beats WowDash's 1rem !important */
.form-control[type="file"] { padding: 0; line-height: 38px; }
.form-control[type="file"]::file-selector-button {
  height: 100%; margin-right: var(--sp-3); padding: 0 var(--sp-3);
  border: 0; border-right: 1px solid var(--line);
  background: var(--panel-sunken); color: var(--ink);
}
/* An appearance:none select paints its text at the top of the box unless the
   line box fills it. */
select.form-select,
select.ds-select,
div.dt-container select {
  line-height: 38px;
  padding-top: 0;
  padding-bottom: 0;
}
select.form-select.form-select-sm { line-height: 30px; }

.input-group-lg > .form-control,
.input-group-lg > .form-select,
.input-group-lg > .input-group-text,
.input-group-lg > .btn {
  height: 40px;
  min-height: 40px;
  padding-top: 0;
  padding-bottom: 0;
  font-size: var(--fs-body);
  border-radius: var(--r-input);
}
.input-group > .input-group-text { padding-inline: var(--sp-3); }

.btn {
  padding: 7px var(--sp-4);
  font-size: var(--fs-ui);
  line-height: var(--lh-ui);
  font-weight: 500;
}
.btn.btn-lg {
  padding: 9px var(--sp-5);
  font-size: var(--fs-ui);
  font-weight: 500;
}
.btn.btn-sm {
  padding: 5px var(--sp-3);
  font-size: var(--fs-ui);
}
.btn .bi, .btn i { line-height: 1; }

/* the rail toggle carries two glyphs; show the one for the current state */
.sidebar-toggle:not(.active) svg.active,
.sidebar-toggle.active svg.non-active { display: none; }

/* the "Azioni" header sits over its buttons (5.6d left-aligns every header) */
.ds-table:has(tbody td:last-child .btn) thead th:last-child,
table.dataTable:has(tbody td:last-child .btn) thead th:last-child,
.ds-table:has(tbody td:last-child .ds-icon-btn) thead th:last-child { text-align: end !important; }  /* beats 5.6d's start !important */

/* DataTables 2 draws its own darker head rule at (0,1,4) */
div.dt-container table.dataTable > thead > tr > th,
div.dt-container table.dataTable > thead > tr > td { border-bottom: 1px solid var(--line); }
div.dt-container table.dataTable > tbody > tr > td { border-top: 0; }

/* the search sits at the end of the control row, and hugs its field */
div.dt-container .dt-layout-cell.dt-end { display: flex; justify-content: flex-end; }
div.dt-container .dt-search { display: inline-flex; }

/* chart grid bands: the ramp is the only fill in a chart */
.apexcharts-grid-row { fill: transparent; }

/* Below 1024 the rail is a drawer: only the hamburger belongs in the topbar
   (.ds-icon-btn's display was re-showing the desktop collapse toggle). */
@media (max-width: 1023.98px) {
  .ds-topbar .sidebar-toggle { display: none; }
}

/* auth forms: WowDash redefines the Bootstrap spacing utilities in pixels
   (.mt-4 is 4px there), so the auth pages space themselves with these */
.ds-auth-submit { margin-top: var(--sp-6); }
.ds-auth-divider { margin: var(--sp-5) 0 var(--sp-3); }
.ds-auth-foot { margin: var(--sp-5) 0 0; }

/* Header cells picked up a 1px top border in Bootstrap's light grey
   (#DEE2E6), which read as a bright rule in dark. The control row above
   already draws the region's divider. */
div.dt-container table.dataTable > thead > tr > th,
.ds-table > thead > tr > th,
.ds-page .table > thead > tr > th { border-top: 0; }

/* Narrow control rows: the list search takes the row instead of forcing a
   260px field that pushed its "Cerca:" label out past the region's edge. */
@media (max-width: 767.98px) {
  div.dt-container .dt-layout-cell.dt-end,
  div.dt-container .dt-layout-cell.dt-start { justify-content: flex-start; width: 100%; }
  div.dt-container .dt-search { display: flex; width: 100%; }
  div.dt-container .dt-search label { flex: none; }
  div.dt-container .dt-search input,
  div.dt-container input[type="search"].dt-input { min-width: 0; flex: 1 1 auto; width: auto; }
}

/* =============================================================================
   5.15 TABLES — the actions column holds the right edge
   -----------------------------------------------------------------------------
   Recipes archetype: on a list table the actions stay in reach while a wide
   row scrolls, behind a --line-strong edge and the system's one gradient,
   an inset shadow that says content continues underneath. List tables only:
   the row editors on create forms keep the plain scroll (5.6g explains why a
   pinned cell there covered part of a money field). "Azioni" is never
   truncated — the column keeps at least 116px.
   ========================================================================== */
table.dataTable.ds-table:not(.ds-row-editor):has(tbody td:last-child .btn) > thead > tr > th:last-child,
table.dataTable.ds-table:not(.ds-row-editor):has(tbody td:last-child .btn) > tbody > tr > td:last-child:not(.dt-empty):not(.dataTables_empty) {
  position: sticky;
  right: 0;
  z-index: 1;
  min-width: 116px;
  white-space: nowrap;
  background-color: var(--panel);
  border-left: 1px solid var(--line-strong) !important;            /* beats 5.6d's border-left: 0 !important */
  box-shadow: inset 14px 0 12px -12px rgba(23, 27, 31, .22) !important;  /* beats DT's row-hover box-shadow reset */
}
table.dataTable.ds-table:not(.ds-row-editor):has(tbody td:last-child .btn) > thead > tr > th:last-child {
  z-index: 2;
  background-color: var(--panel-sunken) !important;                /* beats the page-level th background */
}
table.dataTable.ds-table:not(.ds-row-editor):has(tbody td:last-child .btn) > tbody > tr:hover > td:last-child {
  background-color: var(--ground);
}
:root[data-theme="dark"] table.dataTable.ds-table:not(.ds-row-editor):has(tbody td:last-child .btn) > thead > tr > th:last-child,
:root[data-theme="dark"] table.dataTable.ds-table:not(.ds-row-editor):has(tbody td:last-child .btn) > tbody > tr > td:last-child:not(.dt-empty):not(.dataTables_empty) {
  box-shadow: inset 14px 0 12px -12px rgba(0, 0, 0, .55) !important;   /* beats DT's row-hover box-shadow reset */
}
/* The actions column shrinks to its buttons. DataTables writes a measured
   pixel width onto each header cell, which on a two-column table gave the
   actions a third of the row and put the pinned edge in the middle of it. */
table.dataTable.ds-table:not(.ds-row-editor):has(tbody td:last-child .btn) > thead > tr > th:last-child,
table.dataTable.ds-table:not(.ds-row-editor):has(tbody td:last-child .btn) > tbody > tr > td:last-child:not(.dt-empty):not(.dataTables_empty) {
  width: 1% !important;                    /* beats DataTables' inline measured width */
  text-align: end !important;              /* beats 5.6d's text-align: start !important */
}

/* =============================================================================
   5.16 DETAIL PAGES — label over value
   -----------------------------------------------------------------------------
   Nine show views label each field with h6.text-uppercase.text-muted.small
   and set the value in .fs-3/.fs-5 bold. Laboratorio's rule is a caption,
   12/16/500 in sentence case, never caps — the strings in the markup are
   already sentence case, so only the transform goes. Values take one step,
   h3 18/24/500, tabular, instead of three different sizes.
   ========================================================================== */
.ds-page .text-uppercase.text-muted.small {
  text-transform: none !important;               /* beats Bootstrap .text-uppercase */
  letter-spacing: 0;
  font-size: var(--fs-caption) !important;       /* beats WowDash h6 and the inline 14px */
  line-height: var(--lh-caption);
  font-weight: 500 !important;
  color: var(--ink-muted) !important;            /* beats .text-muted */
  margin-bottom: var(--sp-1);
}
.ds-page .text-uppercase.text-muted.small + p,
.ds-page .text-uppercase.text-muted.small + div {
  font-size: var(--fs-h3) !important;            /* beats .fs-3 / .fs-5 */
  line-height: var(--lh-h3);
  font-weight: 500 !important;                   /* beats .fw-bold */
  color: var(--ink);
  font-variant-numeric: tabular-nums lining-nums;
}

/* a ruled list row inside one region: text on the left, its action on the right */
.ds-list-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line);
}
.ds-list-row:last-child { border-bottom: 0; }
.ds-list-row-text { flex: 1 1 auto; min-width: 0; font-size: var(--fs-body); line-height: var(--lh-body); }
@media (max-width: 479.98px) { .ds-list-row { flex-wrap: wrap; } }
