/* Gatecode (Ragamkata Studio) - demo/personal-use license only. Corporate use requires
   purchasing a commercial license: https://ragamkatastudio.com/gatecode-font/ */
@font-face {
    font-family: "Gatecode";
    src: url("/fonts/Gatecode-Bold.ttf") format("truetype");
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

:root {
    --navy: #14243D;
    --navy-light: #1F3A5F;
    --navy-lighter: #2E5286;
    --amber: #F5A524;
    --amber-light: #FCD34D;
    --amber-dark: #B45309;
    --amber-bg: #FFF7E8;
    --accent: #2563EB;      /* cool accent - info states, links on dark bg */
    --accent-bg: #EFF6FF;
    --bg: #F4F5F7;
    --surface: #FFFFFF;
    --border: #E4E7EC;
    --border-strong: #CBD2DC;
    --text: #1A1F2B;
    --text-muted: #667085;
    --danger: #DC2626;
    --danger-bg: #FEF2F2;
    --success: #059669;
    --success-bg: #ECFDF5;
    --neutral: #4B5563;
    --neutral-bg: #F3F4F6;

    --radius-sm: 6px;
    --radius: 10px;
    --radius-lg: 16px;
    --radius-pill: 999px;

    --shadow-xs: 0 1px 2px rgba(16, 24, 40, 0.05);
    --shadow-sm: 0 1px 3px rgba(16, 24, 40, 0.07);
    --shadow-md: 0 6px 16px rgba(16, 24, 40, 0.10);
    --shadow-lg: 0 12px 32px rgba(16, 24, 40, 0.16);

    /* One typeface across the whole app - --font-mono used to be JetBrains Mono for
       Job Ref Nos/amounts/batch references (.font-mono, <code>), but mixing a monospace
       face with the UI sans felt like two different apps. Both variables now point to the
       same font; --font-mono stays defined (rather than deleting it and every .font-mono
       usage) purely so existing markup doesn't need to change. */
    --font-sans: 'Poppins', -apple-system, 'Segoe UI', system-ui, sans-serif;
    --font-mono: 'Poppins', -apple-system, 'Segoe UI', system-ui, sans-serif;

    /* The navbar top row's layout, as three kept-in-sync pieces instead of separate magic numbers:
       .navbar's own vertical padding, a single shared content-row height every nav-link is forced
       to (see ".navbar .nav-link" below - without this, the Sea Freight item's hand-drawn boat SVG
       rendered a shorter content row than every other item's icon-font glyph, ~33.4px vs ~35.6px,
       which showed up as a sliver of the dark bar peeking out under just that one active tab), and
       the total, which is what "the bar" as a fixed strip actually measures while the mobile
       collapse menu is CLOSED. That total is NOT the same thing as .navbar's own *current* height,
       which grows once the mobile collapse opens (the expanded menu renders below this same top
       row) - anything meant to size/center itself against "the bar" (the brand tab/pill) must use
       --navbar-toprow-height instead of .navbar's own height/100%/50%, confirmed live that doing
       the latter stretches or drifts down into the opened mobile menu instead of staying put. */
    --navbar-toprow-padding: 0.45rem;
    --navbar-toprow-content-height: 2.7rem;
    --navbar-toprow-height: calc(var(--navbar-toprow-padding) * 2 + var(--navbar-toprow-content-height));
}

* { 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(--radius-pill); border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }

::selection { background: var(--amber-light); color: var(--navy); }

body {
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-sans);
    font-size: 0.925rem;
}

h1, h2, h3, h4, h5, h6 {
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text);
}
h2 { font-size: 1.4rem; }
h5, h6 { font-weight: 600; }

a { color: var(--accent); }
a:hover { color: var(--navy); }

code, .font-mono { font-family: var(--font-mono); }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ============================================================
   Navbar
   ============================================================ */
/* The wrapper (not the <nav> itself) is what's sticky - .navbar-brand-tab below needs to be
   positioned against something that includes both the dark bar AND the tab overlapping it, so
   the whole assembly scrolls/sticks as one unit. position:sticky also works fine as a containing
   block for the tab's position:absolute, same as position:relative would. */
.ledgrforge-navbar-wrap {
    position: sticky;
    top: 0;
    z-index: 1030;
}
.navbar.ledgrforge-navbar {
    position: relative;
    background: var(--navy);
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06);
}
.navbar .nav-link {
    color: rgba(255, 255, 255, 0.82);
    font-weight: 500;
}
.navbar .nav-link:hover { color: #fff; }
/* Reserves the brand's own width PLUS a gutter, so the real nav content (toggler, links) starts
   clear of the brand instead of butting right up against it - the brand itself is NOT part of this
   flex row (see .navbar-brand-tab/.navbar-brand-pill below), so without this spacer the first nav
   item would slide left into the brand's footprint the moment the brand is taken out of flow.
   Confirmed live the two rounded shapes touched edge-to-edge with zero gap at the brand's own
   130px width - the extra 20px here is purely breathing room between them. */
.navbar-brand-spacer {
    width: 150px;
    flex-shrink: 0;
}
/* Folder/tab treatment: a cell in the top-left, the same color as the page background, sitting
   ON TOP of the dark bar (higher z-index) rather than cut into it. Sized against the fixed
   --navbar-toprow-* values (see :root), NOT against .navbar's own height/100% - confirmed live
   that opening the mobile collapse menu grows .navbar's total height (the menu renders below
   this same row), and a height:100% tab would stretch down across the whole opened menu instead
   of staying put over just the top row. */
.navbar-brand-tab {
    position: absolute;
    top: 0;
    left: 12px;
    width: 130px;
    height: var(--navbar-toprow-height);
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg);
    border-radius: 12px 12px 0 0;
}
.navbar-brand-tab::before,
.navbar-brand-tab::after {
    content: "";
    position: absolute;
    bottom: 0;
    width: 18px;
    height: 18px;
}
.navbar-brand-tab::before {
    left: -18px;
    background: radial-gradient(circle at top right, transparent 18px, var(--navy) 18px);
}
.navbar-brand-tab::after {
    right: -18px;
    background: radial-gradient(circle at top left, transparent 18px, var(--navy) 18px);
}
/* When a module is active (see ".navbar .navbar-nav .nav-link.active" below), that module's own
   nav item takes over the "folder tab" treatment - the brand steps back into a plain pill instead
   of competing for the same visual identity. Still filled with the page background so the
   dark-navy wordmark stays readable against the dark bar, just without the flare/connects-to-the-
   page-below styling reserved for whichever module tab is actually open.
   Deliberately SHORTER than --navbar-toprow-height (unlike .navbar-brand-tab, which matches it
   exactly) - a pill/oblong shape reads as a badge floating within the bar, not as a second thing
   reaching the bar's edges; confirmed live that matching the tab's full height made the pill's own
   rounded ends bleed past the bar's bottom edge into the page below it at some zoom levels. Still
   comfortably clears both edges of the (now taller) bar at this height - re-check that margin
   before growing this further. */
.navbar-brand-pill {
    position: absolute;
    top: calc(var(--navbar-toprow-height) / 2);
    left: 12px;
    transform: translateY(-50%);
    width: 130px;
    height: 3rem;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg);
    border-radius: 999px;
}
/* One shared size for both states - the brand reads as the same size whether the page is showing
   the tab (no module active) or the pill (a module active), not a bigger one and a smaller one. */
.navbar-brand-tab .brand-logo-img,
.navbar-brand-pill .brand-logo-img {
    height: 2.7rem;
}

/* Active top-level module link (Sea Freight/Receivables/Payables/Financial Statements/DDP) - a
   light, page-background "tab" surface instead of the plain amber highlight, so whichever module
   the current page belongs to visually pops out of the dark bar in the same color as the page
   beneath it. Scoped to ".navbar-nav" (the left-hand module group) - the account/profile dropdown
   is a SIBLING div outside ".navbar-nav" (see _Layout.cshtml), so it's untouched by this and
   keeps the plain amber ".navbar .nav-link.active" style further down; a personal/system menu
   isn't a "module" this treatment is meant to represent. Extra ".navbar" prefix on the selector
   only exists to out-specificity that older amber rule (equal class-count otherwise ties on
   source order) - both rules can't apply to the same element, this one must win here.
   The surface is a ::before layer, not the link's own background, so on desktop it can overflow
   the link's own now-fixed-height box (see ".navbar .nav-link" above) by exactly
   --navbar-toprow-padding on both sides and reach the bar's full height without growing
   .navbar-nav's own height - and therefore the whole bar - to match; only the tab visually pops,
   nothing else moves. `z-index:0` on the link (not the default `auto`) pins the pseudo-element to
   a stacking context local to the link, so its `z-index:-1` reliably stays behind the link's own
   icon/text instead of depending on the navbar's outer stacking.
   Below the ".navbar-expand-lg" breakpoint the collapsed menu stacks items vertically instead of
   sitting in one fixed-height bar, so the overflow trick is skipped there in favor of a plain
   filled rounded rect that stays within its own box. Bold in both cases - the active item should
   read as visibly heavier than its siblings regardless of which visual treatment applies. */
.navbar .navbar-nav .nav-link.active {
    position: relative;
    z-index: 0;
    background: var(--bg);
    color: var(--navy) !important;
    font-weight: 700;
    box-shadow: none;
    border-radius: 8px;
}
.navbar .navbar-nav .nav-link.active:hover {
    background: var(--bg);
    color: var(--navy) !important;
}
@media (min-width: 992px) {
    .navbar .navbar-nav .nav-link.active {
        background: transparent;
        border-radius: 14px 14px 0 0;
    }
    .navbar .navbar-nav .nav-link.active::before {
        content: "";
        position: absolute;
        top: calc(var(--navbar-toprow-padding) * -1);
        bottom: calc(var(--navbar-toprow-padding) * -1);
        left: 0;
        right: 0;
        z-index: -1;
        background: var(--bg);
        border-radius: 14px 14px 0 0;
    }
    /* Curved "flare" where the active tab's straight vertical edges meet the bar, softening what
       would otherwise be a sharp right-angle notch corner - same radial-gradient technique as
       .navbar-brand-tab's own flare above, just hosted on the WRAPPING ".nav-item.nav-item-tab"
       (added alongside "active" in _Layout.cshtml for all five module links, DDP included - it's
       now wrapped in its own plain .nav-item to stay structurally consistent with the four
       dropdowns) instead of on the link itself: Bootstrap's own ".dropdown-toggle::after" caret
       already owns that anchor's ::after, so a second ::after there would silently replace the
       caret instead of coexisting with it. Bottom offset matches the surface's own overflow above
       (both keyed off --navbar-toprow-padding) so the flare lands exactly where the surface
       actually reaches, not at .nav-item's own (shorter, content-driven) box edge.
       `z-index: 1` is required, not cosmetic - confirmed live that DDP (the last item in
       .navbar-nav, with zero gap before the account/profile dropdown that immediately follows as
       a sibling OUTSIDE .navbar-nav) had its right-side flare completely hidden: two z-index:auto
       siblings paint in DOM order, so the later account item painted over the flare's 10px bleed
       zone, leaving what looked like a plain sharp edge. A positive z-index unconditionally beats
       a z-index:auto/0 sibling regardless of DOM order, so this guarantees the flare (and the
       surface/text riding along with this same element) stays on top of ANY neighboring nav item,
       not just the ones that happen to leave enough gap. */
    .navbar-nav > .nav-item.nav-item-tab {
        position: relative;
        z-index: 1;
    }
    .navbar-nav > .nav-item.nav-item-tab::before,
    .navbar-nav > .nav-item.nav-item-tab::after {
        content: "";
        position: absolute;
        bottom: calc(var(--navbar-toprow-padding) * -1);
        width: 20px;
        height: 20px;
    }
    .navbar-nav > .nav-item.nav-item-tab::before {
        left: -20px;
        background: radial-gradient(circle at top right, transparent 20px, var(--navy) 20px);
    }
    .navbar-nav > .nav-item.nav-item-tab::after {
        right: -20px;
        background: radial-gradient(circle at top left, transparent 20px, var(--navy) 20px);
    }
}

/* The full LedgrForge artwork (wwwroot/img/ledgrforge-logo.png) has dark navy wordmark text,
   which needs a light background to read - used as-is on Home/Login where the page itself is
   light, and in the navbar it sits on its own light tab (above) rather than being recolored, so
   the artwork itself is never altered. */
.brand-logo-img {
    height: 2.75rem;
    width: auto;
}
.nav-svg-icon {
    width: 0.95rem;
    height: 0.95rem;
    vertical-align: -0.15em;
}
/* Same glyph reused at the larger, differently-colored home-tile scale (see Home/Index.cshtml) -
   overrides the small nav-bar sizing above rather than a second, near-duplicate SVG class. */
.nav-tile-icon .nav-svg-icon {
    width: 1.3rem;
    height: 1.3rem;
    vertical-align: middle;
}

/* ============================================================
   Buttons - every Bootstrap contextual variant re-themed so the
   whole app reads as one palette instead of stock Bootstrap hues.
   ============================================================ */
.btn {
    border-radius: var(--radius-sm);
    font-weight: 600;
    font-size: 0.875rem;
    letter-spacing: 0.01em;
}
.btn .bi { margin-right: 0.35rem; vertical-align: -1px; }

.btn-primary {
    background-color: var(--navy);
    border-color: var(--navy);
}
.btn-primary:hover, .btn-primary:focus {
    background-color: var(--navy-light);
    border-color: var(--navy-light);
}
.btn-outline-primary {
    color: var(--navy);
    border-color: var(--border-strong);
}
.btn-outline-primary:hover {
    background-color: var(--navy);
    border-color: var(--navy);
}
.btn-success {
    background-color: var(--success);
    border-color: var(--success);
}
.btn-success:hover, .btn-success:focus {
    background-color: #047857;
    border-color: #047857;
}
.btn-outline-success {
    color: var(--success);
    border-color: var(--border-strong);
}
.btn-outline-success:hover {
    background-color: var(--success);
    border-color: var(--success);
}
.btn-danger {
    background-color: var(--danger);
    border-color: var(--danger);
}
.btn-danger:hover, .btn-danger:focus {
    background-color: #B91C1C;
    border-color: #B91C1C;
}
.btn-outline-danger {
    color: var(--danger);
    border-color: var(--border-strong);
}
.btn-outline-danger:hover {
    background-color: var(--danger);
    border-color: var(--danger);
}
.btn-warning {
    background-color: var(--amber);
    border-color: var(--amber);
    color: var(--navy);
}
.btn-warning:hover, .btn-warning:focus {
    background-color: var(--amber-dark);
    border-color: var(--amber-dark);
    color: #fff;
}
.btn-secondary, .btn-outline-secondary {
    color: var(--text-muted);
    border-color: var(--border-strong);
}
.btn-secondary { background: var(--neutral-bg); border-color: var(--border-strong); color: var(--text); }
.btn-secondary:hover { background: var(--border-strong); color: var(--text); }
.btn-outline-secondary:hover {
    background-color: var(--navy);
    border-color: var(--navy);
    color: #fff;
}
.btn-outline-secondary:disabled, .btn-outline-secondary.disabled {
    color: var(--border-strong);
}

/* ============================================================
   Badges & alerts - re-mapped from stock Bootstrap hues onto the
   brand palette so status colors feel like one system.
   ============================================================ */
.badge { font-weight: 600; letter-spacing: 0.01em; border-radius: var(--radius-sm); }
.badge.bg-secondary { background-color: var(--neutral-bg) !important; color: var(--text) !important; }
.badge.bg-info { background-color: var(--accent-bg) !important; color: var(--accent) !important; }
.badge.bg-warning { background-color: var(--amber-bg) !important; color: var(--amber-dark) !important; }
.badge.bg-danger { background-color: var(--danger-bg) !important; color: var(--danger) !important; }
.badge.bg-success { background-color: var(--success-bg) !important; color: var(--success) !important; }
.badge.bg-dark { background-color: var(--navy) !important; color: #fff !important; }

.alert {
    border: 1px solid transparent;
    border-radius: var(--radius);
    font-size: 0.875rem;
}
.alert-danger { background: var(--danger-bg); border-color: #FCA5A5; color: #991B1B; }
.alert-warning { background: var(--amber-bg); border-color: var(--amber-light); color: var(--amber-dark); }
.alert-success { background: var(--success-bg); border-color: #A7F3D0; color: #065F46; }
.alert-secondary { background: var(--neutral-bg); border-color: var(--border-strong); color: var(--text-muted); }

/* ============================================================
   Cards
   ============================================================ */
.card {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-xs);
}
.card-title { font-weight: 700; }
a.card, .card.card-hover {
    transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
a.card:hover, .card.card-hover:hover {
    box-shadow: var(--shadow-md);
    border-color: var(--border-strong);
    transform: translateY(-1px);
}
/* A `transform` other than `none` always creates a new stacking context - confirmed live that an
   open Bootstrap dropdown menu inside a hovered .card-hover (the Home page KPI tiles' per-module
   action dropdowns) got visually trapped BEHIND a later sibling card, showing that card's own
   content bleeding through the menu, because the whole hovered card (menu included) was capped at
   one stacking "slot" that a later-in-DOM sibling card can paint over. The hover-lift only matters
   while nothing inside is open, so it's suspended for exactly that state via :has() instead of
   removing the lift outright. */
.card.card-hover:has(.dropdown-menu.show):hover {
    transform: none;
}
.border-dashed {
    border: 1.5px dashed var(--border-strong) !important;
    background: transparent;
    box-shadow: none;
}

/* ============================================================
   Home page navigation tiles: an icon-anchored card (icon is a
   contained square, sitting inside the card's own padding like a
   logo would - not a full-bleed edge-to-edge photo strip), with
   its action buttons hidden until hover/focus so the resting state
   reads as just a title + description - the buttons are a revealed
   affordance, not a rank of always-visible chrome.
   Stacked vertically (icon+title as a compact header row, then
   description, then actions) rather than icon-left/content-right -
   the actions row spans the tile's FULL width this way instead of
   only "width minus the icon column", which matters once a tile
   holds several per-module dropdown buttons (Payables: Vendor
   Lookup + 3 dropdowns) that would otherwise wrap onto many more
   lines than a lighter tile (Profit & Loss: one button) ever needs.
   `min-height` is shared across every tile AND .border-dashed (the
   "Add Report Module" placeholder) so all of them read as the same
   size regardless of which of the two independent Bootstrap .row
   groups they happen to sit in - h-100 alone only equalizes tiles
   WITHIN one row, not across both. */
.nav-tile,
.border-dashed {
    /* Confirmed live at 180px every tile's natural content (icon+title header, description,
       actions) fits without the floor stretching things out further - re-check this number if a
       tile's description/action count grows enough to need more room than the others. */
    min-height: 180px;
}
.nav-tile {
    display: flex;
    flex-direction: column;
    padding: 1.1rem;
}
.nav-tile-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.6rem;
}
.nav-tile-icon {
    flex: 0 0 2.75rem;
    width: 2.75rem;
    height: 2.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(155deg, var(--navy-light), var(--navy) 60%, #0B1626);
    color: var(--amber-light);
    font-size: 1.15rem;
    border-radius: var(--radius-sm);
}
.nav-tile-title {
    font-weight: 700;
    margin-bottom: 0;
}
.nav-tile-text {
    color: var(--text-muted);
    font-size: 0.83rem;
    margin-bottom: 0.65rem;
}
.nav-tile-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    /* Sits right under the description now, not pinned to the tile's bottom - confirmed live that
       margin-top:auto left a large dead gap above the buttons on every tile lighter than the
       heaviest one (whatever sets the shared min-height below), since the buttons always landed at
       the very bottom regardless of how short the description above them was. */
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 0.18s ease, transform 0.18s ease;
    pointer-events: none;
}
.nav-tile:hover .nav-tile-actions,
.nav-tile:focus-within .nav-tile-actions {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}
/* Same stacking-context trap as .card-hover above, and for the same reason (an open dropdown menu
   here is a descendant of this element too) - `transform: translateY(0)` is still a transform
   other than `none`, so it still creates a stacking context even at zero offset. Suspended only
   while a dropdown inside is actually open; the slide-up entrance is otherwise untouched. Matches
   (and must out-specificity) both states above, since the menu can only ever be open while one of
   them is active. */
.nav-tile:hover .nav-tile-actions:has(.dropdown-menu.show),
.nav-tile:focus-within .nav-tile-actions:has(.dropdown-menu.show) {
    transform: none;
}

/* ============================================================
   Nav tabs - themed to match brand instead of stock Bootstrap
   ============================================================ */
.nav-tabs {
    border-bottom: 1px solid var(--border);
    gap: 0.25rem;
}
.nav-tabs .nav-link {
    border: none;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    color: var(--text-muted);
    font-weight: 600;
    font-size: 0.875rem;
    padding: 0.55rem 1rem;
}
.nav-tabs .nav-link:hover { color: var(--navy); background: var(--neutral-bg); }
.nav-tabs .nav-link.active {
    color: var(--navy);
    background: transparent;
    border-bottom: 2px solid var(--amber);
}

/* ============================================================
   Page header - icon badge + title + subtitle, used at the top
   of every view so headings read as one consistent pattern.
   ============================================================ */
.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.25rem;
}
.page-header-main { display: flex; align-items: center; gap: 0.85rem; }
.page-header-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    flex: none;
    border-radius: var(--radius);
    background: var(--navy);
    color: var(--amber-light);
    font-size: 1.2rem;
}
.page-header h1, .page-header h2 { margin-bottom: 0.1rem; }
.page-header-subtitle {
    color: var(--text-muted);
    font-size: 0.85rem;
    margin: 0;
    max-width: 60ch;
}
.page-header-actions { display: flex; gap: 0.5rem; flex: none; }

/* ============================================================
   Auth / standalone status pages (Login, Error, Access Denied)
   ============================================================ */
/* Login's own full page (see Views/Shared/_AuthLayout.cshtml) - no navbar to share the viewport
   with, so this centers over the whole 100vh rather than .auth-shell's own
   "navbar's-worth-shorter" calc below (still used as-is by ChangePassword, which stays on the
   normal _Layout with its navbar). The brand shows large and once, directly on the page, instead
   of squeezed into a navbar tab/pill - there's no "module" for a signed-out visitor for that
   treatment to represent anyway. */
.auth-page {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem;
    background: var(--bg);
}
.auth-page-brand {
    margin-bottom: 1.75rem;
}
.auth-page-logo {
    height: 4rem;
    width: auto;
}
.auth-shell {
    min-height: calc(100vh - 6rem);
    display: flex;
    align-items: center;
    justify-content: center;
}
.auth-card {
    width: 100%;
    max-width: 380px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 2rem 2rem 1.5rem;
}
.auth-card-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    border-radius: var(--radius);
    background: var(--navy);
    color: var(--amber-light);
    font-size: 1.35rem;
    margin: 0 auto 1rem;
}
.auth-card h3 { text-align: center; font-size: 1.15rem; margin-bottom: 1.25rem; }

.status-shell {
    min-height: calc(100vh - 6rem);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
}
.status-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 4rem;
    height: 4rem;
    border-radius: 50%;
    font-size: 1.8rem;
    margin: 0 auto 1rem;
}
.status-icon.status-icon-danger { background: var(--danger-bg); color: var(--danger); }

/* ============================================================
   Searchable combobox (Unbilled Ageing Batches/Details filter bar -
   Consignee, Job Ref No): a text-filterable, multi-select checkbox
   dropdown - lives outside .grid-scroll, so a plain absolute-positioned
   panel is enough (no need for the reparent-to-<body> trick the
   per-row menus use). Reused for every field that needs this same
   "type to narrow, check several, confirm" combobox - see
   initSearchableCombo in this view's Scripts section.
   ============================================================ */
.searchable-combo {
    position: relative;
}
.searchable-combo-dropdown {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    margin-top: 2px;
    width: 260px;
    max-height: 260px;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    padding: 0.35rem;
}
.searchable-combo-item {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.4rem;
    border-radius: 6px;
    font-size: 0.8rem;
    cursor: pointer;
    margin-bottom: 0;
}
.searchable-combo-item:hover {
    background: var(--amber-bg);
}
.searchable-combo-item input[type="checkbox"] {
    flex: 0 0 auto;
}
.searchable-combo-item span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.searchable-combo-empty {
    padding: 0.5rem;
    color: var(--text-muted);
    font-size: 0.8rem;
}

/* ============================================================
   Modern data grid: sticky header, scrolls both directions,
   never forces the page to overflow - the grid owns its own
   scrollbars so wide/tall reports stay contained and readable.
   ============================================================ */
.grid-scroll {
    max-height: 75vh;
    max-width: 100%;
    overflow: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: 0 1px 3px rgba(16, 24, 40, 0.05);
}

.modern-table {
    margin-bottom: 0;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 0.8125rem;
    white-space: nowrap;
}

.modern-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--navy);
    color: #fff;
    font-weight: 600;
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.45rem 0.65rem;
    border-bottom: 2px solid var(--amber);
    white-space: nowrap;
}

.modern-table tbody td {
    padding: 0.35rem 0.65rem;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
    color: var(--text);
}

.modern-table tbody tr:nth-child(even) {
    background: #FAFBFC;
}

.modern-table tbody tr:hover {
    background: #FFF7E8; /* soft amber tint on hover */
}

.modern-table tbody tr.row-danger {
    background: var(--danger-bg);
}
.modern-table tbody tr.row-danger:hover {
    background: #FCE4E4;
}

.modern-table tbody tr.row-muted {
    background: #F3F4F6;
    color: var(--text-muted);
}

.modern-table td.wrap {
    white-space: normal;
    min-width: 180px;
    max-width: 360px;
    line-height: 1.3;
}

/* Sticky first column, useful once a table scrolls horizontally -
   Job Ref No stays visible while scrolling right through amounts/custom columns.
   Explicit opaque backgrounds (not `inherit`) are required here - sticky elements
   need their own solid paint layer or the row underneath bleeds/ghosts through
   during horizontal scroll. */
.modern-table td.sticky-col,
.modern-table th.sticky-col {
    position: sticky;
    left: 0;
    z-index: 1;
    background-color: var(--surface);
    isolation: isolate;
}
.modern-table tbody tr:nth-child(even) td.sticky-col {
    background-color: #FAFBFC;
}
.modern-table tbody tr:hover td.sticky-col {
    background-color: #FFF7E8;
}
.modern-table tbody tr.row-danger td.sticky-col {
    background-color: var(--danger-bg);
}
.modern-table tbody tr.row-danger:hover td.sticky-col {
    background-color: #FCE4E4;
}
.modern-table tbody tr.row-muted td.sticky-col {
    background-color: #F3F4F6;
}
.modern-table thead th.sticky-col {
    z-index: 3;
    background-color: var(--navy);
}

/* ============================================================
   Excel/SharePoint-style inline cell editing for custom fields:
   at rest a cell looks like plain text (no border/chrome), turns
   into an editable cell on hover/focus, grows in height as you
   type a longer comment, and its save checkmark floats INSIDE
   the cell's bottom-right corner rather than sitting beside it
   as permanent chrome - only appearing while focused or dirty.
   ============================================================ */
.field-inline-form {
    position: relative;
    display: block;
}
.dyn-input {
    display: block;
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
    resize: none;
    overflow: hidden;
    font-family: inherit;
    line-height: 1.35;
    min-height: 1.6rem;
    transition: border-color 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
    border: 1px solid transparent;
    border-radius: 4px;
    background: transparent;
    padding: 0.2rem 1.55rem 0.2rem 0.35rem;
    font-size: 0.8rem;
}

/* Rest state, has a value but not currently focused - reads like plain cell text */
.dyn-input.state-empty:hover { border-color: var(--border-strong); background: var(--surface); }

/* Focused, field was empty when focus started - writing a brand new comment */
.dyn-input.state-new {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.15);
    outline: none;
    background: var(--surface);
}

/* Focused, field already had a value when focus started - editing existing content */
.dyn-input.state-editing {
    border-color: var(--amber);
    box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.18);
    outline: none;
    background: var(--surface);
}

/* Save request in flight */
.dyn-input.state-saving {
    border-color: var(--success);
    box-shadow: 0 0 0 2px rgba(5, 150, 105, 0.18);
    outline: none;
    background: #F0FDF4;
}

/* Save failed */
.dyn-input.state-error {
    border-color: var(--danger);
    box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.18);
    background: var(--surface);
}

.field-save-btn {
    position: absolute;
    right: 3px;
    bottom: 3px;
    width: 18px;
    height: 18px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    border: none;
    background: var(--success);
    color: #fff;
    font-size: 0.62rem;
    line-height: 1;
    opacity: 0;
    transform: scale(0.8);
    pointer-events: none;
    transition: opacity 0.12s ease, transform 0.12s ease;
}
.field-save-btn:hover { background: #047857; }
.field-inline-form:focus-within .field-save-btn,
.field-inline-form.is-dirty .field-save-btn {
    opacity: 1;
    transform: scale(1);
    pointer-events: auto;
}
.field-cell {
    padding: 0 !important;
    vertical-align: top !important;
}
.job-ref-link {
    color: var(--accent);
    font-weight: 600;
    text-decoration: none;
}
.job-ref-link:hover { text-decoration: underline; }

/* ============================================================
   Compact icon buttons - used for row-level actions (view/send/delete)
   so action columns stay narrow instead of stacking full-text buttons.
   ============================================================ */
.action-btn-group {
    display: inline-flex;
    gap: 0.3rem;
}

.btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 7px;
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--navy);
    font-size: 0.9rem;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.btn-icon:hover {
    background: var(--navy);
    border-color: var(--navy);
    color: #fff;
}
.btn-icon-danger { color: var(--danger); }
.btn-icon-danger:hover {
    background: var(--danger);
    border-color: var(--danger);
    color: #fff;
}

.badge-icon {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.78rem;
    font-weight: 600;
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
}
.badge-icon-success { color: var(--success); background: #ECFDF5; }
.badge-icon-muted { color: var(--text-muted); background: #F3F4F6; }
.badge-icon-active {
    color: var(--amber-dark);
    background: var(--amber-bg);
    border: 1px solid var(--amber);
}
.badge-icon-active .active-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--amber-dark);
    display: inline-block;
    animation: activeDotPulse 1.6s ease-in-out infinite;
}
@keyframes activeDotPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

/* The one batch a dashboard is currently reading from (Batches list) - green reads as
   "live/good", distinct from .badge-icon-active's amber (used elsewhere for things that
   need attention, e.g. "Due for purge"). */
.badge-icon-live {
    color: var(--success);
    background: #ECFDF5;
    border: 1px solid var(--success);
}
.badge-icon-live .active-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--success);
    display: inline-block;
    animation: activeDotPulse 1.6s ease-in-out infinite;
}
.modern-table tbody tr.row-active-batch {
    background: #ECFDF5;
}

/* Permanently-purged attachment log rows: greyed out and struck through - the record survives
   (who/when/what), but it reads unmistakably as gone, not as an actionable current item. */
.modern-table tbody tr.row-purged {
    color: var(--text-muted);
    background: #FAFAFA;
}
.modern-table tbody tr.row-purged td {
    text-decoration: line-through;
    color: var(--text-muted);
}

/* ============================================================
   Single-item "record" page (Batches/Item) - a JO opened as its
   own page instead of a grid row. Reuses the same card look as
   .kpi-card so it feels native to the rest of the dashboard.
   ============================================================ */
.detail-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
    padding: 1rem;
}
.detail-card-title {
    margin-bottom: 0.85rem;
    color: var(--navy);
}
.item-fact-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.85rem 1.25rem;
}
.item-fact-wide {
    grid-column: 1 / -1;
}
.item-fact-label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-muted);
    margin-bottom: 0.15rem;
}
.item-fact-value {
    font-size: 0.92rem;
    color: var(--text);
}

/* ============================================================
   Compact navbar with icons
   ============================================================ */
.navbar {
    padding-top: var(--navbar-toprow-padding);
    padding-bottom: var(--navbar-toprow-padding);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
}
.navbar .navbar-brand { font-size: 1.05rem; }
.navbar-nav { gap: 0.15rem; }
.navbar .nav-link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    /* Fixed, not content-driven - see --navbar-toprow-content-height's comment above: every
       nav-link needs an IDENTICAL content-row height regardless of whether its icon is a Bootstrap
       icon-font glyph or a hand-drawn SVG (Sea Freight), or the active-tab overflow trick below
       (sized off this same shared height) falls short under whichever items don't match it. */
    height: var(--navbar-toprow-content-height);
    padding: 0 0.75rem !important;
    border-radius: 7px;
    font-size: 0.86rem;
    transition: background 0.12s ease, color 0.12s ease;
}
.navbar .nav-link:hover { background: rgba(255, 255, 255, 0.08); }
.navbar .nav-link i { font-size: 0.95rem; }

/* Current section stays visibly marked even after the dropdown closes, so orientation doesn't
   depend on remembering which item you clicked - important now that each dropdown groups several
   report types together. */
.navbar .nav-link.active {
    background: rgba(245, 165, 36, 0.16);
    color: var(--amber-light);
    font-weight: 700;
    box-shadow: inset 0 0 0 1px rgba(245, 165, 36, 0.35);
}
.navbar .nav-link.active:hover { background: rgba(245, 165, 36, 0.22); }

.navbar .dropdown-menu {
    background: var(--navy-light);
    border: 1px solid var(--navy-lighter);
    padding: 0.4rem;
    min-width: 14rem;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}
.navbar .dropdown-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.86rem;
    padding: 0.4rem 0.7rem;
    border-radius: 6px;
}
.navbar .dropdown-item:hover,
.navbar .dropdown-item:focus {
    background: var(--navy-lighter);
    color: #fff;
}
.navbar .dropdown-item.active,
.navbar .dropdown-item:active {
    background: var(--amber);
    color: var(--navy);
}

/* Sub-group labels inside a consolidated dropdown (e.g. "AR Ageing" vs "Receipt Listing" within
   one Receivables menu) - quiet enough not to be mistaken for a clickable item. */
.navbar .dropdown-header {
    color: var(--amber-light);
    font-size: 0.68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.3rem 0.7rem 0.15rem;
}
.navbar .dropdown-menu li:first-child .dropdown-header { padding-top: 0.1rem; }
.navbar .dropdown-divider { border-color: rgba(255, 255, 255, 0.12); margin: 0.35rem 0; }

/* Collapsible category rows inside a consolidated dropdown (see _NavCategoryDropdown.cshtml) -
   the category name (e.g. "AP Ageing") is its own click target that expands/collapses its
   sub-items in place, instead of every sub-item across every category being listed flat at once. */
.navbar .dropdown-menu-accordion { min-width: 15.5rem; }
.navbar .dropdown-item-toggle {
    justify-content: space-between;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.92);
}
.navbar .dropdown-item-toggle .submenu-caret {
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.55);
    transition: transform 0.15s ease;
}
.navbar .dropdown-item-toggle[aria-expanded="true"] .submenu-caret { transform: rotate(180deg); }
.navbar .dropdown-item-toggle[aria-expanded="true"] { color: var(--amber-light); }
.navbar .dropdown-submenu-list {
    margin: 0.15rem 0 0.25rem;
    padding-left: 0.6rem;
    border-left: 2px solid rgba(255, 255, 255, 0.1);
}
.navbar .dropdown-subitem { font-size: 0.82rem; }

/* ============================================================
   Milestone timeline - used for the JobRefNo audit trail so
   history reads as a sequence of events rather than a plain log table.
   ============================================================ */
.timeline {
    position: relative;
    padding-left: 2.75rem;
    margin: 1.5rem 0;
}
.timeline::before {
    content: '';
    position: absolute;
    left: 1.05rem;
    top: 0.4rem;
    bottom: 0.4rem;
    width: 2px;
    background: linear-gradient(to bottom, var(--amber), var(--border-strong));
}

.timeline-item {
    position: relative;
    margin-bottom: 1.5rem;
}
.timeline-item:last-child { margin-bottom: 0; }

.timeline-dot {
    position: absolute;
    left: -2.75rem;
    top: 0.15rem;
    width: 2.1rem;
    height: 2.1rem;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--surface);
    border: 2px solid var(--navy);
    color: var(--navy);
    font-size: 0.95rem;
    z-index: 1;
}
.timeline-item.event-created .timeline-dot { border-color: var(--accent); color: var(--accent); }
.timeline-item.event-edit .timeline-dot { border-color: var(--amber); color: var(--amber-dark); }
.timeline-item.event-recalc .timeline-dot { border-color: var(--text-muted); color: var(--text-muted); }
.timeline-item.event-blacklist .timeline-dot { border-color: var(--danger); color: var(--danger); background: var(--danger-bg); }

.timeline-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.85rem 1.1rem;
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
}
.timeline-card:hover { border-color: var(--border-strong); }

.timeline-title {
    font-weight: 600;
    font-size: 0.92rem;
    color: var(--text);
    margin-bottom: 0.15rem;
}
.timeline-meta {
    font-size: 0.78rem;
    color: var(--text-muted);
    margin-bottom: 0.4rem;
}
.timeline-diff {
    font-size: 0.85rem;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
}
.timeline-diff .old-val {
    color: var(--text-muted);
    text-decoration: line-through;
    background: #F3F4F6;
    padding: 0.1rem 0.45rem;
    border-radius: 5px;
}
.timeline-diff .new-val {
    color: var(--text);
    font-weight: 600;
    background: #FFF7E8;
    padding: 0.1rem 0.45rem;
    border-radius: 5px;
}

/* ============================================================
   Job tag pills - shown under Job Ref No in the batch grid.
   ============================================================ */
.tag-pills {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.35rem;
    margin-top: 0.25rem;
}
.tag-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.68rem;
    font-weight: 600;
    color: #fff;
    background: var(--tag-color, var(--text-muted));
    padding: 0.18rem 0.6rem;
    border-radius: 999px;
    white-space: nowrap;
    cursor: pointer;
    border: none;
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.08);
    transition: box-shadow 0.12s ease, transform 0.12s ease;
    max-width: 100%;
}
.tag-pill:hover { box-shadow: 0 2px 5px rgba(16, 24, 40, 0.18); }

.tag-pill-detail {
    display: none;
    align-items: center;
    gap: 0.35rem;
    font-weight: 400;
    white-space: normal;
}

/* Expanded state renders as a small popover card - not a stretched pill -
   so multi-line detail text (assigned-by, date, remove button) never gets
   eaten by the pill's own border-radius. */
.tag-pill.expanded {
    position: relative;
    z-index: 5;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    width: 15rem;
    max-width: 15rem;
    white-space: normal;
    background: var(--surface);
    color: var(--text);
    padding: 0;
    border-radius: var(--radius);
    box-shadow: 0 8px 20px rgba(16, 24, 40, 0.18);
    cursor: default;
}
.tag-pill.expanded:hover { box-shadow: 0 8px 20px rgba(16, 24, 40, 0.18); }
.tag-pill.expanded .tag-pill-name {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    background: var(--tag-color, var(--text-muted));
    color: #fff;
    padding: 0.45rem 0.7rem;
    border-radius: var(--radius) var(--radius) 0 0;
    cursor: pointer;
}
.tag-pill.expanded .tag-pill-detail {
    display: flex;
    flex-wrap: wrap;
    color: var(--text-muted);
    font-size: 0.74rem;
    line-height: 1.4;
    padding: 0.55rem 0.7rem;
}
.tag-pill .remove-tag {
    cursor: pointer;
    font-size: 0.65rem;
    font-weight: 600;
    border: none;
    background: rgba(255, 255, 255, 0.2);
    color: inherit;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    line-height: 1.4;
}
.tag-pill .remove-tag:hover { background: rgba(255, 255, 255, 0.35); }
.tag-pill.expanded .remove-tag {
    background: var(--danger-bg);
    color: var(--danger);
}
.tag-pill.expanded .remove-tag:hover { background: var(--danger); color: #fff; }
.tag-pill-add {
    background: var(--border-strong) !important;
    color: var(--text) !important;
    font-weight: 700;
}
.tag-pill-add:hover { background: var(--navy) !important; color: #fff !important; }
.tag-add-form {
    margin-top: 0.25rem;
}

/* ============================================================
   Multi-item filter modal - tabbed distinct-value pickers with
   a live "selected" panel (used by Details pages' Advanced
   Filter button).
   ============================================================ */
.ms-select-all {
    margin-bottom: 0.4rem;
    padding-left: 1.4rem;
}
.ms-select-all .form-check-input { cursor: pointer; }
.ms-select-all .form-check-label { color: var(--text-muted); cursor: pointer; }

.ms-item-list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 360px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}
.ms-item-list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--border);
    font-size: 0.82rem;
}
.ms-item-list li:last-child { border-bottom: none; }
.ms-item-list li.ms-added { background: var(--amber-bg); }
.ms-item-list li span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ms-item-list .btn-plus {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 6px;
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--navy);
    cursor: pointer;
    font-size: 0.75rem;
}
.ms-item-list .btn-plus:hover { background: var(--navy); border-color: var(--navy); color: #fff; }
.ms-item-list li.ms-added .btn-plus {
    background: var(--success);
    border-color: var(--success);
    color: #fff;
}
.ms-selected-panel {
    max-height: 400px;
    overflow-y: auto;
}
.ms-selected-group-label {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin: 0.6rem 0 0.3rem;
}
.ms-selected-group-label:first-child { margin-top: 0; }
.ms-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    background: var(--navy);
    color: #fff;
    border-radius: 999px;
    font-size: 0.74rem;
    line-height: 1.4;
    padding: 0.3rem 0.4rem 0.3rem 0.7rem;
    margin: 0 0.3rem 0.3rem 0;
    max-width: 100%;
}
.ms-chip span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 220px;
}
.ms-chip button {
    cursor: pointer;
    font-size: 0.65rem;
    font-weight: 600;
    border: none;
    background: rgba(255, 255, 255, 0.2);
    color: inherit;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    line-height: 1.4;
    flex-shrink: 0;
}
.ms-chip button:hover { background: rgba(255, 255, 255, 0.35); }
.ms-empty-hint { color: var(--text-muted); font-size: 0.8rem; }
.tag-add-form select {
    font-size: 0.72rem;
    padding: 0.1rem 0.3rem;
    max-width: 130px;
}

/* ============================================================
   Tag "+" dropdown menu: expands directly from the button (not a
   native <select>) and picking an item adds it immediately - one
   click instead of open-select / pick / click-checkmark.
   ============================================================ */
.tag-add-wrapper {
    position: relative;
    display: inline-block;
}
.tag-menu {
    /* position/top/left are set inline by openFloatingMenu() in site.js once this is
       reparented to <body> - fixed (not absolute) so the viewport-relative coordinates from
       getBoundingClientRect() apply directly, with no scroll-offset math needed. */
    position: fixed;
    z-index: 2000;
    min-width: 11rem;
    max-width: 14rem;
    max-height: 14rem;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 8px 20px rgba(16, 24, 40, 0.18);
    padding: 0.3rem;
}
.tag-menu-item-form + .tag-menu-item-form { margin-top: 0.15rem; }
.tag-menu-item {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    width: 100%;
    border: none;
    background: none;
    text-align: left;
    padding: 0.35rem 0.5rem;
    border-radius: 6px;
    font-size: 0.78rem;
    color: var(--text);
}
.tag-menu-item:hover { background: var(--neutral-bg); }
.tag-menu-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--tag-color, var(--text-muted));
    flex-shrink: 0;
}
.tag-menu-empty {
    padding: 0.4rem 0.5rem;
    font-size: 0.75rem;
    color: var(--text-muted);
}

/* ============================================================
   Job attachments - same pill idiom as tags, neutral color since
   files don't carry a category, plus an upload pill that opens a
   native file picker and submits as soon as a file is chosen.
   ============================================================ */
.attachment-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--text);
    background: var(--neutral-bg);
    border: 1px solid var(--border-strong);
    padding: 0.16rem 0.55rem;
    border-radius: 999px;
    white-space: nowrap;
    cursor: pointer;
    max-width: 100%;
}
.attachment-pill:hover { background: var(--border); }
.attachment-pill .attachment-pill-name {
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 8rem;
}
.attachment-menu {
    /* position/top/left are set inline by openFloatingMenu() in site.js once this is
       reparented to <body> - see .tag-menu above for why fixed (not absolute). */
    position: fixed;
    z-index: 2000;
    width: 15rem;
    background: var(--surface);
    border-radius: var(--radius);
    box-shadow: 0 8px 20px rgba(16, 24, 40, 0.18);
    padding: 0.6rem 0.7rem;
    font-size: 0.75rem;
    color: var(--text-muted);
}
.attachment-menu .attachment-menu-title {
    font-weight: 600;
    color: var(--text);
    margin-bottom: 0.3rem;
    word-break: break-all;
}
.attachment-menu .attachment-menu-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.5rem;
}
.attachment-upload-label {
    background: var(--border-strong) !important;
    color: var(--text) !important;
    font-weight: 700;
}
.attachment-upload-label:hover { background: var(--navy) !important; color: #fff !important; }

/* ============================================================
   Dropdown-options builder (Custom Columns admin) - a textbox +
   "+" button, each add producing a pill, same visual language
   as the tag/attachment pills elsewhere in the app.
   ============================================================ */
.option-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-top: 0.4rem;
    min-height: 1.6rem;
}
.option-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--text);
    background: var(--neutral-bg);
    border: 1px solid var(--border-strong);
    padding: 0.18rem 0.4rem 0.18rem 0.6rem;
    border-radius: 999px;
}
.option-pill-remove {
    background: none;
    border: none;
    color: var(--text-muted);
    font-weight: 700;
    line-height: 1;
    padding: 0 0.15rem;
    cursor: pointer;
}
.option-pill-remove:hover { color: var(--danger); }

/* ============================================================
   System Setup hub - a tile per admin sub-page instead of a
   long dropdown/tab-strip.
   ============================================================ */
.setup-tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 1rem;
    margin-top: 1.5rem;
}
.setup-tile {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1.1rem;
    text-decoration: none;
    color: var(--text);
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
    transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.setup-tile:hover {
    box-shadow: var(--shadow-md);
    border-color: var(--amber);
    transform: translateY(-2px);
    color: var(--text);
    text-decoration: none;
}
.setup-tile-icon {
    width: 40px;
    height: 40px;
    border-radius: 9px;
    background: var(--navy);
    color: var(--amber);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
}
.setup-tile-title {
    font-weight: 700;
    font-size: 0.98rem;
}
.setup-tile-desc {
    font-size: 0.8rem;
    color: var(--text-muted);
    line-height: 1.35;
}

/* "View Attachments" grid button - fixed width so every row's button lines up and doesn't
   shift width just because one job has 12 files and its neighbor has none. The count badge
   always renders (even "0") for the same reason: reserving its space with CSS but hiding the
   element for zero-count rows would still look inconsistent the instant text wraps. */
.btn-attachments {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    min-width: 210px;
    white-space: nowrap;
}
.btn-attachments .badge {
    min-width: 1.5rem;
    font-weight: 600;
}
.bg-attachments-empty {
    background: var(--neutral-bg);
    color: var(--text-muted);
}

/* ============================================================
   Uniform KPI cards - same height regardless of content length,
   width flexes to fit the row.
   ============================================================ */
.kpi-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}
.kpi-card {
    flex: 1 1 150px;
    min-height: 92px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 0.75rem 0.5rem;
}
.kpi-value {
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--navy);
    line-height: 1.2;
}
.kpi-label {
    font-size: 0.72rem;
    color: var(--text-muted);
    margin-top: 0.15rem;
}
.kpi-sub {
    font-size: 0.7rem;
    color: var(--amber-dark);
    font-weight: 600;
    margin-top: 0.1rem;
}

/* ============================================================
   Dashboard cross-filtering: click a chart segment / table row
   to select it, KPI values switch to a "selected on top, overall
   underneath" comparison - the standard drill-down pattern from
   BI tools (Power BI, Tableau) - and clear again once deselected.
   ============================================================ */
.kpi-value.kpi-value-selected {
    color: var(--amber-dark);
}
.kpi-compare {
    font-size: 0.66rem;
    color: var(--text-muted);
    margin-top: 0.05rem;
}
.filter-chip-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.filter-chip {
    background: var(--amber-bg);
    color: var(--amber-dark);
    border: 1px solid var(--amber);
    border-radius: 999px;
    padding: 0.25rem 0.75rem;
    font-size: 0.78rem;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}
.filter-chip button {
    background: none;
    border: none;
    color: inherit;
    line-height: 1;
    padding: 0;
    font-size: 0.9rem;
}
.modern-table tbody tr.clickable-row {
    cursor: pointer;
}
.modern-table tbody tr.clickable-row:hover {
    background: var(--amber-bg);
}
.expand-icon {
    display: inline-block;
    width: 1rem;
    transition: transform 0.15s ease;
    color: var(--text-muted);
}
.jo-detail-row td {
    padding: 0;
    border-top: none;
}
.modern-table tbody tr.clickable-row.row-selected {
    background: var(--amber-bg);
}

/* Job-order drill-down panel nested inside an expanded category row: a
   light-gray panel (not the outer table's navy header) so it reads as a
   detail view rather than a second full table - same idea as the Profit &
   Loss statement's sub-account rows (.pl-detail-row), just with its own
   column set. Header and body share the gray family but at two shades
   (border-strong vs neutral-bg) so the header still reads as a header. */
.jo-detail-panel {
    padding: 0.5rem 0.5rem 0.5rem 1.75rem;
    background: var(--neutral-bg);
    border-left: 3px solid var(--amber);
}
.jo-detail-count {
    font-size: 0.7rem;
    color: var(--text-muted);
    margin-bottom: 0.35rem;
}
.jo-detail-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 0.72rem;
}
.jo-detail-table thead th {
    /* Explicitly NOT sticky - and that has to be stated, not just omitted. This table is
       nested inside the outer table's <tbody> (the expanded row lives there), and CSS
       descendant selectors don't care about table boundaries: .modern-table thead th (below)
       still matches these <th> too, at equal specificity, so without an explicit override here
       its `position: sticky; top: 0` kept winning and this header rendered on top of the
       outer table's own sticky header while scrolling. */
    position: static;
    background: var(--border-strong);
    color: var(--navy);
    font-weight: 600;
    text-align: left;
    padding: 0.3rem 0.6rem;
    white-space: nowrap;
}
.jo-detail-table tbody td {
    background: var(--neutral-bg);
    color: var(--text-muted);
    padding: 0.3rem 0.6rem;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
.jo-detail-table tbody tr:hover td {
    background: var(--border);
}

/* ============================================================
   Financial statement tables (Profit & Loss) - sub-account
   breakdown rows get a subtle tint + smaller/muted type so they
   read as subordinate detail under their parent line, per how a
   real statement note discloses account-level composition.
   ============================================================ */
.modern-table tbody tr.pl-detail-row {
    background: var(--neutral-bg);
}
.modern-table tbody tr.pl-detail-row:hover {
    background: var(--border);
}
.modern-table tbody tr.pl-detail-row td.sticky-col {
    background-color: var(--neutral-bg); /* explicit opaque match for the sticky column - see sticky-col note above */
}
.modern-table tbody tr.pl-detail-row:hover td.sticky-col {
    background-color: var(--border);
}
.modern-table tbody tr.pl-detail-row td {
    font-size: 0.68rem;
    color: var(--text-muted);
    border-top: none;
}
.modern-table tbody tr.pl-detail-row td.pl-detail-label {
    padding-left: 1.75rem;
}
.pl-branch-icon {
    color: var(--border-strong);
    font-size: 0.9em;
    margin-right: 0.35rem;
}

/* ============================================================
   Chart.js canvas wrapper - Chart.js with maintainAspectRatio:false
   sizes the canvas to its parent's height. Without this, the parent
   (a bare column/div) has no defined height of its own, so its height
   comes FROM the canvas - a circular measurement that Chart.js's
   resize observer re-triggers on every scroll/resize, growing forever.
   Giving the immediate parent a fixed height breaks that loop.
   ============================================================ */
.chart-box {
    position: relative;
    width: 100%;
}
.chart-box canvas {
    width: 100% !important;
    height: 100% !important;
}

/* ============================================================
   Drag-and-drop upload zone
   ============================================================ */
.drop-zone {
    border: 2px dashed var(--border-strong);
    border-radius: var(--radius);
    padding: 2rem 1rem;
    text-align: center;
    cursor: pointer;
    background: var(--surface);
    color: var(--text-muted);
    transition: border-color 0.15s ease, background 0.15s ease;
}
.drop-zone:hover { border-color: var(--navy-lighter); }
.drop-zone-active {
    border-color: var(--amber);
    background: #FFF7E8;
}
