@charset "UTF-8";
/* ==========================================================================
   Flex ERP Design System - v4
   --------------------------------------------------------------------------
   Enterprise data UI. Loaded after adminlte.min.css and owns every visual
   decision from here down.

   Brand:      indigo #4f46e5, inherited from v3 and deliberately preserved.
   Neutrals:   one cool slate ramp. No warm/cool mixing anywhere.
   Radius:     inputs & buttons 8px · cards & panels 12px · pills 999px.
               That is the entire system; nothing else invents a corner.
   Elevation:  hairline borders carry structure. Shadow is reserved for things
               that genuinely float (dropdown, modal, drawer, toast, palette).
   Density:    tuned for operators reading hundreds of rows. Figures are always
               tabular so columns align on the decimal.
   Motion:     140ms, ease-out, transform/opacity only. Nothing cinematic.
   ========================================================================== */

/* ==========================================================================
   1. TOKENS
   ========================================================================== */
:root {
    /* Brand ramp */
    --c-primary-50:  #eef2ff;
    --c-primary-100: #e0e7ff;
    --c-primary-200: #c7d2fe;
    --c-primary-300: #a5b4fc;
    --c-primary-400: #818cf8;
    --c-primary-500: #6366f1;
    --c-primary-600: #4f46e5;
    --c-primary-700: #4338ca;
    --c-primary-800: #3730a3;
    --c-primary:     #4f46e5;
    --c-primary-hov: #4338ca;
    --c-on-primary:  #ffffff;

    /* Semantic - each carries fg / surface / border so states never guess */
    --c-success: #047857; --c-success-bg: #ecfdf5; --c-success-bd: #a7f3d0;
    --c-info:    #0369a1; --c-info-bg:    #f0f9ff; --c-info-bd:    #bae6fd;
    --c-warning: #b45309; --c-warning-bg: #fffbeb; --c-warning-bd: #fde68a;
    --c-danger:  #b91c1c; --c-danger-bg:  #fef2f2; --c-danger-bd:  #fecaca;
    --c-neutral: #475569; --c-neutral-bg: #f1f5f9; --c-neutral-bd: #e2e8f0;
    --c-teal:    #0f766e;

    /* Surfaces */
    --bg-app:      #f5f6f8;
    --bg-surface:  #ffffff;
    --bg-raised:   #ffffff;
    --bg-sunken:   #f8fafc;
    --bg-hover:    #f6f7f9;
    --bg-selected: var(--c-primary-50);
    --bg-shell:    #0f1117;
    --bg-shell-2:  #161923;

    /* Text */
    --text-strong: #0f172a;
    --text:        #334155;
    --text-muted:  #64748b;
    --text-faint:  #94a3b8;

    /* Lines */
    --border:        #e6e8ec;
    --border-soft:   #eef0f3;
    --border-strong: #d5d9e0;
    --ring:          rgba(79, 70, 229, .3);

    /* Elevation (overlays only) */
    --shadow-xs: 0 1px 1px rgba(15,23,42,.04);
    --shadow-sm: 0 1px 2px rgba(15,23,42,.06);
    --shadow-md: 0 4px 12px -2px rgba(15,23,42,.10), 0 2px 4px -2px rgba(15,23,42,.06);
    --shadow-lg: 0 12px 32px -8px rgba(15,23,42,.18), 0 4px 10px -4px rgba(15,23,42,.08);
    --shadow-xl: 0 24px 60px -12px rgba(15,23,42,.26);

    /* Geometry */
    --r-control: 8px;
    --r-panel:   12px;
    --r-pill:    999px;

    /* Type */
    --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

    /* Motion */
    --ease:  cubic-bezier(.2, 0, 0, 1);
    --speed: .14s;

    /* Layout */
    --topbar-h:  58px;
    --sidebar-w: 252px;

    /* Layers */
    --z-sticky: 1020; --z-dropdown: 1030; --z-drawer: 1045;
    --z-modal: 1050; --z-toast: 1080; --z-palette: 1090; --z-tooltip: 1100;

    /* --- v3 aliases, kept so existing views keep rendering ------------- */
    --bg-surface-2: var(--bg-sunken);
    --bg-sidebar:   var(--bg-shell);
    --bg-sidebar-2: var(--bg-shell-2);
    --radius-sm: 6px;
    --radius:    var(--r-control);
    --radius-lg: var(--r-panel);
    --radius-pill: var(--r-pill);
    --border-2:  var(--border-soft);
    --c-primary-dark: var(--c-primary-hov);
}

html[data-theme="dark"] {
    --c-primary:     #818cf8;
    --c-primary-hov: #a5b4fc;
    --c-on-primary:  #0f1117;
    --c-primary-50:  #1e2440;
    --c-primary-100: #262e50;

    --c-success: #34d399; --c-success-bg: #062820; --c-success-bd: #14532d;
    --c-info:    #38bdf8; --c-info-bg:    #06202e; --c-info-bd:    #0c4a6e;
    --c-warning: #fbbf24; --c-warning-bg: #2a1d05; --c-warning-bd: #78350f;
    --c-danger:  #f87171; --c-danger-bg:  #2b0f10; --c-danger-bd:  #7f1d1d;
    --c-neutral: #94a3b8; --c-neutral-bg: #1c2230; --c-neutral-bd: #2c3444;

    --bg-app:      #0b0d12;
    --bg-surface:  #12151d;
    --bg-raised:   #171b25;
    --bg-sunken:   #0e1116;
    --bg-hover:    #1a1f2b;
    --bg-selected: #1e2440;
    --bg-shell:    #0a0c11;
    --bg-shell-2:  #10131b;

    --text-strong: #f1f5f9;
    --text:        #cbd5e1;
    --text-muted:  #94a3b8;
    --text-faint:  #64748b;

    --border:        #232936;
    --border-soft:   #1b212c;
    --border-strong: #303849;
    --ring:          rgba(129, 140, 248, .35);

    --shadow-sm: 0 1px 2px rgba(0,0,0,.5);
    --shadow-md: 0 4px 12px -2px rgba(0,0,0,.6);
    --shadow-lg: 0 12px 32px -8px rgba(0,0,0,.7);
    --shadow-xl: 0 24px 60px -12px rgba(0,0,0,.8);
}

/* ==========================================================================
   2. BASE
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

body {
    font-family: var(--font-sans);
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--text);
    background: var(--bg-app);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow-x: hidden;
}

h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5 {
    color: var(--text-strong);
    font-weight: 600;
    letter-spacing: -.011em;
    margin-bottom: .5rem;
}
h1, .h1 { font-size: 1.5rem;    letter-spacing: -.02em; }
h2, .h2 { font-size: 1.25rem;   letter-spacing: -.016em; }
h3, .h3 { font-size: 1.0625rem; }
h4, .h4 { font-size: .9375rem; }
h5, h6, .h5 { font-size: .875rem; }

a { color: var(--c-primary); text-decoration: none; transition: color var(--speed) var(--ease); }
a:hover { color: var(--c-primary-hov); text-decoration: none; }

hr { border-color: var(--border); opacity: 1; }
small, .small { font-size: .8125rem; }
code, kbd, pre, samp { font-family: var(--font-mono); }

/* Every figure is tabular so decimals line up down a column */
td, th, .flux-num, .info-box-number, .small-box .inner h3,
input[type="number"], .text-right, .text-end {
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
}

::selection { background: var(--c-primary-200); color: var(--text-strong); }

:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; border-radius: 4px; }

* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
    background: var(--border-strong); border-radius: var(--r-pill);
    border: 3px solid transparent; background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-faint); background-clip: content-box; }

/* ==========================================================================
   3. APP SHELL
   ========================================================================== */
.wrapper { background: var(--bg-app); }

.content-wrapper {
    background: var(--bg-app) !important;
    min-height: calc(100vh - var(--topbar-h));
    padding: 0;
}
/* Page gutter.
   Views nest their container differently - some as .content > .container-fluid,
   some as a bare .container-fluid - and the old rule only matched direct
   children, so those pages rendered hard against the sidebar with no gutter at
   all. Match the shapes actually in use, and stop a nested container from
   doubling the padding. */
.content-wrapper > .content > .container-fluid,
.content-wrapper > .content > .container,
.content-wrapper .content > .container-fluid,
.content-wrapper > .container-fluid,
.content-wrapper > .container,
.content-wrapper > section > .container-fluid { padding: 1.25rem 1.75rem; }

.content-wrapper .container-fluid .container-fluid,
.content-wrapper .container-fluid .container { padding-left: 0; padding-right: 0; }
.content-header { padding: 1.25rem 1.5rem .25rem; }
.content-header + .content > .container-fluid { padding-top: .75rem; }

@media (max-width: 767.98px) {
    .content-wrapper > .content > .container-fluid,
    .content-wrapper > .container-fluid,
    .content-header { padding-left: 1rem; padding-right: 1rem; }
}
/* Ultra-wide: stop table rows running to 3000px */
@media (min-width: 1900px) {
    .content-wrapper > .content > .container-fluid,
    .content-wrapper > .container-fluid { max-width: 1800px; }
}

/* --------------------------------------------------------- 3.1 Top bar --- */
.flux-topbar {
    height: var(--topbar-h); min-height: var(--topbar-h);
    display: flex; align-items: center; gap: .75rem;
    padding: 0 1rem 0 .5rem;
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border);
    box-shadow: none;
}
.flux-topbar-left  { display: flex; align-items: center; gap: .25rem; min-width: 0; }
.flux-topbar-right { display: flex; align-items: center; gap: .375rem; margin-left: auto; }

.flux-iconbtn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; flex: 0 0 34px;
    border: 0; background: transparent; color: var(--text-muted);
    border-radius: var(--r-control); cursor: pointer; position: relative;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.flux-iconbtn:hover { background: var(--bg-hover); color: var(--text-strong); }
.flux-iconbtn i { font-size: 1.2rem; }
.flux-dot {
    position: absolute; top: 6px; right: 7px;
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--c-danger); border: 2px solid var(--bg-surface);
}
.flux-dot[hidden] { display: none; }

.flux-crumbs {
    display: flex; align-items: center; gap: .25rem;
    font-size: .8125rem; color: var(--text-muted); min-width: 0; overflow: hidden;
}
.flux-crumbs a,
.flux-crumbs span { white-space: nowrap; }
.flux-crumbs a { color: var(--text-muted); }
.flux-crumbs a:hover { color: var(--text-strong); text-decoration: underline; }

/* The page you are on. It is a <span>, not a link - the trail used to put
   aria-current on an <a> pointing at the current URL, so the only clickable
   crumb was the one that went nowhere. */
.flux-crumbs [aria-current="page"] { color: var(--text-strong); font-weight: 550; }

/* A crumb with no href: a path that is real in the URL but is not a page on its
   own (`sales/edit` without an id). Dimmed so it reads as context rather than as
   a link that failed to work. */
.flux-crumbs span:not([aria-current]) { color: var(--text-faint); }

.flux-crumbs i { font-size: 1rem; color: var(--text-faint); flex: 0 0 auto; }

/* Narrow screens keep the ends of the trail and drop the middle: where you are
   and the way out are the useful parts. The last crumb never shrinks. */
@media (max-width: 1399.98px) {
    .flux-crumbs > :nth-child(n+3):nth-last-child(n+4) { display: none; }
}
@media (max-width: 991.98px) { .flux-crumbs { display: none; } }

/* Back. Sits left of the trail and survives the breakpoint that hides it, so a
   phone still has a way up even without the crumbs. */
.flux-back { flex: 0 0 auto; }
.flux-back i { font-size: 1.15rem; }

.flux-search-trigger {
    display: flex; align-items: center; gap: .5rem;
    flex: 1 1 auto; max-width: 460px;
    height: 34px; padding: 0 .5rem 0 .625rem;
    background: var(--bg-sunken); border: 1px solid var(--border);
    border-radius: var(--r-control);
    color: var(--text-faint); font-size: .8125rem; text-align: left; cursor: pointer;
    transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.flux-search-trigger:hover { border-color: var(--border-strong); background: var(--bg-surface); }
.flux-search-trigger i { font-size: 1.05rem; }
.flux-search-trigger span { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 767.98px) {
    .flux-search-trigger span, .flux-search-trigger kbd { display: none; }
    .flux-search-trigger { flex: 0 0 34px; max-width: 34px; justify-content: center; padding: 0; }
}

kbd {
    display: inline-block; padding: .1em .35em;
    font-family: var(--font-sans); font-size: .6875rem; font-weight: 500; line-height: 1.4;
    color: var(--text-muted); background: var(--bg-surface);
    border: 1px solid var(--border); border-bottom-width: 2px;
    border-radius: 5px; box-shadow: none;
}

.flux-contextbtn, .flux-context-static {
    display: inline-flex; align-items: center; gap: .375rem;
    height: 34px; padding: 0 .625rem;
    background: transparent; border: 1px solid var(--border);
    border-radius: var(--r-control);
    font-size: .8125rem; font-weight: 500; color: var(--text); cursor: pointer;
    transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.flux-contextbtn:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.flux-contextbtn i, .flux-context-static i { font-size: 1rem; color: var(--text-muted); }
@media (max-width: 1199.98px) { .flux-contextbtn span, .flux-context-static { display: none; } }

.flux-avatarbtn { border: 0; background: transparent; padding: 0; cursor: pointer; line-height: 0; }
.flux-avatar {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: var(--r-pill);
    background: var(--c-primary); color: var(--c-on-primary);
    font-size: .75rem; font-weight: 600; letter-spacing: .02em;
}
.flux-avatar-lg { width: 42px; height: 42px; font-size: .9375rem; flex: 0 0 42px; }

/* ---------------------------------------------------------- 3.1b Footer --- */
.main-footer {
    background: transparent; border-top: 1px solid var(--border);
    padding: .875rem 1.5rem; font-size: .75rem; color: var(--text-faint);
}
.main-footer a { color: var(--text-muted); }
.main-footer a:hover { color: var(--c-primary); }
.main-footer strong { font-weight: 500; }

/* --------------------------------------------------------- 3.2 Sidebar --- */
.main-sidebar, .main-sidebar::before { width: var(--sidebar-w); }
.flux-sidebar {
    width: var(--sidebar-w);
    background: var(--bg-shell);
    border-right: 1px solid rgba(255,255,255,.06);
    box-shadow: none;
}
/* Only offset for the sidebar on desktop. Below lg AdminLTE slides the
   sidebar over the content, so a static margin would push the top bar
   off-screen. */
@media (min-width: 992px) {
    body:not(.sidebar-collapse) .content-wrapper,
    body:not(.sidebar-collapse) .main-header,
    body:not(.sidebar-collapse) .main-footer { margin-left: var(--sidebar-w); }
}

.flux-sidebar .brand-link {
    display: flex; align-items: center; gap: .625rem;
    height: var(--topbar-h); padding: 0 1rem;
    border-bottom: 1px solid rgba(255,255,255,.06);
    background: var(--bg-shell);
}
.flux-sidebar .brand-image {
    width: 26px; height: 26px; border-radius: 6px; object-fit: cover;
    margin: 0; opacity: 1; float: none; max-height: none;
}
/* Two lines: the PRODUCT in full weight, the company beneath it in a quieter
   size. The masthead used to carry only one name and it was the company's, so
   the product was absent from the interface entirely. */
.flux-sidebar .brand-text {
    display: flex; flex-direction: column; gap: 1px;
    min-width: 0; line-height: 1.15;
}
.flux-sidebar .brand-product {
    font-size: .9375rem; font-weight: 600; letter-spacing: -.012em; color: #fff;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.flux-sidebar .brand-company {
    font-size: .6875rem; font-weight: 450; letter-spacing: 0;
    color: rgba(255,255,255,.58);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Collapsed to icons there is no room for a second line, and AdminLTE fades the
   text out anyway - dropping it stops a half-faded company name overlapping the
   logo mid-transition. */
.sidebar-collapse .flux-sidebar .brand-company { display: none; }

.flux-sidebar .sidebar { padding: .5rem .5rem 2rem; }

.flux-navfilter { position: relative; display: flex; align-items: center; margin: .25rem .25rem .5rem; }
.flux-navfilter i {
    position: absolute; left: .55rem; font-size: 1rem;
    color: rgba(255,255,255,.35); pointer-events: none;
}
.flux-navfilter kbd {
    position: absolute; right: .4rem;
    background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.12);
    color: rgba(255,255,255,.45);
}
.flux-navfilter input {
    width: 100%; height: 32px; padding: 0 2rem 0 1.9rem;
    background: rgba(255,255,255,.045);
    border: 1px solid rgba(255,255,255,.07);
    border-radius: var(--r-control);
    color: #fff; font-size: .8125rem;
    transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.flux-navfilter input::placeholder { color: rgba(255,255,255,.32); }
.flux-navfilter input:focus {
    outline: none; background: rgba(255,255,255,.07); border-color: var(--c-primary-400);
}
.flux-navfilter input::-webkit-search-cancel-button { filter: invert(1); opacity: .4; }
.flux-navfilter-empty { color: rgba(255,255,255,.4); font-size: .8125rem; text-align: center; padding: 1.5rem .5rem; }
.flux-navfilter-empty[hidden] { display: none; }

/* Section heading above a run of related modules */
.flux-sidebar .nav-section {
    padding: 1.1rem .7rem .35rem;
    font-size: .625rem; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
    color: rgba(255,255,255,.34);
}
.flux-sidebar .nav-sidebar > .nav-item:first-child + .nav-section,
.flux-sidebar .nav-section:first-child { padding-top: .35rem; }

/* Top-level entries carry the weight; leaves inside stay lighter */
.flux-sidebar .nav-sidebar > .nav-item > .nav-link {
    font-size: .875rem; font-weight: 550; color: rgba(255,255,255,.82);
    padding: .55rem .7rem;
}
.flux-sidebar .nav-sidebar .nav-link {
    display: flex; align-items: center; gap: .7rem; margin-bottom: 2px;
    border-radius: var(--r-control);
    color: rgba(255,255,255,.68);
    font-size: .8125rem; font-weight: 450;
    padding: .5rem .625rem;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.flux-sidebar .nav-sidebar .nav-link:hover { background: rgba(255,255,255,.07); color: #fff; }
.flux-sidebar .nav-sidebar > .nav-item > .nav-link.active {
    background: rgba(255,255,255,.09); color: #fff; font-weight: 650;
}
/* Current module gets a brand rail so the eye finds it instantly */
.flux-sidebar .nav-sidebar > .nav-item.menu-open > .nav-link,
.flux-sidebar .nav-sidebar > .nav-item > .nav-link.active {
    box-shadow: inset 2px 0 0 var(--c-primary-400);
}
.flux-sidebar .nav-sidebar > .nav-item > .nav-link .nav-icon { font-size: 1.15rem; }
/* Only the leaf claims to be the current page, so the eye lands in one place */
.flux-sidebar .nav-treeview .nav-link.active {
    background: var(--c-primary); color: #fff; font-weight: 550;
}
.flux-sidebar .nav-treeview .nav-link.active .nav-icon { color: #fff; }
.flux-sidebar .nav-icon {
    font-size: 1.05rem; width: 1.25rem; text-align: center;
    color: rgba(255,255,255,.5); flex: 0 0 auto; margin: 0;
}
.flux-sidebar .nav-link:hover .nav-icon, .flux-sidebar .nav-link.active .nav-icon { color: inherit; }
.flux-sidebar .nav-link p {
    margin: 0; flex: 1; display: flex; align-items: center; justify-content: space-between;
}
.flux-sidebar .nav-link p > .right { font-size: 1.05rem; transition: transform var(--speed) var(--ease); }
.flux-sidebar .menu-open > .nav-link p > .right { transform: rotate(90deg); }

.flux-sidebar .nav-treeview { padding-left: .5rem; margin: 2px 0 4px; position: relative; }
.flux-sidebar .nav-treeview::before {
    content: ''; position: absolute; left: 1.05rem; top: 2px; bottom: 2px;
    width: 1px; background: rgba(255,255,255,.08);
}
.flux-sidebar .nav-treeview .nav-link { padding-left: 1.75rem; }
.flux-sidebar .nav-treeview .nav-icon { font-size: .95rem; }

.flux-sidebar .nav-subheader {
    padding: .625rem .625rem .25rem 1.75rem;
    font-size: .6875rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
    color: rgba(255,255,255,.32);
}
.nav-item-signout { margin-top: .75rem; padding-top: .75rem; border-top: 1px solid rgba(255,255,255,.07); }
.nav-item-signout .nav-link:hover { background: rgba(248,113,113,.12) !important; color: #fca5a5 !important; }

body.sidebar-collapse .flux-navfilter,
body.sidebar-collapse .flux-sidebar .nav-subheader,
body.sidebar-collapse .flux-navfilter-empty { display: none; }

/* ==========================================================================
   4. PAGE HEADER
   --------------------------------------------------------------------------
   Title and subtitle on the left, actions hard right, always one row.
   Emitted by page_header() in app/helpers/ui_helper.php, so fixing it here
   fixes the header on every screen at once.
   ========================================================================== */
/* Title block sits left, actions right, both aligned to the top so a two-line
   title does not push the buttons down the page. */
.page-header, .flux-page-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 1rem; flex-wrap: nowrap; margin-bottom: 1.5rem;
}
.page-header > div:first-child, .flux-page-head > div:first-child { min-width: 0; }

/* .ph-title wraps a heading and its description. It was a row-flex container,
   which laid the two out side by side - the description read as a second
   heading rather than a caption under it. Stack them. */
.ph-title {
    display: flex; flex-direction: column; align-items: flex-start;
    gap: .2rem; min-width: 0;
}
/* Two different markups share this class, and they want opposite axes.
   `page_header()` emits <h1 class="ph-title"><i>...</i> Title</h1>, where the
   children are an icon and the title text and belong on ONE line. The rule
   above is for the wrapper form, <div class="ph-title"><h1>...</h1><p>...</p></div>,
   which stacks. Without this the icon sat above its own heading. */
h1.ph-title, .h1.ph-title {
    flex-direction: row; align-items: center; gap: .5rem; margin: 0;
    font-size: 1.375rem; font-weight: 650; letter-spacing: -.02em;
    line-height: 1.25; color: var(--text-strong);
}

.ph-title > h1, .ph-title > .h1,
.flux-page-head h1, .flux-page-head .h1 {
    display: flex; align-items: center; gap: .5rem;
    margin: 0; font-size: 1.375rem; font-weight: 650; letter-spacing: -.02em;
    line-height: 1.25; color: var(--text-strong);
}
/* The description: quieter, lighter and allowed to wrap like a sentence,
   because clipping it to one line hid the half that explained the page. */
.ph-title > p, .ph-sub, .flux-page-head > p {
    margin: 0; color: var(--text-muted);
    font-size: .8125rem; font-weight: 400; line-height: 1.5;
    max-width: 68ch; white-space: normal;
}
.ph-title > i, .ph-title > h1 > i {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: 9px; flex: 0 0 32px;
    background: var(--c-primary-50); color: var(--c-primary); font-size: 1.15rem;
}
.ph-actions, .flux-page-actions {
    margin-left: auto; display: flex; align-items: center; gap: .5rem;
    flex: 0 0 auto; white-space: nowrap;
}
.breadcrumb-modern { display: flex; align-items: center; gap: .3rem; font-size: .75rem; color: var(--text-muted); margin-top: .25rem; }
.breadcrumb-modern a { color: var(--text-muted); }
.breadcrumb-modern a:hover { color: var(--c-primary); }

/* Below md the actions wrap under the title, but stay a single row themselves */
@media (max-width: 767.98px) {
    .page-header, .flux-page-head { flex-wrap: wrap; align-items: flex-start; }
    .ph-actions, .flux-page-actions { margin-left: 0; width: 100%; }
    .ph-actions .btn, .flux-page-actions .btn { flex: 1; }
    .ph-title > p, .ph-sub, .flux-page-head > p { white-space: normal; }
}

.content-header h1 { font-size: 1.375rem; margin: 0; }
.content-header .breadcrumb { background: transparent; padding: 0; margin: 0; font-size: .8125rem; }
.content-header .breadcrumb-item + .breadcrumb-item::before { color: var(--text-faint); }

/* content-header immediately followed by content: one gap, not two */
.content-header + .content { padding-top: 0; }
.content-header + .content > .container-fluid { padding-top: 0; }

/* Section heading inside a page */
.section-title {
    display: flex; align-items: center; gap: .45rem;
    font-size: .8125rem; font-weight: 650; letter-spacing: .01em;
    color: var(--text-strong); margin: 0 0 .75rem;
}
.section-title i { color: var(--c-primary); font-size: 1rem; }
.required-star { color: var(--c-danger); font-weight: 700; }

/* ==========================================================================
   5. BUTTONS
   ========================================================================== */
.btn, .flux-btn {
    --btn-bg: var(--bg-surface);
    --btn-fg: var(--text);
    --btn-bd: var(--border-strong);
    display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
    height: 34px; padding: 0 .875rem;
    font-family: var(--font-sans); font-size: .8125rem; font-weight: 550; line-height: 1;
    white-space: nowrap;
    color: var(--btn-fg); background: var(--btn-bg);
    border: 1px solid var(--btn-bd); border-radius: var(--r-control);
    cursor: pointer; text-decoration: none;
    transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease),
                color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
/* Hover is driven entirely by per-variant tokens. The previous rule set
   `background: var(--bg-hover)` (near-white) on every button while the filled
   variants kept white text, so a blue button became white-on-white on hover. */
.btn:hover, .flux-btn:hover {
    background: var(--btn-bg-h, var(--bg-hover));
    color:      var(--btn-fg-h, var(--btn-fg));
    border-color: var(--btn-bd-h, var(--text-faint));
}
.btn:active, .flux-btn:active { transform: translateY(1px); }
.btn:focus, .btn.focus { box-shadow: 0 0 0 3px var(--ring); outline: none; }
.btn i, .flux-btn i { font-size: 1rem; }
.btn.disabled, .btn:disabled { opacity: .5; pointer-events: none; }

.btn-sm, .flux-btn-sm, .btn-group-sm > .btn { height: 28px; padding: 0 .625rem; font-size: .75rem; border-radius: 6px; }
.btn-lg { height: 40px; padding: 0 1.125rem; font-size: .875rem; }
.btn-block { width: 100%; }

/* Filled buttons invert on hover: the surface goes pale and the label and border
   take the button's own colour. Contrast stays well above WCAG AA in both states,
   and the colour still reads as "this is the primary action". */
.btn-primary {
    --btn-bg: var(--c-primary); --btn-fg: var(--c-on-primary); --btn-bd: var(--c-primary);
    --btn-bg-h: var(--c-primary-50); --btn-fg-h: var(--c-primary-700); --btn-bd-h: var(--c-primary);
}
.btn-secondary {
    --btn-bg: var(--bg-surface); --btn-fg: var(--text); --btn-bd: var(--border-strong);
    --btn-bg-h: var(--bg-hover); --btn-fg-h: var(--text-strong); --btn-bd-h: var(--text-faint);
}
.btn-success {
    --btn-bg: var(--c-success); --btn-fg: #fff; --btn-bd: var(--c-success);
    --btn-bg-h: var(--c-success-bg); --btn-fg-h: var(--c-success); --btn-bd-h: var(--c-success);
}
.btn-danger {
    --btn-bg: var(--c-danger); --btn-fg: #fff; --btn-bd: var(--c-danger);
    --btn-bg-h: var(--c-danger-bg); --btn-fg-h: var(--c-danger); --btn-bd-h: var(--c-danger);
}
.btn-warning {
    --btn-bg: var(--c-warning); --btn-fg: #fff; --btn-bd: var(--c-warning);
    --btn-bg-h: var(--c-warning-bg); --btn-fg-h: var(--c-warning); --btn-bd-h: var(--c-warning);
}
.btn-info {
    --btn-bg: var(--c-info); --btn-fg: #fff; --btn-bd: var(--c-info);
    --btn-bg-h: var(--c-info-bg); --btn-fg-h: var(--c-info); --btn-bd-h: var(--c-info);
}
.btn-dark {
    --btn-bg: var(--text-strong); --btn-fg: var(--bg-surface); --btn-bd: var(--text-strong);
    --btn-bg-h: var(--bg-sunken); --btn-fg-h: var(--text-strong); --btn-bd-h: var(--text-strong);
}
.btn-light {
    --btn-bg: var(--bg-sunken); --btn-fg: var(--text); --btn-bd: var(--border);
    --btn-bg-h: var(--bg-hover); --btn-fg-h: var(--text-strong); --btn-bd-h: var(--border-strong);
}
/* AdminLTE's extra hues, folded onto the same four semantics */
.btn-navy, .btn-indigo, .btn-purple { --btn-bg: var(--c-primary); --btn-fg: var(--c-on-primary); --btn-bd: var(--c-primary);
    --btn-bg-h: var(--c-primary-50); --btn-fg-h: var(--c-primary-700); --btn-bd-h: var(--c-primary); }
.btn-teal, .btn-cyan { --btn-bg: var(--c-info); --btn-fg: #fff; --btn-bd: var(--c-info);
    --btn-bg-h: var(--c-info-bg); --btn-fg-h: var(--c-info); --btn-bd-h: var(--c-info); }
.btn-olive, .btn-lime { --btn-bg: var(--c-success); --btn-fg: #fff; --btn-bd: var(--c-success);
    --btn-bg-h: var(--c-success-bg); --btn-fg-h: var(--c-success); --btn-bd-h: var(--c-success); }
.btn-maroon, .btn-pink, .btn-fuchsia { --btn-bg: var(--c-danger); --btn-fg: #fff; --btn-bd: var(--c-danger);
    --btn-bg-h: var(--c-danger-bg); --btn-fg-h: var(--c-danger); --btn-bd-h: var(--c-danger); }
.btn-orange { --btn-bg: var(--c-warning); --btn-fg: #fff; --btn-bd: var(--c-warning);
    --btn-bg-h: var(--c-warning-bg); --btn-fg-h: var(--c-warning); --btn-bd-h: var(--c-warning); }
.btn-default { --btn-bg: var(--bg-surface); --btn-fg: var(--text); --btn-bd: var(--border-strong);
    --btn-bg-h: var(--bg-hover); --btn-fg-h: var(--text-strong); --btn-bd-h: var(--text-faint); }

/* Outline buttons deepen on hover: pale tinted fill, colour stays the label. */
.btn-outline-primary {
    --btn-bg: transparent; --btn-fg: var(--c-primary); --btn-bd: var(--c-primary);
    --btn-bg-h: var(--c-primary-50); --btn-fg-h: var(--c-primary-700); --btn-bd-h: var(--c-primary);
}
.btn-outline-secondary {
    --btn-bg: transparent; --btn-fg: var(--text); --btn-bd: var(--border-strong);
    --btn-bg-h: var(--bg-hover); --btn-fg-h: var(--text-strong); --btn-bd-h: var(--text-faint);
}
.btn-outline-danger {
    --btn-bg: transparent; --btn-fg: var(--c-danger); --btn-bd: var(--c-danger-bd);
    --btn-bg-h: var(--c-danger-bg); --btn-fg-h: var(--c-danger); --btn-bd-h: var(--c-danger);
}
.btn-outline-success {
    --btn-bg: transparent; --btn-fg: var(--c-success); --btn-bd: var(--c-success-bd);
    --btn-bg-h: var(--c-success-bg); --btn-fg-h: var(--c-success); --btn-bd-h: var(--c-success);
}
.btn-outline-info {
    --btn-bg: transparent; --btn-fg: var(--c-info); --btn-bd: var(--c-info-bd);
    --btn-bg-h: var(--c-info-bg); --btn-fg-h: var(--c-info); --btn-bd-h: var(--c-info);
}
.btn-outline-warning {
    --btn-bg: transparent; --btn-fg: var(--c-warning); --btn-bd: var(--c-warning-bd);
    --btn-bg-h: var(--c-warning-bg); --btn-fg-h: var(--c-warning); --btn-bd-h: var(--c-warning);
}
.btn-outline-dark {
    --btn-bg: transparent; --btn-fg: var(--text-strong); --btn-bd: var(--border-strong);
    --btn-bg-h: var(--bg-sunken); --btn-fg-h: var(--text-strong); --btn-bd-h: var(--text-strong);
}

.flux-btn-quiet {
    --btn-bg: transparent; --btn-bd: transparent; --btn-fg: var(--text-muted);
    --btn-bg-h: var(--bg-hover); --btn-fg-h: var(--text-strong); --btn-bd-h: transparent;
}
.btn-link {
    --btn-bg: transparent; --btn-bd: transparent; --btn-fg: var(--c-primary);
    --btn-bg-h: transparent; --btn-fg-h: var(--c-primary-700); --btn-bd-h: transparent;
    height: auto; padding: 0 .25rem;
}
.btn-link:hover { text-decoration: underline; }

/* An export button that is fetching its engine. The libraries are loaded on
   first click (see views/inc/header.php), which on a slow connection is a
   second or two of nothing happening - long enough for someone to click again. */
.btn.is-loading, .dt-button.is-loading {
    position: relative; color: transparent !important; pointer-events: none;
}
.btn.is-loading::after, .dt-button.is-loading::after {
    content: ''; position: absolute; inset: 0; margin: auto;
    width: 13px; height: 13px; border-radius: 50%;
    border: 2px solid var(--text-muted); border-top-color: transparent;
    animation: fluxbtnspin .6s linear infinite;
}
@keyframes fluxbtnspin { to { transform: rotate(360deg); } }

.btn-group > .btn { border-radius: 0; }
.btn-group > .btn:first-child { border-top-left-radius: var(--r-control); border-bottom-left-radius: var(--r-control); }
.btn-group > .btn:last-child  { border-top-right-radius: var(--r-control); border-bottom-right-radius: var(--r-control); }
.btn-group > .btn + .btn { margin-left: -1px; }
.btn-group > .btn:hover { z-index: 1; }

/* ==========================================================================
   6. FORMS
   ========================================================================== */
label, .col-form-label {
    display: block; margin-bottom: .3rem;
    font-size: .75rem; font-weight: 600; letter-spacing: .01em; color: var(--text-strong);
}
.form-group { margin-bottom: 1rem; }

/* ── Bootstrap 5 class shims ──────────────────────────────────────────────────
 *
 * This application loads Bootstrap 4 (via AdminLTE 3). A number of views were
 * written against Bootstrap 5's class names, which simply do not exist here - so
 * the element renders with NO styling at all rather than looking slightly wrong,
 * which is why it is easy to miss in review and obvious to a user.
 *
 * The visible case was the role selector on Settings → Roles & Permissions:
 * `class="form-select"` on a Bootstrap 4 page is an unstyled native <select>.
 *
 * Aliased here rather than corrected across the views: it is 1 `form-select`,
 * 37 `form-label`, 24 `me-*`, and a handful of others across ~60 files. One block
 * fixes every one of them, cannot introduce a typo into a view, and keeps working
 * if another Bootstrap 5 snippet is pasted in later.
 *
 * Bootstrap 4 equivalents:
 *   form-select -> custom-select      form-label -> (label needs no class)
 *   me-N -> mr-N   ms-N -> ml-N       pe-N -> pr-N   ps-N -> pl-N
 *   text-end -> text-right            text-start -> text-left
 *   fw-bold -> font-weight-bold       g-N -> (row gutters)
 *   float-end -> float-right          float-start -> float-left
 */
.form-label { display: inline-block; margin-bottom: .375rem;
              font-size: .75rem; font-weight: 600; color: var(--text-strong); }
.text-end   { text-align: right !important; }
.text-start { text-align: left !important; }
.float-end  { float: right !important; }
.float-start{ float: left !important; }
.fw-bold    { font-weight: 700 !important; }
.fw-semibold{ font-weight: 600 !important; }
.me-1 { margin-right: .25rem !important; }  .ms-1 { margin-left: .25rem !important; }
.me-2 { margin-right: .5rem !important; }   .ms-2 { margin-left: .5rem !important; }
.me-3 { margin-right: 1rem !important; }    .ms-3 { margin-left: 1rem !important; }
.me-4 { margin-right: 1.5rem !important; }  .ms-4 { margin-left: 1.5rem !important; }
.me-auto { margin-right: auto !important; } .ms-auto { margin-left: auto !important; }
.pe-1 { padding-right: .25rem !important; } .ps-1 { padding-left: .25rem !important; }
.pe-2 { padding-right: .5rem !important; }  .ps-2 { padding-left: .5rem !important; }
.pe-3 { padding-right: 1rem !important; }   .ps-3 { padding-left: 1rem !important; }
.row.g-2 > [class*="col"] { padding-right: .25rem; padding-left: .25rem; }
.row.g-3 > [class*="col"] { padding-right: .5rem;  padding-left: .5rem; }

.form-control, .custom-select, select.form-control, .form-select {
    display: block; width: 100%;   /* stated here: auth pages load no Bootstrap */
    height: 36px; padding: 0 .625rem;
    font-family: var(--font-sans); font-size: .8125rem; color: var(--text);
    background: var(--bg-surface);
    border: 1px solid var(--border-strong); border-radius: var(--r-control);
    box-shadow: none;
    transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
textarea.form-control { height: auto; padding: .5rem .625rem; min-height: 76px; line-height: 1.55; }
.form-control:focus, .custom-select:focus, .form-select:focus {
    border-color: var(--c-primary); box-shadow: 0 0 0 3px var(--ring);
    background: var(--bg-surface); color: var(--text); outline: none;
}
.form-control::placeholder { color: var(--text-faint); opacity: 1; }
.form-control:disabled, .form-control[readonly] { background: var(--bg-sunken); color: var(--text-muted); cursor: not-allowed; }
.form-control-sm { height: 30px; font-size: .75rem; padding: 0 .5rem; }

/* --- Browser autofill -------------------------------------------------------
   Chrome, Edge and Safari paint an autofilled field themselves: a pale blue
   fill and near-black text in light mode, and - because they only look at the
   *page* background, never the field's - a washed grey-on-grey in dark mode.
   Either way the field stopped matching the one next to it the instant the
   password manager touched it, which is what "no visibility after filling"
   was: the value was there, it just had almost no contrast against the fill
   the browser had forced underneath it.

   The fill cannot be overridden with `background`, so it is covered with an
   inset shadow spread wide enough to flood the box, and the glyph colour is
   set through -webkit-text-fill-color, which is the only property the autofill
   style does not win against. `transition: background-color 0s 6e5s` is the
   companion trick: it delays the UA's own colour animation past any session so
   it never gets a chance to repaint over this.

   Stated for every input type a form here uses, because the selector does not
   inherit - an autofilled <select> is styled separately from an <input>. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--text-strong) !important;
    caret-color: var(--text-strong);
    -webkit-box-shadow: 0 0 0 100px var(--bg-surface) inset !important;
            box-shadow: 0 0 0 100px var(--bg-surface) inset !important;
    background-color: var(--bg-surface) !important;
    background-clip: content-box !important;
    border-color: var(--border-strong) !important;
    transition: background-color 0s 600000s, color 0s 600000s !important;
}
/* Focus must still read as focus once the field is autofilled, so the brand
   border and ring are restated at a higher specificity than the rule above. */
input:-webkit-autofill:focus,
textarea:-webkit-autofill:focus,
select:-webkit-autofill:focus {
    border-color: var(--c-primary) !important;
    -webkit-box-shadow: 0 0 0 100px var(--bg-surface) inset, 0 0 0 3px var(--ring) !important;
            box-shadow: 0 0 0 100px var(--bg-surface) inset, 0 0 0 3px var(--ring) !important;
}
/* An autofilled field that failed validation keeps its red border. */
.is-invalid:-webkit-autofill { border-color: var(--c-danger) !important; }

/* Firefox has its own, much simpler, autofill fill. */
input:autofill, textarea:autofill, select:autofill {
    background: var(--bg-surface);
    color: var(--text-strong);
    border-color: var(--border-strong);
}
.form-text, .help-block { font-size: .75rem; color: var(--text-muted); margin-top: .3rem; }

.is-invalid, .form-control.is-invalid { border-color: var(--c-danger) !important; background-image: none; }
.is-invalid:focus { box-shadow: 0 0 0 3px rgba(185,28,28,.22); }
.invalid-feedback { color: var(--c-danger); font-size: .75rem; margin-top: .3rem; }
.is-valid, .form-control.is-valid { border-color: var(--c-success) !important; background-image: none; }

.input-group-text {
    background: var(--bg-sunken); border: 1px solid var(--border-strong);
    color: var(--text-muted); font-size: .8125rem; border-radius: var(--r-control);
}
.input-group > .form-control:not(:last-child),
.input-group > .input-group-prepend > .input-group-text { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.input-group > .form-control:not(:first-child),
.input-group > .input-group-append > .input-group-text { border-top-left-radius: 0; border-bottom-left-radius: 0; }

select.form-control, .custom-select, .form-select {
    appearance: none; -webkit-appearance: none; padding-right: 2rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right .55rem center;
}

.custom-control-label::before { border-radius: 5px; border: 1px solid var(--border-strong); background: var(--bg-surface); }
.custom-radio .custom-control-label::before { border-radius: 50%; }
.custom-control-input:checked ~ .custom-control-label::before { background: var(--c-primary); border-color: var(--c-primary); }
.custom-control-input:focus ~ .custom-control-label::before { box-shadow: 0 0 0 3px var(--ring); }
.custom-control-label { font-weight: 450; font-size: .8125rem; color: var(--text); }
input[type="checkbox"], input[type="radio"] { accent-color: var(--c-primary); width: 15px; height: 15px; }

/* ── Row-selection checkboxes ─────────────────────────────────────────────────
 *
 * The bulk-select boxes on a list are drawn as a plain square: white fill, light
 * border, and the tick only when checked. `accent-color` alone left the browser's
 * own control, which on Chrome/Windows is a solid indigo block the moment it is
 * checked - a column of those down the left of a table reads as a coloured stripe
 * and competes with the data for attention.
 *
 * A selection control should be quiet until it is used. Scoped to `.flux-check-col`
 * and the bulk toggles, so form checkboxes elsewhere keep the standard treatment.
 */
.flux-check-col input[type="checkbox"],
input[data-flux-check],
input[data-flux-check-all] {
    -webkit-appearance: none; -moz-appearance: none; appearance: none;
    width: 16px; height: 16px; margin: 0;
    background: #fff;
    border: 1px solid var(--border-strong);
    border-radius: 3px;
    cursor: pointer;
    display: inline-grid; place-content: center;
    transition: border-color var(--speed) var(--ease);
}
.flux-check-col input[type="checkbox"]:hover,
input[data-flux-check]:hover,
input[data-flux-check-all]:hover { border-color: var(--text-faint); }

/* The tick is drawn, not filled: the box stays white so a selected row is told
   apart by its highlight, not by a block of colour in the first column. */
.flux-check-col input[type="checkbox"]::before,
input[data-flux-check]::before,
input[data-flux-check-all]::before {
    content: ''; width: 9px; height: 9px;
    transform: scale(0); transition: transform 120ms var(--ease);
    box-shadow: inset 1em 1em var(--c-primary);
    clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
.flux-check-col input[type="checkbox"]:checked,
input[data-flux-check]:checked,
input[data-flux-check-all]:checked { border-color: var(--c-primary); background: #fff; }
.flux-check-col input[type="checkbox"]:checked::before,
input[data-flux-check]:checked::before,
input[data-flux-check-all]:checked::before { transform: scale(1); }

/* Indeterminate: the master box when only some rows are selected. A dash, because
   an empty box would say "nothing selected" and a tick would say "all of them". */
.flux-check-col input[type="checkbox"]:indeterminate,
input[data-flux-check-all]:indeterminate { border-color: var(--c-primary); }
.flux-check-col input[type="checkbox"]:indeterminate::before,
input[data-flux-check-all]:indeterminate::before {
    transform: scale(1); clip-path: none; width: 9px; height: 2px; border-radius: 1px;
}

.flux-check-col input[type="checkbox"]:focus-visible,
input[data-flux-check]:focus-visible,
input[data-flux-check-all]:focus-visible {
    outline: none; border-color: var(--c-primary); box-shadow: 0 0 0 3px var(--ring);
}

/* Dark theme: a hard white box would glare against a dark row. */
:root[data-theme="dark"] .flux-check-col input[type="checkbox"],
:root[data-theme="dark"] input[data-flux-check],
:root[data-theme="dark"] input[data-flux-check-all] {
    background: var(--bg-surface); border-color: var(--border-strong);
}
:root[data-theme="dark"] .flux-check-col input[type="checkbox"]:checked,
:root[data-theme="dark"] input[data-flux-check]:checked,
:root[data-theme="dark"] input[data-flux-check-all]:checked { background: var(--bg-surface); }

.flux-switch { position: relative; display: inline-block; width: 38px; height: 21px; flex: 0 0 38px; }
.flux-switch input { opacity: 0; width: 0; height: 0; }
.flux-switch span {
    position: absolute; inset: 0; cursor: pointer;
    background: var(--border-strong); border-radius: var(--r-pill);
    transition: background var(--speed) var(--ease);
}
.flux-switch span::before {
    content: ''; position: absolute; height: 15px; width: 15px; left: 3px; top: 3px;
    background: #fff; border-radius: 50%; box-shadow: var(--shadow-sm);
    transition: transform var(--speed) var(--ease);
}
.flux-switch input:checked + span { background: var(--c-primary); }
.flux-switch input:checked + span::before { transform: translateX(17px); }
.flux-switch input:focus-visible + span { box-shadow: 0 0 0 3px var(--ring); }

.custom-file-label { border: 1px solid var(--border-strong); border-radius: var(--r-control); background: var(--bg-surface); color: var(--text-muted); height: 36px; line-height: 1.6; }
.flux-dropzone {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .5rem;
    padding: 2rem 1rem; text-align: center; cursor: pointer;
    border: 1px dashed var(--border-strong); border-radius: var(--r-control);
    background: var(--bg-sunken); color: var(--text-muted);
    transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.flux-dropzone:hover, .flux-dropzone.is-dragging { border-color: var(--c-primary); background: var(--bg-selected); }
.flux-dropzone i { font-size: 1.75rem; color: var(--text-faint); }

/* Select2 aligned to our inputs */
.select2-container--bootstrap4 .select2-selection,
.select2-container .select2-selection--single {
    height: 36px !important; border: 1px solid var(--border-strong) !important;
    border-radius: var(--r-control) !important; background: var(--bg-surface) !important;
}
.select2-container--bootstrap4 .select2-selection__rendered,
.select2-container .select2-selection--single .select2-selection__rendered {
    line-height: 34px !important; color: var(--text) !important; font-size: .8125rem;
}
.select2-container--bootstrap4 .select2-selection__arrow,
.select2-container .select2-selection--single .select2-selection__arrow { height: 34px !important; }
.select2-dropdown {
    border: 1px solid var(--border) !important; border-radius: var(--r-panel) !important;
    background: var(--bg-raised) !important; box-shadow: var(--shadow-lg) !important; overflow: hidden;
}
.select2-results__option { color: var(--text); font-size: .8125rem; }
.select2-container--bootstrap4 .select2-results__option--highlighted,
.select2-container--default .select2-results__option--highlighted[aria-selected] {
    background: var(--c-primary) !important; color: #fff !important;
}
.select2-search__field {
    background: var(--bg-surface) !important; color: var(--text) !important;
    border-color: var(--border-strong) !important; border-radius: 6px !important;
}

/* ==========================================================================
   7. CARDS & PANELS
   ========================================================================== */
.card {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--r-panel);
    box-shadow: none;
    margin-bottom: 1.25rem;
}
.card-header {
    display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
    padding: .75rem 1rem; min-height: 46px;
    background: transparent;
    border-bottom: 1px solid var(--border-soft);
    border-radius: var(--r-panel) var(--r-panel) 0 0;
}
.card-title { margin: 0; font-size: .875rem; font-weight: 600; color: var(--text-strong); float: none; }
.card-tools { margin-left: auto; display: flex; align-items: center; gap: .375rem; float: none; position: static; }
/* Views commonly wrap header contents in their own flex row; let it fill the
   header so justify-content-between still separates title from action. */
.card-header > .d-flex, .card-header > .w-100, .card-header > .row { flex: 1 1 auto; min-width: 0; }
.card-body { padding: 1rem; }
.card-footer {
    padding: .75rem 1rem; background: var(--bg-sunken);
    border-top: 1px solid var(--border-soft); border-radius: 0 0 var(--r-panel) var(--r-panel);
}
.card-body.p-0 > .table-responsive, .card > .card-body.p-0 { border-radius: 0 0 var(--r-panel) var(--r-panel); overflow: hidden; }

/* Accent variants become a 2px top hairline in one of our semantic colours.
   AdminLTE ships a dozen extra hues (navy, teal, olive, maroon...) that views
   use freely; every one is folded back into the palette here so no card can
   introduce a colour the system does not own. */
.card-outline { border-top: 2px solid var(--c-primary) !important; }
.card-primary.card-outline,
.card-navy.card-outline,
.card-indigo.card-outline,
.card-purple.card-outline    { border-top-color: var(--c-primary) !important; }
.card-success.card-outline,
.card-olive.card-outline,
.card-lime.card-outline      { border-top-color: var(--c-success) !important; }
.card-danger.card-outline,
.card-maroon.card-outline,
.card-pink.card-outline,
.card-fuchsia.card-outline   { border-top-color: var(--c-danger) !important; }
.card-warning.card-outline,
.card-orange.card-outline    { border-top-color: var(--c-warning) !important; }
.card-info.card-outline,
.card-teal.card-outline,
.card-cyan.card-outline      { border-top-color: var(--c-info) !important; }
.card-secondary.card-outline,
.card-gray.card-outline,
.card-gray-dark.card-outline { border-top-color: var(--border-strong) !important; }

/* Solid-header variants: keep the surface calm, never a saturated block */
.card-primary:not(.card-outline) > .card-header,
.card-info:not(.card-outline) > .card-header,
.card-success:not(.card-outline) > .card-header,
.card-warning:not(.card-outline) > .card-header,
.card-danger:not(.card-outline) > .card-header,
.card-navy:not(.card-outline) > .card-header,
.card-teal:not(.card-outline) > .card-header,
.card-olive:not(.card-outline) > .card-header,
.card-maroon:not(.card-outline) > .card-header,
.card-purple:not(.card-outline) > .card-header,
.card-secondary:not(.card-outline) > .card-header {
    background: var(--bg-sunken) !important; color: var(--text-strong) !important;
    border-bottom: 1px solid var(--border-soft);
}
.card:not(.card-outline) > .card-header .card-title,
.card:not(.card-outline) > .card-header a:not(.btn) { color: var(--text-strong) !important; }

/* `:not(.btn)` is the whole point of these two rules.
 *
 * They exist to colour a plain TEXT link in a card header - "View all", "Edit".
 * Written as a bare `a` with `!important` they also hit every `<a class="btn">`
 * in a header, overriding the button's own `color: var(--btn-fg)`.
 *
 * On a primary button that meant `--c-primary` text on a `--c-primary`
 * background: indigo on indigo, so the label vanished. The Manual Adjustment
 * button on Stock Movements was the visible case, and 47 buttons across the
 * application sit in a `.card-tools`, so it was every one of them.
 *
 * A button already owns its foreground, background and border as a set. Nothing
 * outside it should reach in and change one of the three. */
.card:not(.card-outline) > .card-header .card-tools a:not(.btn) { color: var(--c-primary) !important; }

/* Bootstrap's colour-border utilities turn a grid of same-kind tiles into a
   rainbow when a view assigns a different hue per tile. Cards always keep the
   system border; the icon inside carries the category colour instead. */
.card.border-primary, .card.border-secondary, .card.border-success,
.card.border-danger,  .card.border-warning,   .card.border-info,
.card.border-light,   .card.border-dark {
    border-color: var(--border) !important;
}
.card.hover-shadow:hover { border-color: var(--border-strong) !important; }

/* Tile card: a whole card acting as one navigation target */
a.text-decoration-none > .card { height: 100%; }
a.text-decoration-none > .card .card-body { display: flex; flex-direction: column; justify-content: center; }
a.text-decoration-none > .card h4 { font-size: .9375rem; margin-bottom: .2rem; }
a.text-decoration-none > .card p { font-size: .75rem; margin: 0; }
a.text-decoration-none > .card .fa-3x { font-size: 1.6rem !important; margin-bottom: .625rem !important; }

/* ==========================================================================
   8. STAT TILES
   ========================================================================== */
.info-box, .small-box, .flux-stat {
    display: flex; align-items: flex-start; gap: .875rem;
    min-height: 0; padding: 1rem;
    background: var(--bg-surface) !important;
    border: 1px solid var(--border); border-radius: var(--r-panel);
    box-shadow: none; margin-bottom: 1.25rem; color: var(--text) !important;
}
.info-box-icon, .small-box .icon {
    display: inline-flex !important; align-items: center; justify-content: center;
    width: 38px; height: 38px; flex: 0 0 38px;
    border-radius: 10px; font-size: 1.15rem;
    background: var(--bg-selected) !important; color: var(--c-primary) !important;
    position: static !important; opacity: 1 !important;
    top: auto; right: auto; bottom: auto;
}
/* AdminLTE renders these glyphs at 70-90px, which swamps the number they are
   supposed to annotate. */
.info-box-icon > i, .small-box .icon > i,
.info-box-icon > svg, .small-box .icon > svg {
    font-size: 1.15rem !important; width: auto !important; height: auto !important;
    line-height: 1 !important; margin: 0 !important; position: static !important;
}
.small-box > .icon { order: 2; margin-left: auto; }
.info-box-icon.bg-success, .small-box.bg-success .icon { background: var(--c-success-bg) !important; color: var(--c-success) !important; }
.info-box-icon.bg-danger,  .small-box.bg-danger .icon  { background: var(--c-danger-bg)  !important; color: var(--c-danger)  !important; }
.info-box-icon.bg-warning, .small-box.bg-warning .icon { background: var(--c-warning-bg) !important; color: var(--c-warning) !important; }
.info-box-icon.bg-info,    .small-box.bg-info .icon    { background: var(--c-info-bg)    !important; color: var(--c-info)    !important; }
.info-box-icon.bg-secondary, .small-box.bg-secondary .icon { background: var(--c-neutral-bg) !important; color: var(--c-neutral) !important; }

.info-box-content, .small-box .inner { padding: 0; flex: 1; min-width: 0; }
.info-box-text, .small-box .inner p {
    display: block; margin: 0 0 .2rem;
    font-size: .75rem; font-weight: 550; letter-spacing: .01em;
    color: var(--text-muted); text-transform: none;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.info-box-number, .small-box .inner h3 {
    display: block; margin: 0;
    font-size: 1.5rem; font-weight: 650; line-height: 1.15;
    letter-spacing: -.02em; color: var(--text-strong) !important;
}
.small-box .small-box-footer {
    background: transparent !important; color: var(--c-primary) !important;
    padding: .5rem 0 0; font-size: .75rem; font-weight: 550; text-align: left;
}
.small-box .small-box-footer:hover { color: var(--c-primary-hov) !important; }
.small-box > .inner { padding: 0; }

.flux-delta { font-size: .75rem; font-weight: 600; display: inline-flex; align-items: center; gap: .15rem; }
.flux-delta-up   { color: var(--c-success); }
.flux-delta-down { color: var(--c-danger); }

/* KPI tile - the clickable metric used across the dashboards.
   Colour arrives as a tinted top edge and a soft corner wash, so the tile is
   unmistakably "revenue" or "expenses" at a glance while the number itself
   stays near-black on a near-white surface and remains fully legible. */
.kpi-tile {
    position: relative; overflow: hidden;
    display: flex; flex-direction: column; height: 100%;
    padding: .875rem 1rem 1rem;
    background: var(--bg-surface); border: 1px solid var(--border);
    border-radius: var(--r-panel); color: var(--text); text-decoration: none;
    transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease),
                transform var(--speed) var(--ease);
}
.kpi-tile::before {                 /* accent edge */
    content: ''; position: absolute; inset: 0 0 auto 0; height: 3px;
    background: var(--kpi-accent, var(--border));
}
.kpi-tile::after {                  /* soft wash, kept well below text contrast */
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: radial-gradient(120% 90% at 100% 0%, var(--kpi-wash, transparent), transparent 62%);
}
.kpi-tile > * { position: relative; z-index: 1; }
.kpi-tile:hover {
    color: var(--text); border-color: var(--border-strong);
    box-shadow: var(--shadow-md); transform: translateY(-2px);
}

.kpi-tile.tone-primary { --kpi-accent: var(--c-primary); --kpi-wash: var(--c-primary-50); }
.kpi-tile.tone-success { --kpi-accent: var(--c-success); --kpi-wash: var(--c-success-bg); }
.kpi-tile.tone-danger  { --kpi-accent: var(--c-danger);  --kpi-wash: var(--c-danger-bg); }
.kpi-tile.tone-warning { --kpi-accent: var(--c-warning); --kpi-wash: var(--c-warning-bg); }
.kpi-tile.tone-info    { --kpi-accent: var(--c-info);    --kpi-wash: var(--c-info-bg); }

/* Compact variant: icon, number, optional badge on one line */
.kpi-tile.kpi-compact {
    flex-direction: row; align-items: center; gap: .75rem; padding: .8rem 1rem;
}
.kpi-compact .kpi-compact-body { flex: 1; min-width: 0; }
.kpi-compact .kpi-value { font-size: 1.25rem; }
.kpi-compact .kpi-label { margin-top: 0; }
.kpi-compact .badge { flex: 0 0 auto; }

.kpi-cmp { font-size: .6875rem; color: var(--text-muted); }
.kpi-tile:hover .kpi-go { opacity: 1; transform: translateX(2px); }
.kpi-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: .625rem; }
.kpi-ic {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: 9px;
    background: var(--bg-selected); color: var(--c-primary); font-size: 1.05rem;
}
.kpi-ic.c-primary { background: var(--c-primary-50);  color: var(--c-primary); }
.kpi-ic.c-success { background: var(--c-success-bg);  color: var(--c-success); }
.kpi-ic.c-danger  { background: var(--c-danger-bg);   color: var(--c-danger); }
.kpi-ic.c-warning { background: var(--c-warning-bg);  color: var(--c-warning); }
.kpi-ic.c-info    { background: var(--c-info-bg);     color: var(--c-info); }
.kpi-ic.c-teal    { background: var(--c-neutral-bg);  color: var(--c-teal); }
.kpi-go {
    font-size: 1.05rem; color: var(--text-faint); opacity: .5;
    transition: opacity var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.kpi-value {
    font-size: 1.5rem; font-weight: 650; line-height: 1.15;
    letter-spacing: -.02em; color: var(--text-strong);
    font-variant-numeric: tabular-nums;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kpi-label { font-size: .75rem; font-weight: 550; color: var(--text-muted); margin-top: .15rem; }
.kpi-sub {
    margin-top: auto; padding-top: .5rem;
    font-size: .6875rem; color: var(--text-faint);
    display: flex; align-items: center; gap: .3rem;
}
@media (max-width: 575.98px) { .kpi-value { font-size: 1.25rem; } }

/* --------------------------------------------------------------------------
   stat_card() - the KPI used on every module index.
   Colour arrives through a tinted icon chip and a matching wash behind it, so
   the tile reads at a glance without turning the page into a paint chart.
   -------------------------------------------------------------------------- */
.stat-card {
    position: relative; overflow: hidden;
    display: flex; align-items: center; gap: .75rem; height: 100%;
    padding: .875rem 1rem;
    background: var(--bg-surface); border: 1px solid var(--border);
    border-radius: var(--r-panel);
    transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease),
                transform var(--speed) var(--ease);
}
.stat-card::after {                       /* soft corner wash, tinted per colour */
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: radial-gradient(120% 90% at 100% 0%, var(--sc-wash, transparent), transparent 62%);
}
a:hover > .stat-card, .stat-card:hover {
    border-color: var(--border-strong); box-shadow: var(--shadow-md); transform: translateY(-1px);
}
.sc-icon {
    position: relative; z-index: 1;
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; flex: 0 0 40px;
    border-radius: 11px; font-size: 1.2rem;
    background: var(--c-primary-50); color: var(--c-primary);
}
.stat-card > div:last-child { position: relative; z-index: 1; min-width: 0; }
.sc-label {
    font-size: .75rem; font-weight: 550; color: var(--text-muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sc-value {
    font-size: 1.375rem; font-weight: 650; line-height: 1.2;
    letter-spacing: -.02em; color: var(--text-strong);
    font-variant-numeric: tabular-nums;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sc-trend { font-size: .75rem; font-weight: 600; display: flex; align-items: center; gap: .15rem; margin-top: .1rem; }

/* Soft tint utilities - the single place colour is assigned per semantic */
.bg-soft-primary   { background: var(--c-primary-50) !important; color: var(--c-primary) !important; }
.bg-soft-success   { background: var(--c-success-bg) !important; color: var(--c-success) !important; }
.bg-soft-danger    { background: var(--c-danger-bg)  !important; color: var(--c-danger)  !important; }
.bg-soft-warning   { background: var(--c-warning-bg) !important; color: var(--c-warning) !important; }
.bg-soft-info      { background: var(--c-info-bg)    !important; color: var(--c-info)    !important; }
.bg-soft-secondary { background: var(--c-neutral-bg) !important; color: var(--c-neutral) !important; }
.bg-soft-teal      { background: #ecfeff !important; color: var(--c-teal) !important; }
html[data-theme="dark"] .bg-soft-teal { background: #052e2b !important; }

.stat-card:has(.bg-soft-primary)   { --sc-wash: var(--c-primary-50); }
.stat-card:has(.bg-soft-success)   { --sc-wash: var(--c-success-bg); }
.stat-card:has(.bg-soft-danger)    { --sc-wash: var(--c-danger-bg); }
.stat-card:has(.bg-soft-warning)   { --sc-wash: var(--c-warning-bg); }
.stat-card:has(.bg-soft-info)      { --sc-wash: var(--c-info-bg); }
.stat-card:has(.bg-soft-secondary) { --sc-wash: var(--c-neutral-bg); }

/* Comparison delta shown against the previous period */
.sc-delta, .kpi-delta {
    display: inline-flex; align-items: center; gap: .2rem;
    font-size: .6875rem; font-weight: 650; padding: .1rem .35rem;
    border-radius: 5px; margin-top: .3rem;
}
.delta-up   { background: var(--c-success-bg); color: var(--c-success); }
.delta-down { background: var(--c-danger-bg);  color: var(--c-danger); }
.delta-flat { background: var(--c-neutral-bg); color: var(--c-neutral); }
.sc-delta small, .kpi-delta small { font-weight: 500; opacity: .8; }

/* ==========================================================================
   9. TABLES / DATA GRID
   ========================================================================== */
.table {
    width: 100%; margin: 0; color: var(--text); font-size: .8125rem;
    border-collapse: separate; border-spacing: 0;
}
.table thead th {
    padding: .5rem .75rem;
    background: var(--bg-sunken);
    font-size: .6875rem; font-weight: 650; letter-spacing: .04em; text-transform: uppercase;
    color: var(--text-muted); white-space: nowrap;
    border: 0; border-bottom: 1px solid var(--border);
    vertical-align: middle;
}
.table tbody td {
    padding: .5rem .75rem; border: 0; border-bottom: 1px solid var(--border-soft);
    vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table-hover tbody tr { transition: background var(--speed) var(--ease); }
.table-hover tbody tr:hover { background: var(--bg-hover); }
.table-striped tbody tr:nth-of-type(odd)  { background: transparent; }
.table-striped tbody tr:nth-of-type(even) { background: var(--bg-sunken); }
.table-sm td, .table-sm th { padding: .35rem .625rem; }
.table tfoot td, .table tfoot th,
.table .table-active, .table .table-success, .table .table-info {
    background: var(--bg-sunken); font-weight: 600; color: var(--text-strong);
    border-top: 1px solid var(--border);
}
/* Row actions stay quiet. A dense grid should not read as a wall of coloured
   chips, so filled buttons inside a cell drop to a tinted state and take their
   full colour back on hover, where the intent still reads. */
.table td .btn { height: 28px; padding: 0 .5rem; font-size: .75rem; border-radius: 6px; }
.table td .btn-primary, .table td .btn-info, .table td .btn-secondary, .table td .btn-dark {
    background: var(--bg-sunken); color: var(--text-muted); border-color: var(--border);
}
.table td .btn-success { background: var(--c-success-bg); color: var(--c-success); border-color: var(--c-success-bd); }
.table td .btn-warning { background: var(--c-warning-bg); color: var(--c-warning); border-color: var(--c-warning-bd); }
.table td .btn-danger  { background: var(--c-danger-bg);  color: var(--c-danger);  border-color: var(--c-danger-bd); }
.table td .btn-primary:hover, .table td .btn-info:hover { background: var(--c-primary); color: #fff; border-color: var(--c-primary); }
.table td .btn-secondary:hover, .table td .btn-dark:hover { background: var(--text-strong); color: var(--bg-surface); border-color: var(--text-strong); }
.table td .btn-success:hover { background: var(--c-success); color: #fff; border-color: var(--c-success); }
.table td .btn-warning:hover { background: var(--c-warning); color: #fff; border-color: var(--c-warning); }
.table td .btn-danger:hover  { background: var(--c-danger);  color: #fff; border-color: var(--c-danger); }
.table td .btn-group > .btn + .btn { margin-left: -1px; }
.table .thead-light th, .table .thead-dark th { background: var(--bg-sunken); color: var(--text-muted); border-color: var(--border); }
.table-bordered, .table-bordered td, .table-bordered th { border-color: var(--border-soft); }
.table tbody tr.is-selected { background: var(--bg-selected); box-shadow: inset 2px 0 0 var(--c-primary); }

.dataTables_wrapper .row:first-child { padding: .75rem 1rem 0; margin: 0; }
.dataTables_wrapper .row:last-child  { padding: .625rem 1rem; margin: 0; border-top: 1px solid var(--border-soft); }
.dataTables_length label, .dataTables_filter label {
    font-size: .75rem; color: var(--text-muted); font-weight: 450;
    display: flex; align-items: center; gap: .4rem; margin: 0;
}
.dataTables_filter input, .dataTables_length select {
    height: 30px; border: 1px solid var(--border-strong); border-radius: 6px;
    background: var(--bg-surface); color: var(--text); padding: 0 .5rem; font-size: .75rem;
}
.dataTables_info { font-size: .75rem; color: var(--text-muted); padding-top: .35rem !important; }
table.dataTable thead .sorting::after, table.dataTable thead .sorting_asc::after,
table.dataTable thead .sorting_desc::after { opacity: .4; }
.dt-buttons .btn { height: 30px; font-size: .75rem; }

/* ── DataTables toolbar: export buttons and search on ONE line ──────────────
   With dom:'Bfrtip' DataTables emits .dt-buttons and .dataTables_filter as
   plain sibling block elements inside .dataTables_wrapper, so they stacked -
   buttons on one line, the search box on the next, wasting a row on every
   list in the system.

   The wrapper becomes a flex row and the remaining parts (table, info, paging)
   are forced onto their own full-width lines beneath it.

   Scoped to .dt-has-toolbar, a class applied in footer.php only to wrappers
   that actually contain export buttons. Applying it to every wrapper would
   catch the Bootstrap-4 row-based layouts that other tables use and lay their
   rows out sideways. */
.dataTables_wrapper.dt-has-toolbar {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem .75rem;
    padding: .75rem 1rem 0;
}
.dataTables_wrapper.dt-has-toolbar > .dt-buttons {
    order: 1;
    margin-right: auto;      /* buttons left, search right */
    float: none;             /* DataTables floats these; flex needs it cleared */
}
.dataTables_wrapper.dt-has-toolbar > .dataTables_filter {
    order: 2;
    margin-left: auto;
    float: none;
    text-align: right;
}
.dataTables_wrapper.dt-has-toolbar > .dataTables_filter label { justify-content: flex-end; }

/* Everything after the toolbar takes a full row of its own. */
.dataTables_wrapper.dt-has-toolbar > .dataTables_length { order: 0; flex: 0 0 auto; float: none; }
.dataTables_wrapper.dt-has-toolbar > table.dataTable,
.dataTables_wrapper.dt-has-toolbar > .table-responsive,
.dataTables_wrapper.dt-has-toolbar > .dataTables_scroll { order: 3; flex: 1 1 100%; margin: 0; }
.dataTables_wrapper.dt-has-toolbar > .dataTables_info     { order: 4; flex: 1 1 auto; float: none; }
.dataTables_wrapper.dt-has-toolbar > .dataTables_paginate { order: 5; margin-left: auto; float: none; }
.dataTables_wrapper.dt-has-toolbar > .dataTables_processing { order: 3; flex: 1 1 100%; }

/* The buttons themselves read as one group rather than loose pills. */
.dataTables_wrapper.dt-has-toolbar .dt-buttons .btn-group,
.dataTables_wrapper.dt-has-toolbar .dt-buttons { display: inline-flex; flex-wrap: wrap; gap: .35rem; }

@media (max-width: 575.98px) {
    /* On a phone the two stack again - deliberately. Squeezing a button row
       and a search field onto a 360px line makes both unusable. */
    .dataTables_wrapper.dt-has-toolbar > .dt-buttons,
    .dataTables_wrapper.dt-has-toolbar > .dataTables_filter {
        flex: 1 1 100%; margin: 0; text-align: left;
    }
    .dataTables_wrapper.dt-has-toolbar > .dataTables_filter label { justify-content: flex-start; }
    .dataTables_wrapper.dt-has-toolbar > .dataTables_filter input { width: 100%; }
}

.pagination { gap: 2px; margin: 0; }
.page-link {
    min-width: 30px; height: 30px;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0 .5rem; font-size: .75rem; font-weight: 550;
    color: var(--text); background: transparent;
    border: 1px solid transparent; border-radius: 6px;
    transition: background var(--speed) var(--ease);
}
.page-link:hover { background: var(--bg-hover); color: var(--text-strong); border-color: var(--border); }
.page-item.active .page-link { background: var(--c-primary); border-color: var(--c-primary); color: var(--c-on-primary); }
.page-item.disabled .page-link { color: var(--text-faint); background: transparent; }

/* ==========================================================================
   10. BADGES, CHIPS, STATUS
   ========================================================================== */
.badge, .flux-badge {
    display: inline-flex; align-items: center; gap: .25rem;
    padding: .2em .5em; border-radius: 6px;
    font-size: .6875rem; font-weight: 600; letter-spacing: .01em; line-height: 1.5;
    border: 1px solid transparent;
}
.badge-primary   { background: var(--c-primary-50); color: var(--c-primary-700); border-color: var(--c-primary-100); }
.badge-success   { background: var(--c-success-bg); color: var(--c-success); border-color: var(--c-success-bd); }
.badge-danger    { background: var(--c-danger-bg);  color: var(--c-danger);  border-color: var(--c-danger-bd); }
.badge-warning   { background: var(--c-warning-bg); color: var(--c-warning); border-color: var(--c-warning-bd); }
.badge-info      { background: var(--c-info-bg);    color: var(--c-info);    border-color: var(--c-info-bd); }
.badge-secondary, .badge-light, .flux-badge-quiet { background: var(--c-neutral-bg); color: var(--c-neutral); border-color: var(--c-neutral-bd); }
.badge-dark      { background: var(--text-strong); color: var(--bg-surface); }
.badge-pill      { border-radius: var(--r-pill); }
html[data-theme="dark"] .badge-primary { color: var(--c-primary-300); }

/* AdminLTE ships a dozen saturated badge colours (bg-blue, bg-teal, bg-olive...)
   that views apply freely. Fold them into the soft palette so a table of
   quantities does not turn into a row of solid blue pills. */
.badge.bg-blue, .badge.bg-primary, .badge.bg-navy, .badge.bg-indigo {
    background: var(--c-primary-50) !important; color: var(--c-primary-700) !important; border-color: var(--c-primary-100) !important;
}
.badge.bg-green, .badge.bg-success, .badge.bg-olive, .badge.bg-lime {
    background: var(--c-success-bg) !important; color: var(--c-success) !important; border-color: var(--c-success-bd) !important;
}
.badge.bg-red, .badge.bg-danger, .badge.bg-maroon, .badge.bg-pink {
    background: var(--c-danger-bg) !important; color: var(--c-danger) !important; border-color: var(--c-danger-bd) !important;
}
.badge.bg-yellow, .badge.bg-warning, .badge.bg-orange {
    background: var(--c-warning-bg) !important; color: var(--c-warning) !important; border-color: var(--c-warning-bd) !important;
}
.badge.bg-teal, .badge.bg-cyan, .badge.bg-info, .badge.bg-light-blue {
    background: var(--c-info-bg) !important; color: var(--c-info) !important; border-color: var(--c-info-bd) !important;
}
.badge.bg-gray, .badge.bg-secondary, .badge.bg-gray-dark {
    background: var(--c-neutral-bg) !important; color: var(--c-neutral) !important; border-color: var(--c-neutral-bd) !important;
}
html[data-theme="dark"] .badge.bg-blue, html[data-theme="dark"] .badge.bg-primary { color: var(--c-primary-300) !important; }

.flux-status { display: inline-flex; align-items: center; gap: .35rem; font-size: .75rem; font-weight: 550; }
.flux-status::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 6px; }
.flux-status-ok   { color: var(--c-success); }
.flux-status-warn { color: var(--c-warning); }
.flux-status-err  { color: var(--c-danger); }
.flux-status-idle { color: var(--text-faint); }

.flux-chip {
    display: inline-flex; align-items: center; gap: .3rem;
    height: 26px; padding: 0 .5rem;
    background: var(--bg-sunken); border: 1px solid var(--border);
    border-radius: 6px; font-size: .75rem; color: var(--text);
}
.flux-chip button { border: 0; background: none; color: var(--text-faint); padding: 0; line-height: 1; cursor: pointer; }
.flux-chip button:hover { color: var(--c-danger); }

/* ==========================================================================
   11. ALERTS & TOASTS
   ========================================================================== */
/* ── Notices ────────────────────────────────────────────────────────────────
   Seventy-one of these are scattered through the application, most of them
   explaining how a screen works. They were fully tinted boxes: coloured fill,
   coloured border AND coloured body text, in mint, sky, cream and pink. Four
   candy colours competing with the data on a page whose job is to be read.

   Two things were wrong beyond taste.

   The body text took the semantic colour (`color: var(--c-info)` gave #0369a1
   BLUE PROSE). Coloured body copy reads as decoration rather than content, and
   a paragraph of it is genuinely harder to read than the same words in the
   normal text colour. Meaning belongs on the marker, not the sentence.

   And the fill made every explanatory aside as loud as a genuine error. When
   an advisory note and a failed posting look equally urgent, neither reads as
   urgent.

   So: the surface stays neutral, the body takes normal text colour, and the
   semantic colour survives on a 2px left rule and the icon. Meaning intact,
   volume down, prose readable. */
.alert {
    display: flex; align-items: flex-start; gap: .625rem;
    padding: .6875rem .875rem; margin-bottom: 1rem;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-left: 2px solid var(--border-strong);
    border-radius: var(--r-control);
    font-size: .8125rem; line-height: 1.5;
    color: var(--text);
    box-shadow: none;
}
/* The icon most call sites already include carries the semantic colour. */
.alert > i, .alert > .bx, .alert > .fas, .alert > .far { color: var(--text-muted); font-size: 1rem; line-height: 1.35; flex: 0 0 auto; }
.alert strong, .alert b { color: var(--text-strong); font-weight: 600; }

.alert-success   { border-left-color: var(--c-success); }
.alert-danger    { border-left-color: var(--c-danger);  }
.alert-warning   { border-left-color: var(--c-warning); }
.alert-info      { border-left-color: var(--c-info);    }
.alert-secondary { border-left-color: var(--border-strong); }

.alert-success > i, .alert-success > .fas, .alert-success > .bx { color: var(--c-success); }
.alert-danger  > i, .alert-danger  > .fas, .alert-danger  > .bx { color: var(--c-danger);  }
.alert-warning > i, .alert-warning > .fas, .alert-warning > .bx { color: var(--c-warning); }
.alert-info    > i, .alert-info    > .fas, .alert-info    > .bx { color: var(--c-info);    }

/* A flash message reporting a real outcome may take a faint wash - it is
   transient and it is the reason the page reloaded. Static page furniture may
   not. `.alert-flash` is added by `flash()`, nothing else. */
.alert-flash.alert-success { background: var(--c-success-bg); }
.alert-flash.alert-danger  { background: var(--c-danger-bg);  }
.alert-flash.alert-warning { background: var(--c-warning-bg); }

.alert a { color: var(--c-primary); text-decoration: underline; font-weight: 550; }
.alert-dismissible { padding-right: 2.5rem; }
.alert-dismissible .close { padding: .5rem .75rem; color: var(--text-muted); opacity: .6; text-shadow: none; }
.alert-dismissible .close:hover { opacity: 1; }

/* ── Toasts ─────────────────────────────────────────────────────────────────
   These were four solid saturated slabs - a full-bleed green, red, amber or
   blue rectangle sliding over the page. Toastr's own default, and it looks it.

   A toast is a transient acknowledgement, not an announcement. It should read
   as part of the application rather than as a coloured sticker dropped on top
   of it. Same treatment as the notices: the panel surface, and the semantic
   colour reduced to a left rule and the icon. */
.toast-top-right { top: calc(var(--topbar-h) + 12px); right: 16px; }
#toast-container > div {
    border-radius: var(--r-control);
    box-shadow: var(--shadow-lg);
    opacity: 1;
    padding: .75rem 1rem .75rem 2.75rem;
    font-size: .8125rem;
    width: 340px;
    background-color: var(--bg-raised);
    color: var(--text);
    border: 1px solid var(--border);
    border-left: 2px solid var(--border-strong);
    /* Toastr paints its glyph as a background image tinted for a dark slab.
       On a light panel it disappears, so it is replaced below. */
    background-image: none;
}
#toast-container > div:hover { box-shadow: var(--shadow-lg); opacity: 1; }
#toast-container .toast-title { color: var(--text-strong); font-weight: 600; }
#toast-container .toast-message { color: var(--text); }
#toast-container .toast-close-button { color: var(--text-muted); text-shadow: none; opacity: .6; font-weight: 400; }
#toast-container .toast-close-button:hover { color: var(--text-strong); opacity: 1; }

#toast-container > .toast-success { border-left-color: var(--c-success); }
#toast-container > .toast-error   { border-left-color: var(--c-danger);  }
#toast-container > .toast-warning { border-left-color: var(--c-warning); }
#toast-container > .toast-info    { border-left-color: var(--c-info);    }

/* The status glyph, drawn as a small filled disc rather than reinstating
   Toastr's white-on-colour icon sprite. */
#toast-container > div::before {
    content: ""; position: absolute; left: 1rem; top: 1.05rem;
    width: 8px; height: 8px; border-radius: 50%;
}
#toast-container > .toast-success::before { background: var(--c-success); }
#toast-container > .toast-error::before   { background: var(--c-danger);  }
#toast-container > .toast-warning::before { background: var(--c-warning); }
#toast-container > .toast-info::before    { background: var(--c-info);    }

/* The progress bar inherited Toastr's translucent white, invisible on a light
   panel. */
#toast-container .toast-progress { background: var(--border-strong); opacity: 1; height: 2px; }
#toast-container > .toast-success .toast-progress { background: var(--c-success); }
#toast-container > .toast-error   .toast-progress { background: var(--c-danger);  }
#toast-container > .toast-warning .toast-progress { background: var(--c-warning); }
#toast-container > .toast-info    .toast-progress { background: var(--c-info);    }

/* SweetAlert2 aligned to the system */
.swal2-popup { border-radius: var(--r-panel) !important; background: var(--bg-raised) !important; font-family: var(--font-sans) !important; }
.swal2-title { color: var(--text-strong) !important; font-size: 1.0625rem !important; }
.swal2-html-container { color: var(--text) !important; font-size: .8125rem !important; }
.swal2-styled.swal2-confirm { background: var(--c-primary) !important; border-radius: var(--r-control) !important; font-size: .8125rem !important; }
.swal2-styled.swal2-cancel  { background: var(--bg-sunken) !important; color: var(--text) !important; border: 1px solid var(--border-strong) !important; border-radius: var(--r-control) !important; font-size: .8125rem !important; }

/* ==========================================================================
   12. TABS, PILLS, STEPPER, TIMELINE, BOARD
   ========================================================================== */
.nav-tabs { border-bottom: 1px solid var(--border); gap: .125rem; }
.nav-tabs .nav-link {
    padding: .5rem .75rem; margin-bottom: -1px;
    font-size: .8125rem; font-weight: 550; color: var(--text-muted);
    background: transparent; border: 0; border-bottom: 2px solid transparent; border-radius: 0;
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.nav-tabs .nav-link:hover { color: var(--text-strong); border-bottom-color: var(--border-strong); }
.nav-tabs .nav-link.active { color: var(--c-primary); background: transparent; border-bottom-color: var(--c-primary); }
.tab-content { padding-top: 1rem; }

.nav-pills .nav-link {
    padding: .375rem .75rem; border-radius: var(--r-control);
    font-size: .8125rem; font-weight: 550; color: var(--text-muted);
}
.nav-pills .nav-link:hover { background: var(--bg-hover); color: var(--text-strong); }
.nav-pills .nav-link.active { background: var(--c-primary); color: var(--c-on-primary); }

/* Vertical rail inside a card (settings sub-navigation).
   A hairline under every row is the laziest possible list; grouping headings
   already separate the sections, so the rows themselves stay borderless. */
.card .nav.flex-column, .card .nav.flex-column .nav-item { border: 0; }
.card .nav.flex-column .nav-item + .nav-item { border-top: 0; }
.card .nav.flex-column .nav-link {
    display: flex; align-items: center; gap: .5rem;
    padding: .4rem .55rem; margin-bottom: 1px;
    border-radius: var(--r-control);
    font-size: .8125rem; font-weight: 450; color: var(--text);
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.card .nav.flex-column .nav-link i { font-size: 1rem; color: var(--text-muted); width: 1.1rem; flex: 0 0 1.1rem; }
.card .nav.flex-column .nav-link:hover { background: var(--bg-hover); color: var(--text-strong); }
.card .nav.flex-column .nav-link.active { background: var(--bg-selected); color: var(--c-primary); font-weight: 600; }
.card .nav.flex-column .nav-link.active i { color: var(--c-primary); }

/* A second, earlier stepper (.flux-steps / .step-dot) lived here. Nothing
   rendered it - the document header now uses .flux-track - and two stepper
   implementations in one stylesheet is how they drift apart. Removed. */

/* ── The FIRST .flux-timeline implementation lived here and has been removed ──
 * It drew its own bullet with `li::before` at `left: -1.5rem`, and since the
 * current timeline (further down, `.flux-tl-*`) puts a real icon in a
 * `.flux-tl-dot` circle, BOTH rendered: a stray 9px dot sat behind and slightly
 * off from every icon, and no amount of adjusting the icon moved it because it
 * belonged to a different rule set.
 *
 * Two implementations of one component in one stylesheet is how they drift.
 * Nothing uses the old markup any more - `doc_history.php` is the only place
 * `.flux-timeline` appears and it emits `.flux-tl-item` - so the old block is
 * gone rather than being overridden. */
.flux-timeline time { display: block; font-size: .6875rem; color: var(--text-faint); margin-bottom: .1rem; }

.flux-board { display: flex; gap: 1rem; overflow-x: auto; padding-bottom: .5rem; align-items: flex-start; }
.flux-board-col { flex: 0 0 288px; background: var(--bg-sunken); border: 1px solid var(--border); border-radius: var(--r-panel); padding: .75rem; }
.flux-board-col > h4 {
    display: flex; align-items: center; justify-content: space-between;
    font-size: .75rem; font-weight: 650; text-transform: uppercase; letter-spacing: .04em;
    color: var(--text-muted); margin-bottom: .625rem;
}
.flux-board-card {
    background: var(--bg-surface); border: 1px solid var(--border);
    border-radius: var(--r-control); padding: .625rem .75rem; margin-bottom: .5rem;
    box-shadow: var(--shadow-sm);
    transition: box-shadow var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.flux-board-card:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }

/* ==========================================================================
   13. OVERLAYS - dropdown, modal, drawer, tooltip, palette
   ========================================================================== */
.dropdown-menu, .flux-menu {
    padding: .3rem; min-width: 208px;
    background: var(--bg-raised);
    border: 1px solid var(--border); border-radius: var(--r-panel);
    box-shadow: var(--shadow-lg); font-size: .8125rem; color: var(--text);
}
.dropdown-item {
    display: flex; align-items: center; gap: .5rem;
    padding: .4rem .5rem; border-radius: 6px;
    font-size: .8125rem; color: var(--text);
    transition: background var(--speed) var(--ease);
}
.dropdown-item i { font-size: 1rem; color: var(--text-muted); width: 1.1rem; flex: 0 0 1.1rem; }
.dropdown-item small { margin-left: auto; color: var(--text-faint); font-size: .6875rem; }
.dropdown-item:hover, .dropdown-item:focus { background: var(--bg-hover); color: var(--text-strong); }
.dropdown-item.is-current, .dropdown-item.active { background: var(--bg-selected); color: var(--c-primary); font-weight: 600; }
.dropdown-item.is-danger { color: var(--c-danger); }
.dropdown-item.is-danger:hover { background: var(--c-danger-bg); color: var(--c-danger); }
.dropdown-item.is-danger i { color: var(--c-danger); }
.dropdown-divider { margin: .3rem -.3rem; border-color: var(--border-soft); }
.flux-menu-label, .dropdown-header {
    padding: .4rem .5rem .25rem; font-size: .6875rem; font-weight: 650;
    letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint);
}
.flux-menu-account { min-width: 254px; }
.flux-account-head { display: flex; gap: .625rem; padding: .5rem; align-items: center; }
.flux-account-head strong { display: block; font-size: .8125rem; color: var(--text-strong); }
.flux-account-head small { display: block; font-size: .75rem; color: var(--text-muted); margin-bottom: .25rem; }

.modal-content { background: var(--bg-raised); border: 1px solid var(--border); border-radius: var(--r-panel); box-shadow: var(--shadow-xl); }
.modal-header { padding: .875rem 1.125rem; border-bottom: 1px solid var(--border-soft); align-items: center; }
.modal-title { font-size: .9375rem; font-weight: 600; }
.modal-body { padding: 1.125rem; }
.modal-footer { padding: .75rem 1.125rem; border-top: 1px solid var(--border-soft); gap: .5rem; }
.modal-backdrop.show { opacity: .45; background: #0f172a; }
.close { color: var(--text-muted); text-shadow: none; opacity: 1; font-weight: 400; font-size: 1.4rem; }
.close:hover { color: var(--text-strong); }

.flux-drawer {
    position: fixed; top: 0; right: 0; bottom: 0; width: min(460px, 92vw);
    background: var(--bg-raised); border-left: 1px solid var(--border);
    box-shadow: var(--shadow-xl); z-index: var(--z-drawer);
    display: flex; flex-direction: column;
    transform: translateX(100%); transition: transform .2s var(--ease);
}
.flux-drawer.is-open { transform: none; }
.flux-drawer > header { padding: .875rem 1.125rem; border-bottom: 1px solid var(--border-soft); display: flex; align-items: center; gap: .75rem; }
.flux-drawer-body { padding: 1.125rem; overflow-y: auto; flex: 1; }
.flux-drawer > footer { padding: .75rem 1.125rem; border-top: 1px solid var(--border-soft); display: flex; gap: .5rem; justify-content: flex-end; }

.tooltip-inner { background: var(--text-strong); color: var(--bg-surface); border-radius: 6px; padding: .3rem .5rem; font-size: .75rem; max-width: 260px; }
.tooltip .arrow::before { border-top-color: var(--text-strong); border-bottom-color: var(--text-strong); }
.popover { border: 1px solid var(--border); border-radius: var(--r-panel); box-shadow: var(--shadow-lg); background: var(--bg-raised); }
.popover-header { background: transparent; border-bottom: 1px solid var(--border-soft); font-size: .8125rem; font-weight: 600; color: var(--text-strong); }
.popover-body { color: var(--text); font-size: .8125rem; }

.flux-palette { position: fixed; inset: 0; z-index: var(--z-palette); }
.flux-palette[hidden] { display: none; }
.flux-palette-scrim { position: absolute; inset: 0; background: rgba(15,23,42,.45); }
.flux-palette-panel {
    position: relative; width: min(620px, 94vw); margin: 9vh auto 0;
    background: var(--bg-raised); border: 1px solid var(--border);
    border-radius: var(--r-panel); box-shadow: var(--shadow-xl);
    overflow: hidden; display: flex; flex-direction: column; max-height: 72vh;
}
.flux-palette-input { display: flex; align-items: center; gap: .625rem; padding: .875rem 1rem; border-bottom: 1px solid var(--border-soft); }
.flux-palette-input i { font-size: 1.2rem; color: var(--text-faint); }
.flux-palette-input input {
    flex: 1; border: 0; background: transparent; outline: none;
    font-size: .9375rem; color: var(--text-strong); font-family: var(--font-sans);
}
.flux-palette-input input::placeholder { color: var(--text-faint); }
.flux-palette-results { overflow-y: auto; padding: .375rem; flex: 1; }
.flux-palette-group {
    padding: .5rem .625rem .25rem; font-size: .6875rem; font-weight: 650;
    letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint);
}
.flux-palette-item {
    display: flex; align-items: center; gap: .625rem;
    padding: .5rem .625rem; border-radius: 6px;
    color: var(--text); font-size: .8125rem; cursor: pointer; text-decoration: none;
}
.flux-palette-item i { font-size: 1.05rem; color: var(--text-muted); width: 1.15rem; flex: 0 0 1.15rem; }
.flux-palette-item .path { margin-left: auto; font-size: .6875rem; color: var(--text-faint); font-family: var(--font-mono); }
.flux-palette-item:hover, .flux-palette-item.is-active { background: var(--bg-selected); color: var(--c-primary); }
.flux-palette-item.is-active i { color: var(--c-primary); }
.flux-palette-empty { padding: 2rem 1rem; text-align: center; color: var(--text-muted); font-size: .8125rem; }
.flux-palette-foot {
    display: flex; gap: 1rem; padding: .5rem 1rem;
    border-top: 1px solid var(--border-soft); background: var(--bg-sunken);
    font-size: .6875rem; color: var(--text-faint);
}
.flux-palette-foot span { display: inline-flex; align-items: center; gap: .25rem; }

/* ==========================================================================
   14. STATE - empty · loading · error
   ========================================================================== */
.flux-empty {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .5rem; padding: 3rem 1.5rem; text-align: center;
}
.flux-empty-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 46px; height: 46px; border-radius: 12px; margin-bottom: .25rem;
    background: var(--bg-sunken); border: 1px solid var(--border);
    font-size: 1.4rem; color: var(--text-faint);
}
.flux-empty h4 { margin: 0; font-size: .9375rem; color: var(--text-strong); }
.flux-empty p { margin: 0; max-width: 42ch; font-size: .8125rem; color: var(--text-muted); }
.flux-empty .btn { margin-top: .5rem; }

/* Skeletons mirror the shape of what is loading, never a bare spinner */
.flux-skel {
    background: linear-gradient(90deg, var(--bg-sunken) 25%, var(--bg-hover) 37%, var(--bg-sunken) 63%);
    background-size: 400% 100%;
    border-radius: 6px; height: 12px; margin-bottom: .5rem;
    animation: flux-shimmer 1.3s ease-in-out infinite;
}
.flux-skel-title { height: 18px; width: 34%; }
.flux-skel-row   { height: 34px; border-radius: var(--r-control); }
@keyframes flux-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

.flux-spinner {
    width: 16px; height: 16px; border-radius: 50%; display: inline-block;
    border: 2px solid var(--border-strong); border-top-color: var(--c-primary);
    animation: flux-spin .6s linear infinite;
}
@keyframes flux-spin { to { transform: rotate(360deg); } }

.overlay { background: rgba(255,255,255,.66); }
html[data-theme="dark"] .overlay { background: rgba(11,13,18,.66); }

.flux-error-panel {
    border: 1px solid var(--c-danger-bd); background: var(--c-danger-bg);
    border-radius: var(--r-panel); padding: 1rem; color: var(--c-danger); font-size: .8125rem;
}

/* empty_state() from ui_helper */
.empty-state { padding: 2.75rem 1.5rem; text-align: center; color: var(--text-muted); }
.empty-state > i {
    display: inline-flex; align-items: center; justify-content: center;
    width: 48px; height: 48px; border-radius: 13px; margin-bottom: .75rem;
    background: var(--bg-sunken); border: 1px solid var(--border);
    font-size: 1.5rem; color: var(--text-faint);
}
.empty-state h5 { font-size: .9375rem; color: var(--text-strong); margin: 0 0 .25rem; }
.empty-state p { font-size: .8125rem; margin: 0 auto; max-width: 42ch; }

/* ==========================================================================
   14b. RECORD DETAIL VOCABULARY
   Label/value rows, line-item rows and document blocks used by the show pages.
   ========================================================================== */
.info-row {
    display: flex; align-items: baseline; gap: .75rem;
    padding: .5rem 0; border-bottom: 1px dashed var(--border-soft);
}
.info-row:last-child { border-bottom: 0; }
.info-label {
    flex: 0 0 42%; max-width: 190px;
    font-size: .75rem; font-weight: 600; color: var(--text-muted);
}
.info-row > :last-child { flex: 1; font-size: .8125rem; color: var(--text-strong); min-width: 0; }

.item-row { padding: .5rem .75rem; border-bottom: 1px solid var(--border-soft); }
.item-row:last-child { border-bottom: 0; }

.summary-box {
    background: var(--bg-sunken); border: 1px solid var(--border);
    border-radius: var(--r-panel); padding: .875rem 1rem;
}
.summary-row {
    display: flex; align-items: center; justify-content: space-between;
    padding: .35rem 0; font-size: .8125rem; color: var(--text);
}
.summary-row:not(:last-child) { border-bottom: 1px dashed var(--border-soft); }
.summary-row.is-total {
    margin-top: .35rem; padding-top: .6rem; border-top: 1px solid var(--border-strong);
    border-bottom: 0; font-size: .9375rem; font-weight: 700; color: var(--text-strong);
}
.summary-row > span:last-child { font-variant-numeric: tabular-nums; font-weight: 600; }

.doc-info { display: grid; gap: .35rem; }
.doc-label { font-size: .6875rem; font-weight: 650; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
.doc-value { font-size: .875rem; font-weight: 600; color: var(--text-strong); }
.doc-title { font-size: 1.125rem; font-weight: 650; color: var(--text-strong); letter-spacing: -.015em; }

/* Action panel on a record: buttons in a tidy grid, not a stack of full-width
   blocks that makes the column twice as tall as the content beside it. */
.flux-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
.flux-actions .btn { width: 100%; }
.flux-actions .d-contents { display: contents; }
@media (max-width: 575.98px) { .flux-actions { grid-template-columns: 1fr; } }

/* Equal-height cards when a row pairs panels side by side */
.row > [class*="col-"].d-flex > .card { width: 100%; margin-bottom: 1.25rem; }

/* Report directory row: title, one line of what it answers, chevron */
.report-link {
    display: flex; align-items: center; gap: .75rem;
    padding: .5rem .625rem; border-radius: var(--r-control);
    color: var(--text); text-decoration: none;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.report-link[hidden] { display: none; }
.report-link + .report-link { margin-top: 1px; }
.report-link-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.report-link strong { font-size: .8125rem; font-weight: 600; color: var(--text-strong); }
.report-link small {
    font-size: .6875rem; color: var(--text-muted); line-height: 1.35;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.report-link > i { color: var(--text-faint); font-size: 1.1rem; flex: 0 0 auto; transition: transform var(--speed) var(--ease); }
.report-link:hover { background: var(--bg-selected); color: var(--text); }
.report-link:hover strong { color: var(--c-primary); }
.report-link:hover > i { color: var(--c-primary); transform: translateX(2px); }

/* Bootstrap list-group, aligned to the system (used by older report pages) */
.list-group-item {
    background: transparent; border-color: var(--border-soft);
    color: var(--text); font-size: .8125rem; padding: .55rem .75rem;
}
.list-group-item-action:hover { background: var(--bg-hover); color: var(--text-strong); }
.list-group-flush .list-group-item:first-child { border-top: 0; }
.list-group-flush .list-group-item:last-child { border-bottom: 0; }

/* Two-up panel grid: turns a stacked sidebar column into a proper row of
   panels once it spans the full width. */
.flux-cardgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 1.25rem; align-items: start; }
.flux-cardgrid > .card { margin-bottom: 1.25rem; }
.flux-cardgrid > .btn { grid-column: 1 / -1; justify-self: start; }
@media (max-width: 767.98px) { .flux-cardgrid { grid-template-columns: 1fr; } }

.hover-lift { transition: transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease); }
.hover-lift:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }

/* workflow_steps() from ui_helper */
.wf-steps { display: flex; align-items: center; flex-wrap: wrap; gap: .25rem; margin-bottom: 1.25rem; }
.wf-step { display: inline-flex; align-items: center; gap: .45rem; font-size: .8125rem; color: var(--text-faint); }
.wf-dot {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; border-radius: 50%;
    background: var(--bg-sunken); border: 1px solid var(--border-strong);
    font-size: .6875rem; font-weight: 700;
}
.wf-line { width: 34px; height: 2px; background: var(--border); border-radius: 2px; }
.wf-step.done { color: var(--c-success); }
.wf-step.done .wf-dot { background: var(--c-success); border-color: var(--c-success); color: #fff; }
.wf-step.done + .wf-line { background: var(--c-success); }
.wf-step.current { color: var(--text-strong); font-weight: 650; }
.wf-step.current .wf-dot {
    background: var(--c-primary); border-color: var(--c-primary); color: #fff;
    box-shadow: 0 0 0 4px var(--ring);
}

/* ==========================================================================
   15. FILTER BAR
   ========================================================================== */
.flux-filters {
    display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
    padding: .625rem .875rem; margin-bottom: 1rem;
    background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r-panel);
}
.flux-filters .form-control, .flux-filters .custom-select { height: 32px; font-size: .75rem; width: auto; }
.flux-filters label { margin: 0; }
.flux-filters-end { margin-left: auto; display: flex; gap: .5rem; }

/* ==========================================================================
   16. CHARTS
   ========================================================================== */
.flux-chart { min-height: 260px; }
.apexcharts-tooltip {
    background: var(--bg-raised) !important; border: 1px solid var(--border) !important;
    box-shadow: var(--shadow-lg) !important; border-radius: var(--r-control) !important;
    color: var(--text) !important;
}
.apexcharts-tooltip-title { background: var(--bg-sunken) !important; border-bottom: 1px solid var(--border) !important; font-weight: 600 !important; }
.apexcharts-legend-text { color: var(--text-muted) !important; font-family: var(--font-sans) !important; font-size: 12px !important; }
.apexcharts-xaxis text, .apexcharts-yaxis text { fill: var(--text-muted) !important; font-family: var(--font-sans) !important; }
.apexcharts-gridline { stroke: var(--border-soft) !important; }

/* ==========================================================================
   17. AUTH PAGES
   ========================================================================== */
.login-page, .register-page {
    background: var(--bg-app);
    min-height: 100dvh; display: flex; align-items: center; justify-content: center; padding: 1.5rem;
}
.login-box, .register-box { width: 100%; max-width: 388px; margin: 0; }
.login-logo, .register-logo { margin-bottom: 1.25rem; text-align: center; }
.login-logo a, .register-logo a { font-size: 1.375rem; font-weight: 650; color: var(--text-strong); letter-spacing: -.02em; }
.login-card-body, .register-card-body {
    background: var(--bg-surface); border: 1px solid var(--border);
    border-radius: var(--r-panel); box-shadow: var(--shadow-md); padding: 1.75rem;
}
.login-box-msg, .register-box-msg { padding: 0 0 1.25rem; font-size: .8125rem; color: var(--text-muted); text-align: center; }
.login-card-body .input-group, .register-card-body .input-group { margin-bottom: .875rem; }

/* ==========================================================================
   18. UTILITIES  (incl. v3 aliases kept for existing views)
   ========================================================================== */
.text-muted   { color: var(--text-muted) !important; }
.text-primary { color: var(--c-primary) !important; }
.text-success { color: var(--c-success) !important; }
.text-danger  { color: var(--c-danger) !important; }
.text-warning { color: var(--c-warning) !important; }
.text-info    { color: var(--c-info) !important; }
.text-secondary { color: var(--text-muted) !important; }
.text-soft    { color: var(--text-muted); }
.text-teal    { color: var(--c-teal); }
.text-purple  { color: var(--c-primary); }
.bg-white, .bg-light { background: var(--bg-surface) !important; }
.bg-soft { background: var(--bg-sunken); }
.border, .border-top, .border-bottom, .border-left, .border-right { border-color: var(--border) !important; }
.border-teal { border-color: var(--c-teal) !important; }
.rounded { border-radius: var(--r-control) !important; }
.rounded-xl { border-radius: var(--r-panel) !important; }
.shadow, .shadow-sm { box-shadow: var(--shadow-sm) !important; }
.fw-600 { font-weight: 600; } .fw-700 { font-weight: 700; }
.divider { height: 1px; background: var(--border); margin: 1rem 0; }
.hover-shadow { transition: box-shadow var(--speed) var(--ease), transform var(--speed) var(--ease), border-color var(--speed) var(--ease); }
.hover-shadow:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); border-color: var(--border-strong); }
.flux-num { font-family: var(--font-mono); font-size: .8125rem; }
.flux-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.flux-stack { display: flex; flex-direction: column; gap: .25rem; }
.flux-row { display: flex; align-items: center; gap: .5rem; }

/* ==========================================================================
   19. RESPONSIVE
   ========================================================================== */
@media (max-width: 991.98px) {
    .flux-topbar { padding-left: .25rem; }
    .card { border-radius: 10px; }

    /* Wide tables scroll inside their own card rather than squeezing columns
       into unreadable stacks; the page itself never scrolls sideways. */
    .card > .card-body.p-0,
    .card > .card-body.table-responsive,
    .table-responsive {
        overflow-x: auto; -webkit-overflow-scrolling: touch;
    }
    .card > .card-body.p-0 > .table,
    .card > .card-body.table-responsive > .table,
    .table-responsive > .table {
        width: auto; min-width: 100%; white-space: nowrap;
    }
    /* Let the description-ish column breathe once the row can scroll */
    .table td.text-wrap, .table td[data-wrap] { white-space: normal; min-width: 220px; }
}
@media (max-width: 767.98px) {
    body { font-size: 14px; }
    .content-wrapper > .content > .container-fluid,
    .content-wrapper > .container-fluid { padding: 1rem; }
    .flux-page-head { flex-direction: column; align-items: stretch; }
    .flux-page-head .flux-page-actions { margin-left: 0; }
    .flux-page-head .btn { flex: 1; }
    .info-box, .small-box, .flux-stat { margin-bottom: .875rem; }
    .modal-dialog { margin: .5rem; }
    .flux-palette-panel { margin-top: 4vh; max-height: 86vh; }
    .flux-filters { flex-direction: column; align-items: stretch; }
    .flux-filters .form-control, .flux-filters .custom-select { width: 100%; }
    .flux-filters-end { margin-left: 0; }
    /* Comfortable touch targets */
    .btn { height: 38px; }
    .btn-sm { height: 32px; }
    .flux-iconbtn { width: 38px; height: 38px; flex-basis: 38px; }
}

/* ==========================================================================
   20. PRINT - documents and reports
   ========================================================================== */
@media print {
    .main-sidebar, .flux-topbar, .main-header, .main-footer, .no-print,
    .flux-page-actions, .card-tools, .flux-palette, .flux-filters,
    .dataTables_wrapper .row:first-child, .dataTables_wrapper .row:last-child { display: none !important; }

    body, .content-wrapper, .wrapper { background: #fff !important; margin: 0 !important; color: #000; }
    .content-wrapper { margin-left: 0 !important; }
    .content-wrapper > .content > .container-fluid,
    .content-wrapper > .container-fluid, .content-header { padding: 0 !important; }

    .card { border: 1px solid #ccc !important; box-shadow: none !important; break-inside: avoid; margin-bottom: 10px; }
    .card-header { background: #f5f5f5 !important; }

    .table { font-size: 10.5pt; }
    .table thead { display: table-header-group; }   /* repeat header on each page */
    .table thead th { background: #f2f2f2 !important; color: #000 !important; border-bottom: 1px solid #999 !important; }
    .table tbody td { border-bottom: 1px solid #ddd !important; }
    .table tbody tr { break-inside: avoid; }

    a[href]::after { content: ''; }                 /* no URL noise inside documents */
    .badge { border: 1px solid #999 !important; color: #000 !important; background: #fff !important; }
    @page { margin: 14mm; }
}

/* ==========================================================================
   21. MOTION PREFERENCES
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    .flux-skel { animation: none; background: var(--bg-sunken); }
}

/* ==========================================================================
   18. PROFIT & LOSS STATEMENT
   A financial statement reads top-to-bottom as a narrative: revenue, what it
   cost to earn it, what was left, what the business spent running itself, and
   the result. The section bands and the two result rows carry that rhythm.
   ========================================================================== */
.pl-statement td, .pl-statement th { vertical-align: middle; }

.pl-statement .pl-section td {
    background: var(--bg-sunken);
    color: var(--text-muted);
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    border-top: 1px solid var(--border);
}
.pl-statement .pl-subtotal td {
    font-weight: 650;
    border-top: 1px solid var(--border);
}

/* Gross profit - an interim result, so it reads lighter than the bottom line. */
.pl-statement .pl-result td {
    background: var(--bg-selected);
    font-weight: 700;
    font-size: .9375rem;
    border-top: 2px solid var(--c-primary);
    border-bottom: 1px solid var(--border);
}
.pl-statement .pl-result.is-negative td { background: var(--c-danger-bg); }

/* Net profit - the bottom line, and the only row that inverts. */
.pl-statement .pl-total td {
    background: var(--c-primary);
    color: var(--c-on-primary);
    font-weight: 750;
    font-size: 1rem;
    border-top: 2px solid var(--c-primary);
}
.pl-statement .pl-total.is-negative td { background: var(--c-danger); color: #fff; }

.pl-summary {
    margin-top: 1.25rem;
    padding: 1rem 1.25rem;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--r-card, 10px);
}
.pl-summary-title {
    font-size: .6875rem; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-muted); margin-bottom: .75rem;
}
.pl-summary-grid {
    display: grid; gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}
.pl-summary-item { display: flex; flex-direction: column; gap: .15rem; }
.pl-summary-label { font-size: .75rem; color: var(--text-muted); }
.pl-summary-value { font-size: 1.25rem; font-weight: 700; font-variant-numeric: tabular-nums; }

@media print {
    .pl-statement .pl-total td { background: #fff !important; color: #000 !important;
                                 border-top: 3px double #000; border-bottom: 3px double #000; }
    .pl-statement .pl-result td { background: #fff !important; border-top: 1.5px solid #000; }
}

/* ==========================================================================
   HORIZONTAL NAVIGATION
   The alternative to the sidebar, selectable in Settings. It renders the same
   manifest, so nothing has to be maintained twice.
   ========================================================================== */

.flux-topnav {
    position: sticky;
    top: var(--topbar-h, 56px);
    z-index: 1030;
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border, #e5e7eb);
    box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
}
.flux-topnav-inner {
    max-width: 100%;
    padding: 0 1.75rem;
    /* Many modules on a narrow screen must scroll, never wrap into a wall. */
    overflow-x: auto;
    overflow-y: visible;
    scrollbar-width: thin;
}
.flux-topnav-list {
    display: flex;
    align-items: stretch;
    gap: .125rem;
    list-style: none;
    margin: 0;
    padding: 0;
    white-space: nowrap;
}
.flux-topnav-item { position: relative; }

.flux-topnav-link {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .7rem .85rem;
    font-size: .875rem;
    font-weight: 500;
    color: var(--text-muted, #6b7280);
    background: none;
    border: 0;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    text-decoration: none;
    transition: color .15s, border-color .15s, background .15s;
}
.flux-topnav-link:hover,
.flux-topnav-link:focus-visible {
    color: var(--c-primary, #2563eb);
    background: var(--bg-hover, #f3f4f6);
    text-decoration: none;
}
.flux-topnav-link.active {
    color: var(--c-primary, #2563eb);
    border-bottom-color: var(--c-primary, #2563eb);
}
.flux-topnav-link i.bx { font-size: 1.05rem; }
.flux-topnav-caret { font-size: .95rem !important; opacity: .6; transition: transform .15s; }
.flux-topnav-item.is-open .flux-topnav-caret { transform: rotate(180deg); }

.flux-topnav-menu {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 240px;
    max-height: 70vh;
    overflow-y: auto;
    padding: .4rem;
    background: var(--bg-surface);
    border: 1px solid var(--border, #e5e7eb);
    border-radius: .5rem;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
    /* Above both sticky bars. The menu is switched to position:fixed by
       nav_popover.php so it is no longer clipped by the bar's overflow-x
       scroll container; z-index only decides paint order once it is free. */
    z-index: 1200;
    z-index: 1040;
}
.flux-topnav-item.is-open .flux-topnav-menu { display: block; }

/* A group near the right edge would otherwise open off-screen. */
.flux-topnav-item:nth-last-child(-n+3) .flux-topnav-menu { left: auto; right: 0; }

.flux-topnav-heading {
    padding: .5rem .65rem .3rem;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-faint, #9ca3af);
}
.flux-topnav-heading:not(:first-child) {
    margin-top: .3rem;
    border-top: 1px solid var(--border, #e5e7eb);
    padding-top: .55rem;
}
.flux-topnav-menuitem {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .5rem .65rem;
    border-radius: .35rem;
    font-size: .875rem;
    color: var(--text, #111827);
    text-decoration: none;
    white-space: nowrap;
}
.flux-topnav-menuitem:hover,
.flux-topnav-menuitem:focus-visible {
    background: var(--bg-hover, #f3f4f6);
    color: var(--c-primary, #2563eb);
    text-decoration: none;
}
.flux-topnav-menuitem.active {
    background: var(--c-primary-50, #eff6ff);
    color: var(--c-primary-700, #1d4ed8);
    font-weight: 600;
}
.flux-topnav-menuitem i.bx { font-size: 1rem; opacity: .75; width: 1.1rem; }

/* In horizontal mode there is no sidebar, so the content reclaims the width
   and the sidebar toggle has nothing to toggle. */
.flux-nav-horizontal .main-sidebar { display: none !important; }
.flux-nav-horizontal .content-wrapper,
.flux-nav-horizontal .main-header,
.flux-nav-horizontal .main-footer { margin-left: 0 !important; }
.flux-nav-horizontal [data-widget="pushmenu"] { display: none !important; }

@media (max-width: 768px) {
    .flux-topnav-inner { padding: 0 .75rem; }
    .flux-topnav-link span { display: none; }      /* icons only on small screens */
    .flux-topnav-link { padding: .7rem .6rem; }
    .flux-topnav-menu { min-width: 200px; }
}

/* ==========================================================================
   DOCUMENT HEADER - lifecycle stepper, actions, smart buttons
   Shared by every transactional document. They differ in what the states are
   called, not in how a document behaves.
   ========================================================================== */

/* ── DOCUMENT HEADER ─────────────────────────────────────────────────────────
   The masthead of every document in the system. It was a plain white box with a
   hairline border, which on a page of other plain white boxes gave the reader
   nothing to anchor on - the title of the document looked like the title of the
   third panel down.

   It now reads as a masthead: a tinted ground, a coloured rule along the top
   edge, and real elevation. One surface, still quiet, but unmistakably the
   thing the page is about. */
.flux-doc {
    position: relative;
    background: var(--bg-surface);
    border: 1px solid var(--border, #e5e7eb);
    border-radius: .5rem;
    padding: 1.15rem 1.35rem .35rem;
    margin-bottom: 1.25rem;
    box-shadow: 0 1px 2px rgba(16,24,40,.04);
    overflow: hidden;
}
/* The same coloured hairline the section panels carry, so the masthead reads as
   the first and most important of them rather than a different kind of thing. */
.flux-doc::before {
    content: "";
    position: absolute; inset: 0 0 auto 0; height: 2px;
    background: var(--c-primary);
}
/* A cancelled document says so in the masthead, not only in the track. */
.flux-doc:has(.flux-track.is-cancelled)::before {
    background: linear-gradient(90deg, var(--c-danger, #dc2626) 0%,
        color-mix(in srgb, var(--c-danger, #dc2626) 30%, transparent) 100%);
}
/* Three zones on one line: identity, lifecycle track, actions.
   The track is given `margin-right: auto` rather than a flex grow, so it hugs
   the document number it describes instead of floating in the middle of the
   row - the two belong together and read as one statement. */
.flux-doc-top {
    display: flex; flex-wrap: wrap; gap: .75rem 1rem;
    align-items: center; justify-content: flex-start;
    margin-bottom: .9rem;
}
.flux-doc-ident { display: flex; align-items: flex-start; gap: .75rem; min-width: 0; }
.flux-doc-nameblock { min-width: 0; }
.flux-doc-top > .flux-track { margin-right: auto; }
.flux-doc-top > .flux-doc-actions { margin-left: auto; }
.flux-doc-back {
    display: inline-flex; align-items: center; justify-content: center;
    width: 2.1rem; height: 2.1rem; flex: 0 0 auto;
    border: 1px solid var(--border, #e5e7eb); border-radius: .4rem;
    color: var(--text-muted, #6b7280); text-decoration: none; font-size: 1.15rem;
}
.flux-doc-back:hover { border-color: var(--c-primary, #2563eb); color: var(--c-primary, #2563eb); }
.flux-doc-title {
    margin: 0; font-size: 1.4rem; font-weight: 650; line-height: 1.2;
    color: var(--text, #111827); word-break: break-word;
    font-variant-numeric: tabular-nums;
}
.flux-doc-sub { margin: .2rem 0 0; font-size: .875rem; color: var(--text-muted, #6b7280); }
.flux-doc-actions { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.flux-doc-actions form { margin: 0; }

/* ---- lifecycle track ----------------------------------------------------
   Sits on the first row of the document header, beside the number, because
   "where has this got to?" is asked at the same moment as "what is this?".

   The old version was dots joined by a 0.6rem hairline, stacked on its own row
   below the title. Two problems: at that length the connector read as a gap
   rather than a path, so the steps looked like unrelated chips; and being on
   its own row it fell below the fold on a laptop, which is precisely where it
   was least useful. This is one continuous surface with the completed portion
   filled, so progress is legible from the shape alone before any label is
   read.
   -------------------------------------------------------------------------- */
.flux-track {
    display: flex; align-items: stretch; flex-wrap: nowrap;
    list-style: none; margin: 0; padding: .25rem;
    border-radius: 999px;
    background: var(--bg-sunken, #f8fafc);
    border: 1px solid var(--border, #e5e7eb);
    overflow: hidden; max-width: 100%;
}
.flux-tstep {
    display: flex; align-items: center; gap: .45rem;
    padding: .3rem .8rem .3rem .45rem;
    font-size: .8125rem; line-height: 1; white-space: nowrap;
    color: var(--text-faint, #9ca3af);
    position: relative;
}
/* A chevron, not a line: it points, so the track has a direction even when
   every step happens to be the same width. */
.flux-tstep:not(:last-child)::after {
    content: ''; flex: 0 0 auto; margin-left: .35rem;
    width: .34rem; height: .34rem;
    border-top: 1.5px solid var(--border-strong, #d5d9e0);
    border-right: 1.5px solid var(--border-strong, #d5d9e0);
    transform: rotate(45deg);
}
.flux-tstep-mark {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.4rem; height: 1.4rem; flex: 0 0 auto;
    border-radius: 50%; font-size: .7rem; font-weight: 700;
    background: var(--bg-surface, #fff);
    border: 1.5px solid var(--border, #e5e7eb);
    color: var(--text-faint, #9ca3af);
}
.flux-tstep-label { overflow: hidden; text-overflow: ellipsis; }

.flux-tstep.is-done { color: var(--text-muted, #6b7280); }
.flux-tstep.is-done .flux-tstep-mark {
    background: var(--c-success, #059669); border-color: var(--c-success, #059669); color: #fff;
}
.flux-tstep.is-done::after { border-color: var(--c-success, #059669); opacity: .5; }

/* The current step is the one piece of this header that must be unmissable. */
.flux-tstep.is-current {
    color: var(--c-on-primary, #fff); font-weight: 650;
    background: var(--c-primary, #2563eb);
    border-radius: 999px;
    padding-right: 1rem;
}
.flux-tstep.is-current .flux-tstep-mark {
    background: rgba(255, 255, 255, .22);
    border-color: transparent; color: var(--c-on-primary, #fff);
}
.flux-tstep.is-current::after { border-color: var(--c-primary, #2563eb); }

.flux-tstep.is-todo .flux-tstep-label { opacity: .85; }

/* Cancelled is a terminal state, not a position, so it replaces the track. */
.flux-track.is-cancelled {
    background: var(--c-danger-bg, #fef2f2);
    border-color: var(--c-danger-bd, #fecaca);
    padding: .45rem .95rem;
}
.flux-track-cancel {
    display: inline-flex; align-items: center; gap: .4rem;
    font-size: .8125rem; font-weight: 650; line-height: 1;
    color: var(--c-danger, #dc2626); white-space: nowrap;
}
.flux-track-cancel .bx { font-size: 1rem; }

/* ---- smart buttons ---- */
.flux-smart {
    display: flex; flex-wrap: wrap; gap: .5rem;
    margin: .35rem 0 1rem; padding-top: .9rem;
    border-top: 1px solid var(--border, #e5e7eb);
}
/* Compact by design: a document carries up to six of these and they sit above
   the content, so every extra millimetre pushes the document itself down. */
.flux-smart-btn {
    display: flex; align-items: center; gap: .45rem;
    padding: .5rem .6rem; min-width: 7rem;
    border: 1px solid var(--border, #e5e7eb); border-radius: .4rem;
    /* --bg is not a token in this system; the name was invented here and the
       fallback therefore always won, painting these buttons hardcoded white in
       dark mode. --bg-surface is the real one. */
    background: var(--bg-surface, #fff); color: var(--text, #111827);
    text-decoration: none;
    transition: border-color .15s, color .15s, box-shadow .15s;
}
.flux-smart-btn:hover {
    border-color: var(--c-primary, #2563eb);
    color: var(--c-primary, #2563eb); text-decoration: none;
    box-shadow: 0 2px 6px -2px rgba(37, 99, 235, .25);
}
.flux-smart-btn > i.bx {
    font-size: 1.15rem; opacity: .6; flex: 0 0 auto;
    transition: opacity .15s, color .15s;
}
.flux-smart-btn:hover > i.bx { opacity: 1; color: var(--c-primary, #2563eb); }
.flux-smart-value { font-size: .95rem; font-weight: 700; line-height: 1.1; }
.flux-smart-label {
    font-size: .68rem; text-transform: uppercase; letter-spacing: .04em;
    color: var(--text-muted, #6b7280);
}
.flux-smart-btn:hover .flux-smart-label { color: inherit; }

/* A toned smart button: the count is not merely a fact, it is a gap.
   Zero invoices against a delivered order is a document somebody still owes a
   customer; zero against a draft is nothing at all. Rendered as a tinted border
   plus an explicit flag rather than colour alone, so it survives a monochrome
   print and reads for anyone who cannot distinguish the tint. */
.flux-smart-btn.flux-smart-warning {
    border-color: var(--c-warning, #d97706);
    background: var(--bg-warning-soft, rgba(217, 119, 6, .07));
}
.flux-smart-btn.flux-smart-warning .flux-smart-value { color: var(--c-warning, #d97706); }
.flux-smart-btn.flux-smart-warning > i.bx {
    color: var(--c-warning, #d97706); opacity: 1;
    background: var(--bg-warning-soft, rgba(217, 119, 6, .12));
}
.flux-smart-btn.flux-smart-danger {
    border-color: var(--c-danger, #dc2626);
    background: var(--bg-danger-soft, rgba(220, 38, 38, .07));
}
.flux-smart-btn.flux-smart-danger .flux-smart-value,
.flux-smart-btn.flux-smart-danger > i.bx { color: var(--c-danger, #dc2626); opacity: 1; }
.flux-smart-flag {
    margin-left: auto; align-self: flex-start;
    font-size: .7rem; font-weight: 800; line-height: 1;
    width: 1.05rem; height: 1.05rem; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--c-warning, #d97706); color: #fff;
}
.flux-smart-btn.flux-smart-danger .flux-smart-flag { background: var(--c-danger, #dc2626); }

/* Narrow viewports: the track is the one element that cannot usefully wrap -
   a lifecycle broken across two lines stops reading as a sequence - so it
   scrolls sideways within its own pill instead. */
@media (max-width: 900px) {
    .flux-doc-top > .flux-track {
        order: 3; width: 100%; margin-right: 0;
        overflow-x: auto; -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }
    .flux-doc-top > .flux-track::-webkit-scrollbar { display: none; }
    .flux-doc-top > .flux-doc-actions { order: 2; }
}
@media (max-width: 576px) {
    .flux-doc { padding: .9rem 1rem .25rem; }
    .flux-doc-actions { width: 100%; margin-left: 0; }
    .flux-doc-actions .btn { flex: 1 1 auto; }
    .flux-smart-btn { flex: 1 1 45%; min-width: 0; }
    .flux-doc-title { font-size: 1.2rem; }
}

/* ==========================================================================
   LIST TOOLBAR - search, filters and exports on one line
   ========================================================================== */

.flux-listbar {
    background: var(--bg-surface);
    border: 1px solid var(--border, #e5e7eb);
    border-radius: .5rem;
    padding: .6rem .75rem;
    margin-bottom: 1rem;
}
.flux-listbar-form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .5rem;
    margin: 0;
}
/* Search takes the slack so the controls never wrap while space remains. */
.flux-listbar-search {
    position: relative;
    flex: 1 1 14rem;
    min-width: 11rem;
}
.flux-listbar-search > i.bx {
    position: absolute; left: .6rem; top: 50%; transform: translateY(-50%);
    font-size: 1rem; color: var(--text-faint, #9ca3af); pointer-events: none;
}
.flux-listbar-search input {
    width: 100%; height: 34px;
    padding: 0 .65rem 0 2rem;
    font-size: .8125rem;
    color: var(--text, #111827);
    background: var(--bg-surface, #fff);
    border: 1px solid var(--border-strong, #d1d5db);
    border-radius: .35rem;
}
.flux-listbar-field { display: flex; flex-direction: column; gap: .15rem; }
.flux-listbar-field > label {
    margin: 0;
    font-size: .68rem; font-weight: 600; letter-spacing: .03em;
    text-transform: uppercase; color: var(--text-muted, #6b7280);
    white-space: nowrap;
}
.flux-listbar-field input,
.flux-listbar-field select {
    height: 34px; padding: 0 .55rem;
    font-size: .8125rem;
    color: var(--text, #111827);
    background: var(--bg-surface, #fff);
    border: 1px solid var(--border-strong, #d1d5db);
    border-radius: .35rem;
    min-width: 8rem;
}
.flux-listbar-search input:focus,
.flux-listbar-field input:focus,
.flux-listbar-field select:focus {
    outline: none;
    border-color: var(--c-primary, #2563eb);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-primary, #2563eb) 15%, transparent);
}
/* Actions sit hard right on one line with the rest. */
.flux-listbar-actions {
    display: flex; align-items: center; gap: .35rem;
    margin-left: auto; white-space: nowrap;
}
.flux-listbar-actions .btn { height: 34px; display: inline-flex; align-items: center; gap: .3rem; }

@media (max-width: 768px) {
    .flux-listbar-actions { margin-left: 0; width: 100%; }
    .flux-listbar-actions .btn { flex: 1 1 auto; justify-content: center; }
    .flux-listbar-field, .flux-listbar-search { flex: 1 1 100%; }
}

/* ==========================================================================
   PAGER - page size, row count, page links
   --------------------------------------------------------------------------
   Sits inside the card footer, under the table it pages. Three groups on one
   line: size on the left, count in the middle, links hard right. The count is
   centred because it is the thing read most and never clicked.
   ========================================================================== */

.flux-pager {
    display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
    padding: .625rem .875rem;
    border-top: 1px solid var(--border);
    background: var(--bg-sunken);
    font-size: .75rem; color: var(--text-muted);
}

/* The size selector above the table.
 *
 * Same component, mirrored: the rule is on the BOTTOM edge because this one
 * sits over the rows rather than under them, and the count is pushed to the
 * right so the two ends of the bar answer "how much" and "what am I seeing"
 * without meeting in the middle. */
.flux-pager-topbar {
    border-top: 0; border-bottom: 1px solid var(--border);
    border-radius: var(--r-panel) var(--r-panel) 0 0;
}
.flux-pager-topbar .flux-pager-count { margin-left: auto; }
/* Directly inside a .card-body the table has already paid for the padding, so
   the pager reclaims it and sits flush with the card edges. */
.card-body > .flux-pager:last-child {
    margin: .875rem -1rem -1rem;
    border-bottom-left-radius: var(--r-panel);
    border-bottom-right-radius: var(--r-panel);
}

.flux-pager-size { display: flex; align-items: center; gap: .4375rem; white-space: nowrap; }
.flux-pager-size label {
    margin: 0; font-size: .75rem; font-weight: 500; letter-spacing: 0;
    color: var(--text-muted); text-transform: none;
}
.flux-pager-select {
    height: 28px; padding: 0 1.75rem 0 .5rem;
    font-family: var(--font-sans); font-size: .75rem; font-weight: 500;
    color: var(--text-strong); background-color: var(--bg-surface);
    border: 1px solid var(--border-strong); border-radius: 6px;
    appearance: none; -webkit-appearance: none; cursor: pointer;
    font-variant-numeric: tabular-nums;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right .3rem center;
    transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
.flux-pager-select:hover { border-color: var(--text-faint); }
.flux-pager-select:focus { outline: none; border-color: var(--c-primary); box-shadow: 0 0 0 3px var(--ring); }

.flux-pager-count { flex: 1 1 auto; text-align: center; font-variant-numeric: tabular-nums; }
.flux-pager-count strong { color: var(--text-strong); font-weight: 600; }

/* The "All" cap notice. Amber because it is genuinely a state the reader needs
   to act on - there are rows they asked for and are not seeing. */
.flux-pager-cap {
    display: inline-flex; align-items: center; gap: .25rem; margin-left: .5rem;
    padding: .0625rem .4375rem; border-radius: var(--r-pill);
    background: var(--c-warning-bg); color: var(--c-warning);
    border: 1px solid var(--c-warning-bd); font-size: .6875rem; font-weight: 500;
}

.flux-pager-links {
    display: flex; align-items: center; gap: .1875rem;
    list-style: none; margin: 0; padding: 0;
}
.flux-pager-links a,
.flux-pager-links > li > span {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 28px; height: 28px; padding: 0 .375rem;
    font-size: .75rem; font-weight: 500; font-variant-numeric: tabular-nums;
    color: var(--text); background: var(--bg-surface);
    border: 1px solid var(--border); border-radius: 6px;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease),
                border-color var(--speed) var(--ease);
}
.flux-pager-links a:hover {
    background: var(--bg-hover); color: var(--text-strong); border-color: var(--border-strong);
}
.flux-pager-links .is-current {
    background: var(--c-primary); color: var(--c-on-primary);
    border-color: var(--c-primary); font-weight: 600;
}
.flux-pager-links .is-disabled { color: var(--text-faint); opacity: .5; cursor: default; }
.flux-pager-links .flux-pager-gap {
    border-color: transparent; background: transparent; color: var(--text-faint);
    min-width: 18px; padding: 0;
}
.flux-pager-links i { font-size: 1rem; }

@media (max-width: 768px) {
    .flux-pager { gap: .625rem; justify-content: center; }
    /* Count first on a phone: it is the useful half, and a wrapped pager that
       leads with a dropdown buries it below the fold. */
    .flux-pager-count { order: -1; flex: 1 1 100%; text-align: left; }
    .flux-pager-links { margin-left: auto; }
    .flux-pager-cap { margin-left: 0; margin-top: .25rem; }
}

/* ==========================================================================
   REPORT SCAFFOLD - head, KPI row, table card, empty state
   --------------------------------------------------------------------------
   Every report is built from these. See helpers/report_helper.php for the four
   calls that emit them and why they exist.
   ========================================================================== */

.flux-report-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap;
    padding: 1.25rem 0 1rem;
}
.flux-report-title { display: flex; align-items: flex-start; gap: .875rem; min-width: 0; }
.flux-report-icon {
    flex: 0 0 40px; width: 40px; height: 40px; border-radius: 10px;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--c-primary-50); color: var(--c-primary);
    border: 1px solid var(--c-primary-100);
}
.flux-report-icon i { font-size: 1.375rem; }
.flux-report-title h1 {
    font-size: 1.375rem; letter-spacing: -.022em; margin: 0 0 .1875rem; line-height: 1.2;
}
.flux-report-title p { margin: 0; font-size: .8125rem; color: var(--text-muted); max-width: 68ch; }
.flux-report-actions { display: flex; align-items: center; gap: .375rem; flex-wrap: wrap; }
.flux-report-actions .btn-group .btn { border-radius: 0; }
.flux-report-actions .btn-group .btn:first-child { border-top-left-radius: 6px; border-bottom-left-radius: 6px; }
.flux-report-actions .btn-group .btn:last-child  { border-top-right-radius: 6px; border-bottom-right-radius: 6px; }

/* ── KPI tiles ────────────────────────────────────────────────────────────
   A left colour bar carries the tone rather than a filled background: eight
   saturated tiles in a row is a traffic light, not a summary, and the figure is
   what the eye should land on. */
.flux-kpi-row { margin-bottom: .25rem; }
.flux-kpi-row > [class*="col-"] { margin-bottom: .75rem; }

.flux-kpi {
    position: relative; height: 100%;
    display: flex; align-items: center; gap: .75rem;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--r-panel);
    padding: .875rem 1rem .875rem 1.125rem;
    overflow: hidden;
}
.flux-kpi::before {
    content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
    background: var(--kpi-tone, var(--c-primary));
}
.flux-kpi.is-primary { --kpi-tone: var(--c-primary); --kpi-bg: var(--c-primary-50); }
.flux-kpi.is-success { --kpi-tone: var(--c-success); --kpi-bg: var(--c-success-bg); }
.flux-kpi.is-warning { --kpi-tone: var(--c-warning); --kpi-bg: var(--c-warning-bg); }
.flux-kpi.is-danger  { --kpi-tone: var(--c-danger);  --kpi-bg: var(--c-danger-bg); }
.flux-kpi.is-info    { --kpi-tone: var(--c-info);    --kpi-bg: var(--c-info-bg); }
.flux-kpi.is-neutral { --kpi-tone: var(--c-neutral); --kpi-bg: var(--c-neutral-bg); }

.flux-kpi-icon {
    flex: 0 0 34px; width: 34px; height: 34px; border-radius: 9px;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--kpi-bg, var(--c-primary-50));
    color: var(--kpi-tone, var(--c-primary));
}
.flux-kpi-icon i { font-size: 1.125rem; }
.flux-kpi-body { min-width: 0; display: flex; flex-direction: column; }
.flux-kpi-label {
    font-size: .6875rem; font-weight: 600; letter-spacing: .045em;
    text-transform: uppercase; color: var(--text-muted); line-height: 1.3;
}
.flux-kpi-value {
    font-size: 1.25rem; font-weight: 650; letter-spacing: -.02em;
    color: var(--text-strong); line-height: 1.25;
    font-variant-numeric: tabular-nums;
    /* A long figure truncates rather than wrapping the tile out of shape. */
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.flux-kpi-sub { font-size: .6875rem; color: var(--text-faint); line-height: 1.35; margin-top: .0625rem; }

/* ── Report table ─────────────────────────────────────────────────────── */
.flux-report-card { margin-bottom: 1rem; }
.flux-report-card > .card-header {
    display: flex; align-items: center; justify-content: space-between;
    gap: .75rem; flex-wrap: wrap;
}
.flux-report-note { font-size: .75rem; color: var(--text-muted); }

.flux-report-table { margin-bottom: 0; width: 100%; }
.flux-report-table > thead > tr > th {
    background: var(--bg-sunken);
    color: var(--text-muted);
    font-size: .6875rem; font-weight: 600;
    letter-spacing: .045em; text-transform: uppercase;
    white-space: nowrap;
    border-bottom: 1px solid var(--border-strong);
    border-top: 0;
    padding: .5rem .75rem;
    vertical-align: middle;
}
.flux-report-table > tbody > tr > td {
    padding: .5rem .75rem;
    font-size: .8125rem;
    border-top: 1px solid var(--border-soft);
    vertical-align: middle;
}
.flux-report-table > tbody > tr:first-child > td { border-top: 0; }
.flux-report-table > tfoot > tr > th,
.flux-report-table > tfoot > tr > td {
    background: var(--bg-sunken);
    border-top: 2px solid var(--border-strong);
    padding: .5rem .75rem;
    font-size: .8125rem; font-weight: 650; color: var(--text-strong);
}
/* Numeric columns are tabular and right-aligned wherever they are marked, and
   `.text-end` is honoured as well as `.text-right` - several reports were
   written against Bootstrap 5 and used the former, which does nothing here. */
.flux-report-table .text-right, .flux-report-table .text-end,
.flux-report-table th.num, .flux-report-table td.num {
    text-align: right; font-variant-numeric: tabular-nums;
}
.flux-report-table td.num, .flux-report-table td.text-right, .flux-report-table td.text-end {
    font-family: var(--font-mono); font-size: .78125rem; letter-spacing: -.02em;
}
.flux-report-table .strong, .flux-report-table .fw-bold { font-weight: 650; color: var(--text-strong); }

/* A long report scrolls inside its card with the header pinned, so the column
   a figure belongs to is still knowable on row 300. */
.flux-sticky-head { max-height: 68vh; overflow: auto; }
.flux-sticky-head > table > thead > tr > th {
    position: sticky; top: 0; z-index: 2;
    background: var(--bg-sunken);
    box-shadow: inset 0 -1px 0 var(--border-strong);
}

/* Row emphasis used across the reports. */
.flux-report-table tr.is-total > td { background: var(--bg-sunken); font-weight: 650; }
.flux-report-table tr.is-group > td {
    background: var(--bg-hover); font-weight: 650; color: var(--text-strong);
    font-size: .75rem; letter-spacing: .02em; text-transform: uppercase;
}
.flux-report-table tr.is-critical > td { background: var(--c-danger-bg); }
.flux-report-table tr.is-warn > td { background: var(--c-warning-bg); }

/* Deltas. Direction is decided per column by flux_delta_class(), because
   revenue up is good and scrap up is not. */
.flux-up   { color: var(--c-success); font-weight: 600; }
.flux-down { color: var(--c-danger);  font-weight: 600; }
.flux-flat { color: var(--text-faint); }

/* An inline proportion bar, for ageing buckets and utilisation columns. */
.flux-bar {
    position: relative; height: 6px; border-radius: 99px;
    background: var(--bg-sunken); overflow: hidden; min-width: 60px;
}
.flux-bar > span {
    position: absolute; inset: 0 auto 0 0; border-radius: 99px;
    background: var(--c-primary);
}
.flux-bar.is-success > span { background: var(--c-success); }
.flux-bar.is-warning > span { background: var(--c-warning); }
.flux-bar.is-danger  > span { background: var(--c-danger); }

.flux-report-empty {
    display: flex; flex-direction: column; align-items: center; gap: .5rem;
    padding: 2.5rem 1rem; color: var(--text-muted); text-align: center;
}
.flux-report-empty i { font-size: 1.75rem; color: var(--text-faint); }
.flux-report-empty span { font-size: .8125rem; max-width: 46ch; }
.flux-report-emptyrow > td { border-top: 0 !important; }

/* Two panels side by side - a chart and its table, two ageing views. */
.flux-report-split { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
@media (max-width: 992px) { .flux-report-split { grid-template-columns: 1fr; } }

@media (max-width: 768px) {
    .flux-report-head { padding: 1rem 0 .75rem; }
    .flux-report-actions { width: 100%; }
    .flux-report-actions .btn-group { flex: 1 1 auto; }
    .flux-kpi-value { font-size: 1.125rem; }
}

/* On paper the scaffold gets out of the way - the document builder supplies its
   own masthead and styling. See assets/js/flux-document.js. */
@media print {
    .flux-report-head, .flux-report-actions, .flux-period { display: none !important; }
    .flux-sticky-head { max-height: none; overflow: visible; }
    .flux-kpi { break-inside: avoid; }
}

/* A totals row in a table footer.
   Replaces the inline style="background-color:#f8f9fa" several list views
   carried, which painted a light grey band over the dark theme and left the
   most important line on the page as the one unreadable one. */
.flux-total-row > td,
.flux-total-row > th,
tfoot .flux-total-row > td {
    background: var(--bg-sunken) !important;
    font-weight: 650;
    color: var(--text-strong);
    border-top: 2px solid var(--border-strong) !important;
}

/* ==========================================================================
   PERIOD FILTER - preset tabs + custom range
   --------------------------------------------------------------------------
   Segmented control, not a dropdown. The presets answer the question nine
   times out of ten and a <select> hides them behind a click.
   ========================================================================== */

.flux-period {
    display: flex; align-items: center; flex-wrap: wrap; gap: .625rem;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--r-panel);
    padding: .5rem .625rem;
    margin-bottom: 1rem;
}

.flux-period-tabs {
    display: inline-flex; align-items: stretch;
    background: var(--bg-sunken);
    border: 1px solid var(--border);
    border-radius: var(--r-control);
    padding: 2px;
    gap: 2px;
    flex-wrap: wrap;
}
.flux-period-tab {
    display: inline-flex; align-items: center; gap: .3rem;
    height: 28px; padding: 0 .625rem;
    font-size: .75rem; font-weight: 500; white-space: nowrap;
    color: var(--text-muted); background: transparent;
    border: 0; border-radius: 6px; cursor: pointer;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.flux-period-tab:hover { background: var(--bg-hover); color: var(--text-strong); text-decoration: none; }
.flux-period-tab.is-active {
    background: var(--c-primary); color: var(--c-on-primary); font-weight: 600;
    box-shadow: var(--shadow-xs);
}
.flux-period-tab.is-active:hover { background: var(--c-primary-hov); color: var(--c-on-primary); }
.flux-period-tab i { font-size: .9375rem; }

/* The custom range is collapsed until asked for, so the common case stays a
   single row and the uncommon case does not need a second screen. */
.flux-period-range {
    display: none; align-items: center; gap: .375rem; margin: 0;
}
.flux-period-range.is-open { display: flex; }
.flux-period-range label {
    margin: 0; font-size: .6875rem; font-weight: 600; letter-spacing: .02em;
    text-transform: uppercase; color: var(--text-faint);
}
.flux-period-range input[type="date"] {
    height: 28px; padding: 0 .4375rem;
    font-family: var(--font-sans); font-size: .75rem; color: var(--text-strong);
    background: var(--bg-surface);
    border: 1px solid var(--border-strong); border-radius: 6px;
}
.flux-period-range input[type="date"]:focus {
    outline: none; border-color: var(--c-primary); box-shadow: 0 0 0 3px var(--ring);
}
.flux-period-range .btn { height: 28px; }

.flux-period-extra { display: flex; align-items: center; gap: .375rem; }
.flux-period-extra select, .flux-period-extra .custom-select {
    height: 28px; font-size: .75rem; width: auto; padding: 0 1.75rem 0 .5rem;
}

/* The resolved dates, spelled out. "This Quarter" is not the same as knowing
   the report covers 1 Jul - 30 Sep, and it is the second that belongs on a
   printed document. */
.flux-period-caption {
    display: inline-flex; align-items: center; gap: .3rem;
    margin-left: auto; padding-left: .5rem;
    font-size: .75rem; color: var(--text-muted); white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.flux-period-caption i { font-size: .9375rem; color: var(--text-faint); }

@media (max-width: 900px) {
    .flux-period-caption { margin-left: 0; width: 100%; padding-left: 0; }
    .flux-period-tabs { width: 100%; }
    .flux-period-tab { flex: 1 1 auto; justify-content: center; }
}

/* ==========================================================================
   BULK SELECTION - checkbox column + sticky action bar
   ========================================================================== */

.flux-check-col {
    width: 34px; padding-left: .625rem !important; padding-right: 0 !important;
    text-align: center; vertical-align: middle; cursor: pointer;
}
.flux-check-col input[type="checkbox"] { width: 15px; height: 15px; cursor: pointer; margin: 0; vertical-align: middle; }
/* Sorting by the checkbox column is meaningless, so it never offers to. */
.flux-check-col.sorting, .flux-check-col.sorting_asc, .flux-check-col.sorting_desc {
    pointer-events: auto; background-image: none !important;
}

/* A selected row has to be obvious while the eye is on the toolbar at the
   bottom, so it takes a tinted fill and a brand-coloured leading edge rather
   than a faint background alone. */
.table tbody tr.is-selected > td {
    background: var(--bg-selected) !important;
}
.table tbody tr.is-selected > td:first-child {
    box-shadow: inset 3px 0 0 0 var(--c-primary);
}

/* Sticky to the BOTTOM: on a hundred-row page the selection is made at the
   bottom, and a bar pinned above the table would be off-screen at the exact
   moment it became relevant. */
/* FIXED, not sticky.
 *
 * It was `position: sticky; bottom: .875rem`, which works only while no
 * ancestor establishes a scroll container. Two rules down this same file,
 * `.card > .card-body.p-0 { overflow: hidden }` rounds off the corners of a
 * full-bleed table - and any list whose bulk bar sits inside such a card lost
 * the stickiness completely: the bar rendered at its natural position after the
 * last row, so on a long list you had to scroll to the bottom to reach the
 * actions for rows you had selected at the top.
 *
 * Fixing it in the views by moving the bar outside the card would work until
 * the next list put it back inside. Anchoring to the viewport instead makes the
 * bar independent of wherever it is placed in the markup, which is the property
 * a floating action bar should have had in the first place.
 *
 * `left: var(--sidebar-w)` keeps it clear of the sidebar, and the two overrides
 * below drop it to the edge when the sidebar is collapsed or off-canvas. */
.flux-bulkbar {
    position: fixed; bottom: 1rem; z-index: var(--z-sticky);
    left: var(--sidebar-w); right: 0;
    margin: 0; padding: 0 1.5rem;
    animation: fluxBulkIn .16s var(--ease);
    pointer-events: none;              /* the padded gutter must not eat clicks */
}
.flux-bulkbar > * { pointer-events: auto; }
body.sidebar-collapse .flux-bulkbar { left: 0; }
@media (max-width: 991.98px) { .flux-bulkbar { left: 0; padding: 0 .75rem; } }
.flux-bulkbar[hidden] { display: none; }
@keyframes fluxBulkIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}
.flux-bulkbar-inner {
    display: flex; align-items: center; justify-content: space-between;
    flex-wrap: wrap; gap: .75rem;
    background: var(--bg-shell); color: #fff;
    border: 1px solid rgba(255,255,255,.10);
    border-radius: var(--r-panel);
    padding: .5rem .5rem .5rem .875rem;
    box-shadow: var(--shadow-lg);
}
.flux-bulkbar-count { font-size: .8125rem; color: rgba(255,255,255,.72); white-space: nowrap; }
.flux-bulkbar-count strong {
    color: #fff; font-weight: 650; font-size: .9375rem;
    font-variant-numeric: tabular-nums; margin-right: .125rem;
}
.flux-bulkbar-actions { display: flex; align-items: center; gap: .375rem; flex-wrap: wrap; }

/* The bar sits on the dark shell colour, so the buttons are restated against
   it - the light-surface variants would be invisible here. */
.flux-bulkbar-actions .btn {
    --btn-bg: rgba(255,255,255,.10); --btn-fg: #fff; --btn-bd: rgba(255,255,255,.16);
    --btn-bg-h: rgba(255,255,255,.18); --btn-fg-h: #fff; --btn-bd-h: rgba(255,255,255,.28);
}
.flux-bulkbar-actions .btn-primary {
    --btn-bg: var(--c-primary-500); --btn-fg: #fff; --btn-bd: var(--c-primary-500);
    --btn-bg-h: var(--c-primary-400); --btn-fg-h: #fff; --btn-bd-h: var(--c-primary-400);
}
.flux-bulkbar-actions .btn-outline-danger {
    --btn-bg: transparent; --btn-fg: #fca5a5; --btn-bd: rgba(252,165,165,.42);
    --btn-bg-h: #b91c1c; --btn-fg-h: #fff; --btn-bd-h: #b91c1c;
}
.flux-bulkbar-actions .btn-outline-warning {
    --btn-bg: transparent; --btn-fg: #fcd34d; --btn-bd: rgba(252,211,77,.42);
    --btn-bg-h: #b45309; --btn-fg-h: #fff; --btn-bd-h: #b45309;
}
.flux-bulkbar-actions .btn-outline-success {
    --btn-bg: transparent; --btn-fg: #6ee7b7; --btn-bd: rgba(110,231,183,.42);
    --btn-bg-h: #047857; --btn-fg-h: #fff; --btn-bd-h: #047857;
}
.flux-bulkbar-actions .flux-btn-quiet {
    --btn-bg: transparent; --btn-bd: transparent; --btn-fg: rgba(255,255,255,.55);
    --btn-bg-h: rgba(255,255,255,.10); --btn-fg-h: #fff; --btn-bd-h: transparent;
}

@media (max-width: 640px) {
    .flux-bulkbar-inner { padding: .5rem; }
    .flux-bulkbar-count { width: 100%; text-align: center; }
    .flux-bulkbar-actions { width: 100%; }
    .flux-bulkbar-actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* Neither belongs in a printed document. */
@media print {
    .flux-bulkbar, .flux-check-col, .flux-period-range, .flux-period-tabs { display: none !important; }
}

/* Status and type chips - readable words, not lowercase database values. */
.flux-chip {
    display: inline-block; padding: .2rem .55rem;
    font-size: .72rem; font-weight: 600; line-height: 1.4;
    border-radius: 2rem; white-space: nowrap;
}
.flux-chip-neutral { background: var(--bg-hover, #f3f4f6); color: var(--text-muted, #6b7280); }
.flux-chip-info    { background: #dbeafe; color: #1d4ed8; }
.flux-chip-success { background: #d1fae5; color: #047857; }
.flux-chip-warning { background: #fef3c7; color: #b45309; }
.flux-chip-danger  { background: #fee2e2; color: #b91c1c; }
@media (prefers-color-scheme: dark) {
    .flux-chip-info    { background: rgba(37,99,235,.22);  color: #93c5fd; }
    .flux-chip-success { background: rgba(5,150,105,.22);  color: #6ee7b7; }
    .flux-chip-warning { background: rgba(217,119,6,.22);  color: #fcd34d; }
    .flux-chip-danger  { background: rgba(220,38,38,.22);  color: #fca5a5; }
}

/* ==========================================================================
   DATE FIELDS - the calendar must be visible and clickable everywhere
   Every date field in the application is a native <input type="date">, so the
   picker was always there; it was just invisible against the dark theme and
   easy to miss against the light one. This makes the affordance explicit.
   ========================================================================== */

input[type="date"],
input[type="datetime-local"],
input[type="month"] {
    /* Room for the indicator so it never sits on top of the text. */
    padding-right: 2rem;
    position: relative;
    cursor: pointer;
}
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator {
    position: absolute;
    right: .5rem;
    width: 1.05rem;
    height: 1.05rem;
    padding: 0;
    cursor: pointer;
    opacity: .75;
    /* Drawn rather than relying on the browser glyph, which is invisible on a
       dark background because it is a black bitmap. */
    background: transparent
        url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232563eb' stroke-width='2' stroke-linecap='round'><rect x='3' y='5' width='18' height='16' rx='2'/><path d='M16 3v4M8 3v4M3 10h18'/></svg>")
        no-repeat center / contain;
}
input[type="date"]:hover::-webkit-calendar-picker-indicator,
input[type="datetime-local"]:hover::-webkit-calendar-picker-indicator { opacity: 1; }

/* Firefox has no indicator pseudo-element; a background icon keeps the
   affordance consistent there. */
@supports (-moz-appearance: none) {
    input[type="date"], input[type="datetime-local"], input[type="month"] {
        background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232563eb' stroke-width='2' stroke-linecap='round'><rect x='3' y='5' width='18' height='16' rx='2'/><path d='M16 3v4M8 3v4M3 10h18'/></svg>");
        background-repeat: no-repeat;
        background-position: right .5rem center;
        background-size: 1.05rem;
    }
}

/* An input-group that already supplies its own calendar button must not show
   two - the field's own indicator is redundant there. (An empty
   `.input-group input[type="date"] + .input-group-append ~ *` selector sat here
   and styled nothing; the rule below is what actually does the work.) */
.input-group:has(.input-group-append) input[type="date"]::-webkit-calendar-picker-indicator {
    opacity: 0; width: 100%; right: 0; /* keep it clickable, hide the duplicate */
}

/* ---- list toolbar: search sits at a settled width -----------------------
   Search used to take all the slack, so with five filters it squeezed to
   nothing on a narrow window and the filters shuffled position as the page
   resized. A fixed comfortable width keeps every control where the eye last
   found it, and a spacer absorbs the leftover space instead. */
.flux-listbar-search { flex: 0 1 20rem; min-width: 13rem; }
.flux-listbar-spacer { flex: 1 1 auto; }

/* Keep the whole toolbar on one line at ordinary widths.
   With a search box and five filters the actions were being pushed onto a
   second row, which is the thing the single-line layout exists to avoid.
   Tighter controls buy back the space; below 1200px it wraps deliberately
   rather than shrinking the fields to unusable widths. */
.flux-listbar-search { flex: 0 1 15rem; min-width: 10rem; }
.flux-listbar-field select { min-width: 6.5rem; }
.flux-listbar-field input[type="date"] { width: 8.75rem; min-width: 0; }
.flux-listbar-field input[type="text"] { min-width: 7rem; }
.flux-listbar-actions .btn { padding-left: .55rem; padding-right: .55rem; font-size: .8rem; }

@media (max-width: 1200px) {
    .flux-listbar-actions { margin-left: auto; }
}

/* The spacer was forcing the wrap it was meant to prevent: as a flex item that
   grows, it claimed the remaining width and pushed the actions to a second row.
   margin-left:auto on the actions right-aligns them without occupying a slot. */
.flux-listbar-spacer { display: none; }

/* Final trim: the row overflowed by roughly forty pixels, which is all it took
   to push the actions onto a second line. */
.flux-listbar-form { gap: .4rem; }
.flux-listbar-search { flex: 0 1 13rem; min-width: 9rem; }
.flux-listbar-field select { min-width: 6rem; }
.flux-listbar-field input[type="date"] { width: 8.25rem; }

/* ==========================================================================
   CHIPS - readable, not washed out
   The first version used a pale tint behind text of the same pale hue, which
   on a white card left roughly 2:1 contrast: legible if you already knew what
   it said. These use a strong text colour of the hue, a light fill and a
   matching border, which holds up in both themes and passes AA.
   ========================================================================== */
.flux-chip {
    display: inline-block;
    padding: .18rem .5rem;
    font-size: .72rem;
    font-weight: 600;
    line-height: 1.45;
    letter-spacing: .01em;
    border-radius: .3rem;
    border: 1px solid transparent;
    white-space: nowrap;
}
.flux-chip-neutral { background:#f1f3f5; border-color:#dde1e6; color:#3d4753; }
.flux-chip-info    { background:#e8f0fe; border-color:#c3d8fd; color:#1a4fbd; }
.flux-chip-success { background:#e6f6ed; border-color:#b7e4c9; color:#12633b; }
.flux-chip-warning { background:#fef4e5; border-color:#f8dcae; color:#8a5200; }
.flux-chip-danger  { background:#fdeceb; border-color:#f7c6c3; color:#9c2620; }

html[data-theme="dark"] .flux-chip-neutral,
html[data-theme="dark"] .flux-chip-info,
html[data-theme="dark"] .flux-chip-success,
html[data-theme="dark"] .flux-chip-warning,
html[data-theme="dark"] .flux-chip-danger { border-color: transparent; }
html[data-theme="dark"] .flux-chip-neutral { background:#2a303a; color:#c3cbd6; }
html[data-theme="dark"] .flux-chip-info    { background:#16305c; color:#a8c8ff; }
html[data-theme="dark"] .flux-chip-success { background:#123a27; color:#8fe0b4; }
html[data-theme="dark"] .flux-chip-warning { background:#3d2d0e; color:#f3c874; }
html[data-theme="dark"] .flux-chip-danger  { background:#431c1a; color:#f5a29b; }

/* ==========================================================================
   FILTER BAR - dates and dimensions only, with Apply
   ========================================================================== */
.flux-filterbar {
    background: var(--bg-surface);
    border: 1px solid var(--border, #e5e7eb);
    border-radius: .5rem;
    padding: .7rem .85rem;
    margin-bottom: .75rem;
}
.flux-filterbar-form { display:flex; flex-wrap:wrap; align-items:flex-end; gap:.5rem; margin:0; }
.flux-filterbar-field { display:flex; flex-direction:column; gap:.2rem; }
.flux-filterbar-field > label {
    margin:0; font-size:.68rem; font-weight:600; letter-spacing:.04em;
    text-transform:uppercase; color:var(--text-muted,#6b7280); white-space:nowrap;
}
.flux-filterbar-field input,
.flux-filterbar-field select {
    height:34px; padding:0 .55rem; font-size:.8125rem;
    color:var(--text,#111827); background:var(--bg-surface,#fff);
    border:1px solid var(--border-strong,#d1d5db); border-radius:.35rem;
}
.flux-filterbar-field select { min-width:8.5rem; }
.flux-filterbar-field input[type="date"] { width:9.5rem; }
.flux-filterbar-actions { display:flex; gap:.35rem; margin-left:auto; }
.flux-filterbar-actions .btn { height:34px; display:inline-flex; align-items:center; gap:.3rem; }

/* ==========================================================================
   TABLE BAR - one row above the table: wide search, then exports
   ========================================================================== */
.flux-tablebar {
    display:flex; align-items:center; gap:.5rem;
    padding:.6rem .85rem;
    border-bottom:1px solid var(--border, #e5e7eb);
}
.flux-tablebar-search { position:relative; flex:1 1 auto; min-width:0; }
.flux-tablebar-search > i.bx {
    position:absolute; left:.65rem; top:50%; transform:translateY(-50%);
    font-size:1.05rem; color:var(--text-faint,#9ca3af); pointer-events:none;
}
.flux-tablebar-search input {
    width:100%; height:36px; padding:0 .7rem 0 2.1rem;
    font-size:.8125rem; color:var(--text,#111827);
    background:var(--bg-surface,#fff);
    border:1px solid var(--border-strong,#d1d5db); border-radius:.35rem;
}
.flux-tablebar-search input:focus {
    outline:none; border-color:var(--c-primary,#2563eb);
    box-shadow:0 0 0 3px color-mix(in srgb, var(--c-primary,#2563eb) 15%, transparent);
}
.flux-tablebar-actions { display:flex; gap:.35rem; flex:0 0 auto; }
.flux-tablebar-actions .btn { height:36px; display:inline-flex; align-items:center; gap:.35rem; }

@media (max-width: 768px) {
    .flux-tablebar { flex-wrap:wrap; }
    .flux-tablebar-actions { width:100%; }
    .flux-tablebar-actions .btn { flex:1 1 auto; justify-content:center; }
}

/* ==========================================================================
   APP LAUNCHER
   --------------------------------------------------------------------------
   Minimal-premium. Dials: variance 3, motion 3, density 3.

   The centred column is deliberate and is the one place the anti-centre rule
   is correctly suspended: at low variance a single focused column IS the
   design, and this screen has exactly one message and one grid.

   Removed from the previous version: blurred colour orbs, gradient text, and
   the glossy gradient squircles. Each was a generated-page tell. What carries
   the premium feel now is spacing, one radius system, one accent, and icons
   drawn by an icon foundry rather than by hand.
   ========================================================================== */
.flux-launch-page {
    position:relative; isolation:isolate;
    min-height:calc(100vh - var(--topbar-h,57px) - 56px);
    display:flex; justify-content:center;
}
/* Background: a fine dot field with one low-chroma wash behind the greeting.
   Deliberately NOT blurred colour orbs or a mesh gradient - those are the most
   recognisable generated-page tells there are. A technical dot grid is the
   opposite move: it reads as precision, it survives at any zoom, and it costs
   two gradients and no markup. */
.flux-launch-page::before {
    content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
    /* The dots were --border (#e6e8ec) on a --bg-app (#f5f6f8) page: a 4%
       luminance difference, then masked down to 20% opacity. That is why the
       page still read as blank - the texture was there and mathematically
       invisible. Same idea, a colour that can actually be seen against the
       surface it sits on. */
    background-image:radial-gradient(#a9b2c4 2px, transparent 2px);
    background-size:24px 24px;
    /* Never fully faded: the field carries to the bottom of the page so the
       lower half is not bare, and only drops to 55% behind the tiles. */
    -webkit-mask-image:linear-gradient(180deg, #000 0%, rgba(0,0,0,.7) 55%, rgba(0,0,0,.55) 100%);
            mask-image:linear-gradient(180deg, #000 0%, rgba(0,0,0,.7) 55%, rgba(0,0,0,.55) 100%);
}

.flux-launch-page::after {
    content:""; position:absolute; inset:0 0 auto; height:26rem; z-index:-2;
    pointer-events:none;
    /* One low-chroma wash behind the greeting. #eef2ff on #f5f6f8 was another
       near-invisible pairing, so this is the 100-step of the same ramp with a
       soft falloff rather than the 50. */
    background:
        radial-gradient(58rem 24rem at 50% -5rem, rgba(99,102,241,.16) 0%, rgba(99,102,241,0) 68%),
        linear-gradient(180deg, rgba(148,163,184,.10) 0%, rgba(148,163,184,0) 100%);
}

.flux-launch-shell {
    position:relative; z-index:1;
    width:100%; max-width:78rem;
    padding:4.5rem 1.75rem 4rem;
}

/* --- greeting: one centred column ---------------------------------------- */
.flux-launch-head { text-align:center; margin-bottom:3.25rem; }

.flux-launch-clock {
    display:inline-flex; align-items:center; justify-content:center; gap:.7rem;
    margin:0 0 1.1rem; padding:.4rem .95rem;
    border:1px solid var(--border); border-radius:2rem;
    background:var(--bg-surface);
    box-shadow:0 1px 2px rgba(16,24,40,.04);
    font-size:.9375rem; font-weight:600;
    color:var(--text-strong); letter-spacing:-.005em;
}
/* A hairline between date and time, rather than a middle dot. */
.flux-launch-time {
    padding-left:.7rem; border-left:1px solid var(--border);
    font-variant-numeric:tabular-nums; font-weight:700;
    color:var(--c-primary-700, #4338ca);
}

.flux-launch-head h1 {
    margin:0; font-size:2.125rem; line-height:1.15; font-weight:600;
    letter-spacing:-.028em; color:var(--text-strong);
}
/* Solid accent, not a gradient fill. Gradient text is the shortcut every
   generated page reaches for; one flat brand colour says the same thing and
   stays legible at every weight and size. */
.flux-launch-name { color:var(--c-primary); }

/* Slightly stronger than the usual muted tone: this line sits directly on the
   dot field, and at --text-muted the pattern competed with it. */
.flux-launch-sub {
    margin:.55rem 0 0; font-size:.9375rem; font-weight:500; color:var(--text);
}


/* --- the grid ------------------------------------------------------------
   auto-fill, so it reflows by available width rather than at fixed columns. */
.flux-launch { display:grid; gap:.75rem; }
/* Six across at desktop. Twelve applications divide into 6x2 with no orphan
   row. auto-fill keeps it reflowing down to one column rather than snapping at
   fixed breakpoints. */
.flux-launch { grid-template-columns:repeat(auto-fill, minmax(11.5rem, 1fr)); }
@media (min-width:64rem) {
    .flux-launch { grid-template-columns:repeat(6, 1fr); }
}

.flux-app {
    position:relative; display:flex; align-items:center;
    background:var(--bg-surface); border:1px solid var(--border);
    border-radius:.75rem; color:var(--text); text-decoration:none;
    transition:border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
/* Restraint on hover too: a 1px lift and a tinted shadow, not a jump. */
.flux-app:hover {
    transform:translateY(-1px); text-decoration:none; color:var(--text);
    border-color:var(--c-primary-200, #c7d2fe);
    box-shadow:0 6px 18px rgba(16,24,40,.07);
}
.flux-app:active { transform:translateY(0); }
.flux-app:focus-visible { outline:2px solid var(--c-primary); outline-offset:2px; }

.flux-app {
    flex-direction:column; justify-content:center; text-align:center;
    padding:1.75rem .75rem 1.4rem; gap:1rem;
}

/* Phosphor duotone on a soft tint. Size and colours are set inline per module
   by flux_app_icon(); only the shape and centring live here. */
.flux-appicon {
    display:inline-flex; align-items:center; justify-content:center;
    flex:0 0 auto; border-radius:.65rem; line-height:1;
}
.flux-appicon i { line-height:1; }

.flux-app-name {
    font-size:.875rem; font-weight:600; letter-spacing:-.008em;
    color:var(--text-strong); line-height:1.3;
}

/* Muted rather than alarm-red: on a screen you see twenty times a day, a
   scarlet dot on three tiles stops meaning anything by Tuesday. */
.flux-app-badge {
    position:absolute; top:.6rem; right:.6rem;
    min-width:1.25rem; height:1.25rem;
    display:inline-flex; align-items:center; justify-content:center; padding:0 .35rem;
    border-radius:1rem; background:#fef2f2; color:#b91c1c;
    border:1px solid #fecaca;
    font-size:.6875rem; font-weight:600; line-height:1;
    font-variant-numeric:tabular-nums;
}

.flux-launch-empty {
    text-align:center; padding:4rem 1rem; color:var(--text-muted);
    border:1px dashed var(--border); border-radius:.75rem;
}
.flux-launch-empty p:first-child { font-weight:600; color:var(--text-strong); margin-bottom:.35rem; }
.flux-launch-empty p:last-child { font-size:.85rem; margin:0; }

@media (prefers-reduced-motion: reduce) {
    .flux-app { transition:none; }
    .flux-app:hover { transform:none; }
}

html[data-theme="dark"] .flux-launch-page::before {
    background-image:radial-gradient(#39435a 2px, transparent 2px);
}
html[data-theme="dark"] .flux-launch-page::after {
    background:radial-gradient(70rem 22rem at 50% -6rem,
        rgba(99,102,241,.14) 0%, transparent 70%);
}
html[data-theme="dark"] .flux-launch-time { color:#a5b4fc; }

html[data-theme="dark"] .flux-app-badge {
    background:#450a0a; color:#fca5a5; border-color:#7f1d1d;
}
/* The tint behind each icon is a light value set inline, so in dark mode it is
   dropped to a flat surface and the glyph keeps its hue. */
html[data-theme="dark"] .flux-appicon {
    background:rgba(255,255,255,.06) !important;
    filter:brightness(1.45) saturate(1.1);
}

@media (max-width:768px) {
    .flux-launch-shell { padding:3rem 1.1rem 3rem; }
    .flux-launch-head h1 { font-size:1.75rem; }
}
@media (max-width:576px) {
    .flux-launch { grid-template-columns:repeat(auto-fill, minmax(8rem, 1fr)); }
}
/* ============================================================================
   LAUNCHER LAYOUT - contextual module bar
   ----------------------------------------------------------------------------
   No persistent menu. The launcher is the menu; once inside a module, that
   module's pages run flat across one bar. Flat rather than dropdowns, because
   at this point there is only one group to show and hiding six links behind a
   click to save a row of space is a bad trade.
   ========================================================================== */
.flux-nav-launcher .main-sidebar { display: none !important; }
.flux-nav-launcher .content-wrapper,
.flux-nav-launcher .main-header,
.flux-nav-launcher .main-footer { margin-left: 0 !important; }
.flux-nav-launcher [data-widget="pushmenu"] { display: none !important; }

.flux-ctxnav {
    position: sticky; top: var(--topbar-h, 57px); z-index: 1030;
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border, #e5e7eb);
    box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
}
.flux-ctxnav-inner {
    display: flex; align-items: center; gap: .25rem;
    padding: 0 1rem; min-height: 46px;
    overflow-x: auto; scrollbar-width: thin;
}
.flux-ctxnav-inner::-webkit-scrollbar { height: 4px; }
.flux-ctxnav-inner::-webkit-scrollbar-thumb {
    background: var(--border, #e5e7eb); border-radius: 2px;
}

.flux-ctxnav-home {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; flex: 0 0 auto; border-radius: .375rem;
    color: var(--text-muted, #6b7280); text-decoration: none;
    transition: background .15s, color .15s;
}
.flux-ctxnav-home:hover {
    background: var(--c-primary-50, #eff6ff); color: var(--c-primary, #2563eb);
}
.flux-ctxnav-home i { font-size: 1.15rem; }

/* The module name is a label, not a link - you are already here. */
.flux-ctxnav-app {
    display: inline-flex; align-items: center; gap: .4rem; flex: 0 0 auto;
    padding: 0 .85rem 0 .5rem; margin-right: .35rem;
    border-right: 1px solid var(--border, #e5e7eb);
    font-weight: 700; font-size: .875rem; color: var(--text, #111827);
    white-space: nowrap;
}
.flux-ctxnav-app i { font-size: 1.05rem; color: var(--c-primary, #2563eb); }

.flux-ctxnav-list {
    display: flex; align-items: center; gap: .1rem;
    list-style: none; margin: 0; padding: 0;
}
.flux-ctxnav-link {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .45rem .7rem; border-radius: .375rem;
    font-size: .8125rem; font-weight: 500; white-space: nowrap;
    color: var(--text-muted, #6b7280); text-decoration: none;
    transition: background .15s, color .15s;
}
.flux-ctxnav-link:hover {
    background: var(--c-primary-50, #eff6ff);
    color: var(--c-primary-700, #1d4ed8); text-decoration: none;
}
/* Weight plus a bar, not colour alone - colour alone fails for anyone who
   cannot separate these two hues. */
.flux-ctxnav-link.active {
    background: var(--c-primary-50, #eff6ff);
    color: var(--c-primary-700, #1d4ed8); font-weight: 700;
    box-shadow: inset 0 -2px 0 var(--c-primary, #2563eb);
}
.flux-ctxnav-link i.bx { font-size: 1rem; opacity: .8; }

/* On the launcher itself there is no bar at all, so the page starts higher. */
.flux-on-launcher .content-wrapper { padding-top: 0; }

@media (max-width: 768px) {
    .flux-ctxnav-inner { padding: 0 .6rem; }
    .flux-ctxnav-app span { display: none; }
    .flux-ctxnav-link span { display: none; }   /* icons only when space is tight */
    .flux-ctxnav-link { padding: .45rem .55rem; }
}

/* Dark mode here is driven by html[data-theme], the same switch the rest of the
   application uses. An earlier version of this block keyed off
   prefers-color-scheme, which meant the bar went dark from the OS setting while
   every other surface stayed light - and the module name became unreadable
   against it. */
html[data-theme="dark"] .flux-ctxnav {
    background: var(--bg-surface);
    border-bottom-color: var(--border, #2a313d);
}
html[data-theme="dark"] .flux-ctxnav-menu {
    background: var(--bg-surface);
    border-color: var(--border, #2a313d);
}

/* Overflow menu for groups too large to sit flat on one bar, and parent chips
   that open onto their own pages - Finance's Customers and Vendors. Both use
   the same control so the bar has one dropdown behaviour, not two. */
.flux-ctxnav-more,
.flux-ctxnav-sub { position: relative; }
.flux-ctxnav-more > .flux-ctxnav-link,
.flux-ctxnav-sub  > .flux-ctxnav-link {
    background: none; border: 0; cursor: pointer; font: inherit;
    font-size: .8125rem; font-weight: 500;
}
/* A parent's chevron leans on the label rather than sitting apart from it. */
.flux-ctxnav-sub > .flux-ctxnav-link > .bx-chevron-down {
    font-size: .9rem; opacity: .6; margin-left: -.15rem;
}
/* Its menu hangs from the left, under the chip it belongs to. The overflow
   menu is right-aligned because it sits at the end of the bar; a parent can be
   anywhere along it, so right-aligning would push the panel off-screen. */
.flux-ctxnav-sub .flux-ctxnav-menu { right: auto; left: 0; min-width: 210px; }

/* Hover-to-open is done in fluxNavPopover(), not here. A CSS `:hover` rule
   would show the menu without running the script's viewport measurement, so it
   would appear wherever the previous click left it - and be clipped by the
   bar's horizontal scroll container. */
.flux-ctxnav-menu {
    display: none; position: absolute; top: calc(100% + 4px); right: 0;
    min-width: 240px; max-height: 70vh; overflow-y: auto;
    /* See the note on .flux-topnav-menu: position is switched to fixed on open
       so the bar's horizontal scroll container cannot clip it. */
    z-index: 1200;
    padding: .35rem; border-radius: .5rem;
    background: var(--bg-surface); border: 1px solid var(--border, #e5e7eb);
    box-shadow: 0 10px 24px rgba(16, 24, 40, .12);
}
.flux-ctxnav-more.is-open .flux-ctxnav-menu,
.flux-ctxnav-sub.is-open  .flux-ctxnav-menu { display: block; }
.flux-ctxnav-heading {
    padding: .5rem .6rem .25rem; font-size: .6875rem; font-weight: 700;
    letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted, #6b7280);
}
.flux-ctxnav-menuitem {
    display: flex; align-items: center; gap: .5rem;
    padding: .45rem .6rem; border-radius: .375rem;
    font-size: .8125rem; color: var(--text, #111827); text-decoration: none;
}
.flux-ctxnav-menuitem:hover {
    background: var(--c-primary-50, #eff6ff);
    color: var(--c-primary-700, #1d4ed8); text-decoration: none;
}
.flux-ctxnav-menuitem.active {
    background: var(--c-primary-50, #eff6ff);
    color: var(--c-primary-700, #1d4ed8); font-weight: 700;
}
.flux-ctxnav-menuitem i.bx { font-size: 1rem; opacity: .75; width: 1.1rem; }

/* Brand in the top bar, for the layouts that have no sidebar to carry it. */
.flux-topbrand {
    display: inline-flex; align-items: center; gap: .5rem; flex: 0 0 auto;
    padding: .25rem .6rem .25rem .25rem; margin-right: .35rem;
    font-weight: 700; font-size: .95rem; letter-spacing: -.01em;
    color: var(--text, #111827); text-decoration: none; white-space: nowrap;
}
.flux-topbrand:hover { color: var(--c-primary, #2563eb); text-decoration: none; }
.flux-topbrand img { width: 26px; height: 26px; object-fit: contain; border-radius: .35rem; }
@media (max-width: 768px) { .flux-topbrand span { display: none; } }

/* ==========================================================================
   REPORTS LANDING
   --------------------------------------------------------------------------
   Headline figures, four charts, then the catalogue. The two halves are styled
   differently on purpose: the figures are a display surface and the catalogue
   is a list you scan, so they should not look like the same component.
   ========================================================================== */
.rk-grid {
    display:grid; gap:1rem; margin-bottom:1rem;
    grid-template-columns:repeat(auto-fit, minmax(15rem, 1fr));
}
.rk {
    position:relative; overflow:hidden;
    display:flex; flex-direction:column; gap:.3rem;
    padding:1.15rem 1.25rem; border-radius:.75rem;
    background:var(--bg-surface); border:1px solid var(--border);
    box-shadow:0 1px 2px rgba(16,24,40,.04);
}
/* A 3px edge carries the meaning. Tinting the whole card would put four
   saturated blocks side by side and make the numbers harder to read, which is
   the opposite of the point. */
.rk::before { content:""; position:absolute; inset:0 auto 0 0; width:3px; }
.rk--rev::before    { background:#10b981; }
.rk--exp::before    { background:#f43f5e; }
.rk--profit::before { background:#4f46e5; }
.rk--loss::before   { background:#dc2626; }
.rk--margin::before { background:#0ea5e9; }

.rk-label {
    font-size:.75rem; font-weight:600; letter-spacing:.06em;
    text-transform:uppercase; color:var(--text-muted);
}
.rk-value {
    font-size:1.75rem; font-weight:700; line-height:1.15;
    letter-spacing:-.025em; color:var(--text-strong);
    font-variant-numeric:tabular-nums;
}
.rk-up, .rk-down, .rk-flat {
    display:inline-flex; align-items:center; gap:.3rem;
    font-size:.78rem; font-weight:600;
}
.rk-up   { color:#047857; }
.rk-down { color:#b91c1c; }
.rk-flat { color:var(--text-muted); font-weight:500; }

/* Balances, not movements. Deliberately quieter than the cards above. */
.rk-strip {
    display:grid; gap:0; margin-bottom:1.25rem;
    grid-template-columns:repeat(auto-fit, minmax(11rem, 1fr));
    background:var(--bg-surface); border:1px solid var(--border);
    border-radius:.75rem; overflow:hidden;
}
.rk-strip > div {
    display:flex; flex-direction:column; gap:.15rem;
    padding:.85rem 1.1rem; border-right:1px solid var(--border);
}
.rk-strip > div:last-child { border-right:0; }
.rk-strip span { font-size:.75rem; color:var(--text-muted); font-weight:500; }
.rk-strip strong {
    font-size:1.05rem; font-weight:650; color:var(--text-strong);
    font-variant-numeric:tabular-nums;
}

.card-sub { display:block; font-size:.78rem; color:var(--text-muted); margin-top:.15rem; }

/* --- catalogue ------------------------------------------------------------ */
.rc-head {
    display:flex; align-items:flex-end; justify-content:space-between;
    gap:1.5rem; flex-wrap:wrap; margin:1.75rem 0 1rem;
}
.rc-head h2 {
    margin:0; font-size:1.25rem; font-weight:700;
    letter-spacing:-.02em; color:var(--text-strong);
}
.rc-head p { margin:.25rem 0 0; font-size:.85rem; color:var(--text-muted); max-width:46rem; }

.rc-search { position:relative; flex:0 0 auto; min-width:16rem; }
.rc-search i {
    position:absolute; left:.7rem; top:50%; transform:translateY(-50%);
    color:var(--text-muted); font-size:1.05rem; pointer-events:none;
}
.rc-search input {
    width:100%; padding:.5rem .75rem .5rem 2.2rem;
    border:1px solid var(--border); border-radius:.5rem;
    background:var(--bg-surface); color:var(--text); font-size:.875rem;
}
.rc-search input:focus {
    outline:none; border-color:var(--c-primary);
    box-shadow:0 0 0 3px var(--c-primary-100);
}

.rc-groups {
    display:grid; gap:1rem;
    grid-template-columns:repeat(auto-fill, minmax(21rem, 1fr));
    align-items:start;
}
.rc-group {
    background:var(--bg-surface); border:1px solid var(--border);
    border-radius:.75rem; overflow:hidden;
    box-shadow:0 1px 2px rgba(16,24,40,.04);
}
.rc-group[hidden] { display:none; }
.rc-group > header {
    display:flex; align-items:flex-start; gap:.75rem;
    padding:1rem 1.1rem; border-bottom:1px solid var(--border);
}
.rc-head-text { flex:1 1 auto; min-width:0; }
.rc-group h3 {
    margin:0; font-size:.95rem; font-weight:700;
    letter-spacing:-.01em; color:var(--text-strong);
}
.rc-group > header p { margin:.15rem 0 0; font-size:.78rem; color:var(--text-muted); }
.rc-count {
    flex:0 0 auto; min-width:1.5rem; height:1.5rem;
    display:inline-flex; align-items:center; justify-content:center;
    padding:0 .4rem; border-radius:1rem;
    background:var(--bg-subtle, #f1f5f9); color:var(--text-muted);
    font-size:.75rem; font-weight:700;
}

.rc-icon {
    flex:0 0 auto; width:2.25rem; height:2.25rem; border-radius:.55rem;
    display:inline-flex; align-items:center; justify-content:center; font-size:1.2rem;
}
/* Same hue family as the launcher icons, so a module reads the same colour
   wherever it appears. */
.rc-icon--finance       { background:#eff6ff; color:#1d4ed8; }
.rc-icon--expenses      { background:#fff1f2; color:#be123c; }
.rc-icon--sales         { background:#ecfdf5; color:#047857; }
.rc-icon--purchases     { background:#fffbeb; color:#b45309; }
.rc-icon--inventory     { background:#f0f9ff; color:#0369a1; }
.rc-icon--manufacturing { background:#fff7ed; color:#c2410c; }
.rc-icon--dashboards    { background:#eef2ff; color:#4338ca; }
.rc-icon--payroll       { background:#f0fdfa; color:#0f766e; }

.rc-group ul { list-style:none; margin:0; padding:.35rem; }
.rc-group li[hidden] { display:none; }
.rc-group li a {
    display:flex; align-items:center; gap:.75rem;
    padding:.55rem .7rem; border-radius:.5rem;
    color:var(--text); text-decoration:none;
    transition:background .13s, color .13s;
}
.rc-group li a:hover { background:var(--c-primary-50); text-decoration:none; }
.rc-text { flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:.1rem; }
.rc-name { font-size:.85rem; font-weight:600; color:var(--text-strong); }
.rc-desc { font-size:.75rem; color:var(--text-muted); line-height:1.35; }
.rc-group li a i {
    flex:0 0 auto; font-size:1.15rem; color:var(--text-muted); opacity:.5;
    transition:transform .13s, opacity .13s;
}
.rc-group li a:hover i { opacity:1; transform:translateX(2px); color:var(--c-primary); }

.rc-empty {
    margin:2rem 0; text-align:center; color:var(--text-muted); font-size:.9rem;
}
.rc-empty[hidden] { display:none; }

html[data-theme="dark"] .rc-count { background:#1e293b; }
html[data-theme="dark"] .rc-icon--finance       { background:#172554; color:#93c5fd; }
html[data-theme="dark"] .rc-icon--expenses      { background:#4c0519; color:#fda4af; }
html[data-theme="dark"] .rc-icon--sales         { background:#022c22; color:#6ee7b7; }
html[data-theme="dark"] .rc-icon--purchases     { background:#451a03; color:#fcd34d; }
html[data-theme="dark"] .rc-icon--inventory     { background:#082f49; color:#7dd3fc; }
html[data-theme="dark"] .rc-icon--manufacturing { background:#431407; color:#fdba74; }
html[data-theme="dark"] .rc-icon--dashboards    { background:#1e1b4b; color:#a5b4fc; }
html[data-theme="dark"] .rc-icon--payroll       { background:#042f2e; color:#5eead4; }

@media (max-width:576px) {
    .rk-value { font-size:1.5rem; }
    .rc-search { min-width:100%; }
}

/* ==========================================================================
   Explanatory note inside a settings panel
   --------------------------------------------------------------------------
   Bootstrap's .alert-light hardcodes a near-white background and a dark border,
   so in dark mode it renders as a white slab carrying --text-muted text on it:
   pale grey on white, effectively unreadable. Every note in the document-flow
   panel is exactly this shape, so it gets a token-driven surface instead and
   follows the theme like everything else.
   ========================================================================== */
.flux-note {
    background: var(--bg-sunken);
    border: 1px solid var(--border);
    border-radius: var(--r-control, 8px);
    padding: .75rem .875rem;
    color: var(--text-muted);
    font-size: .8125rem;
    line-height: 1.5;
}
.flux-note code {
    background: var(--bg-hover);
    border: 1px solid var(--border-soft);
    border-radius: 4px;
    padding: .05rem .3rem;
    color: var(--text);
}

/* ══════════════════════════════════════════════════════════════════════════
   SETTINGS CONTEXT BAR
   ══════════════════════════════════════════════════════════════════════════
   Rendered by the shell on every settings page (see inc/settings_context.php).

   Two rows, because the two questions are different: which section am I in,
   and which page within it. A single row of thirty links would be the wall
   this whole restructure removed.

   Deliberately flat - no card, no shadow. It sits between the top bar and the
   page, and a raised surface there reads as a second header competing with the
   real one.
   ────────────────────────────────────────────────────────────────────────── */
.set-ctx {
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
}

.set-ctx-row {
    display: flex;
    align-items: center;
    gap: .85rem;
    min-height: 46px;
    padding: .3rem 0;
    flex-wrap: wrap;
}

.set-ctx-home {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: .82rem;
    font-weight: 600;
    color: var(--text-strong);
    text-decoration: none;
    white-space: nowrap;
}
.set-ctx-home i { font-size: 1.05rem; color: var(--text-muted); }
.set-ctx-home:hover { color: var(--c-primary); text-decoration: none; }
.set-ctx-home:hover i { color: var(--c-primary); }
.set-ctx-home.is-current i { color: var(--c-primary); }

.set-ctx-div {
    width: 1px;
    align-self: stretch;
    margin: .35rem 0;
    background: var(--border);
}

.set-ctx-sections {
    display: flex;
    gap: .15rem;
    flex-wrap: wrap;
    min-width: 0;
}

.set-ctx-section {
    padding: .35rem .6rem;
    border-radius: var(--r-control);
    font-size: .8rem;
    font-weight: 550;
    color: var(--text-muted);
    text-decoration: none;
    white-space: nowrap;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.set-ctx-section:hover {
    background: var(--bg-hover);
    color: var(--text-strong);
    text-decoration: none;
}
.set-ctx-section.is-current {
    background: var(--bg-selected);
    color: var(--c-primary);
}

/* The row that was missing: siblings of the current page. Scrolls rather than
   wraps, so a long section does not push the page content down the screen. */
.set-ctx-items {
    display: flex;
    gap: .1rem;
    overflow-x: auto;
    scrollbar-width: thin;
    border-top: 1px solid var(--border-soft);
    padding: .25rem 0;
}
.set-ctx-items::-webkit-scrollbar { height: 3px; }
.set-ctx-items::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }

.set-ctx-item {
    display: inline-flex;
    align-items: center;
    gap: .38rem;
    padding: .4rem .65rem;
    font-size: .795rem;
    font-weight: 500;
    color: var(--text-muted);
    text-decoration: none;
    white-space: nowrap;
    border-bottom: 2px solid transparent;
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.set-ctx-item i { font-size: .95rem; opacity: .75; }
.set-ctx-item:hover { color: var(--text-strong); text-decoration: none; }
.set-ctx-item:hover i { opacity: 1; }
.set-ctx-item.is-current {
    color: var(--c-primary);
    font-weight: 600;
    border-bottom-color: var(--c-primary);
}
.set-ctx-item.is-current i { opacity: 1; }

/* Anchored headings on the hub must clear the sticky bar. */
.set-section { scroll-margin-top: 7.5rem; }

@media (max-width: 640px) {
    .set-ctx-sections { overflow-x: auto; flex-wrap: nowrap; }
    .set-ctx-home span { display: none; }
}

/* A generated code, shown but not editable.
   Read-only rather than disabled, so the value is still submitted - a disabled
   field sends nothing and the server would generate a second, different code.
   Styled as settled fact rather than as a broken input: the muted background
   says it is not yours to change, the monospace says it is an identifier. */
.flux-code-field {
    background: var(--bg-sunken) !important;
    font-family: ui-monospace, Consolas, monospace;
    letter-spacing: .02em;
    cursor: default;
}

/* ============================================================================
   Confirmation dialogue (flux-dialog.js)
   ---------------------------------------------------------------------------
   Replaces the browser's own confirm/alert/prompt. Built entirely from the
   design tokens, so it follows the light and dark themes without a single
   colour being restated below - the reason the old native dialogue could never
   be made to match.
   ========================================================================== */
.flux-dlg-backdrop {
    position: fixed;
    inset: 0;
    z-index: 20000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(15, 23, 42, .45);
    backdrop-filter: blur(2px);
    opacity: 0;
    transition: opacity .14s ease;
}
.flux-dlg-backdrop.is-in { opacity: 1; }

/* Scroll lock. The page behind must not move while a decision is pending. */
body.flux-dlg-open { overflow: hidden; }

.flux-dlg {
    width: 100%;
    max-width: 27rem;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--r-panel);
    box-shadow: var(--shadow-xl);
    transform: translateY(8px) scale(.98);
    transition: transform .16s cubic-bezier(.2, .8, .3, 1);
    overflow: hidden;
}
.flux-dlg-backdrop.is-in .flux-dlg { transform: none; }

/* Head -------------------------------------------------------------------- */
.flux-dlg-head {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: 1.15rem 1.35rem .35rem;
}
.flux-dlg-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--r-pill);
    font-size: 1.3rem;
}
.flux-dlg-title {
    margin: 0;
    font-size: 1.02rem;
    font-weight: 650;
    letter-spacing: -.01em;
    color: var(--text-strong);
}

/* Tone is carried by the icon only. Tinting the whole dialogue made an
   ordinary "post this bill?" look like an error. */
.flux-dlg-primary .flux-dlg-icon { background: var(--c-primary-50);   color: var(--c-primary); }
.flux-dlg-danger  .flux-dlg-icon { background: var(--c-danger-bg);    color: var(--c-danger); }
.flux-dlg-warning .flux-dlg-icon { background: var(--c-warning-bg);   color: var(--c-warning); }
.flux-dlg-success .flux-dlg-icon { background: var(--c-success-bg);   color: var(--c-success); }
.flux-dlg-info    .flux-dlg-icon { background: var(--c-info-bg);      color: var(--c-info); }

/* Body -------------------------------------------------------------------- */
.flux-dlg-body { padding: .35rem 1.35rem 1.1rem 4.35rem; }
.flux-dlg-msg {
    margin: 0;
    color: var(--text);
    font-size: .9rem;
    line-height: 1.55;
}
.flux-dlg-detail {
    margin: .5rem 0 0;
    color: var(--text-muted);
    font-size: .82rem;
    line-height: 1.5;
}
.flux-dlg-points {
    margin: .6rem 0 0;
    padding-left: 1.05rem;
    color: var(--text-muted);
    font-size: .82rem;
    line-height: 1.6;
}
.flux-dlg-points li { margin-bottom: .15rem; }
.flux-dlg-input { margin-top: .8rem; }

/* Foot -------------------------------------------------------------------- */
.flux-dlg-foot {
    display: flex;
    justify-content: flex-end;
    gap: .5rem;
    padding: .85rem 1.35rem;
    background: var(--bg-sunken);
    border-top: 1px solid var(--border-soft);
}
.flux-dlg-foot .btn { min-width: 6rem; }

@media (max-width: 30rem) {
    .flux-dlg-body { padding-left: 1.35rem; }
    .flux-dlg-foot { flex-direction: column-reverse; }
    .flux-dlg-foot .btn { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
    .flux-dlg-backdrop, .flux-dlg { transition: none; }
}

/* Key/value detail list, used on document side panels (receipts, bills).
   A <dl> rather than a two-column table: these are labelled facts about one
   record, not rows of like things, and a table made them wrap badly in the
   narrow side column. */
.flux-kv { margin: 0; }
.flux-kv dt {
    font-size: .72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-faint);
    margin-top: .7rem;
}
.flux-kv dt:first-child { margin-top: 0; }
.flux-kv dd {
    margin: .1rem 0 0;
    color: var(--text-strong);
    font-size: .9rem;
}

/* ============================================================================
   Fact grid - labelled read-only facts about one record, laid out to fill the
   width. Used where a document is confirmed and its details become a record
   rather than a form (goods receipt, bill). A <dl> in a narrow gutter wrapped
   badly; this reflows to as many columns as the width allows.
   ========================================================================== */
/* ── FACT GRID ───────────────────────────────────────────────────────────────
   The label/value pairs on every party and details panel. Deliberately plain:
   no cell backgrounds, no boxes inside boxes. What separates one fact from the
   next is the label's own weight and colour against the value beneath it -
   a tinted cell for every pair turned a details panel into a mosaic. */
.flux-factgrid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: .8rem 1.3rem;
}
.flux-factgrid > div { min-width: 0; }
.flux-fact-k {
    display: block;
    font-size: .68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .045em;
    color: var(--text-faint);
    margin-bottom: .1rem;
}
.flux-fact-v {
    display: block;
    font-size: .9rem;
    color: var(--text-strong);
    overflow-wrap: anywhere;
    font-variant-numeric: tabular-nums;
}

/* A quantity short of what was ordered. Amber rather than red: a short
   delivery is an open balance, not an error - the balance is carried forward
   automatically on confirm. */
.flux-short { color: var(--c-warning); font-weight: 600; }

/* Summary table for document totals (order value, bill totals). Right-aligned
   figures against left labels, with the grand total set apart - a <table>
   rather than flex rows so the numbers line up on their decimal regardless of
   label length. */
.flux-sumtable { width: 100%; font-size: .9rem; }
.flux-sumtable td { padding: .3rem 0; }
.flux-sumtable td:first-child { color: var(--text-muted); }
.flux-sumtable td:last-child { text-align: right; font-variant-numeric: tabular-nums; }
.flux-sumtable tr.flux-sum-grand td {
    padding-top: .6rem;
    border-top: 1px solid var(--border);
    font-size: 1.02rem;
    font-weight: 700;
    color: var(--text-strong);
}

/* ============================================================================
   Add-row control. Sits directly beneath the table it appends to, spanning the
   full width, so it reads as the next empty row rather than a button in the
   opposite corner of the panel from the rows it creates.
   ========================================================================== */
.flux-addrow {
    display: block;
    width: 100%;
    padding: .6rem 1rem;
    border: 0;
    border-top: 1px dashed var(--border-strong);
    background: transparent;
    color: var(--c-primary);
    font-size: .85rem;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.flux-addrow:hover { background: var(--bg-hover); color: var(--c-primary-hov); }
.flux-addrow:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
.flux-addrow i { vertical-align: -2px; margin-right: .25rem; }

/* ============================================================================
   Document timeline. What has happened to one record, oldest first. Built from
   the child documents rather than the audit log - see doc_history_helper.php.
   ========================================================================== */
/* ── Document timeline ───────────────────────────────────────────────────────
   The list reserves the gutter the dots sit in (`padding-left`) instead of
   letting them hang at `left: 0`. Previously the dot was pinned to the item's
   own left edge, which is the card's content edge - so a 1.6rem circle sat
   half outside the panel and clipped against its border. */
.flux-timeline { list-style: none; margin: 0; padding: 0 0 0 .15rem; position: relative; }
/* The connector must run through the CENTRE of the dot, or the chain reads as
   two things that nearly line up. The dot sits at left .2rem and is 1.5rem
   wide, so its centre is .95rem; the 1px line is offset half its own width from
   that. Change one and the other has to follow. */
.flux-timeline::before {
    content: "";
    position: absolute;
    left: calc(.95rem - .5px); top: .9rem; bottom: .9rem;
    width: 1px;
    background: var(--border);
}
.flux-tl-item {
    position: relative;
    padding: .35rem .5rem .35rem 2.5rem;
    border-radius: .4rem;
    transition: background .12s ease;
}
/* The row lifts under the pointer, so a long history can be tracked across
   without losing which line the eye is on. */
.flux-tl-item:hover { background: var(--bg-subtle, rgba(15, 23, 42, .03)); }

.flux-tl-dot {
    position: absolute; left: .2rem; top: .35rem;
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.5rem; height: 1.5rem;
    border-radius: var(--r-pill);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    font-size: .85rem;
    color: var(--text-muted);
    /* Sits above the connector rather than being crossed by it. */
    z-index: 1;
    box-shadow: 0 0 0 3px var(--bg-surface);
}

.flux-tl-head { display: flex; justify-content: space-between; gap: .75rem;
                align-items: flex-start; flex-wrap: wrap; }
.flux-tl-title { font-size: .9rem; font-weight: 650; color: var(--text-strong); }
.flux-tl-title a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--border-strong); }
.flux-tl-title a:hover { color: var(--c-primary); border-color: currentColor; }

/* The stamp is the point of a timeline, so it is legible rather than a faint
   afterthought: its own chip, tabular figures, and the time carried at full
   contrast beside a quieter date. */
/* No chip, no border - the stamp is legible because the time is set at full
   contrast against a quieter date, not because it has a box round it. */
.flux-tl-date {
    display: inline-flex; align-items: baseline; gap: .35rem;
    font-size: .74rem; color: var(--text-faint); white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.flux-tl-time { font-variant-numeric: tabular-nums; font-weight: 700;
                font-size: .75rem; color: var(--text-muted); margin-left: 0; }
.flux-tl-detail { margin: .2rem 0 0; font-size: .82rem; color: var(--text-muted); line-height: 1.5; }

/* ── WHO DID IT ──────────────────────────────────────────────────────────────
   Attribution sits under the event as a quiet line of its own. It is the third
   thing a reader wants after what and when, so it must be present on every
   event, but never loud enough to compete with the event itself. The initial
   disc gives the eye something to scan down a long timeline without having to
   read each name. */
.flux-tl-by {
    display: inline-flex; align-items: center; gap: .4rem;
    margin-top: .35rem;
}
.flux-tl-avatar {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.15rem; height: 1.15rem; flex: 0 0 auto;
    border-radius: 50%;
    background: var(--bg-subtle, rgba(127, 127, 127, .14));
    color: var(--text-muted);
    font-size: .58rem; font-weight: 700; letter-spacing: .02em;
    line-height: 1;
}
.flux-tl-byname { font-size: .75rem; color: var(--text-muted); font-weight: 600; }

/* ── ALERTS ON DOCUMENT SCREENS ──────────────────────────────────────────────
   These carry the explanations - what posting will do, what is still short,
   where the balance went. Bootstrap's default is a flat tinted slab with no
   hierarchy; a coloured spine and a lighter ground make them read as a note in
   the margin rather than a block of coloured text. */
.content .alert {
    border: 1px solid var(--border);
    border-left: 3px solid var(--text-faint);
    border-radius: .5rem;
    padding: .75rem .9rem;
    font-size: .875rem;
    line-height: 1.55;
}
.content .alert-info      { border-left-color: var(--c-info, #0ea5e9); }
.content .alert-success   { border-left-color: var(--c-success, #16a34a); }
.content .alert-warning   { border-left-color: var(--c-warning, #d97706); }
.content .alert-danger    { border-left-color: var(--c-danger, #dc2626); }
.content .alert-secondary { border-left-color: var(--text-faint); }
.content .alert > .bx:first-child { font-size: 1.05rem; vertical-align: -2px; margin-right: .15rem; }
.content .alert ul { padding-left: 1.1rem; }

/* ── Panels on a document screen ─────────────────────────────────────────────
   The document pages were a stack of identical square boxes: one border, black
   text, nothing to separate a heading from a body or to show which panel the
   pointer is over. These give the cards on those pages a little depth and a
   quiet hover, without turning the page into a toy.

   Scoped to `.content` so it lifts document and list screens, and leaves the
   dashboard tiles and the launcher - which have their own visual language -
   alone. */
/* A coloured hairline along the top edge is the whole decoration: it marks
   where one section begins without tinting anything inside it, and gives a
   stack of panels a spine to read down. */
.content .card {
    position: relative;
    border: 1px solid var(--border);
    border-radius: .5rem;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
    transition: box-shadow .14s ease, border-color .14s ease;
    overflow: hidden;
}
/* --c-primary is the theme's indigo (#4f46e5 light, #818cf8 dark), so the rule
   follows the theme instead of hardcoding a purple that would go wrong the
   moment anyone switches to dark. */
.content .card::before {
    content: "";
    position: absolute; inset: 0 0 auto 0; height: 2px;
    background: var(--c-primary);
    opacity: .8;
}
/* AdminLTE's `card-outline` already draws its own coloured top border. Two
   would sit on top of each other, so this one stands down. */
.content .card.card-outline::before { display: none; }
.content .card:hover {
    border-color: var(--border-strong, var(--border));
    box-shadow: 0 4px 14px rgba(16, 24, 40, .07);
}
.content .card:hover::before { opacity: 1; }
.content .card > .card-header {
    background: transparent;
    border-bottom: 1px solid var(--border);
    padding: .65rem 1rem;
}
.content .card > .card-header .card-title {
    font-size: .9rem; font-weight: 650; letter-spacing: -.005em;
    color: var(--text-strong);
}
.content .card > .card-footer {
    background: transparent;
    border-top: 1px solid var(--border);
}
/* Panels that carry a state colour keep it, rather than everything on the page
   wearing the same accent. */
.content .card.card-success::before { background: var(--c-success, #16a34a); }
.content .card.card-warning::before { background: var(--c-warning, #d97706); }
.content .card.card-danger::before  { background: var(--c-danger,  #dc2626); }
.content .card.card-info::before    { background: var(--c-info,    #0ea5e9); }
/* ── TABLES ON DOCUMENT AND LIST SCREENS ─────────────────────────────────────
   The line tables were unstyled: black text, a hairline under every row, and a
   header that looked like another row. These give the header its own weight and
   ground, put figures in tabular numerals so columns align, and mark the
   footer as a total rather than one more line. */
.content table.table > thead > tr > th {
    background: transparent;
    border-bottom: 1.5px solid var(--border);
    border-top: 0;
    font-size: .7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-muted);
    white-space: nowrap;
    padding: .5rem .7rem;
}
.content table.table > tbody > tr > td { padding: .5rem .7rem; vertical-align: middle; }
.content table.table > tbody > tr:last-child > td { border-bottom: 0; }
/* Figures line up whatever their width. */
.content table.table td.text-right,
.content table.table th.text-right { font-variant-numeric: tabular-nums; }
.content table.table > tfoot > tr > td,
.content table.table > tfoot > tr > th {
    background: transparent;
    border-top: 1.5px solid var(--border);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    padding: .5rem .7rem;
}

/* Rows that lead somewhere say so under the pointer. */
.content table.table tbody tr[data-flux-row-href] { cursor: pointer; }
.content table.table tbody tr[data-flux-row-href]:hover { background: var(--c-primary-50, rgba(59,130,246,.06)); }
/* A left edge appears on hover, so the row being pointed at is unmistakable in
   a dense table without moving anything. */
.content table.table tbody tr[data-flux-row-href] > td:first-child {
    box-shadow: inset 3px 0 0 transparent; transition: box-shadow .12s ease;
}
.content table.table tbody tr[data-flux-row-href]:hover > td:first-child {
    box-shadow: inset 3px 0 0 var(--c-primary, #2563eb);
}

/* The fact-grid cells carry their own ground and hover - see .flux-factgrid.
   A dashed underline here as well gave each cell two separators. */

.flux-tl-success .flux-tl-dot { background: var(--c-success-bg); border-color: var(--c-success-bd); color: var(--c-success); }
.flux-tl-warning .flux-tl-dot { background: var(--c-warning-bg); border-color: var(--c-warning-bd); color: var(--c-warning); }
.flux-tl-danger  .flux-tl-dot { background: var(--c-danger-bg);  border-color: var(--c-danger-bd);  color: var(--c-danger); }
.flux-tl-primary .flux-tl-dot { background: var(--c-primary-50); border-color: var(--c-primary-200); color: var(--c-primary); }

/* Equal-height pair: two panels side by side that must line up at the bottom
   however much text each carries. */
.flux-pair { display: flex; }
.flux-pair > .card { width: 100%; margin-bottom: 1rem; }

/* ============================================================================
   Document cross-reference link. Used wherever one record names another - an
   order number on a receipt, a product on a line, a supplier on a bill.

   Underlined on hover only: a dense table where every document number is
   permanently underlined reads as noise, but a reference that gives no hint it
   is clickable is the dead end this replaced.
   ========================================================================== */
.flux-doclink { color: var(--c-primary); text-decoration: none; }
.flux-doclink:hover, .flux-doclink:focus-visible {
    color: var(--c-primary-hov);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.flux-doclink.is-plain { color: inherit; cursor: default; }
.flux-doclink-code {
    display: block;
    font-family: var(--font-mono);
    font-size: .72rem;
    color: var(--text-faint);
    margin-top: .05rem;
}
/* Inside a table cell the link should not fight the row hover. */
.table td .flux-doclink { font-weight: inherit; }

/* A mapping with no account behind it: every transaction of that kind is
   refused until it is set, so the row says so rather than looking ordinary. */
.flux-map-unset { background: var(--c-warning-bg); }
.flux-map-unset code { color: var(--c-warning); }

/* ============================================================================
   Clickable list rows (flux-rowlink.js). The affordance has to be obvious
   without being loud - a row that looks like a button in a table of fifty is
   worse than one that looks like a row.
   ========================================================================== */
tr[data-flux-row-href] { cursor: pointer; }
tr[data-flux-row-href]:hover > td { background: var(--bg-hover); }
tr[data-flux-row-href]:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: -2px;
}
/* Controls inside the row keep the default cursor, so it stays obvious that
   they do their own thing rather than opening the record. */
tr[data-flux-row-href] a,
tr[data-flux-row-href] button,
tr[data-flux-row-href] input,
tr[data-flux-row-href] select,
tr[data-flux-row-href] label { cursor: auto; }
tr[data-flux-row-href] a { cursor: pointer; }

/* ── Sortable column headers ────────────────────────────────────────────────
 * The whole header is the target, not just the label: a 60px column with a
 * five-character name gives a tiny click area, and people aim at the cell.
 *
 * The arrow on the ACTIVE column is full strength; every other sortable column
 * carries a faint one so it reads as "this can be sorted" without turning the
 * header row into a row of chevrons competing with the labels.
 *
 * Colours come from the theme tokens - the old DataTables sort arrows were
 * hard-coded light-theme values and vanished on the dark theme.
 */
th.flux-sort-th { padding: 0 !important; white-space: nowrap; }

th.flux-sort-th > .flux-sort-link {
    display: flex;
    align-items: center;
    gap: .25rem;
    padding: .5rem .6rem;
    color: inherit;
    text-decoration: none;
    font: inherit;
}
th.flux-sort-th.text-right > .flux-sort-link  { justify-content: flex-end; }
th.flux-sort-th.text-center > .flux-sort-link { justify-content: center; }

th.flux-sort-th > .flux-sort-link:hover {
    color: var(--c-primary);
    text-decoration: none;
    background: var(--surface-2, rgba(127,127,127,.08));
}
th.flux-sort-th > .flux-sort-link:focus-visible {
    outline: 2px solid var(--c-primary);
    outline-offset: -2px;
}

th.flux-sort-th .bx { font-size: 1rem; line-height: 1; flex: none; }
th.flux-sort-th .flux-sort-idle { opacity: .28; }
th.flux-sort-th.is-sorted > .flux-sort-link { color: var(--c-primary); font-weight: 600; }

/* ── Number fields in data-entry tables ─────────────────────────────────────
 * MEASURED, not guessed: on /sales/add the quantity input renders 67.6px wide
 * with `text-align:right` and 8px of padding, and Chrome's native spinner takes
 * a further ~17px off the right-hand side - INSIDE the box. So the usable text
 * width was about 34px, four digits, and anything longer disappeared under the
 * arrows. Entering 1,250.00 showed "50.00" with a spinner on top of it.
 *
 * The spinner is not worth that. These are decimal fields with step="0.01", so
 * one click moves the value by a hundredth - nobody prices an order that way,
 * and every real edit is typed.
 *
 * Scoped to inputs inside tables and to .flux-nospin, so a standalone numeric
 * field elsewhere in the app keeps its stepper if it wants one.
 */
table input[type="number"],
input[type="number"].flux-nospin {
    -moz-appearance: textfield;
    appearance: textfield;
}
table input[type="number"]::-webkit-outer-spin-button,
table input[type="number"]::-webkit-inner-spin-button,
input[type="number"].flux-nospin::-webkit-outer-spin-button,
input[type="number"].flux-nospin::-webkit-inner-spin-button {
    -webkit-appearance: none;
    appearance: none;
    margin: 0;
}

/* Floor under the columns that hold a figure, so a line total or a quantity
   with thousands separators is readable rather than merely present. */
table input[type="number"] { min-width: 5.5rem; }

/* ── Sidebar: the menu meets the brand bar ──────────────────────────────────
 * MEASURED: the brand bar is 58px tall (--topbar-h) and ends at y=58, but
 * AdminLTE's `.layout-navbar-fixed` fixes the brand and reserves only
 * `3.5rem + 1px` = 57px for it, on top of which this sidebar added 8px of its
 * own padding and the search box another 4px of margin. The first thing under
 * the brand therefore started at y=69 - an 11px band of dead space that made
 * the menu look detached from the header above it.
 *
 * Tying the offset to the same token that sets the brand's height means the two
 * cannot drift apart again: change --topbar-h and both move together.
 */
@media (min-width: 992px) {
    body.layout-navbar-fixed .wrapper .flux-sidebar .sidebar {
        margin-top: var(--topbar-h);
        padding-top: .25rem;
    }
}
