/* ==========================================================================
   Quercia viva 2.0 — Design tokens
   Single source of truth for color/type/motion of the web back-office.
   Loaded AFTER Tabler: it remaps the --tblr-* variables onto the --qc-*
   tokens, so the whole UI (buttons aside, re-skinned in components.css)
   recolors without touching the templates.

   Color architecture (2.0): a warm NEUTRAL canvas (surfaces/text/borders)
   with forest green reserved for action / active / brand, and acorn bronze
   as the rare brand accent. Data pops on a calm ground; the app reads
   "designed, not skinned". See docs/brand/quercia-viva.md §2.

   Golden rules: use ONLY these tokens. Never hardcode hex in feature
   stylesheets. Every text >= 4.5:1 on its background (verified, brand §2).
   ========================================================================== */

:root, [data-bs-theme="light"] {
  /* ---- Quercia viva 2.0 — light theme: tela neutra calda (brand §2.1) ---- */
  /* Five surfaces, named for what they are and not for how light they are.
     A level means the same thing in both themes; what changes is how it is
     told apart — by shadow here, by lightness in dark, where the higher a
     thing sits the lighter it is. */
  --qc-surface-sunken: #F5F6F1;   /* the page — warm calm paper */
  --qc-surface-well: #ECEEE6;     /* set into a card: a field, a track, a header band */
  --qc-surface-base: #FCFCFA;     /* a card, a grid */
  --qc-surface-raised: #FCFCFA;   /* floating over content: a control on a map */
  --qc-surface-overlay: #FCFCFA;  /* above everything: menu, dialog, toast, drawer */
  /* The older names, kept because most of the stylesheets say them. They used
     to be two literal colours whose meaning swapped with the theme — `surface`
     was the card in light and the sunken fill in dark — so a rule written
     looking at one theme was wrong in the other. Each is one role now. */
  --qc-background: var(--qc-surface-sunken);
  --qc-surface: var(--qc-surface-base);
  --qc-surface-light: var(--qc-surface-well);
  --qc-primary: #2E6B45;         /* the jewel: action, links, active, focus */
  --qc-primary-light: #43855A;
  --qc-primary-dark: #1F5233;
  --qc-on-primary: #FFFFFF;
  /* Primary as a label on a wash of itself — a secondary button. A step darker
     than the action colour in light, so it holds 4.5:1 on a tinted ground over
     the page, a card and a well alike (5.7:1 at worst); the mint itself in dark. */
  --qc-primary-ink: #1F5233;
  /* A printed sheet shown on screen (the estimate preview): the colour of the
     paper the PDF comes out on, in both themes — a logo drawn for white paper
     is judged on white. Media, like a photo, which is why it may be pure. */
  --qc-paper: #FFFFFF;
  --qc-accent: #8F6128;          /* acorn bronze — brand highlight only */
  --qc-on-accent: #FFFFFF;
  --qc-text-primary: #1A1E1A;    /* warm near-black neutral */
  /* The veil a side panel lays over the page: always a darkening, never the
     text colour — which is near-white in the dark theme and bleached the page
     every time the help, the bell or the search opened. */
  --qc-scrim: rgba(26, 30, 26, .32);
  --qc-text-secondary: #4C5650;  /* neutral grey-green */
  --qc-text-tertiary: #636C65;   /* metadata / placeholders / hints */
  --qc-border: #DBDED4;          /* the edge of a card */
  --qc-divider: #E7E9E1;         /* a rule between rows */
  /* The edge of something floating. The two above are opaque and tuned on the
     card; an overlay is a level of its own, so its edge is drawn in a colour
     that adapts to whatever it lands on. For borders only: translucent, so it
     must not be used as a fill or mixed into another colour. */
  --qc-edge: rgb(22 38 26 / .16);
  /* The light an overlay catches on its top edge in dark; nothing in light,
     where the shadow already says it is above. */
  --qc-shadow-top: 0 0 #0000;
  /* The one thing the page's ground carries: a faint light from above, behind
     the content, fading to nothing. Not a texture and not a brand moment. */
  --qc-page-glow: radial-gradient(120% 28rem at 50% -6rem, color-mix(in srgb, var(--qc-primary) 8%, transparent), transparent 70%);

  --qc-card-bg: var(--qc-surface-base);
  --qc-field-bg: var(--qc-surface-well);
  /* The outline of something you type in or tick. Its own token because the
     card hairline is too faint for it in dark (see below); in light the
     sunken fill already carries the field, so it stays the hairline. */
  /* What you type in is a control, and a control's boundary owes 3:1 (WCAG
     1.4.11). It was the card's hairline here (1.33:1), with the sunken fill
     left to say "this is a field"; the box and the field now have one edge. */
  --qc-field-border: var(--qc-control-border);
  /* The one halo of a field that has the focus. A button, and everything that
     is not typed into, takes the 2px outline instead (components.css). */
  --qc-focus-ring: 0 0 0 .2rem color-mix(in srgb, var(--qc-primary) 30%, transparent);
  /* The edge of a control that is only an edge — an unticked box. The
     hairline is ~1.2:1 and left a tick box invisible on a table; a control's
     boundary owes 3:1 (WCAG 1.4.11) on the card, the field and the row alike. */
  --qc-control-border: #7C847B;

  /* RGB forms (for rgba()/color-mix fallbacks) */
  --qc-background-rgb: 245, 246, 241;
  --qc-surface-light-rgb: 236, 238, 230;
  --qc-primary-rgb: 46, 107, 69;
  --qc-text-primary-rgb: 26, 30, 26;

  /* ---- State layers ----
     What a row, an item or a cell wears while it is pointed at or chosen. Laid
     over whatever surface it sits on, so one pair serves a card and a drawer
     alike. Tertiary text holds 4.5:1 on each, on every surface.
     Four names, and a green ground is one of them: pointed at, chosen, a wash
     (an icon's well, the chosen segment, a step that is done) and a highlight
     (a wash under the pointer, a place something may be dropped). There were
     thirty percentages for these four jobs. What is not a ground — an edge, a
     halo, a gradient, a mix into a named surface — keeps its own figure. */
  --qc-layer-hover: color-mix(in srgb, var(--qc-primary) 6%, transparent);
  --qc-layer-selected: color-mix(in srgb, var(--qc-primary) 11%, transparent);
  --qc-layer-wash: color-mix(in srgb, var(--qc-primary) 14%, transparent);
  --qc-layer-highlight: color-mix(in srgb, var(--qc-primary) 20%, transparent);
  /* The shadow a part that stays put casts on what scrolls under it. */
  --qc-scroll-shade: rgba(20, 30, 20, .14);

  /* ---- Semantic (brand §2.3) ----
     Strong in light, where the edge of a field in error has to be seen on
     near-white; the dark block restates them on the status ramp. */
  --qc-success: #66BB6A;
  /* One step lighter, for the grounds that carry near-black text — the
     presence badge above all. The fill above is tuned for a button, where it is
     darkened again by the gradient and read under white text; on a chip that
     same green is heavier than it needs to be. Same family, same hue, one stop
     up the ramp. */
  --qc-success-light: #81C784;
  --qc-error: #EF5350;
  --qc-warning: #FFCA28;
  --qc-info: #64B5F6;
  /* ---- Status ramp ----
     Nine hues at one lightness, so that how loud a status reads depends on
     what it is and not on which hue it was given: side by side, lime used to
     shout and red to whisper. Each theme has its own nine (the dark block);
     the names are what a company saves, and they do not change. Near-black
     holds on every one of them as a fill (≥9:1 here, ≥7.9:1 in dark). */
  --qc-status-yellow: #DFC268;
  --qc-status-orange: #FAB373;
  --qc-status-purple: #DDAEFA;
  --qc-status-red: #FFABA1;
  --qc-status-lime: #AED380;
  --qc-status-cyan: #5CD7F2;
  /* The rest of the palette a status may be given on the settings page. Same
     family as the six above, so an eighth status still looks like this product.
     "accounted" wears green and had no token at all until now, which is how
     `var(--qc-status-)` reached a page. */
  --qc-status-green: #8CD99C;
  --qc-status-teal: #5CDCCE;
  /* The operator's badge colour, so a colour swatch can paint it like the others. */
  --qc-status-primary: var(--qc-primary);
  --qc-status-pink: #FFA6C4;
  /* How strong the wash of a state pill is, and the ink drawn from a hue —
     its icon, the ring of an outlined pill, the mark on a job's tile. The ink
     keeps the hue and takes this lightness (components.css, `--st-ink`):
     ≥7:1 on the wash it sits on, in both themes. */
  --qc-status-wash: 22%;
  /* A job's tile — on the calendar, the plan board, a day's list — and the same
     tile under the pointer. Stronger in light, where a pale hue has to show on
     near-white (at 12% a tile stood off its card at 1.06:1); the dark pair is
     what the tiles already wore there. */
  --qc-tile-wash: 22%;
  --qc-tile-wash-hover: 28%;
  --qc-ink-l: .42;

  /* ---- Chart series (the profitability chart) ----
     Four categorical colours in a fixed order — never cycled, never reused for
     a state. Checked with the dataviz validator against the card surface of
     each theme (lightness band, chroma, colour-blind separation, 3:1 on the
     surface); the dark block restates them a step lighter. */
  --qc-chart-materials: #3A78B8;
  --qc-chart-machines: #B7791F;
  --qc-chart-labour: #7B5EA7;
  --qc-chart-margin: #2E8B50;
  /* The accounting summary's chart: what a month invoiced, in what came in
     and what is still out. Two of the four above under names of their own —
     a pair already checked against both surfaces — so that chart can change
     without the profitability one following. */
  --qc-chart-cashed: var(--qc-chart-margin);
  --qc-chart-open: var(--qc-chart-machines);

  /* Text-safe variants of the semantic colors — for colored DATA TEXT
     (numbers, deltas). The status hues above are tuned for FILLS; as text on
     the warm-white canvas they fail AA. These are darkened to clear ≥4.5:1 on
     background / surface / surface-light (brand §2.2). Fills/badges keep the
     vivid hue; only text utilities are re-pointed (components.css). */
  --qc-warning-text: #8A5A00; /* dark amber */
  --qc-error-text: #B92C1D;   /* deep red */
  --qc-success-text: var(--qc-primary); /* forest green already AA */
  --qc-info-text: #1565C0;    /* deep blue — 5.59:1 on surface, in band with the two above */

  /* ---- Typography (brand §3) ---- */
  --qc-font-display: 'Bricolage Grotesque', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --qc-font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  /* ---- Type sizes (brand §3.1) ----
     Seven, and a size in a stylesheet is one of them. There were forty-nine,
     half-pixels included, because each sheet had picked its own. Named by
     size and not by role: 15 is a section's heading and a comfortable grid's
     data. In rem, so they follow the reader's own zoom; the same in both
     themes. Nothing is set under 12. What stays a figure of its own: an
     icon's size (not text), a brand moment over 28, and an input a phone
     would zoom into under 16px. */
  --qc-fs-xs: .75rem;      /* 12 — labels, headers, pills, metadata */
  --qc-fs-sm: .8125rem;    /* 13 — data, second lines, small buttons */
  --qc-fs-md: .875rem;     /* 14 — body */
  --qc-fs-lg: .9375rem;    /* 15 — a section's heading, comfortable grid data */
  --qc-fs-xl: 1.0625rem;   /* 17 — the page's name, a group's title */
  --qc-fs-2xl: 1.25rem;    /* 20 — a title */
  --qc-fs-3xl: 1.75rem;    /* 28 — display */

  /* ---- Radii (brand §6) ---- */
  --qc-radius-sm: 4px;
  --qc-radius-input: 10px;   /* crisper field radius (was 8px) */
  --qc-radius-btn: 12px;     /* action radius — tighter, modern (was 16px) */
  --qc-radius-card: 16px;
  --qc-radius-panel: 20px;
  --qc-radius-pill: 999px;

  /* ---- Control height — one line for buttons and fields sitting side by
     side in a toolbar (see .qc-toolbar in components.css). Same height the
     grid quick-search box already used. ---- */
  --qc-control-h: 40px;

  /* ---- Data grid (brand §5.3) ----
     One knob set for every AG Grid in the app. The JS theme
     (assets/ag_grid/theme.js) reads these through var(), so a change here
     re-skins all the tables without re-initialising a single grid.

     Density is a per-user account preference (ConfigurationManager::
     ACCOUNT_GRID_DENSITY): "comfortable" is the absence of the attribute,
     "compact" is [data-qc-density="compact"] on <html>, stamped before the
     first paint by the bootstrap script in templates/minimal.html.twig — the
     same mechanism the theme uses, so it survives Turbo body swaps.

     Like the radii and the motion tokens, these are theme-invariant: they are
     declared once and are NOT re-declared in the dark block. The three row
     colours below are mixes of tokens that flip on their own, so they follow
     the theme without being restated either. ---- */
  --qc-grid-spacing: 8px;            /* AG Grid's master density knob */
  --qc-grid-header-h: 44px;
  --qc-grid-font-size: var(--qc-fs-sm);    /* chrome: header, pagination, filters */
  --qc-grid-data-font-size: var(--qc-fs-lg);
  --qc-grid-icon-size: 16px;
  --qc-grid-cell-px: 1rem;
  --qc-grid-cell-py: 0.5rem;         /* was .35rem: two lines sat too close to the rules */
  --qc-grid-cell-lines: 2;           /* how many lines a cell clamps to */
  /* Room inside a cell for the horizontal scrollbar a one-line cell offers when
     its value is wider than its column. Zero here: a cell that wraps has no
     horizontal overflow to scroll, so there is no bar to make room for. */
  --qc-grid-cell-scroll-h: 0px;

  /* The height of every row of a server-paged grid, and of its phone cards.
     AG Grid's Infinite Row Model places row n at n × rowHeight, so a row cannot
     grow to its content there: it is the box the cells already keep (below, and
     `.ag-cell-auto-height` in ag-grid.css) — the taller of the clamped lines and
     a control — plus the cell's padding and the row's hairline. Written in the
     other tokens, so the compact density moves it with them; the grid reads it
     through a probe element, which is what resolves the calc(). */
  --qc-grid-row-h: calc(
    max(var(--qc-grid-cell-lines) * 1.3 * var(--qc-grid-data-font-size) + var(--qc-grid-cell-scroll-h),
        var(--qc-grid-control-h))
    + 2 * var(--qc-grid-cell-py) + 1px);
  /* A title of two lines, three fields, the row of buttons. */
  --qc-grid-card-h: 14rem;

  /* The tallest thing a row can be asked to carry, and the floor under the row
     height because of it. A row is not as tall as its text: it is as tall as
     its text OR its controls, whichever asks for more. Spelling that out is
     what stops a density from deciding, by arithmetic nobody looked at, that a
     36px button fits in 25px. Everything below reads this one number, so a
     control and the room made for it can never drift apart.

     The -full / -tight pair follows the shape --qc-sidebar-w already uses, so
     that the phone card layout can put the touch target back to full size with
     one line: a finger is the same size whichever density the account asked
     for. */
  --qc-grid-control-h-full: 2.25rem;      /* 36px */
  --qc-grid-control-h-tight: 1.75rem;     /* 28px */
  --qc-grid-control-h: var(--qc-grid-control-h-full);
  --qc-grid-action-px: 0.75rem;      /* the action button's side padding */
  --qc-grid-action-gap: 0.375rem;    /* between two action buttons */
  --qc-grid-action-icon: 19px;
  --qc-grid-badge-font-size: var(--qc-fs-sm);   /* brand §5.5 */
  --qc-grid-cell-small-size: var(--qc-fs-sm);   /* the twoLine renderer's second line */
  --qc-grid-header-font-size: var(--qc-fs-xs);

  --qc-grid-row-hover: var(--qc-layer-hover);
  --qc-grid-row-selected: var(--qc-layer-selected);
  /* No stripes: a rule between two rows, and the only grounds a row ever
     wears are the pointer's and the selection's — which a stripe competed with
     (a hover over an odd row stood off it at 1.04:1). The token stays, so one
     line brings them back. */
  --qc-grid-row-odd: transparent;
  --qc-grid-row-border: var(--qc-divider);

  /* ---- Shadows (brand §6) — two-stop, warm neutral tint ---- */
  --qc-shadow-sm: 0 1px 2px rgba(20, 30, 20, .05), 0 1px 3px rgba(20, 30, 20, .07);
  --qc-shadow-md: 0 4px 12px rgba(20, 30, 20, .08), 0 2px 4px rgba(20, 30, 20, .05);
  --qc-shadow-lg: 0 16px 40px rgba(20, 30, 20, .14), 0 4px 10px rgba(20, 30, 20, .07);
  /* Bare shadow colour, for the places where a box-shadow token cannot be
     used — drop-shadow() on an SVG silhouette, for one. */
  --qc-shadow-color: rgba(20, 30, 20, .32);

  /* ---- Side menu width ----
     Tabler writes 15rem twice as a literal — once as the width of
     .navbar-vertical.navbar-expand-lg, once as the margin the `~ .page-wrapper`
     sibling rule keeps clear for it — so both are restated in components.css
     against --qc-sidebar-w, and this is the one place the number lives.

     Collapsed is a per-user account setting (ACCOUNT_SIDEBAR): "expanded" is the
     absence of the attribute, "collapsed" is [data-qc-sidebar="collapsed"] on
     <html>, stamped before the first paint by the bootstrap script in
     templates/minimal.html.twig — the same mechanism the theme and the table
     density use, so it survives Turbo body swaps.

     Theme-invariant, like the radii and the motion tokens: declared once and NOT
     re-declared in the dark block. ---- */
  --qc-sidebar-w-full: 15rem;
  --qc-sidebar-w-rail: 4.5rem;   /* a 24px icon centred, with a 44px target around it */
  --qc-sidebar-w: var(--qc-sidebar-w-full);

  /* The head that carries the mark, stated rather than left to the mark's own
     rendered height. The bar changes width over 280ms and the two logos are not
     the same shape, so a head measured from its contents was a different height
     at every frame of that — and the whole menu under it moved. A number here is
     what makes the head a fixed box the marks are placed inside; components.css
     §5.4 does the placing. Not re-declared in the dark block. */
  --qc-sidebar-head-h: 5rem;
  --qc-sidebar-head-pad: .5rem;

  /* ---- Motion (brand §7) ---- */
  /* One way out for everything that answers a gesture or arrives: it starts at
     once and settles slowly (ease-out-quint). A press used to ease *in*, which
     made the button hesitate under the finger. */
  --qc-ease-press: cubic-bezier(.23, 1, .32, 1);
  --qc-ease-fast: cubic-bezier(.23, 1, .32, 1);
  --qc-ease-standard: cubic-bezier(.645, .045, .355, 1);  /* ease-in-out-cubic */
  --qc-ease-reveal: cubic-bezier(.23, 1, .32, 1);
  --qc-ease-emphasized: cubic-bezier(.34, 1.56, .64, 1);  /* overshoot */
  --qc-dur-press: 100ms;
  --qc-dur-fast: 180ms;
  --qc-dur-standard: 280ms;
  --qc-dur-reveal: 400ms;
  --qc-dur-emphasized: 550ms;

  /* ==== Remap Tabler/Bootstrap → Quercia (light theme) ==== */
  --tblr-primary: var(--qc-primary);
  --tblr-primary-rgb: var(--qc-primary-rgb);
  --tblr-primary-fg: var(--qc-on-primary);

  --tblr-body-bg: var(--qc-background);
  --tblr-body-bg-rgb: var(--qc-background-rgb);
  --tblr-body-color: var(--qc-text-primary);
  --tblr-body-color-rgb: var(--qc-text-primary-rgb);
  --tblr-secondary-color: var(--qc-text-secondary);
  --tblr-muted: var(--qc-text-tertiary);
  --tblr-secondary: var(--qc-text-secondary);

  --tblr-border-color: var(--qc-border);
  --tblr-border-color-translucent: var(--qc-border);

  --tblr-card-bg: var(--qc-card-bg);
  --tblr-card-color: var(--qc-text-primary);
  --tblr-card-border-color: var(--qc-border);
  --tblr-bg-surface: var(--qc-surface-base);
  --tblr-bg-surface-secondary: var(--qc-surface-well);
  /* The foot of a card or a dialog. It used to be the page colour, which in
     dark put the darkest surface inside the highest one. */
  --tblr-bg-surface-tertiary: var(--qc-surface-well);
  /* `.bg-light` subtle panel → theme-aware surface (was literal near-white,
     which stayed white in dark, e.g. the timbratura datetime box) */
  --tblr-light: var(--qc-surface-well);
  --tblr-light-rgb: var(--qc-surface-light-rgb);

  --tblr-link-color: var(--qc-primary);
  --tblr-link-color-rgb: var(--qc-primary-rgb);
  --tblr-link-hover-color: var(--qc-primary-dark);

  /* One palette. Tabler keeps a green, a red, an amber and a blue of its own
     (#2fb344, #d63939, #f59f00, #4299e1) and a navy "dark"; left alone they
     sat beside ours on `.bg-*`, `.btn-outline-*` and `.text-dark`. Their ink
     is near-black: every one of these is a light ground. */
  --tblr-success: var(--qc-success);
  --tblr-success-rgb: 102, 187, 106;
  --tblr-danger: var(--qc-error);
  --tblr-danger-rgb: 239, 83, 80;
  --tblr-warning: var(--qc-warning);
  --tblr-warning-rgb: 255, 202, 40;
  --tblr-info: var(--qc-info);
  --tblr-info-rgb: 100, 181, 246;
  --tblr-success-fg: rgba(0, 0, 0, .87);
  --tblr-danger-fg: rgba(0, 0, 0, .87);
  --tblr-warning-fg: rgba(0, 0, 0, .87);
  --tblr-info-fg: rgba(0, 0, 0, .87);
  --tblr-dark: var(--qc-text-primary);
  --tblr-dark-rgb: var(--qc-text-primary-rgb);
  /* Tabler's own greys are blue (hue 260 beside our 150). Two of them are read
     directly: 500 by the footer, a `.nav` link, the breadcrumb's divider and an
     input group's label; 400 by placeholders and icons. */
  --tblr-gray-500: var(--qc-text-tertiary);
  --tblr-gray-400: var(--qc-text-tertiary);
  --tblr-active-bg: var(--qc-layer-selected);

  --tblr-font-sans-serif: var(--qc-font-body);

  /* Corner radii. Tabler 1.x routes every component radius through these three,
     so pointing them at the brand scale rounds the components we never restyle —
     dropdowns, popovers, toasts, offcanvas, progress — to the same curve as the
     ones we did, and drops the need to chase each new component with a rule.

     1.x also offers --tblr-border-radius-scale, a single multiplier over its own
     4/6/8. That is the tidier knob and it is the wrong one here: no one number
     carries 4px, 10px, 12px and 16px at once, so it would settle two of the four
     and miss the rest. These are named values, not a ratio, so they are set as
     named values. */
  --tblr-border-radius-sm: var(--qc-radius-sm);
  --tblr-border-radius: var(--qc-radius-input);
  --tblr-border-radius-md: var(--qc-radius-btn);
  --tblr-border-radius-lg: var(--qc-radius-card);
  --tblr-border-radius-pill: var(--qc-radius-pill);

  /* Historically-blue "brand/action" colors → forest green, for cohesion */
  --tblr-blue: var(--qc-primary);
  --tblr-blue-rgb: var(--qc-primary-rgb);
  --tblr-azure: var(--qc-primary);
  --tblr-azure-rgb: var(--qc-primary-rgb);
  --tblr-indigo: var(--qc-primary);
  --tblr-indigo-rgb: var(--qc-primary-rgb);

  /* Status hues → the ramp above. The triplets are the same nine colours
     written as numbers, because Tabler reads some of them that way; the dark
     block restates both, and has to: a `data-bs-theme="dark"` set on an
     element inside a light page would otherwise inherit these. */
  --tblr-yellow: var(--qc-status-yellow);
  --tblr-yellow-rgb: 223, 194, 104;
  --tblr-orange: var(--qc-status-orange);
  --tblr-orange-rgb: 250, 179, 115;
  --tblr-purple: var(--qc-status-purple);
  --tblr-purple-rgb: 221, 174, 250;
  --tblr-red: var(--qc-status-red);
  --tblr-red-rgb: 255, 171, 161;
  --tblr-lime: var(--qc-status-lime);
  --tblr-lime-rgb: 174, 211, 128;
  --tblr-cyan: var(--qc-status-cyan);
  --tblr-cyan-rgb: 92, 215, 242;
  --tblr-green: var(--qc-status-green);
  --tblr-green-rgb: 140, 217, 156;
  --tblr-teal: var(--qc-status-teal);
  --tblr-teal-rgb: 92, 220, 206;
  --tblr-pink: var(--qc-status-pink);
  --tblr-pink-rgb: 255, 166, 196;

  /* card/button radius (compat with legacy quercia.css) */
  --quercia-card-border-radius: var(--qc-radius-card);
  --quercia-btn-border-radius: var(--qc-radius-btn);
}

[data-bs-theme="dark"] {
  /* ---- Quercia viva — dark theme: carbone caldo (brand §2.2) ----
     The higher a thing sits, the lighter it is: page, card, floating control,
     overlay, each a step up, with the well under the card. Shadows barely show
     on a dark ground, so the steps and the edges carry the depth. */
  --qc-chart-materials: #3F86D1;
  --qc-chart-machines: #BD8420;
  --qc-chart-labour: #9270D0;
  --qc-chart-margin: #3FA868;
  --qc-surface-sunken: #0D110E;
  --qc-surface-well: #141815;
  --qc-surface-base: #1E241F;
  --qc-surface-raised: #292F2A;
  --qc-surface-overlay: #313833;
  /* Restated, like every alias below: a variable that names another is
     resolved where it is declared, and this block may be the only one on an
     element (a dark preview inside a light page). */
  --qc-background: var(--qc-surface-sunken);
  --qc-surface: var(--qc-surface-base);
  --qc-surface-light: var(--qc-surface-well);
  --qc-card-bg: var(--qc-surface-base);
  --qc-field-bg: var(--qc-surface-well);
  --qc-primary: #7EDB9B;         /* mint jewel */
  --qc-primary-light: #A9EBBB;
  --qc-primary-dark: #54C17B;
  --qc-on-primary: #04120A; /* near-black green, NOT #000 (brand §9.9) */
  --qc-primary-ink: #7EDB9B;
  --qc-accent: #E0AC66;
  --qc-on-accent: #241703;
  /* Text holds 4.5:1 on all five surfaces, the overlay included (16.2 → 10.3,
     10.9 → 6.9, 9.4 → 5.9), and tertiary holds it under a hover or a selection. */
  --qc-text-primary: #E9EEEA;
  --qc-text-secondary: #BFC6C0;
  --qc-text-tertiary: #B0B8B2;
  --qc-scroll-shade: rgba(0, 0, 0, .4);
  --qc-scrim-stacked: rgba(4, 18, 10, .3);  /* a second veil, over a modal the first already dimmed */
  --qc-scrim: rgba(4, 18, 10, .62);  /* on-primary's near-black green, deeper: the page is dark already */
  /* Opaque, and tuned on the card: 1.50:1 and 1.23:1 there (they were 1.12
     and 1.07, the second darker than the card it ruled). Opaque because both
     are also used as fills, strokes and the base of a mix. */
  --qc-border: #3B403C;
  --qc-divider: #2E332F;
  --qc-edge: rgb(255 255 255 / .13);
  --qc-shadow-top: inset 0 1px 0 rgb(255 255 255 / .07);
  --qc-page-glow: radial-gradient(120% 28rem at 50% -6rem, color-mix(in srgb, var(--qc-primary) 7%, transparent), transparent 70%);
  /* What you type in or tick: ≥3:1 on every surface (5.8 on the page, 4.8 on
     a card, 3.7 on an overlay — a dialog is where most forms are). */
  --qc-field-border: #889089;
  --qc-focus-ring: 0 0 0 .2rem color-mix(in srgb, var(--qc-primary) 30%, transparent);
  --qc-control-border: #889089;

  --qc-layer-hover: color-mix(in srgb, var(--qc-primary) 8%, transparent);
  --qc-layer-selected: color-mix(in srgb, var(--qc-primary) 12%, transparent);
  --qc-layer-wash: color-mix(in srgb, var(--qc-primary) 16%, transparent);
  --qc-layer-highlight: color-mix(in srgb, var(--qc-primary) 20%, transparent);
  --qc-grid-row-hover: var(--qc-layer-hover);
  --qc-grid-row-selected: var(--qc-layer-selected);
  --qc-grid-row-odd: transparent;
  --qc-grid-row-border: var(--qc-divider);

  /* The status ramp of this theme: the light one's hues, a step down, so a
     filled pill is no longer the brightest thing on the screen (7.0–7.8:1 on a
     card; the old fills ran from 5 to 12). */
  --qc-status-yellow: #CBAF54;
  --qc-status-orange: #E5A061;
  --qc-status-purple: #CA9CE6;
  --qc-status-red: #F19489;
  --qc-status-lime: #9CBF6E;
  --qc-status-cyan: #44C3DF;
  --qc-status-green: #79C68A;
  --qc-status-teal: #45C9BB;
  --qc-status-pink: #EC91B1;
  --qc-status-primary: var(--qc-primary);
  --qc-status-wash: 14%;
  --qc-tile-wash: 12%;
  --qc-tile-wash-hover: 19%;
  --qc-ink-l: .86;
  /* The semantic four join the ramp here: on a dark ground the light theme's
     vivid fills glowed. */
  --qc-success: #79C68A;
  --qc-success-light: #95D7A2;
  --qc-error: #F19489;
  --qc-warning: #CBAF54;
  --qc-info: #76B6F7;

  /* Text-safe semantic colors: bright, legible variants. The red is light
     enough to hold on a wash of itself over an overlay — a delete button in a
     dialog (5.1:1 under the pointer; #F08A86 was 4.0). */
  --qc-warning-text: #F5B84B;
  --qc-error-text: #F6A6A0;
  --qc-success-text: var(--qc-primary); /* mint jewel, AA on dark */
  --qc-info-text: var(--qc-info);

  --qc-background-rgb: 13, 17, 14;
  --qc-surface-light-rgb: 20, 24, 21;
  --qc-primary-rgb: 126, 219, 155;
  --qc-text-primary-rgb: 233, 238, 234;

  /* A shadow says little on a dark ground; it is kept short, and long only
     under an overlay, where the lighter surface and its edge do the rest. */
  --qc-shadow-sm: 0 1px 2px rgba(0, 0, 0, .30);
  --qc-shadow-md: 0 1px 2px rgba(0, 0, 0, .30), 0 6px 14px -4px rgba(0, 0, 0, .40);
  --qc-shadow-lg: 0 2px 4px rgba(0, 0, 0, .35), 0 18px 44px -10px rgba(0, 0, 0, .65);
  --qc-shadow-color: rgba(0, 0, 0, .55);

  /* The --tblr-* remaps must be reassigned here: Tabler sets them
     explicitly under [data-bs-theme="dark"] (equal specificity, last
     loaded wins: this file). */
  --tblr-primary: var(--qc-primary);
  --tblr-primary-rgb: var(--qc-primary-rgb);
  --tblr-primary-fg: var(--qc-on-primary);
  --tblr-body-bg: var(--qc-background);
  --tblr-body-bg-rgb: var(--qc-background-rgb);
  --tblr-body-color: var(--qc-text-primary);
  --tblr-body-color-rgb: var(--qc-text-primary-rgb);
  --tblr-secondary-color: var(--qc-text-secondary);
  --tblr-muted: var(--qc-text-tertiary);
  --tblr-secondary: var(--qc-text-secondary);
  --tblr-border-color: var(--qc-border);
  --tblr-border-color-translucent: var(--qc-border);
  --tblr-card-bg: var(--qc-card-bg);
  --tblr-card-color: var(--qc-text-primary);
  --tblr-card-border-color: var(--qc-border);
  --tblr-bg-surface: var(--qc-surface-base);
  --tblr-bg-surface-secondary: var(--qc-surface-well);
  --tblr-bg-surface-tertiary: var(--qc-surface-well);
  --tblr-light: var(--qc-surface-well);
  --tblr-light-rgb: var(--qc-surface-light-rgb);
  --tblr-link-color: var(--qc-primary);
  --tblr-link-color-rgb: var(--qc-primary-rgb);
  --tblr-link-hover-color: var(--qc-primary-light);
  --tblr-blue: var(--qc-primary);
  --tblr-blue-rgb: var(--qc-primary-rgb);
  --tblr-azure: var(--qc-primary);
  --tblr-azure-rgb: var(--qc-primary-rgb);
  --tblr-indigo: var(--qc-primary);
  --tblr-indigo-rgb: var(--qc-primary-rgb);

  --tblr-yellow: var(--qc-status-yellow);
  --tblr-yellow-rgb: 203, 175, 84;
  --tblr-orange: var(--qc-status-orange);
  --tblr-orange-rgb: 229, 160, 97;
  --tblr-purple: var(--qc-status-purple);
  --tblr-purple-rgb: 202, 156, 230;
  --tblr-red: var(--qc-status-red);
  --tblr-red-rgb: 241, 148, 137;
  --tblr-lime: var(--qc-status-lime);
  --tblr-lime-rgb: 156, 191, 110;
  --tblr-cyan: var(--qc-status-cyan);
  --tblr-cyan-rgb: 68, 195, 223;
  --tblr-green: var(--qc-status-green);
  --tblr-green-rgb: 121, 198, 138;
  --tblr-teal: var(--qc-status-teal);
  --tblr-teal-rgb: 69, 201, 187;
  --tblr-pink: var(--qc-status-pink);
  --tblr-pink-rgb: 236, 145, 177;

  --tblr-success: var(--qc-success);
  --tblr-success-rgb: 121, 198, 138;
  --tblr-danger: var(--qc-error);
  --tblr-danger-rgb: 241, 148, 137;
  --tblr-warning: var(--qc-warning);
  --tblr-warning-rgb: 203, 175, 84;
  --tblr-info: var(--qc-info);
  --tblr-info-rgb: 118, 182, 247;
  --tblr-dark: var(--qc-text-primary);
  --tblr-dark-rgb: var(--qc-text-primary-rgb);
  --tblr-gray-500: var(--qc-text-tertiary);
  --tblr-gray-400: var(--qc-text-tertiary);

  /* What Tabler sets for its own dark theme and nothing here answered: a
     blue-grey beside green-tinted neutrals wherever one of them showed —
     the active item of a menu, the border of a button under the pointer, a
     file field, a tooltip. */
  --tblr-active-bg: var(--qc-layer-selected);
  --tblr-emphasis-color: var(--qc-text-primary);
  --tblr-emphasis-color-rgb: var(--qc-text-primary-rgb);
  --tblr-tertiary-color: var(--qc-text-tertiary);
  --tblr-secondary-bg: var(--qc-surface-well);
  --tblr-tertiary-bg: var(--qc-surface-well);
  --tblr-bg-forms: var(--qc-field-bg);
  --tblr-border-active-color: var(--qc-control-border);
  --tblr-border-dark-color: var(--qc-border);
  --tblr-code-color: var(--qc-text-secondary);
  --tblr-highlight-color: var(--qc-text-primary);
  --tblr-highlight-bg: color-mix(in srgb, var(--qc-warning) 30%, transparent);
  --tblr-bg-surface-inverted: var(--qc-text-primary);
  --tblr-text-inverted: var(--qc-surface-sunken);
  --tblr-form-valid-color: var(--qc-success-text);
  --tblr-form-valid-border-color: var(--qc-success);
  --tblr-form-invalid-color: var(--qc-error-text);
  --tblr-form-invalid-border-color: var(--qc-error);
}

/* ---- Collapsed side menu ----
   One token flips, and both the bar and the room the page keeps clear for it
   follow (components.css §5.4). Same specificity as the :root block above — an
   attribute selector and :root both weigh (0,1,0) — so this has to stay after
   it to win. Only the width moves here; whether the rail is honoured at all is
   the media query in components.css, which is what keeps the attribute inert on
   a phone. */
[data-qc-sidebar="collapsed"] {
  --qc-sidebar-w: var(--qc-sidebar-w-rail);
}

/* ---- Compact density ----
   The account setting stamps [data-qc-density="compact"] on <html>; every grid
   on the page tightens at once, with no grid re-initialisation, because the JS
   theme reads these tokens through var(). A compact cell drops to one line of
   text — but the row is not one line tall, it is the taller of that line and
   --qc-grid-control-h, because a row still has to hold a status pill, a set of
   operator initials, a selection box and an action button whole.

   That was the bug this block used to have: the row was measured from the text
   alone and came out at ~25px, so every one of those five was clipped along its
   bottom edge. The controls are scaled down here instead — a compact row lands
   at ~35px, inside the 32-36px band the dense tables of the major design
   systems sit in, and the action button stays 28x28, above the 24x24 floor
   WCAG 2.2 SC 2.5.8 sets for a pointer target. */
[data-qc-density="compact"] {
  --qc-grid-spacing: 5px;
  --qc-grid-header-h: 36px;
  --qc-grid-font-size: var(--qc-fs-xs);
  --qc-grid-data-font-size: var(--qc-fs-md);
  --qc-grid-icon-size: 14px;
  --qc-grid-cell-px: 0.625rem;
  --qc-grid-cell-py: 0.15rem;        /* 2.4px: with a 28px box the row lands at 34.8px */
  --qc-grid-cell-lines: 1;
  /* Firefox's `scrollbar-width: thin` is about ten pixels and it is reserved,
     not overlaid; Chromium's is the 6px the ::-webkit-scrollbar rule asks for.
     Ten, then — and because the box is the *taller* of this and the 28px
     control floor, 18.2 + 10 clears the floor by a fifth of a pixel and the
     compact row does not grow to pay for it. */
  --qc-grid-cell-scroll-h: 0.625rem;
  --qc-grid-control-h: var(--qc-grid-control-h-tight);
  --qc-grid-action-px: 0.5rem;
  --qc-grid-action-gap: 0.25rem;
  --qc-grid-action-icon: 16px;
  --qc-grid-badge-font-size: var(--qc-fs-xs);
  --qc-grid-cell-small-size: var(--qc-fs-xs);
  --qc-grid-header-font-size: var(--qc-fs-xs);
}

/* Tabular figures — mandatory on data (brand §3.2) */
.qc-numeric {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}

/* Base body: brand font + warm neutral canvas */
body {
  font-family: var(--qc-font-body);
  background-color: var(--qc-background);
  color: var(--qc-text-primary);
}

/* prefers-reduced-motion: disable loops/reveals, keep fades (brand §7) */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
