/* ==========================================================================
   Analytics Portal - site.css
   Plain responsive CSS (flexbox/grid + media queries), no framework dependency.
   ========================================================================== */

:root {
    --header-bg: #BFE3F5;
    /* Very faint grey page canvas (html/body/.side-nav) - deliberately NOT the same white as
       --card-bg, so a card is still visibly distinct from the page even where its shadow is faint
       (edges, corners) rather than relying on the shadow to do 100% of the separation. See
       --surface-tint below for the separate, more saturated pale blue-grey used for hover states,
       readonly fields, and panel highlights - a different shade for a different job, so those stay
       clearly visible against white cards/panels too. */
    --page-bg: #F8F9FA;
    --surface-tint: #EAF3F8;
    --brand-red: #E31E24;
    --profile-pink: #C77DBA;
    --card-bg: #FFFFFF;
    --card-border: #DCE7EC;
    --text-dark: #24313A;
    --text-muted: #6C818C;
    --green: #2E9E5B;
    --green-dark: #23824A;
    --red: #D8412F;
    --blue: #2C7BE5;
    --blue-dark: #1F5FB8;
    --yellow: #E8A93A;
    --nav-width: 270px;
    /* Width of the always-visible icon-only rail (collapsed state) - .nav-width above is only used
       now for the wider "expanded" state (labels visible), toggled via the hamburger at the top of
       the rail - see .side-nav / .side-nav.expanded below. */
    --nav-rail-width: 64px;
    --header-height: 58px;
    --footer-height: 32px;
    /* Site-wide background gradient (same one the Login page introduced) - painted on <html>, see
       "Site-wide background" below. Light: pale sky blues from the header colour. */
    --app-bg-color: #F1F6F9;
    --app-bg-image:
        radial-gradient(ellipse at 12% 8%, rgba(44, 123, 229, 0.08) 0%, rgba(44, 123, 229, 0) 45%),
        radial-gradient(ellipse at 88% 92%, rgba(191, 227, 245, 0.45) 0%, rgba(191, 227, 245, 0) 55%),
        radial-gradient(ellipse at 90% 10%, rgba(199, 125, 186, 0.06) 0%, rgba(199, 125, 186, 0) 40%),
        linear-gradient(150deg, #F8F9FA 0%, #F2F6F9 50%, #E8F1F7 100%);
    /* Frosted see-through header / left menu / footer over that gradient. */
    --chrome-glass: rgba(248, 249, 250, 0.55);
    --nav-glass: rgba(248, 249, 250, 0.72);
}

/* ---------------- Dark theme ----------------
   Overrides the custom properties above when <html data-theme="dark"> is set (toggled by
   initThemeToggle() in site.js, persisted to CS_ECLAIM_USERS.CS_IS_DARK_THEME via
   SetThemePreference.ashx - see Site.Master.cs for how it's rendered server-side on load). Since
   almost every rule in this file already reads colors via var(--...), this block alone re-themes the
   large majority of the site automatically. The handful of components below that use hardcoded
   literal colors instead of a variable (e.g. .user-icon's white circle, status badge pastels,
   banners) get their own explicit html[data-theme="dark"] override further down, next to the
   light-mode rule they're overriding. .announcement-banner now follows the theme (light card in
   light mode, dark gradient card in dark mode - explicit request: "cater for light theme as well" -
   see its own html[data-theme="dark"] override further down). The three Home page usage stat cards
   (.stat-card-green/.stat-card-blue/.stat-card-pink) are still intentionally left unthemed - they're
   deliberately colored cards in both modes. */
html[data-theme="dark"] {
    --header-bg: #1B2530;
    --page-bg: #12181D;
    --surface-tint: #12181D;
    --brand-red: #FF6961;
    --profile-pink: #E2A6D8;
    --card-bg: #1E2830;
    --card-border: #333F4A;
    --text-dark: #E8EDF0;
    --text-muted: #94A6B0;
    --green: #3FBE78;
    --green-dark: #2E9E5B;
    --red: #E8624F;
    --blue: #5B9BF0;
    --blue-dark: #4C93F0;
    --yellow: #F0BC5A;
    /* Dark: deep navy from the dark page/header/card colours with a glow of the dark-theme blue. */
    --app-bg-color: #12181D;
    --app-bg-image:
        radial-gradient(ellipse at 12% 8%, rgba(91, 155, 240, 0.12) 0%, rgba(91, 155, 240, 0) 45%),
        radial-gradient(ellipse at 88% 92%, rgba(76, 147, 240, 0.08) 0%, rgba(76, 147, 240, 0) 50%),
        radial-gradient(ellipse at 90% 10%, rgba(226, 166, 216, 0.06) 0%, rgba(226, 166, 216, 0) 40%),
        linear-gradient(150deg, #0D1217 0%, #12181D 40%, #182028 75%, #141B22 100%);
    --chrome-glass: rgba(18, 24, 29, 0.55);
    --nav-glass: rgba(18, 24, 29, 0.78);
}

/* Mobile browsers (mainly Chrome/Safari on Android/iOS) paint a translucent blue flash over
   whatever you tap - buttons, links, cards, icons - by default. -webkit-tap-highlight-color is the
   only way to turn that off; applied globally (not just on buttons/links) since the flash also shows
   up on non-interactive tappable elements like cards and dropdown-toggle icons. */
* { -webkit-tap-highlight-color: transparent; }

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--page-bg);
    color: var(--text-dark);
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-size: 14px;
    /* Safety net, not the primary fix - there was previously nothing anywhere in the page clipping
       horizontal overflow, so any single element that computed wider than the viewport (e.g. a
       <select> whose long selected option text won some browser's intrinsic-sizing quirk, ignoring
       its own width/flex constraints) pushed the ENTIRE page wider and the mobile browser would
       auto-scroll to reveal it, shifting the whole header/logo off-screen along with it - exactly
       the "still displaying out of screen" report. This guarantees that can never happen again, on
       any page, regardless of what overflows. */
    overflow-x: hidden;
}

/* ---------------- Site-wide background ----------------
   The gradient (--app-bg-image, light/dark versions above) is painted on <html> and stays put while
   the page scrolls; body is transparent so it shows through everywhere. Cards stay solid on top. */
html {
    background-color: var(--app-bg-color);
    background-image: var(--app-bg-image);
    background-attachment: fixed;
    min-height: 100%;
}
body { background: transparent; }

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

h1, h2, h3, h4 { margin: 0 0 12px 0; }

/* ---------------- Header ---------------- */

.site-header {
    /* Same near-white canvas as the rest of the page now, not a distinct blue - no border/shadow
       separating it from the page below, per explicit request. */
    background: var(--page-bg);
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* Extra left padding on desktop only - the rail spans the full viewport height there (top:0 to
       bottom:0, higher z-index than this header - see .side-nav) and is permanently docked, so
       without this offset the logo (.brand-wrap) and .header-user would render underneath the
       rail's opaque background. Reset back to the plain 16px on mobile (see the media query below)
       since the rail is hidden off-screen there by default and isn't covering anything. */
    padding: 10px 16px 10px calc(var(--nav-rail-width) + 16px);
    min-height: var(--header-height);
    /* position:fixed, not sticky (explicit request: "make the header sticky. It should not be able to
       scroll away" - for every viewport). sticky alone was unreliable here because html/body's own
       overflow-x:hidden (top of this file - the "no element can ever push the page wider" safety net)
       forces the browser to implicitly compute their overflow-y as auto too (a well-known CSS quirk:
       specifying only one axis's overflow as non-visible auto-promotes the other axis away from
       visible as well), which can turn body into its own extra scrolling box and break position:sticky
       on children pinned against the "real" viewport. fixed sidesteps that entirely - same proven
       approach already used for #sideNav and .site-footer below (both fixed for the same "must never
       scroll away" reason). left/right explicit since fixed elements no longer inherit a width from
       flow - .app-body's own margin-top (below) reserves the space this no longer occupies in-flow. */
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 50;
}

/* Hidden on desktop (the rail is always visible there, with its own hamburger at the top - see
   .side-nav .nav-toggle-btn) - only shown below 768px, where the rail itself starts hidden
   off-screen and this is the only way to reveal it. See the mobile media query further down for the
   display:flex override, and initNav() in site.js for the click handler (shared with the rail's own
   hamburger). */
.hamburger-btn-mobile {
    display: none;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-dark);
    padding: 4px 8px;
    align-items: center;
    justify-content: center;
}

.hamburger-btn-mobile .material-icons { font-size: 26px; }

/* Fills the available space between .site-header's edges and centers .brand within it - this
   wrapper (not the link itself) is what takes flex:1, so only the logo image ends up clickable,
   not the whole wide flex box around it. */
.brand-wrap {
    display: flex;
    justify-content: center;
    align-items: center;
    flex: 1;
}

.brand {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.brand-logo {
    height: 38px;
    width: auto;
    display: block;
}

/* Defensive-only: the actual logo image's own colors are unknown (it's Vincent's own file, not
   something built here), so if it happens to be dark text/shapes on a transparent background it
   could effectively disappear against the dark header. Wrapping it in a small white rounded backdrop
   guarantees it stays visible no matter what the logo itself looks like, at the cost of a visible
   white chip around it in dark mode - worth flagging/reverting if the real logo turns out to already
   read fine on a dark background once Vincent sees it live. */
html[data-theme="dark"] .brand-logo {
    background: #fff;
    border-radius: 8px;
    padding: 4px 8px;
}

.header-user {
    display: flex;
    align-items: center;
    gap: 8px;
    position: relative;
}

.header-username {
    font-weight: 600;
    color: var(--text-dark);
    display: none;
}

.user-icon {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    overflow: hidden;
}

html[data-theme="dark"] .user-icon { background: var(--card-bg); }

.user-icon .material-icons {
    font-size: 36px;
    color: var(--profile-pink);
    line-height: 1;
}

/* Dark mode toggle button - sits to the left of .user-icon inside .header-user, same circular
   footprint/hit-area as the avatar so the two sit flush together in the header. */
.theme-toggle-btn {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: none;
    background: transparent;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 0;
}

.theme-toggle-btn:hover { background: rgba(0,0,0,0.06); }
html[data-theme="dark"] .theme-toggle-btn:hover { background: rgba(255,255,255,0.08); }

.theme-toggle-icon {
    font-size: 24px;
    line-height: 1;
    color: var(--text-dark);
}

.user-menu {
    display: none;
    position: absolute;
    right: 0;
    top: 42px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 8px;
    box-shadow: 0 4px 14px rgba(0,0,0,0.12);
    min-width: 140px;
    overflow: hidden;
    z-index: 100;
}

.user-menu.open { display: block; }

.user-menu-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 10px 14px;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-dark);
}

.user-menu-item:hover { background: var(--surface-tint); }

/* ---------------- Layout / Nav ---------------- */

.app-body {
    min-height: calc(100vh - var(--header-height));
    /* .site-header is now position:fixed (see above), out of normal flow - without this, .app-body
       would start from the very top of <form> and render underneath it. min-height above is
       unchanged (still "the rest of the viewport, minus the header"), this just shifts .app-body's
       own box down to start below the header instead. */
    margin-top: var(--header-height);
    /* Reserves space so fixed .site-footer below never overlaps the bottom of the page content. */
    padding-bottom: var(--footer-height);
}

/* Desktop: always-visible icon-only rail (not off-canvas) - collapsed at --nav-rail-width by
   default. Clicking the hamburger at its top toggles .expanded, which widens it to --nav-width and
   reveals each item's .nav-label text (see initNav() in site.js). The expanded state is positioned
   the same (fixed, so it floats over .content-area rather than reflowing it) - only its width and
   shadow change - so nothing else on the page needs to shift when it opens/closes.
   Mobile: hidden off-screen by default instead - see the media query further down, which overrides
   this with a slide-in transform. */
.side-nav {
    position: fixed;
    /* Spans the full viewport height - top:0 to bottom:0 - overlapping .site-header and
       .site-footer rather than sitting between them, per explicit request. Its z-index (70) is
       higher than both .site-header (50) and .site-footer, so it paints on top of them where they
       overlap. Stays within the page's own faint-grey body area - same background color as the page
       itself (var(--page-bg)), separated only by a border/shadow. */
    top: 0;
    left: 0;
    bottom: 0;
    width: var(--nav-rail-width);
    background: var(--page-bg);
    border-right: 1px solid var(--card-border);
    padding: 12px 0;
    transition: width 0.2s ease;
    z-index: 70;
    overflow-x: hidden;
    overflow-y: auto;
    /* Explicit request: hide this rail's own scrollbar while still allowing scroll (mouse wheel/trackpad/
       touch all still work, only the visual scrollbar track+thumb are hidden) - same 3-vendor-prefixed-
       rules approach as .vendor-chart-scroll elsewhere in this file (no unprefixed standard for this yet). */
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* legacy Edge/IE */
}
.side-nav::-webkit-scrollbar {
    display: none; /* Chrome/Safari/Chromium Edge */
}

.side-nav.expanded {
    width: var(--nav-width);
    box-shadow: 2px 0 12px rgba(0,0,0,0.15);
}

/* Hamburger - the first item in the rail, at every viewport width (no separate header hamburger
   anymore - this is the single toggle). Shares the same icon column alignment (left padding, icon
   size) as .nav-icon-link below it so every icon in the rail lines up in one vertical column
   whether collapsed or expanded. */
.nav-toggle-btn {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    padding: 10px 0 14px 20px;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-dark);
    text-align: left;
}

.nav-toggle-btn .material-icons { font-size: 24px; flex-shrink: 0; }

.nav-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.nav-icon-link {
    display: flex;
    /* flex-start (not center) - once a long label wraps to a 2nd line (see .nav-label below), this
       keeps the icon pinned level with the label's first line instead of re-centering vertically
       against the label's now-taller full height. */
    align-items: flex-start;
    gap: 14px;
    padding: 12px 16px 12px 20px;
    color: var(--text-dark);
    font-weight: 700;
    font-size: 14px;
    text-decoration: none;
}

.nav-icon-link:hover { background: var(--card-bg); text-decoration: none; }

.nav-icon-link .material-icons { font-size: 24px; flex-shrink: 0; }

/* Green circular "+" (Create New Tickets) at the top of the rail - same row size as every other
   .nav-icon-link, the icon itself sits in a solid green circle. 24px circle so it lines up with the
   24px icons below it. */
/* 40px green rounded square with a white circled "+" (add_circle) inside. Bigger than the 24px rail
   icons, so this row gets its own padding/gap: 12px left padding keeps the square centred in the
   64px collapsed rail (12 + 40/2 = 32, same centre as the other icons: 20 + 24/2), and the 6px gap
   puts "Create New Tickets" at the same x as every other label (12 + 40 + 6 = 58 = 20 + 24 + 14). */
.side-nav .nav-icon-link.nav-create-link { padding-left: 12px; gap: 6px; }
.nav-create-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    background: var(--green);
    box-shadow: 0 2px 6px rgba(46, 158, 91, 0.35);
}
.nav-create-icon .material-icons { font-size: 24px; color: #fff; }
.nav-create-link:hover .nav-create-icon,
.st-create-btn:hover .nav-create-icon { background: var(--green-dark); }
/* Vertically centre "Create New Tickets" on the green square - .nav-icon-link top-aligns
   (flex-start, for wrapping labels), which left this single-line label sitting higher than the
   24px icon. Selector matches the specificity of the expanded-label rule below so it wins there too. */
.side-nav .nav-icon-link.nav-create-link { align-items: center; }
/* Team Leader Permission's "identity_platform" icon (explicit request) is a Material Symbols glyph, not
   a classic Material Icons one - same font-size/flex-shrink as the rule above so it lines up with every
   other rail icon despite coming from a different font. */
.nav-icon-link .material-symbols-outlined { font-size: 24px; flex-shrink: 0; }

/* Label text - hidden entirely (not just visually) while the rail is collapsed, so it never
   contributes to the collapsed rail's width or gets read out mid-transition. Only shown once
   .side-nav has .expanded. */
.nav-label { display: none; }
.side-nav.expanded .nav-label { display: inline; }

/* A label too long for one line (e.g. "Company Overview - Net Income") wraps instead of being
   clipped (explicit request) - flex:1/min-width:0 lets this label shrink below its own text's
   natural width so it actually wraps within the rail rather than pushing/overflowing it, and since
   wrapping happens inside the label's own box (which starts right after the icon, not at the icon's
   left edge), the 2nd line lands under the label's first word, not under the icon - no extra
   indent-matching needed. */
.side-nav.expanded .nav-icon-link .nav-label {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    white-space: normal;
    line-height: 1.3;
}

/* Custom hover tooltip for the collapsed icon rail - explicit request: "When user onhover on the icon
   in the left menu bar when the left menu bar is collapse, show onhover text of the page name." First
   attempt used a CSS-only content:attr(title) ::after on the icon itself, but it never actually painted
   - #sideNav has overflow-x:hidden (needed so nav-label text doesn't spill out mid collapse/expand
   transition), which silently clips ANY child content positioned past the rail's own right edge,
   tooltip included, bug report: "tooltip still no show after i deploy the latest code". Fixed instead
   as a single shared element appended to document.body by initNavIconTooltips() in site.js (a
   "portal", same idea as the dropdown menus/confirm modal already floating outside their trigger's own
   containing box) - position is computed fresh off the hovered icon's getBoundingClientRect() on every
   hover, so it's never subject to any ancestor's overflow clipping. This block is just the shared
   element's visual styling; showing/hiding/positioning/text all happen in JS. */
.nav-icon-tooltip {
    position: fixed;
    transform: translateY(-50%);
    /* Matches the current theme (explicit request: light tooltip in light mode) - was a fixed dark
       "inverted" label in both themes. */
    background: var(--card-bg);
    color: var(--text-dark);
    border: 1px solid var(--card-border);
    font-size: 12.5px;
    font-weight: 600;
    white-space: nowrap;
    padding: 6px 10px;
    border-radius: 6px;
    box-shadow: 0 6px 18px rgba(31, 95, 184, 0.14);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.12s ease;
    z-index: 500;
    /* display:flex (rather than the plain block/inline text this div used to just render) - explicit
       bug report: a group's own label text + its trailing .nav-icon-tooltip-arrow icon (see
       initNavIconTooltips() in site.js) were rendering on 2 visually separate rows instead of side by
       side on one, since the 2 nodes' own natural line-heights/baselines (plain text vs. an icon-font
       glyph) don't line up against each other by default in normal inline flow. A flex row with
       align-items:center vertically centers both children against each other regardless of their own
       font metrics, guaranteeing one single row every time - and is a no-op for every OTHER (plain-text-
       only) tooltip, since a single text-node child lays out identically in a flex container as it did
       in a block one. */
    display: flex;
    align-items: center;
}

html[data-theme="dark"] .nav-icon-tooltip {
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
}

.nav-icon-tooltip.nav-icon-tooltip-visible {
    opacity: 1;
    visibility: visible;
}

/* Trailing "arrow right" icon appended after a GROUP's own tooltip label (see initNavIconTooltips() in
   site.js - only .nav-group-toggle triggers get this, every plain .nav-icon-link keeps its own plain-
   text-only tooltip). Sized down from the classic Material Icons font's own 24px default to fit this
   small pill-shaped tooltip next to its 12.5px text - vertical alignment against that text is handled
   by .nav-icon-tooltip's own flex/align-items:center now (not a vertical-align nudge here), since that's
   what actually keeps the 2 on one consistent row regardless of either one's own font metrics. */
.nav-icon-tooltip-arrow {
    font-size: 15px;
    line-height: 1;
    margin-left: 3px;
    flex-shrink: 0;
}

/* ==========================================================================
   Grouped nav categories - "Purchase Order" is the first one (explicit request: redesign the left nav
   to support grouping/categorizing items, trying it on ONE category first before applying it to the
   rest of the nav). The rail's own existing visual design/behavior is UNCHANGED (explicit follow-up:
   "remain the current design of the left menu bar. Just work on the additional pop out on the right and
   the grouping tree option") - everything below is 2 new pieces layered on top of it:
     1. An inline accordion, shown only once the rail is .expanded - .nav-group-toggle is styled just
        like .nav-icon-link above it (so the collapsed rail's icon column still lines up), and
        .nav-group-list is its indented, collapsible child list (a vertical "tree" guide line down the
        left edge, same idea as the reference design this was modeled on).
     2. A floating popout, shown only while the rail is COLLAPSED - clicking a group's icon then can't
        show an inline list (no room, and the rail's own labels are already hidden), so it opens
        .nav-group-popout instead: a small floating card, portalled onto document.body (same overflow-
        clipping-avoidance reasoning as .nav-icon-tooltip above), listing the group's label as a heading
        and its children as clickable rows.
   Both interaction states are wired up by initNavGroups() in site.js - everything here is just the 2
   static shapes it shows/hides and positions. Uses the exact same var(--card-bg)/var(--text-dark)/
   var(--surface-tint)/var(--card-border) theme variables as every other themed surface in this file
   (e.g. .user-menu), so light/dark theme support is automatic - no separate dark-theme override block
   needed here, unlike .nav-icon-tooltip above (that one deliberately INVERTS relative to the page's own
   theme; this is a normal menu surface that should just MATCH it, like .user-menu does).
   ========================================================================== */

/* Mirrors .nav-icon-link's own box model (gap/padding/font) exactly, so a group's collapsed-rail icon
   sits in the exact same column as every plain .nav-icon-link icon above/below it - a <button> rather
   than an <a> since it never navigates directly (accordion toggle when expanded, popout trigger when
   collapsed), but needs to look pixel-identical to its siblings either way. align-items:flex-start (not
   center) for the same 2-line-label wrapping reason .nav-icon-link documents on itself. */
.nav-group-toggle {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    width: 100%;
    padding: 12px 16px 12px 20px;
    background: none;
    border: none;
    color: var(--text-dark);
    font-weight: 700;
    font-size: 14px;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
}

.nav-group-toggle:hover { background: var(--card-bg); }

.nav-group-toggle > .material-icons:first-child { font-size: 24px; flex-shrink: 0; }

/* Reuses .nav-label's own show-only-once-expanded behavior (see .side-nav.expanded .nav-label above) -
   this extra class only adds the flex-grow needed so the chevron after it gets pushed to the row's far
   right instead of hugging the label text. */
.nav-group-toggle-label {
    flex: 1 1 auto;
    min-width: 0;
}

/* Hidden entirely while collapsed (nothing to rotate/point at - a click opens a popout instead of an
   inline list there), shown once expanded, same display-toggle mechanism as .nav-label. Rotates 180deg
   once its own .nav-group is .open, so it visually flips from "collapsed, click to expand" (pointing
   down) to "expanded, click to collapse" (pointing up) - standard accordion chevron convention. */
.nav-group-chevron {
    display: none;
    font-size: 20px !important;
    flex-shrink: 0;
    color: var(--text-muted);
    transition: transform 0.15s ease;
}

/* Collapsed rail: the chevron must be fully hidden. The plain .nav-group-chevron rule above can lose
   to Google's own .material-icons { display: inline-block } (same specificity, loaded later), which
   left a sliver of the chevron poking out at the rail's right edge as a small "dot". */
.side-nav .nav-group-toggle .nav-group-chevron { display: none; }
.side-nav.expanded .nav-group-toggle .nav-group-chevron { display: inline-flex; }

.nav-group.open > .nav-group-toggle .nav-group-chevron { transform: rotate(180deg); }

/* Collapsed inline (both collapsed rail AND expanded-but-not-yet-opened), same "hidden until its own
   trigger says otherwise" convention as .nav-group-chevron above - only actually shown once BOTH the
   rail is .expanded AND this specific group has been clicked open (.nav-group.open), so a 2nd, 3rd, etc.
   group added later each opens/closes independently without affecting this one. Indented under where
   .nav-icon-link's own label text starts (20px icon inset + 24px icon + 14px gap = 58px), with a
   vertical guide line drawn once via ::before on the shared <ul> (not repeated per <li>) so it reads as
   one continuous line down the whole group rather than N separate short segments. */
.nav-group-list {
    display: none;
    list-style: none;
    position: relative;
    margin: 2px 0 4px 46px;
    padding: 2px 0 2px 12px;
}

.side-nav.expanded .nav-group.open > .nav-group-list { display: block; }

.nav-group-list::before {
    content: "";
    position: absolute;
    top: 4px;
    bottom: 12px;
    left: 0;
    width: 1px;
    background: var(--card-border);
}

/* Plain text row, no icon (explicit design choice, matching the reference this was modeled on - once a
   row is already indented/grouped under its own category icon, repeating an icon per child just adds
   visual noise without adding information). Slightly smaller/lighter than a top-level .nav-icon-link
   (13.5px, --text-muted at rest) since these are one level down in the hierarchy, same "children read as
   secondary to their parent" convention .nav-separator's own label already uses elsewhere in this file. */
.nav-group-item {
    display: block;
    padding: 8px 10px;
    margin: 2px 0;
    border-radius: 8px;
    color: var(--text-muted);
    font-weight: 600;
    font-size: 13.5px;
    text-decoration: none;
}

.nav-group-item:hover {
    background: var(--card-bg);
    color: var(--text-dark);
    text-decoration: none;
}

/* Floating popout for a group clicked while the rail is COLLAPSED - built/positioned by
   initNavGroups() in site.js (position: fixed, top/left set inline per click, same "measure the
   trigger's own getBoundingClientRect() and portal past any clipping ancestor" approach
   initNavIconTooltips() already uses). Sized/rounded/shadowed like .user-menu (a normal, same-theme
   dropdown menu), not like the inverted-contrast .nav-icon-tooltip above - this is a full navigational
   menu a user reads and clicks into, not a 1-line hover label. */
.nav-group-popout {
    position: fixed;
    min-width: 200px;
    max-width: 260px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 12px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.18);
    padding: 8px;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.15s ease, transform 0.15s ease;
    pointer-events: none;
    z-index: 500;
}

.nav-group-popout.open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
}

.nav-group-popout-title {
    padding: 8px 10px 6px 10px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.nav-group-popout-item {
    display: block;
    padding: 9px 10px;
    border-radius: 8px;
    color: var(--text-dark);
    font-weight: 600;
    font-size: 13.5px;
    text-decoration: none;
}

.nav-group-popout-item:hover {
    background: var(--surface-tint);
    text-decoration: none;
}

/* "+ New Claims" - the one visually highlighted (solid green) item in the rail, same idea as the
   old full-width .btn.btn-primary CTA, just icon-sized to fit the collapsed rail. Its icon sits at
   the same x-position as every .nav-icon-link's icon (20px inset + button's own left padding lines
   up once the green pill's own edge is accounted for) so the whole rail's icon column still reads
   as aligned. */
.nav-new-claim-link {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 4px 10px 12px 10px;
    padding: 10px 0 10px 10px;
    border-radius: 10px;
    background: var(--green);
    color: #fff;
    font-weight: 700;
    font-size: 14px;
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
}

.nav-new-claim-link:hover { background: var(--green-dark); color: #fff; text-decoration: none; }

.nav-new-claim-link .material-icons { font-size: 24px; flex-shrink: 0; }

/* "..." divider between the regular nav items and the admin-only icons - replaces the old
   expandable "Admin" submenu entirely (every admin page now gets its own always-clickable icon
   directly below this, no expand/collapse). Collapsed rail: shows just the dots. Expanded rail: the
   dots hide and the "Admin" section title (reusing .nav-label's own show/hide-on-expand behavior)
   takes their place instead - see .nav-label in site.css and the two spans in Site.Master. */
.nav-separator {
    text-align: center;
    padding: 8px 0;
    user-select: none;
}

.nav-separator-dots {
    color: var(--text-muted);
    font-weight: 700;
    font-size: 20px;
    letter-spacing: 3px;
}

.side-nav.expanded .nav-separator-dots { display: none; }

/* Overrides the plain inline look .nav-label gets inside a .nav-icon-link - here it reads as a
   small section heading instead, same treatment the old .side-nav-title once had. */
.nav-separator .nav-label {
    color: var(--text-muted);
    font-weight: 700;
    text-transform: uppercase;
    font-size: 12px;
    letter-spacing: 1px;
}

.nav-overlay {
    display: none;
    position: fixed;
    /* Starts from the very top of the viewport now (not just below the header) so expanding the nav
       dims/blurs .site-header too, not just the page body below it - explicit request. .site-header
       has its own lower z-index (50) than this overlay (60), so it paints underneath here, same as
       .content-area always has. The rail itself (#sideNav, including its own #btnHamburger) sits at
       z-index 70 - above this overlay - so it stays visible and clickable the whole time; closing
       the nav is done either by clicking #btnHamburger again or by clicking anywhere on this dimmed
       backdrop (see overlay's own click handler in initNav()). */
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(255,255,255,0.25);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    z-index: 60;
}

html[data-theme="dark"] .nav-overlay { background: rgba(0,0,0,0.45); }

.nav-overlay.open { display: block; }

.content-area {
    /* Reserves permanent space for the always-visible collapsed rail - the rail itself is
       position:fixed (out of normal flow) so without this, page content would render underneath it.
       Only the collapsed width is reserved; the wider .expanded state deliberately floats over this
       (via the overlay above) rather than pushing content over every time it opens. No padding or
       max-width here anymore - that's .content-inner's job now (see below), since this element's
       margin-left is a fixed rail offset rather than "auto" and can't be used to center anything. */
    margin-left: var(--nav-rail-width);
    max-width: 100%;
    overflow: visible;
}

/* Caps every page's actual content at a comfortable reading width and centers it in the space to
   the right of the rail, rather than letting it stretch full-bleed on wide monitors - explicit
   request. Below 1140px + padding, margin: 0 auto has no room to add space and this behaves exactly
   like the old full-width .content-area padding did. */
.content-inner {
    max-width: 1140px;
    margin: 0 auto;
    padding: 24px;
}

/* Same near-white canvas as the rest of the page now (matches .site-header) rather than a distinct
   blue - a border-top line is what separates it from the page above instead of a color change.
   Offset from the left by the same amount as .content-area, so the rail (which now spans the full
   viewport height, overlapping this footer's row too - see .side-nav's top:0/bottom:0) doesn't sit
   on top of / clip the copyright text. A plain copyright strip pinned to the bottom of the viewport
   (fixed, not just at the end of the page) so it's visible without scrolling down. */
.site-footer {
    position: fixed;
    left: var(--nav-rail-width);
    right: 0;
    bottom: 0;
    height: var(--footer-height);
    background: var(--page-bg);
    color: var(--text-dark);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    font-size: 12px;
    padding: 0 16px;
    border-top: 1px solid var(--card-border);
    z-index: 40;
}

/* ---------------- Cards ---------------- */

/* Elevated "floating on the page" look - a visible soft shadow rather than a border line is what
   separates the card from the page now that the page canvas itself is a near-white grey rather than
   the old, more strongly-tinted blue (see --page-bg in :root); the old thin border + faint 1px/4px
   shadow combo read as flat/blended once there was so little color contrast left to lean on. No
   border in light mode at all - the shadow alone does that job. Dark mode gets its own thin border
   back further down (html[data-theme="dark"] .card), since box-shadow reads far less clearly against
   a dark page. */
.card {
    background: var(--card-bg);
    border: none;
    border-radius: 16px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.08);
    padding: 18px;
    margin-bottom: 16px;
}

html[data-theme="dark"] .card { border: 1px solid var(--card-border); }

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 16px;
}

/* Admin/Users.aspx: tighter vertical spacing between card rows. .card-grid's own 16px gap and
   each .card's own 16px margin-bottom (see .card above) were both adding vertical space between
   rows at once - the grid gap already spaces every row on its own, so the extra margin-bottom was
   pure double-spacing here. Zeroing that margin and shrinking the grid gap gives one deliberate,
   smaller gap instead of two stacked ones. Scoped to this modifier class rather than changing
   .card/.card-grid globally, since other pages' card grids aren't affected by this request. */
.card-grid-tight { gap: 8px; }
.card-grid-tight .card { margin-bottom: 0; padding: 10px; }

/* Admin/Users.aspx "View More" toggle - mobile only (see the mobile media query below), where
   Company Code / Cost Center / Created Date / Created User start hidden to save vertical space.
   Always visible on desktop, so the toggle button itself has no reason to show there. */
.card-view-more { display: none; }

.card-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
}

.card-title { font-weight: 700; font-size: 16px; flex: 1; min-width: 0; }

/* Header ID (card-title) with the status chip stacked directly below it (NewClaims.aspx) - keeps
   a long transaction number and a long status label (e.g. "Pending Resubmission") from ever
   overlapping each other, since they're no longer forced onto the same line. */
.card-title-group {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    flex: 1;
    min-width: 0;
}

.card-title-group .card-title { flex: none; }

/* Claims Pending Approval - wraps the bulk-select checkbox and the card-title-group together in one
   <label> so clicking the checkbox, claim number, or status badge all toggle the checkbox natively. */
.card-select-group {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    flex: 1;
    min-width: 0;
    cursor: pointer;
}

.card-select-group .claim-checkbox {
    margin-top: 4px;
    flex: none;
    cursor: pointer;
}

/* The rest of the card (blank space, detail rows) is also clickable to toggle selection - see the
   card-click handler in ClaimsPendingApproval.aspx. Uses its own class rather than :has(.claim-checkbox)
   for older-browser compatibility. */
.selectable-card { cursor: pointer; }

/* Wraps the edit-pencil / delete-minus icon buttons in a card header - keeps them side by
   side on one row no matter how narrow the card gets (the title shrinks/wraps first). */
.card-actions-icons {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    white-space: nowrap;
}

.card-row {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 10px;
    padding: 4px 0;
    font-size: 13px;
    color: var(--text-muted);
}

/* The label (always the first span in a .card-row - e.g. "Rejection Reason", "Total Claim Amount
   (MYR)") never wraps to a second line, no matter how long the value next to it is - a long value
   (like a rejection reason) used to squeeze the label span down until it wrapped mid-word. Capped
   at a max-width too so one row's label can never crowd out the value column entirely. Applies to
   every .card-row across the app (My Current Claims, View All Claims, Admin cards, etc.) since they
   all share this one component - text-overflow: ellipsis is just a safety net for the one label
   long enough to actually hit the cap. */
.card-row > span:first-child {
    flex: 0 0 auto;
    max-width: 55%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.card-row .value {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--text-dark);
    font-weight: 600;
    text-align: right;
    word-break: break-word;
}

/* NewClaims.aspx / ClaimsPendingApproval.aspx "Total Foreign Amount" row - one line per distinct
   currency (see ClaimHeader.ForeignAmounts), each rendered as its own <div> by the rptForeignAmounts
   Repeater. display:inline-block + text-align:right keeps every line right-aligned under the
   "Total Foreign Amount" label same as every other .card-row value, while still stacking vertically
   (block-level <div> children) when a claim mixes more than one currency instead of running them
   together on one line. */
.foreign-amount-value { display: inline-block; text-align: right; }

/* My Current Claims card's rejection reason row (only shown for a rejected claim - which carries
   the "Pending Resubmission" status, there's no separate terminal "Rejected" status) - the value
   reads in the same red used for the "Pending Resubmission" status chip so it reads as a warning,
   not just more data. */
.card-row-rejection .value { color: #B23223; }
html[data-theme="dark"] .card-row-rejection .value { color: #F0958A; }

.card-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
}

/* Admin/ApprovalSetup.aspx - one row per approval level inside a Company+Cost Center card (see
   rptLevels in ApprovalSetup.aspx). Like .card-row but with a third segment (per-level Edit/Delete
   icons via .card-actions-icons) - .card-row itself is just a two-column label/value layout, so this
   is its own class rather than reusing .card-row directly. */
.approval-level-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 0;
    border-top: 1px solid var(--card-border);
}

.approval-level-row:first-of-type { border-top: none; padding-top: 0; }

.approval-level-label {
    flex: 0 0 auto;
    font-size: 13px;
    color: var(--text-muted);
}

.approval-level-value {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-dark);
}

/* ---------------- Status badges / icons ---------------- */

.status-badge {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 700;
    background: #EEF3F6;
    color: var(--text-dark);
}

/* Grey chip (explicit request) - distinct from every other pastel-hue status chip below, since
   "Pending Submission" (a draft that hasn't been sent for approval yet) isn't really a "state
   awaiting someone else's action" the way the others are. */
.status-pending-submission { background: #E3E6EA; color: #52606D; }
.status-pending-approval { background: #FCF3C7; color: #96790B; }
.status-pending-finance { background: #FCE4CC; color: #B15E0A; }
.status-pending-resubmission { background: #FAE1DE; color: #B23223; }
.status-approved { background: #E2F6EA; color: var(--green-dark); }
.status-rejected { background: #FAE1DE; color: #B23223; }
.status-badge-active { background: #E2F6EA; color: var(--green-dark); }

/* Orange "Super Admin" chip (Admin/Users.aspx master/detail list + detail pane's "Super Admin" field) -
   same color used for .status-pending-finance elsewhere, reused here to flag admin access rather than
   introducing a brand new hue for a single chip. */
.status-badge-admin { background: #FCE4CC; color: #B15E0A; }

/* Dark mode: same pastel-chip-with-dark-text look doesn't work against a dark page, so each status
   flips to a deeper, desaturated version of its own hue with a light text color instead - keeps every
   status visually distinct and recognizable rather than collapsing them all into one grey. */
html[data-theme="dark"] .status-badge { background: #2A343D; color: var(--text-dark); }
html[data-theme="dark"] .status-pending-submission { background: #333D47; color: #C4CDD5; }
html[data-theme="dark"] .status-pending-approval { background: #453B12; color: #E8D46B; }
html[data-theme="dark"] .status-pending-finance { background: #472F14; color: #EFAE72; }
html[data-theme="dark"] .status-pending-resubmission { background: #4A2320; color: #F0958A; }
html[data-theme="dark"] .status-approved { background: #1E3B29; color: #6FDC9E; }
html[data-theme="dark"] .status-rejected { background: #4A2320; color: #F0958A; }
html[data-theme="dark"] .status-badge-active { background: #1E3B29; color: #6FDC9E; }
html[data-theme="dark"] .status-badge-admin { background: #472F14; color: #EFAE72; }

.icon-btn {
    border: none;
    background: none;
    cursor: pointer;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    text-decoration: none;
}

.icon-btn:hover { text-decoration: none; }

.icon-btn-disabled { opacity: 0.35; cursor: not-allowed; pointer-events: none; }

.icon-btn .material-icons { font-size: 18px; }

.icon-approve { color: #fff; background: var(--green); }
.icon-approve:hover { background: var(--green-dark); }

.icon-reject { color: #fff; background: var(--red); }
.icon-reject:hover { background: #B23223; }

.icon-edit { color: #fff; background: var(--blue); }
.icon-edit:hover { background: var(--blue-dark); }

/* Users list activate/deactivate icon (Admin/Users.aspx) - yellow "person_off" for an Active user
   (clicking deactivates them), green "person" for an Inactive user (clicking reactivates them). */
.icon-deactivate { color: #fff; background: var(--yellow); }
.icon-deactivate:hover { background: #C68F2E; }

.icon-activate { color: #fff; background: var(--green); }
.icon-activate:hover { background: var(--green-dark); }

.icon-dots { color: var(--text-muted); font-size: 20px; font-weight: 900; }
.icon-dots:hover { background: var(--surface-tint); }

.dropdown-wrap { position: relative; display: inline-block; }

.dropdown-menu {
    display: none;
    position: absolute;
    right: 0;
    top: 34px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 8px;
    box-shadow: 0 4px 14px rgba(0,0,0,0.12);
    min-width: 170px;
    z-index: 20;
    overflow: hidden;
}

.dropdown-menu.open { display: block; }

.dropdown-menu a, .dropdown-menu button {
    display: block;
    width: 100%;
    text-align: left;
    padding: 10px 14px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 13px;
    color: var(--text-dark);
}

.dropdown-menu a:hover, .dropdown-menu button:hover { background: var(--surface-tint); text-decoration: none; }

/* Destructive dropdown item (e.g. My Claims' Delete option) - same red used for rejection/danger text
   elsewhere (.card-row-rejection .value, .status-rejected) so it reads consistently as the one
   irreversible-looking action in the menu. Text is red at rest (not just on hover); on hover the
   background switches to the same light red used by the error snackbar (.banner-error), text stays
   the same red. The extra "a"/"button" type selector (matching .dropdown-menu a / .dropdown-menu
   button above) is needed so this reliably beats the plain .dropdown-menu a color/hover rules instead
   of losing to them by source order. */
.dropdown-menu a.dropdown-menu-danger,
.dropdown-menu button.dropdown-menu-danger {
    color: #B23223;
}
.dropdown-menu a.dropdown-menu-danger:hover,
.dropdown-menu button.dropdown-menu-danger:hover {
    background: #FAE1DE;
    color: #B23223;
}
html[data-theme="dark"] .dropdown-menu a.dropdown-menu-danger,
html[data-theme="dark"] .dropdown-menu button.dropdown-menu-danger {
    color: #F0958A;
}
html[data-theme="dark"] .dropdown-menu a.dropdown-menu-danger:hover,
html[data-theme="dark"] .dropdown-menu button.dropdown-menu-danger:hover {
    background: #4A2320;
    color: #F0958A;
}

/* ---------------- Buttons ---------------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 10px 20px;
    border-radius: 8px;
    font-weight: 700;
    font-size: 14px;
    border: 1px solid transparent;
    cursor: pointer;
    text-decoration: none;
}

.btn:hover { text-decoration: none; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-primary { background: var(--green); color: #fff; }
.btn-primary:hover { background: var(--green-dark); }

.btn-blue { background: var(--blue); color: #fff; }
.btn-blue:hover { background: var(--blue-dark); }

/* Every "Search" button app-wide (NewClaims, ClaimsPendingApproval, Admin/Users) - shows the "Search"
   text by default; the material-icons "search" glyph is only ever shown on mobile (see the
   @media max-width:768px override below), where it hides the text instead and shows just the icon. */
.btn-search-icon-mobile-icon { display: none; }

.btn-outline {
    background: transparent;
    border: 1px solid var(--card-border);
    color: var(--text-dark);
}
.btn-outline:hover { background: var(--surface-tint); }

.btn-grey { background: var(--text-muted); color: #fff; }
.btn-grey:hover { background: var(--text-dark); }

.btn-text {
    background: none;
    border: none;
    color: var(--blue-dark);
    padding: 10px 12px;
}

.btn-danger { background: var(--red); color: #fff; }
.btn-danger:hover { background: #B23223; }

.btn-outline-success {
    background: transparent;
    border: 1.5px solid var(--green);
    color: var(--green-dark);
}
.btn-outline-success:hover { background: var(--green); color: #fff; }

.btn-plain {
    background: none;
    border: none;
    color: var(--text-dark);
    font-weight: 700;
    font-size: 14px;
    padding: 10px 12px;
    cursor: pointer;
    text-decoration: none;
}
.btn-plain:hover { text-decoration: underline; }

.btn-sm { padding: 6px 12px; font-size: 12px; }

/* ---------------- Forms ---------------- */

.form-group { margin-bottom: 12px; }

.form-label {
    display: block;
    font-weight: 700;
    margin-bottom: 6px;
    font-size: 13px;
    color: var(--text-dark);
}

.form-control {
    width: 100%;
    padding: 10px 12px;
    border-radius: 8px;
    border: 1px solid var(--card-border);
    background: #fff;
    font-size: 14px;
    color: var(--text-dark);
}

html[data-theme="dark"] .form-control { background: var(--card-bg); }

.form-control:focus { outline: 2px solid var(--blue); outline-offset: 1px; }

.form-control[readonly] { background: var(--surface-tint); color: var(--text-muted); cursor: not-allowed; }

textarea.form-control { resize: vertical; min-height: 90px; }

/* Character counter overlaid in the bottom-right corner of a textarea (e.g. the Reject Reason
   textbox in AnalyticsRejectModal - see initRejectModal() in site.js). resize:none here (scoped to
   just this wrapper, not textarea.form-control generally) so the native drag-to-resize grip never
   sits underneath/overlaps the counter text. */
.textarea-wrap { position: relative; }
.textarea-wrap textarea.form-control { resize: none; padding-bottom: 22px; }
.textarea-char-count {
    position: absolute;
    right: 10px;
    bottom: 7px;
    font-size: 11px;
    color: var(--text-muted);
    background: var(--card-bg);
    padding: 0 3px;
    border-radius: 3px;
    pointer-events: none;
}

/* Turns red once the textarea has hit its maxlength - toggled by JS (updateOkState() in
   initRejectModal() / updateDescCount() on CreateClaim.aspx) alongside the counter text itself. */
.textarea-char-count.at-max { color: #B23223; font-weight: 700; }
html[data-theme="dark"] .textarea-char-count.at-max { color: #F0958A; }

/* ---------------- Custom dropdown (replaces native <select> popup) ----------------
   The native <select> stays in the DOM (hidden) so ASP.NET postback/ViewState and
   AutoPostBack still work exactly as before - see initCustomSelects() in site.js. */

.custom-select {
    position: relative;
    width: 100%;
    min-width: 0;
}

.custom-select select.form-control {
    display: none;
}

.custom-select-trigger {
    width: 100%;
    /* Explicit request: "When user select N number of options from the dropdown, do not auto extend the
       filter width." min-width:0 overrides the flex/box default of min-width:auto, which otherwise lets
       a long comma-joined selected-values string (see .custom-select-value's own syncTrigger() call in
       site.js) force this box - and the whole .filters-bar row around it - wider than its normal
       column width instead of staying put and truncating with an ellipsis. Same "min-width:0 needed to
       actually let a nowrap-text box shrink to fit" fix pattern as .co-kpi-card's own comment
       elsewhere in this file, box-sizing:border-box makes this an accurate box once it's allowed to
       shrink. */
    min-width: 0;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 10px 12px;
    border-radius: 8px;
    border: 1px solid var(--card-border);
    background: #fff;
    font-family: inherit;
    font-size: 14px;
    color: var(--text-dark);
    cursor: pointer;
    text-align: left;
    position: relative;
}

html[data-theme="dark"] .custom-select-trigger { background: var(--card-bg); }

.custom-select-trigger:focus {
    outline: 2px solid var(--blue);
    outline-offset: 1px;
}

.custom-select.disabled .custom-select-trigger {
    background: #F2F5F7;
    color: var(--text-muted);
    cursor: not-allowed;
}

html[data-theme="dark"] .custom-select.disabled .custom-select-trigger { background: #263039; }

.custom-select-value {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.custom-select-arrow {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    border-right: 2px solid var(--text-muted);
    border-bottom: 2px solid var(--text-muted);
    transform: rotate(45deg);
    margin-top: -4px;
    transition: transform 0.15s ease;
}

.custom-select.open .custom-select-arrow {
    transform: rotate(-135deg);
    margin-top: 4px;
}

/* Hover/focus popout showing the full list of selected option labels for a multi-select filter trigger.
   Explicit request: "for all filters in all pages. When user select N number of options from the dropdown,
   do not auto extend the filter width. Instead, when user onhover on any of the filter, show a pop out of
   the options selected. Then for the onhover pop out, limit the max number of characters to 500. Anything
   else longer, truncate it and show ..." The width-auto-extend part is handled above via min-width:0 on
   .custom-select/.custom-select-trigger/.custom-select-value (same fix pattern as .co-kpi-card - see that
   rule's own comment); this block is the popout itself. buildCheckboxMultiSelect()'s syncTrigger() (site.js)
   sets/removes the data-tooltip attribute this is keyed off of (comma-joined selected option text, truncated
   to 500 chars + "..." - the truncation itself happens in JS, not here, since CSS can't truncate at a
   character count). Same pure-CSS ::after + data-tooltip approach as .chart-info-tooltip (native title=""
   was already rejected elsewhere in this app as too slow/unstylable/no-wrap-support), and reuses that same
   component's inverted dark/light color convention: this base rule (light theme active) uses the DARK
   theme's own literal colors, and the html[data-theme="dark"] override below flips to the LIGHT theme's own
   literal colors - see .chart-info-tooltip::after's own comment for the full reasoning. Anchored left:0
   (not right:0 like the chart-title icon) since filter triggers sit near the left edge of their own filter
   column, not a card's right edge. Suppressed while the dropdown itself is open (.custom-select.open rule
   further down) since the checkbox list is already showing the same information at that point. */
.custom-select-trigger[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    min-width: 220px;
    max-width: 320px;
    background: #1E2830;
    color: #E8EDF0;
    border: 1px solid #333F4A;
    font-size: 12px;
    font-weight: 400;
    line-height: 1.5;
    text-align: left;
    white-space: normal;
    padding: 10px 12px;
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.24);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.15s ease, transform 0.15s ease;
    z-index: 30;
    pointer-events: none;
}

.custom-select-trigger[data-tooltip]:hover::after,
.custom-select-trigger[data-tooltip]:focus::after {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

html[data-theme="dark"] .custom-select-trigger[data-tooltip]::after {
    background: #FFFFFF;
    color: #24313A;
    border: 1px solid #DCE7EC;
}

.custom-select.open .custom-select-trigger[data-tooltip]::after {
    display: none;
}

/* The floating box itself (border/shadow/background/position) - holds the search box and the
   scrollable option list as two stacked children. Used to live directly on .custom-select-options
   (the <ul>) before the search box was added; split out since a search <input> can't validly live
   inside a <ul> as a sibling of the <li> options. */
.custom-select-panel {
    margin: 4px 0 0 0;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    background: #fff;
    border: 1px solid var(--card-border);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.14);
    z-index: 120;
    display: none;
}

html[data-theme="dark"] .custom-select-panel { background: var(--card-bg); }

.custom-select.open .custom-select-panel {
    display: block;
}

.custom-select-search-wrap {
    padding: 8px 10px;
    border-bottom: 1px solid var(--card-border);
}

.custom-select-search {
    width: 100%;
    padding: 7px 10px;
    border: 1px solid var(--card-border);
    border-radius: 6px;
    font-family: inherit;
    font-size: 13px;
    color: var(--text-dark);
    background: #fff;
}

html[data-theme="dark"] .custom-select-search { background: var(--page-bg); }

.custom-select-search:focus {
    outline: 2px solid var(--blue);
    outline-offset: 1px;
}

/* The scrollable option list - capped to roughly 10 rows (each .custom-select-option is ~36px tall:
   9px top/bottom padding + ~18px of 14px-font line height) so a long list (Vendor Name, etc.) never
   pushes the rest of the page down; scrolling or narrowing the search above reaches anything past
   the first 10. */
.custom-select-options {
    list-style: none;
    margin: 0;
    padding: 6px 0;
    max-height: 360px;
    overflow-y: auto;
    /* Explicit request: "When i am scrolling in the filters dropdown also, it should not allow background
       page to scroll as well." Without this, once a scroll gesture reaches the top/bottom of this list
       (a long Vendor Name/Part Code list, say), the browser's default is to "chain" the rest of the
       gesture through to whatever's behind it - the page - and keep scrolling that instead.
       overscroll-behavior is the purpose-built CSS property for stopping exactly that chaining at this
       element's own edge. Unlike the "View Details" pop-out's html.modal-open lock (see .modal-overlay.
       open above), this doesn't need a JS-driven body/html lock - a dropdown isn't a modal (the rest of
       the page stays visible and clickable-to-close-this behind it), so containing the scroll right here
       is both the correct fix and the simplest one. */
    overscroll-behavior: contain;
}

.custom-select-option {
    padding: 9px 14px;
    font-size: 14px;
    color: var(--text-dark);
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.custom-select-option:hover {
    background: var(--surface-tint);
}

.custom-select-option.selected {
    background: #E4EEFB;
    color: var(--blue-dark);
    font-weight: 600;
}

html[data-theme="dark"] .custom-select-option.selected { background: #223349; color: #7FB3F5; }

/* Shown in place of the option list when a search query matches nothing - not clickable/hoverable,
   unlike a real option row. */
.custom-select-option-empty {
    padding: 9px 14px;
    font-size: 13px;
    color: var(--text-muted);
    cursor: default;
    font-style: italic;
}

.custom-select-placeholder {
    color: var(--text-muted);
}

/* Checkbox rows for multi-select dropdowns (e.g. Approver(s) on Approval Setup) - same trigger/
   panel shell as a single-select custom-select, just each row has its own checkbox and clicking
   a row toggles it instead of closing the dropdown. */
.custom-select-option-checkbox {
    display: flex;
    align-items: center;
    gap: 10px;
}

.custom-select-option-checkbox input[type="checkbox"] {
    margin: 0;
    flex-shrink: 0;
    pointer-events: none;
}

/* ---------------- Month/Year picker (replaces separate Month + Year dropdowns) ---------------- */

.month-year-picker {
    position: relative;
    width: 100%;
}

.month-year-picker input.form-control[readonly] {
    cursor: pointer;
    background: #fff;
}

html[data-theme="dark"] .month-year-picker input.form-control[readonly] { background: var(--card-bg); }

.month-year-panel {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    margin-top: 4px;
    background: #fff;
    border: 1px solid var(--card-border);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.14);
    padding: 12px;
    width: 240px;
    z-index: 130;
}

html[data-theme="dark"] .month-year-panel { background: var(--card-bg); }

.month-year-picker.open .month-year-panel {
    display: block;
}

.my-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
}

.my-year-label {
    font-weight: 600;
    font-size: 15px;
    color: var(--text-dark);
}

.my-nav-btn {
    border: none;
    background: var(--surface-tint);
    width: 28px;
    height: 28px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
    color: var(--text-dark);
    display: flex;
    align-items: center;
    justify-content: center;
}

.my-nav-btn:hover {
    background: #E4EEFB;
}

html[data-theme="dark"] .my-nav-btn { background: var(--surface-tint); }
html[data-theme="dark"] .my-nav-btn:hover { background: #223349; }

.my-month-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 6px;
    margin-bottom: 10px;
}

.my-month-btn {
    padding: 8px 0;
    border: 1px solid var(--card-border);
    border-radius: 6px;
    background: #fff;
    font-size: 13px;
    color: var(--text-dark);
    cursor: pointer;
}

html[data-theme="dark"] .my-month-btn { background: var(--card-bg); }

.my-month-btn:hover {
    background: var(--surface-tint);
}

.my-month-btn.selected {
    background: var(--blue);
    color: #fff;
    border-color: var(--blue);
    font-weight: 600;
}

.my-clear-btn {
    width: 100%;
    padding: 8px 0 0;
    border: none;
    border-top: 1px solid var(--card-border);
    background: none;
    color: var(--blue-dark);
    font-size: 13px;
    cursor: pointer;
    font-weight: 600;
}

.my-clear-btn:hover {
    text-decoration: underline;
}

/* column-gap/row-gap instead of the shorthand "gap" - keeps the 16px only as horizontal spacing
   between fields on the same line. Every .form-group already carries its own 12px margin-bottom
   (see .form-group below), which is what spaces one .form-row from the next one - a row-gap here
   too would double up with that (16+12=28px) any time a row's own fields wrap onto more than one
   line (e.g. three min-width:220px fields not fitting on one line in a narrower card), which read
   as a visibly bigger, inconsistent gap right at that one spot. Explicit request. */
.form-row {
    display: flex;
    column-gap: 16px;
    row-gap: 0;
    flex-wrap: wrap;
}

.form-row > .form-group { flex: 1; min-width: 220px; }

/* A lone field on its own row that should stay the width of one column in a 2-column form (e.g.
   Account Status on Admin/CustomerUserForm.aspx) instead of stretching across the whole card. Still
   goes full width on narrow screens via min-width + flex-wrap. */
.form-row > .form-group.form-group-half { flex: 0 1 calc(50% - 8px); }

/* Admin toggle on Admin/UserForm.aspx sits in the same .form-row as the Company Code / Cost
   Center dropdowns - .form-row stretches every .form-group to the row's full height, but a
   toggle-switch (22px) is far shorter than a dropdown (~42px), so without this it just sits flush
   at the top of that stretched space instead of level with the dropdowns beside it. */
.form-group-toggle { display: flex; flex-direction: column; }
.form-group-toggle .toggle-align-middle { flex: 1; display: flex; align-items: center; }
.form-row > .form-group-btn { flex: 0 0 auto; min-width: 0; display: flex; align-items: flex-end; }

/* Boxed panel for a short Yes/No question plus whatever appears once it's switched to Yes (Claim
   in Foreign Currency / Split Expenses on CreateClaim.aspx) - two of these sit side by side in a
   .form-row on desktop/tablet so both pictures are visible without scrolling, and stack on mobile
   like any other .form-row pair. */
.panel-box {
    background: #fff;
    border-radius: 8px;
}

html[data-theme="dark"] .panel-box { background: var(--card-bg); }

.form-row > .panel-box { flex: 1; min-width: 280px; }

/* Vertical rule between the two panel-box columns (desktop/tablet only - matches the same
   769px breakpoint the panels themselves switch to side-by-side at; see the max-width:768px
   .form-row override further down that stacks everything, divider included, on mobile). */
.panel-divider {
    display: none;
    width: 1px;
    background: var(--card-border);
    align-self: stretch;
}

@media (min-width: 769px) {
    .panel-divider { display: block; }

    /* Padding only kicks in once the two panels sit side by side - on mobile (stacked, full-width)
       that extra 16px on each side was enough to push their fixed-width inner content (the split
       search/button row, split-row amount boxes) past the panel's own edge and out beyond the
       card. */
    .panel-box { padding: 16px; }
}

/* Plain divider line - unlike .mobile-section-divider this one shows at every viewport width. */
.section-divider {
    border: none;
    border-top: 1px solid var(--card-border);
    margin: 20px 0;
}

/* "Add to Split" search box + button. Uses grid instead of the .form-row flex/wrap/min-width
   pattern used elsewhere - flex's "shrink until it wraps" behavior isn't reliable inside the
   narrower panel-box (min-width 280px minus padding leaves little room), and was letting the
   button spill past the panel's edge instead of actually staying on the row. A 2-column grid
   (search box flexible, button sized to its own content) has no wrap behavior to fight - the
   button is always in the second column, on the same row, guaranteed. */
.split-add-row {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 10px;
    align-items: end;
}

.split-add-row .split-search-group,
.split-add-row .form-group-btn {
    min-width: 0;
}

.split-add-row .btn-outline {
    padding: 8px 12px;
    font-size: 13px;
    white-space: nowrap;
}

.approver-chip {
    display: inline-flex; align-items: center; gap: 8px;
    background: #E4EEFB; color: var(--text-dark); border-radius: 999px;
    padding: 6px 8px 6px 14px; font-size: 13px; font-weight: 600;
}
html[data-theme="dark"] .approver-chip { background: #223349; }
.approver-chip .chip-remove {
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px; border-radius: 50%; background: rgba(0,0,0,0.08);
    color: var(--text-dark); font-size: 11px; text-decoration: none;
    /* Explicit request: "the x button inside the chip need to be in the center of the circle as well" -
       flex centering alone left the × glyph reading slightly off-center (its own font metrics aren't
       optically centered within a 1-line box) - pinning line-height to the circle's own height, on top
       of the flex centering already here, is what actually fixes it. */
    line-height: 18px;
}
html[data-theme="dark"] .approver-chip .chip-remove { background: rgba(255,255,255,0.12); }
.approver-chip .chip-remove:hover { background: var(--red); color: #fff; }

.pending-approver-row {
    padding: 12px; border: 1px solid var(--card-border); border-radius: 8px; margin-bottom: 12px; background: var(--surface-tint);
}
.pending-approver-row .form-group-btn { align-items: flex-end; }

.radio-group { display: flex; gap: 20px; align-items: center; }
.radio-group label { font-weight: 600; display: flex; align-items: center; gap: 6px; }

.form-actions {
    display: flex;
    gap: 12px;
    margin-top: 20px;
    justify-content: flex-end;
}

.field-hint { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
.field-error { font-size: 12px; color: var(--red); margin-top: 4px; }
.field-optional { font-size: 12px; font-weight: 400; color: var(--text-muted); }

/* ---------------- CreateClaim.aspx: two-column layout (main card + sidebar cards) ---------------- */

/* Upload sidebar (Upload Receipt/Invoice, Upload Supporting Documents) on the left, the main form
   (Company/Cost Center, Claim Type/Category/Subcategory, Description, Foreign Currency/Split
   Expenses, Save/Cancel) on the right - explicit request to swap their positions from the original
   main-left/sidebar-right layout, while keeping each one's own relative width (sidebar still the
   narrower 1fr column, main form still the wider 2fr one - just now first/left instead of second/
   right). The column order in the grid-template-columns track list below is what actually
   controls left-to-right position; .claim-form-sidebar's order:-1 (further down) is what makes it
   auto-place into that first (now 1fr) track instead of .claim-form-col-main, which stays in
   plain DOM/markup order - see the comment there. align-items: start keeps the shorter sidebar
   column from stretching to the taller main card's height. */
.claim-form-layout {
    display: grid;
    grid-template-columns: 1fr 2fr;
    gap: 20px;
    align-items: start;
}

.claim-form-main { margin-bottom: 0; }

/* Wraps the main card + the Cancel/Save row below it, so both stay together in the grid's left
   column (Cancel/Save deliberately sit outside the white card now - on the plain page background,
   not inside its padding/border - explicit request) without an extra top gap doubling up with
   .form-actions' own margin-top. */
.claim-form-col-main {
    display: flex;
    flex-direction: column;
    gap: 0;
    min-width: 0;
}

/* order:-1 (lower than .claim-form-col-main's default order:0) makes this the first grid item in
   auto-placement order regardless of markup order, so it lands in .claim-form-layout's first
   column track (now 1fr, on the left after the swap above) instead of the second. Kept at every
   width (not just the narrow/single-column breakpoint) since the sidebar is meant to render first/
   above the main form at every size now - see the mobile note further down for the single-column
   case specifically. */
.claim-form-sidebar {
    display: flex;
    flex-direction: column;
    gap: 20px;
    order: -1;
}

.claim-form-sidebar-card { margin-bottom: 0; }

/* .upload-section's normal side-by-side dropzone/file-list layout (see below) needs real width to
   avoid overflowing or squeezing below its own min-widths - the sidebar column here is much
   narrower than the old full-width card ever was, so this always stacks it vertically (dropzone,
   then file list underneath) regardless of viewport width, rather than only at the 768px mobile
   breakpoint .upload-section itself switches at. */
.claim-form-sidebar-card .upload-section { flex-direction: column; }

/* Section heading for each sidebar card ("Upload Receipt/Invoice" / "Upload Supporting Documents") -
   same label element/text as before, just styled bigger/bolder like a card title instead of a plain
   .form-label, since each one is now the header of its own card rather than one field among many. */
.sidebar-card-heading {
    display: block;
    font-size: 17px;
    font-weight: 700;
    color: var(--text-dark);
    margin-bottom: 14px;
}

@media (max-width: 900px) {
    /* Single column once the grid is this narrow - .claim-form-sidebar's order:-1 (set
       unconditionally above) already keeps the upload cards shown first, above the main form card,
       once there's only one column left for both to stack into. */
    .claim-form-layout { grid-template-columns: 1fr; }

    /* Explicit request: tighter vertical gaps on mobile, now that .claim-form-layout's gap and
       .claim-form-sidebar's gap both act as plain vertical spacing once everything stacks into one
       column (on desktop the layout's own gap is a column gap instead) - the desktop 20px reads as
       too much dead space stacked vertically. .claim-form-layout's gap here is what separates the
       upload sidebar from the main form card below it; .claim-form-sidebar's is what separates the
       two upload cards (Receipt/Invoice, Supporting Documents) from each other. */
    .claim-form-layout { gap: 12px; }
    .claim-form-sidebar { gap: 1px; }
}

/* ---------------- ChangePassword.aspx requirement list ---------------- */

/* Plain/neutral until JS (see ChangePassword.aspx's inline script) flags each <li> green once met,
   or red once Confirm Password has input and it's still unmet - see .req-met/.req-unmet-flagged. */
/* Rich text editor on Admin/Announcement.aspx - contenteditable + execCommand toolbar (bold/italic/
   underline/lists/clear formatting), no external editor library, same zero-dependency approach as
   the rest of this app. */
.rich-editor-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    position: relative;
    gap: 2px;
    padding: 6px;
    border: 1px solid var(--card-border);
    border-bottom: none;
    border-radius: 8px 8px 0 0;
    background: var(--surface-tint);
}
.rich-editor-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: none;
    border-radius: 6px;
    background: none;
    color: var(--text-dark);
    cursor: pointer;
}
.rich-editor-btn:hover { background: var(--card-bg); }
.rich-editor-btn .material-icons { font-size: 18px; }

/* Heading level dropdown - see richEditorHeadingSelect in Admin/Announcement.aspx. Plain native
   select, deliberately NOT the boxed .form-control look (this sits inline in the toolbar strip
   alongside the icon buttons, not in a form). */
.rich-editor-heading-select {
    height: 32px;
    padding: 0 6px;
    border: 1px solid var(--card-border);
    border-radius: 6px;
    background: var(--card-bg);
    color: var(--text-dark);
    font-size: 13px;
    cursor: pointer;
}

.rich-editor-toolbar-divider {
    width: 1px;
    height: 20px;
    margin: 0 4px;
    background: var(--card-border);
}

.rich-editor {
    min-height: 160px;
    padding: 10px 12px;
    border: 1px solid var(--card-border);
    border-radius: 0 0 8px 8px;
    background: #fff;
    font-size: 14px;
    color: var(--text-dark);
    overflow-y: auto;
}

html[data-theme="dark"] .rich-editor { background: var(--card-bg); }
.rich-editor:focus { outline: 2px solid var(--blue); outline-offset: -1px; }
.rich-editor ul, .rich-editor ol { margin: 4px 0; padding-left: 22px; }
.rich-editor-char-count {
    margin: 6px 0 0;
    font-size: 12px;
    color: var(--text-muted);
    text-align: right;
}
/* Over the limit (Create New Tickets' 2000-character description) - red + bold. */
.rich-editor-char-count.rich-editor-char-count-over { color: var(--red); font-weight: 700; }

/* Eye icon inside New Password / Confirm Password fields on ChangePassword.aspx - toggles the
   field between type="password"/"text" (see the password-toggle-btn JS there). Same
   position:relative + absolute-icon pattern as .split-search-wrap/.split-search-icon. */
.password-field-wrap { position: relative; }
.password-field-wrap .form-control { padding-right: 40px; }
.password-toggle-btn {
    position: absolute;
    top: 50%;
    right: 8px;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
    color: var(--text-muted);
}
.password-toggle-btn:hover { color: var(--text-dark); }
.password-toggle-btn .material-icons { font-size: 20px; }

.password-requirements-title {
    margin: 8px 0 2px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
}
.password-requirements {
    list-style: disc;
    margin: 0;
    padding: 0 0 0 18px;
    font-size: 12px;
    color: var(--text-muted);
}
.password-requirements li {
    padding: 2px 0;
    transition: color 0.15s ease;
}
.password-requirements li.req-met { color: var(--green-dark); font-weight: 600; }
.password-requirements li.req-unmet-flagged { color: var(--red); font-weight: 600; }

/* ---------------- Upload / dropzone ---------------- */

.dropzone {
    border: 2px dashed var(--card-border);
    border-radius: 12px;
    padding: 30px;
    text-align: center;
    color: var(--text-muted);
    background: #FBFDFE;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}

html[data-theme="dark"] .dropzone { background: var(--card-bg); }

.dropzone.dragover {
    border-color: var(--blue);
    background: #EEF6FF;
}

html[data-theme="dark"] .dropzone.dragover { background: #1B2E42; }

.upload-link {
    color: var(--blue-dark);
    text-decoration: underline;
    cursor: pointer;
}

/* "Take Photo" button below each dropzone - opens the device camera directly on mobile via the
   paired <input capture="environment"> (see wireCameraCapture in CreateClaim.aspx). Full width and
   sits right under the dropzone so it reads as an alternative to it, not a competing action. */
.btn-camera-capture {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    margin-top: 10px;
    padding: 10px;
    border: 1px solid var(--card-border);
    border-radius: 8px;
    background: #fff;
    color: var(--text-dark);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

html[data-theme="dark"] .btn-camera-capture { background: var(--card-bg); }

.btn-camera-capture:hover { background: #FBFDFE; border-color: var(--blue); }
html[data-theme="dark"] .btn-camera-capture:hover { background: #1B2E42; }
.btn-camera-capture .material-icons { font-size: 18px; color: var(--blue-dark); }

/* Receipt/Invoice and Supporting Documents each render as a dropzone alongside its own file list -
   side by side on desktop, stacked on narrow screens (see the 768px breakpoint below). */
.upload-section {
    display: flex;
    gap: 20px;
    align-items: flex-start;
}

.upload-dropzone-wrap { flex: 1; min-width: 220px; }
.upload-filelist-wrap { flex: 1; min-width: 200px; }
.upload-filelist-label { font-weight: 700; font-size: 13px; margin-bottom: 8px; }

/* Divider between the Upload Receipt/Invoice and Upload Supporting Documents sections - only
   shown on mobile, where the two upload sections stack directly on top of each other and need a
   visual break between them (on desktop the surrounding spacing already separates them clearly). */
.mobile-section-divider { display: none; }

.upload-list { margin-top: 14px; }

.upload-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    border: 1px solid var(--card-border);
    border-radius: 8px;
    margin-bottom: 8px;
    background: #fff;
    font-size: 13px;
}

html[data-theme="dark"] .upload-item { background: var(--card-bg); }

.upload-item .file-name { display: flex; align-items: center; gap: 8px; }

.upload-item-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }

.icon-view { color: #fff; background: var(--blue); }
.icon-view:hover { background: var(--blue-dark); }

.icon-download { color: #fff; background: var(--text-muted); }
.icon-download:hover { background: var(--text-dark); }

.upload-remove {
    display: inline-flex;
    align-items: center;
    color: var(--red);
    cursor: pointer;
    border: none;
    background: none;
    padding: 0;
}
.upload-remove .material-icons { font-size: 18px; }

/* ---------------- Split expense rows ---------------- */

.split-row {
    display: flex;
    gap: 10px;
    align-items: center;
    background: #fff;
    border-radius: 8px;
    padding: 8px 12px;
    margin-bottom: 8px;
}

html[data-theme="dark"] .split-row { background: var(--card-bg); }

/* Amount box cut down to about a quarter of its old (flex:1, fill-the-row) width - it only ever
   holds a short currency figure, no need for it to stretch the full row. */
.split-row .form-control { flex: 0 0 90px; max-width: 90px; }

/* Person name label - shrinks to fit (truncating with an ellipsis rather than wrapping) instead of
   the old fixed 160px floor, which forced the row wider than its panel whenever the panel itself
   was narrower than about 330px (e.g. inside CreateClaim.aspx's two-column layout - see
   .claim-form-layout) and pushed the delete button out past the panel's own right edge. flex:1
   claims whatever space the fixed-width amount box + delete button next to it don't need. */
.split-row-label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Search box for picking a person to split with - typing filters the list of matches below;
   clicking a match selects it, then "Add to split" adds them as a row. */
.split-search-wrap { position: relative; }

.split-search-input { padding-right: 36px; }

.split-search-icon {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    font-size: 20px;
    pointer-events: none;
}

/* Admin/Users.aspx's Username search box only - a real Clear postback control sitting in the same
   spot .split-search-icon's decorative, non-clickable magnifying glass occupies on every other
   .split-search-wrap box, styled as a small round icon button (same look/feel as .icon-btn) instead
   of a separate full button in the filter row - frees up room so Search fits on the same row as this
   box even at the master-detail list panel's narrow 30% desktop width (see .filters-bar-inline
   below). */
.split-search-clear-btn {
    position: absolute;
    right: 4px;
    top: 50%;
    transform: translateY(-50%);
    width: 26px;
    height: 26px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    text-decoration: none;
}

.split-search-clear-btn:hover { background: var(--surface-tint); text-decoration: none; }

.split-search-clear-btn .material-icons { font-size: 18px; }

.split-search-results {
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    margin-top: 4px;
    background: #fff;
    border: 1px solid var(--card-border);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.14);
    max-height: 220px;
    overflow-y: auto;
    /* Same scroll-containment fix as .custom-select-options above (same "floating scrollable list over
       the page" pattern, just for split-expense row search instead of a filter) - stops scrolling to the
       end of this list from chaining into the page behind it. */
    overscroll-behavior: contain;
    z-index: 120;
}

html[data-theme="dark"] .split-search-results { background: var(--card-bg); }

.split-search-results.open { display: block; }

.split-search-result-item {
    padding: 9px 14px;
    font-size: 14px;
    color: var(--text-dark);
    cursor: pointer;
}

.split-search-result-item:hover { background: var(--surface-tint); }

/* Company Name searchable dropdown (Admin/CustomerUserForm.aspx). Reuses .split-search-* for the box
   and floating list; the list's max-height is set in JS to exactly 10 rows. */
.company-combo .split-search-input { cursor: text; }
.company-combo-caret { pointer-events: none; }
.company-combo-item.active,
.company-combo-item.selected { background: var(--surface-tint); }
.company-combo-item.selected { font-weight: 700; }
.company-combo-empty { padding: 9px 14px; font-size: 13px; color: var(--text-muted); }

/* ---------------- Page-transition loading indicator ---------------- */
/* History, in order: a spinner, then a generic floating skeleton card (both felt disconnected from the
   app), then reusing the CURRENT page's own component skeleton right before it unloads (made the page
   being LEFT briefly impersonate its own dashboard content right before swapping to a different page,
   which read as misleading), then a thin shimmering top progress bar (worked, but explicit follow-up
   feedback: "i think change the progress indicator back to loading spinner would be better") - so this
   was a centered CSS-spun ring, shown the instant a nav link is clicked (beforeunload) and cleared once
   the destination page finishes loading (pageshow) - see initNavLoadingIndicator() in site.js. Most
   recently, the ring itself was swapped for a lottie-web animation (explicit request: "I want this
   lottie file to replace all loading spinner") - .nav-loading-spinner is now just a positioned container
   that lottie.loadAnimation() renders an SVG into, no border/animation of its own.

   Paired with a frosted-glass backdrop covering the whole page, explicit request: "so that user would not
   click on anything else on page" while a navigation is in flight - same frosted-glass treatment the
   original spinner overlay used. .nav-loading-spinner's z-index (700) is deliberately higher than
   .nav-loading-backdrop's (690) so the animation stays visibly on top of the blur rather than being dimmed
   along with everything else underneath it. */
.nav-loading-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(255, 255, 255, 0.45);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    z-index: 690;
}

/* Lighter than the app's other dark-mode frosted backdrops (e.g. .nav-overlay's rgba(0,0,0,0.45)) - explicit
   request: "in dark mode, when loading, make the frosted glass background lighter in color" - a light tint
   over the dark page reads as "frosted glass" more than a heavy black scrim does. */
html[data-theme="dark"] .nav-loading-backdrop { background: rgba(255, 255, 255, 0.15); }

.nav-loading-backdrop.open { display: block; }

/* 405px = 270px * 1.5 (explicit follow-up request: "make the lottie animation another 1.5 times bigger" -
   180px original * 1.5 * 1.5) - margin stays at exactly half the width/height so it's still centered via
   the same top:50%/left:50% + negative-margin trick. */
.nav-loading-spinner {
    display: none;
    position: fixed;
    top: 50%;
    left: 50%;
    width: 405px;
    height: 405px;
    margin: -202.5px 0 0 -202.5px;
    z-index: 700;
}

.nav-loading-spinner.open { display: block; }

/* ---------------- Skeleton loading (wave animation) ----------------
   Explicit requests: "Try with customer sales overview page, change the loading spinner to use skeleton
   loading with wave animation" (referencing MUI's Skeleton "wave" variant), then "when the above is done,
   replace every loading spinner to skeleton loading" - rolled out from Customer Sales Overview (the
   original pilot) to AccountsPayable/AccountsReceivable/CompanyOverviewNetIncome as well, all 4 sharing
   this same CSS. .skeleton-overlay covers a page's own KPI cards/charts while their own chart-click/
   range-toggle/Clear All/table-sort fetch is in flight (plain fetch() calls that never triggered any
   loading indicator before), and each page's transaction/detail table swaps its real rows for skeleton
   rows the same way (see showSkeletons()/hideSkeletons()/skeletonRowHtml() in site.js). */
.skeleton-block {
    position: relative;
    display: block;
    overflow: hidden;
    border-radius: 6px;
    background: var(--card-border);
}

.skeleton-block::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
    animation: skeleton-wave 1.6s ease-in-out infinite;
}

html[data-theme="dark"] .skeleton-block { background: rgba(255, 255, 255, 0.08); }
html[data-theme="dark"] .skeleton-block::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.14), transparent); }

@keyframes skeleton-wave {
    100% { transform: translateX(100%); }
}

/* Opaque overlay sitting on top of a card's real content (KPI value/caption or a chart canvas) while its
   own fetch is in flight - parent element needs position:relative for inset:0 to anchor correctly (see
   .co-kpi-card/.co-chart-wrap/.ap-chart-canvas-wrap below). Hidden by default; toggled via .open. */
.skeleton-overlay {
    display: none;
    position: absolute;
    inset: 0;
    background: var(--card-bg);
    z-index: 5;
    border-radius: inherit;
}

.skeleton-overlay.open { display: flex; }

.co-kpi-card { position: relative; }

/* KPI card skeleton - 2 stacked bars standing in for the label + value text, vertically centered to
   match the real .co-kpi-body layout it covers. */
.kpi-skeleton {
    flex-direction: column;
    justify-content: center;
    gap: 8px;
    padding: 0 20px;
}
.kpi-skeleton .skeleton-block:first-child { height: 11px; width: 55%; }
.kpi-skeleton .skeleton-block:last-child { height: 20px; width: 75%; }

/* Chart skeleton - one block filling the whole canvas area. */
.chart-skeleton { padding: 12px; box-sizing: border-box; }
.chart-skeleton .skeleton-block { width: 100%; height: 100%; }

/* Table skeleton rows - swapped in for tbody's real rows (see skeletonRowHtml() in site.js), not an
   absolutely-positioned overlay like the KPI/chart ones above, since the table's row count/height is
   dynamic rather than a fixed-size card. Each cell gets its own short skeleton bar so the loading state
   still roughly reads as "a table", rather than one wide bar spanning every column. */
.table-skeleton-cell { padding: 10px 12px; }
.table-skeleton-cell .skeleton-block { height: 12px; width: 100%; }

/* ---------------- Modal ---------------- */

.modal-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.45);
    z-index: 200;
    align-items: center;
    justify-content: center;
}

.modal-overlay.open { display: flex; }

/* Locks background scroll while ANY modal is open, app-wide - explicit request: "when user scroll up or
   down when the pop out is show, do not allow scrolling the background until user close the pop out." A
   fixed, full-viewport .modal-overlay alone doesn't stop the real page from scrolling underneath it - it
   has no scrollable content of its own to absorb a wheel/trackpad/touch gesture. Kept in sync with "is any
   .modal-overlay currently .open" by a single MutationObserver - see initModalBodyScrollLock() in
   site.js - rather than hooking every individual modal's own open()/close(), so this applies to every
   modal in the app (confirm dialogs, export filter popups, chart-expand modals, this page's own "View
   Details" pop-out, etc.) with no per-modal wiring, on every screen size (unlike
   body.filters-panel-active below, which is mobile-sheet-specific). Class is toggled on <html> (not just
   <body> - explicit follow-up bug report: "i still can scroll the background page up and down" after a
   body-only version of this rule) since <html>, not <body>, is the element that actually owns the
   viewport's scrollbar in standards mode here - html/body share no overflow rule of their own beyond
   overflow-x above, so body alone had nothing to actually stop scrolling. Both selectors below cover
   whichever one a given browser treats as the true scrolling box. */
html.modal-open,
html.modal-open body {
    overflow: hidden;
}

.modal-box {
    background: #fff;
    border-radius: 5px;
    padding: 28px;
    max-width: 400px;
    width: 90%;
    text-align: center;
}

html[data-theme="dark"] .modal-box { background: var(--card-bg); }

/* Wider override for content modals that aren't a short confirm-dialog prompt (currently just
   apChartExpandModal on AccountsPayable.aspx) - .modal-box's own 400px max-width is sized for a
   sentence of confirm text, not a chart. Left-aligned (unlike the centered confirm-dialog text)
   since it wraps a .card-header-row + canvas, not a paragraph. */
.modal-box-large {
    max-width: 900px;
    text-align: left;
}

/* CompanyOverviewNetIncome.aspx's own expand-chart modal (companyOverviewChartExpandModal) wants a
   wider cap than the shared .modal-box-large's 900px - explicit request: "max the pop out max width
   1200px". Scoped to this one modal (not a .modal-box-large change) so AccountsPayable.aspx/
   AccountsReceivable.aspx's own expand modals keep their existing 900px cap. On small screens,
   .modal-box's own width:90% (5% each side) isn't enough breathing room for a wide chart, so this
   widens the side gutters to 10% each side instead - explicit request: "For smaller screen size, put
   at 10% left right padding." */
.company-overview-chart-modal-box {
    max-width: 1200px;
}

@media (max-width: 768px) {
    .company-overview-chart-modal-box {
        width: 80%;
    }
}

/* Export filter popup (apExportFilterModal/arExportFilterModal) - a form, not a confirm prompt or a
   chart, so it needs its own width/alignment: left-aligned like .modal-box-large but narrower (a stack
   of filter fields doesn't need 900px), and .form-group's own margin-bottom handles field spacing
   instead of the centered-text default. */
.modal-box-form {
    max-width: 480px;
    text-align: left;
}

.modal-box-form .form-group:last-of-type { margin-bottom: 0; }

.modal-actions {
    display: flex;
    gap: 12px;
    justify-content: center;
    margin-top: 20px;
}

/* Buttons inside any modal are pill-shaped and uppercase, matching the confirm dialog design below -
   this reskins every modal's Cancel/Confirm pair the same way, including CreateClaim's Save modal. */
.modal-actions .btn {
    flex: 1;
    border-radius: 5px;
    text-transform: uppercase;
    font-weight: 700;
    letter-spacing: 0.4px;
    font-size: 13px;
    padding: 12px 22px;
}

.modal-actions .btn-outline { border: none; background: transparent; color: var(--text-dark); }
.modal-actions .btn-outline:hover { background: rgba(0,0,0,0.05); }
html[data-theme="dark"] .modal-actions .btn-outline:hover { background: rgba(255,255,255,0.08); }

/* No teal override here anymore - every positive/affirmative modal button is green now:
   - The shared confirm dialog (AnalyticsConfirm.ask in site.js) always assigns btn-confirm-green for
     its non-"red" tones, so it never renders btn-primary at all any more.
   - CreateClaim.aspx's own "Confirm & Submit Claim?" modal button is plain markup with
     class="btn btn-primary" (not routed through AnalyticsConfirm.ask) - with no override here, it
     now falls back to .btn-primary's own base color, which is already var(--green). */
.modal-actions .btn-confirm-green { background: var(--green); color: #fff; }
.modal-actions .btn-confirm-green:hover { background: var(--green-dark); }

/* ---------------- Walkthrough / guided tour ---------------- */
/* Built entirely in JS (initTour() in site.js) - no markup for this lives in Site.Master, just the
   #btnWalkthrough trigger button.
   .tour-overlay is a full-viewport fixed layer with the default (auto) pointer-events, so it
   intercepts every click across the whole screen while a tour is open - including clicks over the
   "spotlighted" target underneath, which is deliberate (the tour is look-only; clicking through to
   the real nav icon mid-tour would navigate away and abandon the tour). Sits above literally
   everything else in the app (.side-nav is the next highest at z-index 70, .modal-overlay at 200). */
.tour-overlay {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 500;
}

.tour-overlay.open { display: block; }

/* The "spotlight" itself - a transparent box whose box-shadow spreads a dark fill across the ENTIRE
   viewport except for this box's own bounds (9999px spread is just "bigger than any real screen"),
   which is what creates the dimmed-everywhere-except-here look without any clip-path/mask math.
   pointer-events:none so it never itself intercepts clicks - .tour-overlay (behind it in paint order
   but covering the same full-viewport area) is what actually catches every click, spotlighted area
   included (see the comment above). Position/size are set per-step in JS (positionSpotlight()) from
   the target element's live getBoundingClientRect(), so this tracks the target through scrolling,
   window resizes, and (via the nav rail's own expand/collapse transition) layout changes too. */
.tour-spotlight {
    position: fixed;
    border-radius: 10px;
    /* Two layered shadows, closest-to-farthest: a solid var(--blue) ring right at the box's own edge
       (this is what actually reads as "this is the highlighted thing" - relying on brightness-contrast
       against the dimmed backdrop alone wasn't enough in dark mode, since the rail/page background
       there is already dark, so a merely undimmed dark element barely stood out from the dimmed dark
       backdrop around it - explicit bug report), then the same 9999px spread used before for the
       dimmed backdrop itself. var(--blue) is themed differently light vs dark (see :root /
       html[data-theme="dark"] above) so the ring itself stays high-contrast in both modes too. */
    box-shadow: 0 0 0 3px var(--blue), 0 0 0 9999px rgba(10, 16, 20, 0.65);
    pointer-events: none;
    transition: top 0.25s ease, left 0.25s ease, width 0.25s ease, height 0.25s ease;
}

/* Dark mode's own page/card backgrounds are already fairly dark, so the plain 0.65 dim used above
   left too little brightness contrast between "dimmed" and "spotlighted" - darkens the backdrop
   further here so the (undimmed, real) highlighted element still pops clearly against it. */
html[data-theme="dark"] .tour-spotlight {
    box-shadow: 0 0 0 3px var(--blue), 0 0 0 9999px rgba(0, 0, 0, 0.82);
}

.tour-tooltip {
    position: fixed;
    z-index: 501;
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 8px 28px rgba(0,0,0,0.28);
    padding: 20px 22px;
    width: 300px;
    max-width: calc(100vw - 20px);
    transition: top 0.25s ease, left 0.25s ease;
}

html[data-theme="dark"] .tour-tooltip {
    background: var(--card-bg);
    border: 1px solid var(--card-border);
}

.tour-close {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 26px;
    height: 26px;
    border: none;
    background: none;
    cursor: pointer;
    font-size: 20px;
    line-height: 1;
    color: var(--text-muted);
    border-radius: 50%;
}

.tour-close:hover { background: var(--surface-tint); color: var(--text-dark); }

.tour-step-count {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--green);
    margin-bottom: 6px;
}

.tour-title {
    font-size: 16px;
    font-weight: 800;
    color: var(--text-dark);
    margin: 0 0 6px 0;
    padding-right: 20px;
}

.tour-desc {
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-muted);
    margin: 0;
}

.tour-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 18px;
}

/* Back/Skip/Next are deliberately smaller than this app's standard .btn (which is sized for
   full-width form actions) - a tour tooltip is a compact floating box, not a form footer. */
.tour-actions .btn {
    padding: 7px 14px;
    font-size: 13px;
    flex: none;
}

/* Skip sits apart from Back/Next (auto margin pushes it to the tooltip's left edge) rather than in
   the same tight cluster - makes it visually clear it's a distinct "leave the tour" action, not a
   third option alongside Back/Next. Swapped here from Back's original position per explicit request
   - Back now sits in the right-hand cluster directly next to Next instead. Back is still hidden via
   visibility (not display:none) on step 1 - see goToStep() in site.js - so hiding it doesn't shift
   Skip/Next's positions around between steps. */
.tour-skip-btn { margin-right: auto; }

/* Smaller footprint below 768px (same breakpoint the tour's own JS uses to switch every step to
   above/below placement - see positionTooltip() in site.js) - explicit request: the tooltip was
   taking up too much of a phone screen, leaving too little of the actual highlighted item visible
   around it. Tighter padding + smaller type on every piece of copy shrinks the box's overall height
   substantially, which matters more here than the width - the JS side already keeps this box fully
   outside the target's own vertical bounds, but a physically shorter box also just leaves more of
   the rest of the page (and the spotlighted item itself) visible and in context around it. */
@media (max-width: 768px) {
    .tour-tooltip {
        width: calc(100vw - 24px);
        padding: 14px 16px;
    }

    .tour-step-count { font-size: 10px; margin-bottom: 4px; }

    .tour-title { font-size: 14px; margin-bottom: 4px; }

    .tour-desc { font-size: 12px; line-height: 1.4; }

    .tour-actions { margin-top: 12px; }

    .tour-actions .btn { padding: 6px 12px; font-size: 12px; }

    .tour-close { top: 6px; right: 6px; width: 22px; height: 22px; font-size: 18px; }
}

/* ---------------- Document viewer modal (ViewDocuments.aspx "View" button) ---------------- */

.doc-viewer-box {
    max-width: 900px;
    width: 92%;
    height: 85vh;
    display: flex;
    flex-direction: column;
    text-align: left;
    padding: 20px;
}

.doc-viewer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
}

.doc-viewer-title {
    font-weight: 700;
    font-size: 14px;
    color: var(--text-dark);
    word-break: break-all;
}

.doc-viewer-body {
    flex: 1;
    overflow: auto;
    background: #f5f5f5;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
}

html[data-theme="dark"] .doc-viewer-body { background: var(--page-bg); }

.doc-viewer-iframe { width: 100%; height: 100%; border: none; align-self: stretch; }
.doc-viewer-close { color: var(--text-muted); width: 34px; height: 34px; border-radius: 8px; flex: 0 0 auto; }
.doc-viewer-close:hover { background: var(--surface-tint); color: var(--text-dark); }
.doc-viewer-close .material-icons { font-size: 22px; }
/* Wider/taller on big screens - PDFs need the room. */
@media (min-width: 1200px) { .doc-viewer-box { max-width: 1100px; height: 90vh; } }
@media (max-width: 768px) { .doc-viewer-box { width: 96%; height: 88vh; padding: 14px; } }

.doc-viewer-image { max-width: 100%; max-height: 100%; object-fit: contain; }

/* ---------------- Dedicated confirm dialog (replaces native window.confirm() everywhere) ---------------- */

.confirm-box { padding: 36px 30px; max-width: 440px; }

.confirm-title {
    font-size: 21px;
    font-weight: 800;
    line-height: 1.35;
    margin: 0;
}

/* Used only when AnalyticsConfirm.ask()'s inlineItem flag folds the item into the title as one
   sentence (e.g. Submit Claim's "Do you want to submit this claim "PMC..."?") - the surrounding
   sentence drops to normal weight so the bolded item inside <strong> actually stands out, instead
   of blending into .confirm-title's own already-heavy 800 weight. */
.confirm-title-inline { font-weight: 400; }
.confirm-title-inline strong { font-weight: 800; }

.confirm-item {
    font-size: 19px;
    font-weight: 400;
    line-height: 1.35;
    margin: 4px 0 0 0;
}

.confirm-qmark { font-weight: 800; }

/* List variant of .confirm-item - used when AnalyticsConfirm.ask() is passed an array of strings
   instead of a single line, e.g. to spell out every identifying field of a row being deleted.
   No bullet marker - just stacked lines. */
.confirm-item-list {
    list-style: none;
    font-size: 15px;
    font-weight: 400;
    line-height: 1.5;
    margin: 10px 0 0 0;
    padding: 0;
    color: var(--text-secondary, #555);
}

html[data-theme="dark"] .confirm-item-list { color: var(--text-muted); }

.confirm-item-list li { margin-bottom: 4px; }
.confirm-item-list li:last-child { margin-bottom: 0; }

/* ---------------- Banners ---------------- */
/* Every .banner rendered by the server (the pnlMessage/pnlError success/error flashes used
   throughout the app after a save/submit/delete, following the Post/Redirect/Get ?msg=/?err=
   pattern) is turned into a MUI-style snackbar by initSnackbars() in site.js: moved into a fixed
   top-right stack (#analyticsSnackbarContainer), given an auto-injected close (x) button, and a
   5-second countdown line along the bottom that auto-dismisses it - no per-page markup changes
   needed for any of that.
   .banner-inline opts a banner OUT of all of the above, for the couple of banners that are
   persistent page context rather than a one-time notification (MFA.aspx's dev-mode code banner,
   ViewAllClaims.aspx's rejection-reason banner) - those stay exactly where the server rendered
   them, un-timed, un-moved. */

.banner {
    padding: 12px 40px 14px 16px;
    border-radius: 8px;
    margin-bottom: 16px;
    font-size: 13px;
    position: relative;
}

.banner-info { background: #E4EEFB; color: #1F5FB8; }
.banner-error { background: #FAE1DE; color: #B23223; }
.banner-success { background: #E2F6EA; color: var(--green-dark); }

html[data-theme="dark"] .banner-info { background: #1B2E42; color: #7FB3F5; }
html[data-theme="dark"] .banner-error { background: #4A2320; color: #F0958A; }
html[data-theme="dark"] .banner-success { background: #1E3B29; color: #6FDC9E; }

.banner ul {
    margin: 6px 0 0 0;
    padding: 0 0 0 18px;
}

.banner ul li {
    margin-bottom: 2px;
}

.banner-close {
    position: absolute;
    right: 8px;
    top: 8px;
    width: 22px;
    height: 22px;
    background: none;
    border: none;
    cursor: pointer;
    font-weight: 700;
    font-size: 16px;
    line-height: 1;
    color: inherit;
    opacity: 0.65;
}

.banner-close:hover { opacity: 1; }

/* Snackbar stack - fixed just under the sticky header, top-right corner. Every toast .banner gets
   moved in here (in the order they're found in the page), stacking downward with a small gap. */
#analyticsSnackbarContainer {
    position: fixed;
    top: calc(var(--header-height) + 18px);
    right: 20px;
    z-index: 600;
    display: flex;
    flex-direction: column;
    gap: 10px;
    width: 340px;
    max-width: calc(100vw - 40px);
}

#analyticsSnackbarContainer .banner {
    margin-bottom: 0;
    box-shadow: 0 4px 16px rgba(0,0,0,0.18);
    animation: bannerSlideIn 0.3s cubic-bezier(0.2, 0.8, 0.3, 1);
}

/* Only snackbars with a countdown line (success/info - see dressUpSnackbar() in site.js) need the
   extra bottom padding to keep the text clear of it. Error snackbars have no countdown line (they
   stay until closed), so they keep .banner's plain default padding. */
#analyticsSnackbarContainer .banner.has-countdown { padding-bottom: 18px; }

/* Slides in from the top-right corner of the page (both right-to-left AND top-to-bottom at once,
   not just a straight sideways slide) so a snackbar visibly arrives from the corner it's anchored
   to, then settles into the stack. */
@keyframes bannerSlideIn {
    from { transform: translate(60px, -40px); opacity: 0; }
    to { transform: translate(0, 0); opacity: 1; }
}

/* 5-second countdown line along the very bottom of the snackbar. Starts full-width; the ".run"
   modifier (added a frame after insertion - see initSnackbars() in site.js) transitions it down to
   zero width over 5s, matching the auto-dismiss timer exactly so the line always empties right as
   the snackbar disappears. currentColor picks up whichever banner-success/error/info text color is
   already set, so the line always matches that banner's own accent. Error snackbars never get one
   (see dressUpSnackbar() in site.js) since they don't auto-dismiss. */
.banner-progress {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 3px;
    width: 100%;
    background: currentColor;
    opacity: 0.55;
    transform: scaleX(1);
    transform-origin: left;
}

.banner-progress.run {
    transition: transform 5s linear;
    transform: scaleX(0);
}

/* ---------------- Login / MFA centered card ---------------- */

.auth-wrapper {
    min-height: calc(100vh - 70px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

/* Login.aspx/MFA.aspx don't render Site.Master, so their dark mode comes from ThemeCookie (the
   theme last used in this browser, else the device's own setting) - see App_Code/ThemeCookie.cs.
   ChangePassword.aspx renders inside Site.Master (see .auth-wrapper-inline below). The dark
   .auth-card override below covers all three. */
.auth-card {
    background: #fff;
    border-radius: 14px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.10);
    padding: 34px;
    width: 100%;
    max-width: 400px;
}
html[data-theme="dark"] .auth-card {
    background: var(--card-bg);
    border: 1px solid var(--card-border);
}

/* Login / MFA: the site-wide gradient background (see "Site-wide background" near the top). */
body.auth-page { min-height: 100vh; }
/* Frosted-glass login / MFA card: see-through, blurs the gradient behind it, bright edge highlight. */
body.auth-page .auth-card {
    background: rgba(255, 255, 255, 0.28);
    border: 1px solid rgba(255, 255, 255, 0.7);
    -webkit-backdrop-filter: blur(22px) saturate(170%);
    backdrop-filter: blur(22px) saturate(170%);
    box-shadow: 0 16px 50px rgba(31, 95, 184, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.9), inset 0 -1px 0 rgba(255, 255, 255, 0.25);
}
body.auth-page .auth-card .form-control {
    background: rgba(255, 255, 255, 0.6);
    border-color: rgba(255, 255, 255, 0.8);
}
html[data-theme="dark"] body.auth-page .auth-card {
    background: rgba(30, 40, 48, 0.35);
    border: 1px solid rgba(255, 255, 255, 0.16);
    box-shadow: 0 16px 50px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.14);
}
html[data-theme="dark"] body.auth-page .auth-card .form-control {
    background: rgba(18, 24, 29, 0.5);
    border-color: rgba(255, 255, 255, 0.14);
}
/* Old browsers without blur support: fall back to a solid card so the text stays readable. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    body.auth-page .auth-card { background: #fff; }
    html[data-theme="dark"] body.auth-page .auth-card { background: var(--card-bg); }
}

.auth-card h2 {
    text-align: center;
    margin-bottom: 20px;
}
/* Logo above the login title, centred. */
.auth-logo {
    display: block;
    height: 28px;
    width: auto;
    margin: 0 auto 12px;
}
html[data-theme="dark"] .auth-logo {
    background: #fff;
    border-radius: 6px;
    padding: 3px 6px;
    box-sizing: content-box;
}

/* ChangePassword.aspx reuses the .auth-card look above (explicit request: "design it like the login
   page") but, unlike the standalone Login.aspx, is still rendered inside Site.Master with the normal
   header/nav/footer chrome around it - so it uses this lighter wrapper instead of .auth-wrapper's own
   min-height: calc(100vh - 70px), which assumes it's the only thing on the page and would otherwise
   fight with the surrounding layout. Also widens the card slightly past the login card's 400px, since
   this page's content (two password fields + a 4-line requirements list) is taller. */
.auth-wrapper-inline {
    min-height: 0;
    padding: 24px 20px 40px;
}
.auth-wrapper-inline .auth-card { max-width: 440px; }

.pin-inputs {
    display: flex;
    gap: 8px;
    justify-content: center;
    margin: 16px 0;
}

.pin-inputs input {
    width: 42px;
    height: 48px;
    text-align: center;
    font-size: 20px;
    border-radius: 8px;
    border: 1px solid var(--card-border);
}

/* ---------------- Home badges ---------------- */

/* Home page announcement banner below the welcome heading, showing the admin-authored message from
   Admin/Announcement.aspx (see AnnouncementRepository.cs). Soft blurred circle blobs + a round icon
   badge stand in for a custom illustration, styled with the app's own green accent rather than
   pulling in external artwork. Light theme (default below) is a plain card matching the rest of the
   Home page - var(--card-bg)/var(--card-border)/var(--text-dark) - dark theme keeps its own distinct
   dark navy/green gradient card with white text via the html[data-theme="dark"] override right below
   (explicit request: "cater for light theme as well" - this used to be the same hardcoded dark card
   in both themes). */
.announcement-banner {
    position: relative;
    overflow: hidden;
    border-radius: 20px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    padding: 20px 25px;
    margin: 4px 0 24px;
    color: var(--text-dark);
    /* Now that the page canvas is a near-white grey (see --page-bg in :root) rather than the old,
       more strongly-tinted blue, every Home page component gets its own drop shadow so it visibly
       lifts off the page instead of relying on a stronger color contrast to do that job alone. */
    box-shadow: 0 4px 14px rgba(0,0,0,0.12);
}
html[data-theme="dark"] .announcement-banner {
    background: linear-gradient(135deg, #101923, #1b2a3a 55%, #142a1c);
    border-color: transparent;
    color: #fff;
}
.announcement-banner::before,
.announcement-banner::after {
    content: "";
    position: absolute;
    border-radius: 50%;
    pointer-events: none;
}
.announcement-banner::before {
    width: 220px;
    height: 220px;
    top: -70px;
    left: -50px;
    background: radial-gradient(circle, rgba(46,158,91,0.35), transparent 70%);
    filter: blur(10px);
}
.announcement-banner::after {
    width: 260px;
    height: 260px;
    bottom: -100px;
    right: 100px;
    background: radial-gradient(circle, rgba(232,169,58,0.22), transparent 70%);
    filter: blur(14px);
}
.announcement-banner-inner {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
}
.announcement-banner-text { flex: 1; min-width: 0; }
.announcement-banner-label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--green);
    margin-bottom: 8px;
}
.announcement-banner-label .material-icons { font-size: 18px; }
.announcement-banner-message {
    font-size: 15px;
    line-height: 1.6;
    color: var(--text-dark);
    word-break: break-word;
}
html[data-theme="dark"] .announcement-banner-message { color: rgba(255,255,255,0.92); }
.announcement-banner-message p { margin: 0 0 8px; }
.announcement-banner-message p:last-child { margin-bottom: 0; }
.announcement-banner-message ul, .announcement-banner-message ol { margin: 0 0 8px; padding-left: 22px; }
.announcement-banner-message strong { color: var(--text-dark); }
html[data-theme="dark"] .announcement-banner-message strong { color: #fff; }
.announcement-banner-graphic {
    flex: 0 0 auto;
    width: 72px;
    height: 72px;
    border-radius: 50%;
    background: rgba(46,158,91,0.18);
    border: 1px solid rgba(46,158,91,0.4);
    display: flex;
    align-items: center;
    justify-content: center;
}
.announcement-banner-graphic .material-icons { font-size: 32px; color: var(--green); }

/* Home page module cards - one per dashboard page the current user is allowed to open (explicit
   request: "display a card like button for each of the analytics page in analytics homepage. Just
   without the count number. Use different icons for each module.") - same icon-badge/label shape as
   the eclaim PM copy Home page's .count-card, just without that card's count number/count-driven
   color (there's nothing to count here, so every card stays one plain neutral --card-bg/--card-border
   surface, themed automatically in dark mode like every other plain card in this file). auto-fill +
   minmax means however many of the 15 possible cards this user is actually granted (see
   Home.aspx.cs's BuildModuleCards) reflow into as many even columns as fit the available width, rather
   than a fixed column count leaving a half-empty last row for a user with a smaller permission set. */
.module-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
    gap: 16px;
    margin: 24px 0 0;
}

.module-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 24px 14px;
    border-radius: 20px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    box-shadow: 0 4px 14px rgba(0,0,0,0.08);
    text-decoration: none;
    text-align: center;
    color: var(--text-dark);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.module-card:hover {
    text-decoration: none;
    color: var(--text-dark);
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(0,0,0,0.14);
}

.module-card-icon-wrap {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: rgba(44,123,229,0.12);
    display: flex;
    align-items: center;
    justify-content: center;
}

.module-card-icon-wrap .material-icons { font-size: 26px; color: var(--blue); }

.module-card-label { font-size: 13.5px; font-weight: 600; line-height: 1.3; }

html[data-theme="dark"] .module-card { box-shadow: 0 4px 14px rgba(0,0,0,0.35); }
html[data-theme="dark"] .module-card:hover { box-shadow: 0 8px 20px rgba(0,0,0,0.45); }
html[data-theme="dark"] .module-card-icon-wrap { background: rgba(44,123,229,0.22); }

@media (max-width: 640px) {
    .module-card-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 12px; }
    .module-card { padding: 18px 10px; border-radius: 16px; }
    .module-card-icon-wrap { width: 46px; height: 46px; }
    .module-card-icon-wrap .material-icons { font-size: 22px; }
    .module-card-label { font-size: 12px; }
}

/* Outpatient/Hospitalisation usage stat cards on the Home page, right below the announcement banner.
   Grid (not flex) so the requested "33% each on desktop / 50% each on mobile" split is exact and
   gap-independent - 3 equal columns on desktop (Outpatient/Clinic, Hospitalisation, and Dental now
   fill all 3), 2 equal columns on mobile - see the media query below. */
.stat-card-row {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
    margin: 0 0 24px;
}
.stat-card {
    position: relative;
    overflow: hidden;
    border-radius: 20px;
    padding: 22px;
    display: flex;
    align-items: center;
    gap: 18px;
    color: #fff;
    /* Same reasoning as .announcement-banner's own shadow above - lifts the card off the now-white
       page background. */
    box-shadow: 0 4px 14px rgba(0,0,0,0.12);
}
.stat-card-green { background: linear-gradient(135deg, #1c6b45, #2E9E5B); }
.stat-card-blue { background: linear-gradient(135deg, #1b4f80, #2C7BE5); }
/* Dental - dark rose to the requested #FFB4C1 pastel pink, same two-stop diagonal gradient
   convention as the green/blue cards above. */
.stat-card-pink { background: linear-gradient(135deg, #B23A55, #FFB4C1); }

/* Decorative icon badge in the top-right corner, standing in for the illustration in the reference
   design - built from the app's own Material Icons set rather than external artwork. */
.stat-card-icon-decor {
    position: absolute;
    top: -14px;
    right: -14px;
    width: 84px;
    height: 84px;
    border-radius: 50%;
    background: rgba(255,255,255,0.12);
    display: flex;
    align-items: center;
    justify-content: center;
}
.stat-card-icon-decor .material-icons,
.stat-card-icon-decor .material-symbols-outlined { font-size: 36px; color: rgba(255,255,255,0.32); }

/* Percentage ring - conic-gradient sliced to "--pct" percent (set server-side per card, see
   Home.aspx.cs), masked into a donut via a radial-gradient mask so no SVG/background-color-matching
   trick is needed. The percentage label sits on top as an absolutely-positioned sibling. */
.stat-ring-wrap { position: relative; width: 72px; height: 72px; flex: 0 0 auto; z-index: 1; }
.stat-ring {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: conic-gradient(#fff calc(var(--pct, 0) * 1%), rgba(255,255,255,0.25) 0);
    -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 9px), #000 calc(100% - 9px));
    mask: radial-gradient(farthest-side, transparent calc(100% - 9px), #000 calc(100% - 9px));
}
.stat-ring-label {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 14px;
    z-index: 2;
}

.stat-card-text { position: relative; z-index: 1; min-width: 0; }
.stat-card-number { font-size: 22px; font-weight: 800; line-height: 1.15; margin-bottom: 2px; white-space: normal; }
.stat-card-label { font-size: 12px; color: rgba(255,255,255,0.85); }

/* Configured limit (e.g. "1,000.00 MYR limit") - only rendered server-side (Visible=false by default,
   see BindUsageStat in Home.aspx.cs) when a real limit is actually set up for this Subcategory, sits
   just below .stat-card-label. Dimmer than the label above it (0.7 vs 0.85 opacity white) since it's
   secondary/supporting info, not the card's own headline text. */
.stat-card-limit { font-size: 11px; color: rgba(255,255,255,0.7); margin-top: 1px; }

.badge-row {
    display: flex;
    /* stretch (not flex-start) so both cards match the row's tallest card - "Claims Pending
       Approval" wraps to two lines while "My Claims" doesn't, so without this the two cards end up
       different heights, each sized to just its own content. */
    align-items: stretch;
    gap: 16px;
    flex-wrap: wrap;
    margin-top: 24px;
}

/* My Claims / Claims Pending Approval stat cards - icon badge, label, then the count itself, one
   flat card whose background/icon/label/number all share a single color (green/yellow/red - see
   .count-card.count-zero etc. below), rather than the old plain-page-background layout with just a
   colored number circle. Card-level "color" is set once and every child inherits it via
   color: inherit, so recoloring the whole card for a different count is a one-property change. */
.count-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Centers the icon/label/number as a group within whatever height .badge-row's "stretch" above
       ends up giving this card, so a shorter one-line label doesn't leave its content sitting flush
       at the top with empty space below - both cards' content stays vertically centered regardless
       of which one is taller. */
    justify-content: center;
    gap: 8px;
    width: 160px;
    padding: 24px 16px;
    border-radius: 20px;
    text-decoration: none;
    text-align: center;
    /* Same reasoning as .announcement-banner/.stat-card's own shadow - lifts the card off the now-
       white page background. */
    box-shadow: 0 4px 14px rgba(0,0,0,0.12);
}

.count-card:hover { text-decoration: none; opacity: 0.92; }

.count-card-icon-wrap {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.count-card-icon-wrap .material-icons { font-size: 26px; color: inherit; }

.count-card-label { font-size: 14px; font-weight: 600; color: inherit; }

.count-card-number { font-size: 30px; font-weight: 800; color: inherit; line-height: 1; }

/* Card color reflects the count: 0 = green (all clear), 1-4 = yellow (some to attend to), 5+ = red
   (needs attention soon) - same pastel-bg/dark-text pairing already used for status badges
   elsewhere (.status-approved/.status-pending-approval/.status-rejected), reused here rather than
   inventing a new palette so the "what does this color mean" association stays consistent app-wide.
   The icon badge circle behind the icon is a tinted-up version of the same color, not a flat fill -
   plain rgba() per variant rather than color-mix()/currentColor-with-alpha, since older WebViews some
   users may still be on don't support those. */
.count-card.count-zero { background: #E2F6EA; color: var(--green-dark); }
.count-card.count-zero .count-card-icon-wrap { background: rgba(46,158,91,0.18); }

.count-card.count-low { background: #FCF3C7; color: #96790B; }
.count-card.count-low .count-card-icon-wrap { background: rgba(232,169,58,0.25); }

.count-card.count-high { background: #FAE1DE; color: #B23223; }
.count-card.count-high .count-card-icon-wrap { background: rgba(216,65,47,0.18); }

/* Dark mode: same deeper-bg/lighter-text flip as the status badges' own dark overrides. The icon
   badge circle stays a plain white-on-dark overlay in every color variant (matching the stat cards'
   own .stat-card-icon-decor treatment) rather than three separate tinted versions - simpler, and
   reads fine against any of the three darker card backgrounds. */
html[data-theme="dark"] .count-card.count-zero { background: #1E3B29; color: #6FDC9E; }
html[data-theme="dark"] .count-card.count-low { background: #453B12; color: #E8D46B; }
html[data-theme="dark"] .count-card.count-high { background: #4A2320; color: #F0958A; }
html[data-theme="dark"] .count-card .count-card-icon-wrap { background: rgba(255,255,255,0.14); }

/* ---------------- Accordion (Admin/ApprovalSetup.aspx: Company Code -> Cost Center groups) ---------------- */
/* Two levels: .accordion-group is one Company Code (outer, card-styled); nested inside its
   .accordion-body, .accordion-subgroup is one Cost Center within that company. Both levels toggle
   the same way - a header button carrying [data-accordion-toggle] flips the "open" class on its
   closest .accordion-group/.accordion-subgroup ancestor - see initAccordions() in site.js. Both
   start with "open" already in the markup so nothing is hidden by default. */

/* Same elevated look as .card above (soft shadow, no border in light mode) - kept as its own rule
   since .accordion-group has overflow:hidden and no padding of its own (its header/body sections
   pad themselves), unlike .card. */
.accordion-group {
    background: var(--card-bg);
    border: none;
    border-radius: 16px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.08);
    margin-bottom: 16px;
    overflow: hidden;
}

html[data-theme="dark"] .accordion-group { border: 1px solid var(--card-border); }

.accordion-header, .accordion-subheader {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    background: none;
    border: none;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    color: inherit;
}

.accordion-header { padding: 16px 18px; }
/* Deliberately NOT var(--page-bg) - the accordion sits on top of that same page background, so
   using it here would make hover look like no feedback at all. Plain neutral grey instead of the
   app's blue accent, so hovering a group doesn't read as a link/active-state color. */
.accordion-header:hover { background: #E7E7E7; }
html[data-theme="dark"] .accordion-header:hover { background: #2A343D; }

.accordion-title { font-weight: 700; font-size: 15px; color: var(--text-dark); }

/* Google Material Icons' keyboard_arrow_down glyph rotates 180deg to point up once expanded,
   instead of swapping to a separate keyboard_arrow_up glyph - same approach as .card-view-more-icon. */
.accordion-chevron {
    font-size: 22px;
    color: var(--text-muted);
    transition: transform 0.2s ease;
    flex: none;
}

.accordion-group.open > .accordion-header .accordion-chevron,
.accordion-subgroup.open > .accordion-subheader .accordion-chevron {
    transform: rotate(180deg);
}

.accordion-body { display: none; border-top: 1px solid var(--card-border); }
.accordion-group.open > .accordion-body { display: block; }

.accordion-subgroup { border-bottom: 1px solid var(--card-border); }
.accordion-subgroup:last-child { border-bottom: none; }

/* Indented + tinted so nested Cost Center rows read as one level down from their Company Code.
   Neutral grey rather than var(--page-bg) - the whole accordion already sits on that page
   background, so anything "inside" using the same color would visually disappear into it. */
.accordion-subheader {
    padding: 12px 18px 12px 34px;
    background: #EEF1F4;
}
.accordion-subheader:hover { background: #DEE4E9; }
.accordion-subheader .accordion-title { font-weight: 600; font-size: 14px; color: var(--text-muted); }

html[data-theme="dark"] .accordion-subheader { background: #232D36; }
html[data-theme="dark"] .accordion-subheader:hover { background: #2A343D; }

.accordion-subbody { display: none; padding: 14px 18px 18px 34px; background: #F7F9FA; }
html[data-theme="dark"] .accordion-subbody { background: #1B2530; }
.accordion-subgroup.open > .accordion-subbody { display: block; }

/* ---------------- Tables (Admin) ---------------- */

/* Admin/Inquiry.aspx's Download XLSX/PDF buttons - right-aligned row directly above the results
   table, wraps onto its own line rather than overflowing on a narrow screen (no page-specific mobile
   override needed - flex-wrap handles it same as any other button pair in the app). */
.inquiry-export-row {
    display: flex;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 12px;
}

.table-wrap { overflow-x: auto; }

table.data-table {
    width: 100%;
    border-collapse: collapse;
    background: #fff;
}

html[data-theme="dark"] table.data-table { background: var(--card-bg); }

table.data-table th, table.data-table td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--card-border);
    text-align: left;
    font-size: 13px;
}

table.data-table th {
    background: #F5FAFC;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    font-size: 11px;
    text-align: left;
}

html[data-theme="dark"] table.data-table th { background: #232D36; }

/* Sortable column headers (AccountsPayable.aspx's Transaction Details table - see
   initApTransactionsTable() in site.js). Each sortable header's text is a plain <button> (AJAX sort,
   no postback - see AccountsPayableTransactions.ashx), so browser default button chrome (border,
   background, padding, font) is reset here to look like the rest of the (non-link) header text
   instead. The up/down arrow is a ::after glyph driven purely by the "col-sort-asc"/"col-sort-desc"
   class initApTransactionsTable() sets - only ever one or the other, only on whichever single header
   is currently active. */
table.data-table th .data-table-sort-link {
    display: flex;
    width: 100%;
    justify-content: flex-start;
    align-items: center;
    gap: 4px;
    border: none;
    background: none;
    margin: 0;
    padding: 0;
    font: inherit;
    text-align: left;
    text-transform: inherit;
    color: inherit;
    text-decoration: none;
    cursor: pointer;
}

table.data-table th .data-table-sort-link:hover { text-decoration: underline; }

table.data-table th .data-table-sort-link.col-sort-asc::after,
table.data-table th .data-table-sort-link.col-sort-desc::after {
    font-size: 9px;
    line-height: 1;
}

table.data-table th .data-table-sort-link.col-sort-asc::after { content: "\25B2"; }
table.data-table th .data-table-sort-link.col-sort-desc::after { content: "\25BC"; }

.toggle-switch { position: relative; display: inline-block; width: 42px; height: 22px; }
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.toggle-slider {
    position: absolute; cursor: pointer; inset: 0;
    background-color: #ccc; transition: 0.2s; border-radius: 22px;
}
.toggle-slider:before {
    position: absolute; content: ""; height: 16px; width: 16px; left: 3px; bottom: 3px;
    background-color: white; transition: 0.2s; border-radius: 50%;
}
.toggle-switch input:checked + .toggle-slider { background-color: var(--green); }
.toggle-switch input:checked + .toggle-slider:before { transform: translateX(20px); }

/* ---------------- Filters toolbar ---------------- */

.filters-bar {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    align-items: flex-end;
    margin-bottom: 18px;
}

/* max-width added alongside the existing min-width: without it, a flex item's hypothetical main size
   (used both for flex-wrap's line-breaking decision AND as its final width whenever the line has room)
   is based on its content's natural width - normally harmless since placeholder text like "All Company
   Names" is short, but a multi-select trigger's joined "selected option 1, selected option 2, ..." text
   (see buildCheckboxMultiSelect()'s syncTrigger() in site.js) can be arbitrarily long, and flex-wrap
   simply wraps the OTHER filters onto a new line rather than shrinking this one, since shrinking is
   only invoked when a line's items don't already fit - so the box visibly grew with every extra
   selection instead of staying a fixed size. Per the flexbox spec, max-width clamps the hypothetical
   main size itself (not just the final rendered size), so this box now has a real ceiling regardless of
   how much text is inside it; .custom-select-value's own overflow:hidden/text-overflow:ellipsis (further
   down) then truncates whatever doesn't fit, and the full text is still available via its hover popout
   (.custom-select-trigger[data-tooltip]::after, also further down). Explicit request: "for all filters
   in all pages... do not auto extend the filter width... the width should stays the same." */
.filters-bar .form-group { margin-bottom: 0; min-width: 160px; max-width: 240px; }

/* Wider than the default 160px filter min-width so long status strings (e.g. "Pending
   Resubmission") show in full in the dropdown trigger instead of being ellipsis-truncated. */
.filters-bar .filter-status-group { min-width: 230px; }

/* Admin/Users.aspx's filter bar only (its one Username field + Search button) - keeps Search on
   the same row as the search box at any width, not just true mobile viewports, since this list panel
   is just 30% of the page even on desktop (see .master-detail-list below) and would otherwise wrap
   under the base .filters-bar flex-wrap:wrap rule above well before an actual mobile breakpoint. */
.filters-bar.filters-bar-inline { flex-wrap: nowrap; }
.filters-bar.filters-bar-inline .form-group { min-width: 0; }
.filters-bar.filters-bar-inline .form-group:first-child { flex: 1 1 auto; }
.filters-bar.filters-bar-inline .filter-buttons-group { flex: 0 0 auto; width: auto; }

/* Month/Year picker takes a fixed 30% share of the filter row on desktop instead of sizing to its
   own content - keeps it from crowding out the Employee Name search box next to it, especially now
   that its display text is the short "MMM YYYY" form and doesn't need much room. Overridden on
   mobile (see the flex: 1 1 45% rule in the mobile media query below), where it instead shares a
   row with Claim Status. */
/* max-width caps it at just enough to fit "MMM YYYY" (e.g. "Sep 2026", the longest it ever
   displays - see the monthAbbr list in site.js) - the 30% flex-basis alone made it far wider than
   the text needed on a normal desktop-width filter bar. Shared class, so this applies to every page
   with a Month/Year filter (NewClaims, ClaimsPendingApproval, ...), not just one. */
.filters-bar .filter-monthyear-group { flex: 0 1 30%; min-width: 0; max-width: 150px; }

/* Clear button inside any filter bar never shows its outline border - scoped to .filters-bar
   specifically (not .btn-outline generally) since that class is also reused for unrelated buttons
   elsewhere (Cancel, Remove, +Add New Level, ...) that should keep their border. Back buttons have
   their border removed too, but via the dedicated .btn-back class below instead - not lumped in
   with this .filters-bar rule since Back links live outside any filter bar. */
.filters-bar .btn-outline { border: none; }

/* Every "Back" / "Back to X" link across the app (Admin/UserForm) - scoped to this class
   specifically, not .btn-outline generally, so unrelated outline buttons keep their border. */
.btn-back { border: none; }

/* Mobile-only "Filters" bottom sheet + floating trigger button (opt-in via .filters-bar-mobile-sheet -
   see the markup comment on AccountsPayable.aspx's filter bar and initMobileFilterPanel() in site.js,
   which builds all these pieces once per page). Hidden/inert here by default (not display:none on
   .filters-bar-mobile-sheet itself - see the @media block below for why); each needs its own explicit
   "off" state outside mobile, since the sheet's own bottom-half positioning only exists inside that
   media query to begin with.
   Explicit request: only the Back button should stay pinned in place, the title/subtitle need to
   scroll away with everything else below them. Deliberately NOT built with position:sticky (tried
   twice, broke in a way sticky-plus-nested-overflow-plus-fixed-ancestor edge cases are notorious for -
   see the removed comments in git history) - .filters-panel-backbar is a plain, non-scrolling flex
   sibling of .filters-panel-scroll instead, sitting above it. 2 ordinary flex boxes stacked vertically
   is a completely unambiguous layout with no containing-block/stacking edge cases to get wrong. */
.filters-panel-backbar { display: none; }
.filters-panel-title { display: none; }
.filters-panel-subtitle { display: none; }
.filters-panel-backdrop { display: none; }
.filter-fab { display: none; }

/* Mobile-only "scroll to top" floating button (explicit request) - see the markup comment on this
   same button in Site.Master and initScrollToTopButton() in site.js, which toggles .visible based on
   scroll position and handles the click-to-scroll-up behavior. Off by default outside mobile, same
   "explicit off state, not just relying on the media query below" reasoning as .filter-fab above -
   this button isn't opt-in per page like that one (it's rendered once, app-wide, in Site.Master), so
   without this rule it would otherwise be a plain unstyled inline button sitting at the bottom of
   every page's markup on desktop/tablet. */
.scroll-top-fab { display: none; }

/* Bug fix: initMobileFilterPanel() (site.js) always moves every filter field into this wrapper,
   regardless of viewport width - it was only ever given real layout CSS inside the mobile media query
   below, so on desktop/tablet it was rendering as a plain, unstyled block <div>, which broke the
   original horizontal filters-bar row into a vertical stack (explicit bug report - "for the filters in
   desktop and tablet view, display the filters like last time, don't change it"). display:contents
   makes this wrapper's own box disappear from layout entirely, so its children - the form-groups + the
   button group - act as direct children of .filters-bar again for flex purposes, restoring the
   original desktop/tablet row/wrap layout exactly as it was. The mobile media query below overrides
   this back to a real display:flex box, since that's where this wrapper actually needs to exist as its
   own scrollable element. */
.filters-panel-scroll { display: contents; }

.page-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 18px;
}

/* h1's own default margin-bottom (12px, no margin-top - see the shared h1/h2/h3/h4 rule) makes its
   margin box taller below than above, so align-items:center (which centers each flex item's full
   margin box) visually shifts the title upward relative to a plain sibling button that has no
   margin of its own - most noticeable with the small square "+" button on My Claims' mobile view.
   Zeroing it here keeps the title's own text vertically centered against whatever sits beside it. */
.page-header-row h1 { margin: 0; }

/* CreateClaim.aspx's title + its own #btnFormWalkthrough tour trigger - keeps the two sitting flush
   together as one group on the left of .page-header-row, so adding this button doesn't disturb
   .page-header-row's own space-between (which would otherwise land a 3rd flex child somewhere in the
   middle of the row instead of right next to the title - see the markup comment in CreateClaim.aspx). */
.page-header-title-group {
    display: flex;
    align-items: center;
    gap: 4px;
}

/* Title + "Data as of ..." cache badge (below) sit as one flush group on the left of .page-header-row,
   same "keeps the two sitting flush together, doesn't disturb .page-header-row's own space-between"
   reasoning as .page-header-title-group above, just with a wider gap (14px vs that class's own 4px) - a
   small text pill reads better with some breathing room next to the title than an icon button does. Used
   on every DashboardCache&lt;T&gt;-backed page (originally added for InventorySummary.aspx only, then
   broadened app-wide - explicit request: "this is from inventory summary page 'Data as of ...'. Display
   this in all pages" - see the RunSearch()/RunLiveQueries() comment on each page's own code-behind for how
   CachedAt gets populated). */
.page-cache-badge-group {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
}

/* A small muted pill showing when the data currently on screen was actually queried - "Data as of ...".
   Every DashboardCache&lt;T&gt;-backed page's own RunLiveQueries()-equivalent stamps CachedAt =
   DateTime.Now on every snapshot it returns (not just DashboardCache&lt;T&gt;.Store()/RefreshInBackground(),
   which already did this for the cached default view), so this always reflects "when THIS response's
   numbers were fetched" whether that came from today's cache or a live/filtered query just now. */
.page-cache-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-muted);
    background: var(--surface-tint);
    border-radius: 999px;
    padding: 5px 12px 5px 10px;
}

.page-cache-badge .material-icons { font-size: 14px; line-height: 1; }

/* Same circular icon-button treatment as the header's own .theme-toggle-btn/#btnWalkthrough, just a
   touch smaller (28px vs 34px) to sit comfortably inline with the page title's own text size rather
   than the taller header bar it's normally paired with. */
.page-header-tour-btn {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: none;
    background: transparent;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 0;
    color: var(--text-muted);
    flex-shrink: 0;
}

.page-header-tour-btn:hover { background: var(--surface-tint); color: var(--text-dark); }

.page-header-tour-btn .material-icons { font-size: 20px; line-height: 1; }

/* Tighter gap under the header specifically on pages where a .filters-bar sits directly below it
   (Admin/Users) - the default 18px reads as too much empty space between the title and the filter
   row on those pages. Applied via a
   modifier class rather than an adjacent-sibling selector since a hidden success/error banner Panel
   can sit between the two in markup. */
.page-header-row-tight { margin-bottom: 8px; }

/* ClaimsPendingApprovalDetail.aspx's header actions: Back on its own line, with the Approve/Reject
   tick/X icon buttons stacked below it rather than side by side. */
.header-actions-stack {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 10px;
}

/* ViewAllClaims.aspx: claim number and its status chip sit side by side (not stacked) - wraps to
   its own line on narrow screens rather than ever overlapping the title text. */
.viewall-title-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
}

/* Back button and the tick/X status icon both stay pinned to the right side of the page - grouped
   together so the icon doesn't need its own row (unlike .header-actions-stack above, which stacks
   because its icons are real Approve/Reject actions, not a static status indicator). */
.viewall-header-actions {
    display: flex;
    align-items: center;
    gap: 10px;
}

.reset-link {
    font-weight: 600;
    font-size: 14px;
    color: var(--text-dark);
    text-decoration: underline;
}
.reset-link:hover { color: var(--blue-dark); }

/* ---------------- Pagination bar (bottom of every listing page - see Controls/Pagination.ascx) ---------------- */

.pagination-bar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 28px;
    padding: 16px 4px 4px;
    flex-wrap: wrap;
}

.pagination-rows {
    display: flex;
    align-items: center;
    gap: 8px;
}

.pagination-rows-label {
    font-size: 13px;
    color: var(--text-muted);
}

/* Plain native <select>, deliberately NOT the boxed .form-control/.custom-select look used
   elsewhere - this one reads as inline text with a small dropdown affordance (just "10" plus a tiny
   arrow, no border/background), matching the reference design. The arrow is a CSS border-triangle
   wrapped around the select (same trick as .custom-select-arrow above) instead of a background-image,
   so it needs no extra asset and follows dark-mode text-muted automatically. */
.pagination-rows-select-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.pagination-rows-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background: none;
    border: none;
    color: var(--text-dark);
    font-size: 13px;
    font-weight: 700;
    padding: 4px 20px 4px 4px;
    cursor: pointer;
}

.pagination-rows-select:focus { outline: none; }

.pagination-rows-select-wrap::after {
    content: "";
    position: absolute;
    right: 4px;
    top: 50%;
    width: 6px;
    height: 6px;
    border-right: 2px solid var(--text-muted);
    border-bottom: 2px solid var(--text-muted);
    transform: translateY(-70%) rotate(45deg);
    pointer-events: none;
}

.pagination-range {
    font-size: 13px;
    color: var(--text-muted);
    white-space: nowrap;
}

.pagination-nav {
    display: flex;
    align-items: center;
    gap: 2px;
}

/* Plain grey chevrons (not one of the filled color .icon-btn variants like .icon-approve/.icon-edit
   used elsewhere) - these are page-navigation controls, not record actions, so they stay neutral. */
.pagination-nav-btn { color: var(--text-muted); }
.pagination-nav-btn:hover { background: var(--surface-tint); }
.pagination-nav-btn .material-icons { font-size: 20px; }

/* Same neutral-grey treatment as .pagination-nav-btn, reused for the chart card's header buttons
   (3-dot menu / expand / modal close on AccountsPayable.aspx - see initApChartExpand() in site.js) -
   these are chart controls, not record actions, so they stay grey rather than one of the filled
   color .icon-btn variants. Covers both the classic .material-icons font (more_vert, close) and the
   newer .material-symbols-outlined font (expand_content - not in the classic set). */
.chart-icon-btn { color: var(--text-muted); }
.chart-icon-btn:hover { background: var(--surface-tint); }
.chart-icon-btn .material-icons,
.chart-icon-btn .material-symbols-outlined { font-size: 20px; }

/* Generic "info icon + hover tooltip" component - explicit request: "put the tooltips right at the end of
   the chart title. For example: 'Outstanding Requisitions - Aging [i]'" (OutstandingRequisitionOverview.aspx's
   "Outstanding Requisitions - Aging" chart). Drop <span class="chart-info-tooltip" tabindex="0"
   data-tooltip="...explanation..."><span class="material-icons">info</span></span> NESTED INSIDE a
   .card-title span, right after its text (not as a sibling of .card-title within .card-header-row - a
   sibling would get pushed to the row's far-right edge by .card-title's own flex:1 +
   .card-header-row's justify-content:space-between, which reads as "at the end of the ROW", not "at the
   end of the TITLE" as asked here). vertical-align:middle lines the icon up with the title text's
   baseline since .card-title itself stays a plain inline span. Pure-CSS tooltip (no JS/site.js changes
   needed) via a ::after bubble shown on :hover/:focus (:focus so it's reachable by keyboard Tab, not just
   a mouse) - not a native title="" attribute, since native tooltips are slow to appear, can't be styled to
   match the app's look, and don't wrap long formula explanations cleanly. Reusable on any other chart in
   the app that wants the same "explain the formula on hover" treatment later. */
.chart-info-tooltip {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    color: var(--text-muted);
    cursor: help;
    vertical-align: middle;
    margin-left: 6px;
}
.chart-info-tooltip:hover,
.chart-info-tooltip:focus {
    color: var(--text-dark);
    outline: none;
}
.chart-info-tooltip .material-icons { font-size: 16px; }

.chart-info-tooltip::after {
    content: attr(data-tooltip);
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    width: 260px;
    max-width: 70vw;
    /* Explicit request: "cater for both dark theme and light theme. If user is in light theme, then the
       tooltips pop out use dark theme background. If user is in dark theme, then the tooltips pop out use
       light theme background." Inverted-contrast tooltip: this base rule (applies when light theme is
       active, i.e. no html[data-theme="dark"] on <html>) uses the DARK theme's own literal colors
       (matching :root[data-theme="dark"]'s --card-bg/--text-dark/--card-border values below) - the
       html[data-theme="dark"] override further down flips these to the LIGHT theme's own literal colors
       instead. Hardcoded literal values rather than var(--card-bg)/var(--text-dark) themselves, since
       those variables track the CURRENT theme - using them here would make the tooltip match its own
       page's theme, which is the opposite of what's wanted. */
    background: #1E2830;
    color: #E8EDF0;
    border: 1px solid #333F4A;
    font-size: 12px;
    font-weight: 400;
    line-height: 1.5;
    text-align: left;
    white-space: normal;
    padding: 10px 12px;
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.24);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.15s ease, transform 0.15s ease;
    z-index: 30;
    pointer-events: none;
}
.chart-info-tooltip:hover::after,
.chart-info-tooltip:focus::after {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

/* Dark theme active - flip the tooltip to the LIGHT theme's own literal colors (matching :root's own
   --card-bg/--text-dark/--card-border above), inverse of the base rule's dark-theme colors. See that
   rule's own comment for the full "why hardcoded literals, not variables" reasoning. */
html[data-theme="dark"] .chart-info-tooltip::after {
    background: #FFFFFF;
    color: #24313A;
    border: 1px solid #DCE7EC;
}

/* Segmented All/5Y/10Y year-range toggle - sits to the left of a chart's expand button inside
   .card-actions-icons (see CompanyOverviewNetIncome.aspx's "Total Profits by Year" card). Same
   neutral/grey-first visual weight as .chart-icon-btn, just grouped into a single bordered pill
   instead of separate circular icon buttons, since these are 3 mutually-exclusive text buttons
   rather than icons. Sized to sit comfortably next to the 30px .icon-btn/.chart-icon-btn circle
   next to it. See initCompanyOverviewChartRangeToggle() in site.js for the click wiring. */
.chart-range-toggle {
    display: flex;
    align-items: center;
    border: 1px solid var(--card-border);
    border-radius: 7px;
    overflow: hidden;
}

.chart-range-btn {
    border: none;
    background: transparent;
    color: var(--text-muted);
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    padding: 7px 10px;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.chart-range-btn + .chart-range-btn { border-left: 1px solid var(--card-border); }

.chart-range-btn:hover { background: var(--surface-tint); }

.chart-range-btn-active,
.chart-range-btn-active:hover {
    background: var(--blue);
    color: #fff;
}

@media (max-width: 768px) {
    .pagination-bar { justify-content: center; gap: 16px; }
}

/* Year-count dropdown for the "Periods by Total Profits" chart (CompanyOverviewNetIncome.aspx) - explicit
   request: "put a dropdown for the year. All, 2 Years, 3 Years, 5 Years" replacing the other 2 charts'
   .chart-range-toggle button group for this one chart. Sized/bordered to sit at the same visual weight as
   .chart-range-toggle/.chart-icon-btn next to it inside .card-actions-icons, rather than a full-width
   .form-control. See initCompanyOverviewPeriodRangeSelect() in site.js. */
.chart-period-range-select {
    border: 1px solid var(--card-border);
    border-radius: 7px;
    background: var(--card-bg);
    color: var(--text-dark);
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    padding: 7px 24px 7px 10px;
    cursor: pointer;
    height: auto;
}

.empty-state {
    text-align: center;
    color: var(--text-muted);
    padding: 40px 20px;
}

/* ---------------- Admin/Users.aspx master/detail layout ---------------- */

/* Contact-list style split (same design as the eclaim PM app's own Admin/Users.aspx) - a searchable
   list of users on the left (name + Account Status only) and every field for whichever user is
   selected on the right. 30%/70% desktop split via flex-basis (not CSS grid - the two sides don't
   need to line up in rows against each other the way a grid's cells would). Below 768px this
   collapses to a single column and the right side becomes a fixed slide-over instead - see the
   mobile override further down. */
.master-detail {
    display: flex;
    align-items: flex-start;
    gap: 20px;
}

.master-detail-list {
    flex: 0 0 30%;
    min-width: 0;
}

/* Right-hand 70% column - wraps .master-detail-panel (see Admin/Users.aspx). Carries the
   flex-basis that used to live on .master-detail-panel itself. */
.master-detail-right {
    flex: 1 1 70%;
    min-width: 0;
}

.master-detail-panel {
    background: var(--card-bg);
    border-radius: 16px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.08);
    padding: 24px;
}

html[data-theme="dark"] .master-detail-panel { border: 1px solid var(--card-border); }

/* Desktop only - see the mobile override below, where this becomes the actual close affordance for
   the slide-over. Hidden here since there's nothing to "go back" to on a wide screen (the list is
   already sitting right next to this panel the whole time). */
.master-detail-back-btn { display: none; }

.master-detail-empty {
    text-align: center;
    color: var(--text-muted);
    padding: 60px 20px;
}

.master-detail-empty .material-icons {
    font-size: 40px;
    display: block;
    margin: 0 auto 12px;
    color: var(--card-border);
}

/* Circular initials badge - this app has no per-user profile photo, so every avatar (list row or
   detail header) is just the first letter of the user's first and last name on a solid color. Same
   two sizes reused for both contexts via the .user-avatar-lg modifier below. */
.user-avatar {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--blue);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 700;
}

.user-avatar-lg {
    width: 64px;
    height: 64px;
    font-size: 22px;
}

/* Fixed 8-color palette, picked by the user's first letter (A/I/Q -> c0, B/J/R -> c1, ... - see
   GetAvatarColorClass in Users.aspx.cs and its mirrored avatarColorClass() in the page's own script
   block) so a given person always gets the same color everywhere they show up, without storing
   anything extra in the DB. Same solid-saturated-color-plus-white-text look in both themes (no
   separate dark mode override needed) as the Home page's own stat cards. */
.user-avatar-c0 { background: #2C7BE5; } /* blue */
.user-avatar-c1 { background: #2E9E5B; } /* green */
.user-avatar-c2 { background: #8B5CF6; } /* violet */
.user-avatar-c3 { background: #DD7A3A; } /* orange */
.user-avatar-c4 { background: #14968A; } /* teal */
.user-avatar-c5 { background: #D6558C; } /* pink */
.user-avatar-c6 { background: #5C6BC0; } /* indigo */
.user-avatar-c7 { background: #B8860B; } /* amber */

.user-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 14px 0;
}

.user-list-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border-radius: 12px;
    color: var(--text-dark);
    text-decoration: none;
    background: var(--card-bg);
    box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}

.user-list-item:hover { background: var(--surface-tint); text-decoration: none; }

/* Whichever user the detail pane on the right is currently showing - explicit selection or, on
   desktop only, the default first row (see GetRowSelectedClass in Users.aspx.cs). */
.user-list-item.selected {
    background: var(--surface-tint);
    box-shadow: 0 0 0 1.5px var(--blue) inset;
}

html[data-theme="dark"] .user-list-item { border: 1px solid var(--card-border); box-shadow: none; }
html[data-theme="dark"] .user-list-item.selected { background: #223349; }

.user-list-info {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.user-list-name {
    font-weight: 700;
    font-size: 14px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-list-item .status-badge { align-self: flex-start; }

/* Row of chips below the name (Active/Inactive, plus the "Admin" chip when that user has Admin
   Access) - a plain flex row so a second chip lands to the right of the first rather than each one
   getting its own line. */
.user-list-badges { display: flex; align-items: center; gap: 6px; }

.user-list-chevron {
    flex: 0 0 auto;
    color: var(--card-border);
    font-size: 20px;
}

.master-detail-header {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 20px;
}

.master-detail-header-text h2 { margin: 0 0 6px; font-size: 20px; }

/* Edit/Deactivate-Activate icon buttons, pushed to the far right of the header row (same row as the
   username) via margin-left:auto rather than sized/positioned relative to .master-detail-header-text,
   so they stay pinned right regardless of how long the name/status badge next to them are. */
.master-detail-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    flex: 0 0 auto;
}

/* Two-column read-only field grid for the selected user's details - same label/value language as
   .card-row elsewhere in the app (muted label, bold value), just laid out as a grid instead of
   stacked rows since there's a lot more room to work with in the 70%-wide detail pane. */
.detail-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 18px 32px;
    padding: 18px 0;
    border-top: 1px solid var(--card-border);
    border-bottom: 1px solid var(--card-border);
    margin-bottom: 20px;
}

.detail-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }

.detail-label { font-size: 12px; color: var(--text-muted); }

.detail-value {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-dark);
    word-break: break-word;
}

.detail-field .status-badge { align-self: flex-start; }

/* Chart.js needs an explicitly-sized parent when responsive:true + maintainAspectRatio:false (see
   initApMonthlyChart() in site.js) - otherwise it has no height to fill and renders collapsed to
   nothing. position:relative is Chart.js's own documented requirement for this setup (its internal
   resize observer measures the nearest positioned ancestor). */
.ap-chart-canvas-wrap {
    position: relative;
    height: 360px;
    padding: 8px 4px 0;
}

/* Taller variant for the same canvas wrapper inside apChartExpandModal - "bigger so user can see
   more details" was the explicit ask, so this gets noticeably more vertical room than the card's
   360px, not just a copy of it. */
.ap-chart-canvas-wrap-large {
    height: 540px;
}

/* Removed: margin-top was pushing the legend down away from the title row, but the actual gap the
   user wanted was between the legend and the bars below it, not between the title and the legend -
   see the Chart.js legend "afterFit" callback in buildCompanyOverviewChartInstance() (site.js) for
   where that spacing now lives instead. */

/* Trims 3px off .ap-chart-canvas-wrap's own 8px top padding - explicit follow-up request ("move the
   legends up more. Maybe another 3px") after the Chart.js-side layout.padding.top was already reduced
   to 0, so the only remaining top offset left to shave was this CSS wrapper padding. Scoped to this
   page's chart only, so AccountsPayable.aspx/AccountsReceivable.aspx's own charts (sharing the base
   .ap-chart-canvas-wrap class) keep their existing 8px. */
.company-overview-chart-wrap {
    padding-top: 5px;
}

/* ---------------- Company Overview - Net Income: Total Income/Expenses by Year (split panels) --------
   Explicit follow-up request: "create this new chart below the existing chart" - an EXPENSES panel and
   an INCOME panel side by side (2 separate Chart.js canvases - see
   initCompanyOverviewIncomeExpenseCharts() in site.js), sharing one "Account Identity" legend row above
   them and a vertical divider between them, matching a supplied reference chart. */

.company-overview-split-card .card-header-row { margin-bottom: 4px; }

.company-overview-split-legend {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 18px;
}

.company-overview-split-legend-label {
    font-weight: 700;
    font-size: 13px;
    color: var(--text-dark);
}

.company-overview-split-legend-items {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 16px;
}

.company-overview-split-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-dark);
}

.company-overview-split-legend-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
}

/* Clickable Year legend on the "Periods by Total Profits" chart - explicit request: "allow the legend to
   be clickable as well for filter" - see wireCompanyOverviewPeriodLegendClicks() in site.js. Not applied
   to the other 2 charts' (non-clickable) legend items, which don't get this class. */
.company-overview-split-legend-item-clickable {
    cursor: pointer;
    user-select: none;
}

/* Toggled-off state - same "faded + struck through" convention as a real Chart.js legend's own hidden-
   dataset styling, so this custom HTML legend reads the same way once a Year's bar series is hidden. */
.company-overview-split-legend-item-hidden {
    opacity: 0.5;
    text-decoration: line-through;
}

.company-overview-split-panels {
    display: flex;
    align-items: stretch;
}

.company-overview-split-panel {
    flex: 1 1 0;
    min-width: 0;
    padding: 0 20px;
}

.company-overview-split-panel:first-child { padding-left: 0; }

/* The vertical divider between the 2 panels - a plain border, no extra markup needed. */
.company-overview-split-panel-right {
    border-left: 1px solid var(--card-border);
}

.company-overview-split-panel-caption {
    font-weight: 700;
    font-size: 14px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-dark);
    margin-bottom: 8px;
}

@media (max-width: 768px) {
    /* Side-by-side panels don't have room to breathe on a phone-width screen - stack them instead,
       swapping the vertical divider for a horizontal one above the 2nd (Income) panel. */
    .company-overview-split-panels {
        flex-direction: column;
    }

    .company-overview-split-panel {
        padding: 0;
    }

    .company-overview-split-panel-right {
        border-left: none;
        border-top: 1px solid var(--card-border);
        padding-top: 20px;
        margin-top: 20px;
    }
}

/* ---------------- AP "by Vendor Name" charts + KPI cards (AccountsPayable.aspx) ---------------- */

/* Single grid wrapping both vendor bar charts AND all 4 KPI cards together, so their relative position
   can be redefined per breakpoint via grid-template-areas instead of needing 2 separate row containers
   with a fixed DOM order. Desktop (default, below) keeps the original shape: the 2 charts side by side,
   then all 4 KPI cards in one row underneath both of them. The max-width:768px override further down
   regroups this instead into "each chart directly followed by its own 2 matching KPI cards" (explicit
   request) - same single breakpoint the rest of the app's responsive rules use for "tablet and below".
   grid-area names below tie each of the 6 cells to which color/chart it belongs to: kpiOverdueValue/
   kpiOverdueVendor are the 2 orange cards (paired with the orange overdue-by-vendor chart),
   kpiUnpaidValue/kpiUnpaidVendor are the 2 green cards (paired with the green unpaid-amount-by-vendor
   chart) - see AccountsPayable.aspx for which literal card is which. */
/* row-gap is deliberately tighter than column-gap (8px vs 16px, rather than one uniform "gap") -
   explicit request: the space below a chart should read as tighter than the space between unrelated
   cells, since the KPI row directly beneath it is the "biggest value" callout FOR that chart, not a
   separate section. Desktop only has one row transition (charts row -> KPI row), and it's entirely a
   "belongs to" relationship (each KPI column sits under its own matching chart), so row-gap alone is
   the whole story there. Mobile has 2 more row transitions the tighter row-gap would otherwise also
   catch (KPI row 1 -> chart 2, a NOT-related boundary) - see the compensating margin-top on
   .ap-grid-unpaid-chart in the max-width:768px override below, which adds back the difference so that
   boundary still reads as the original, more generous 16px. */
.ap-report-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-areas:
        "overdueChart overdueChart unpaidChart unpaidChart"
        "kpiOverdueValue kpiOverdueVendor kpiUnpaidValue kpiUnpaidVendor";
    column-gap: 16px;
    row-gap: 8px;
    margin-bottom: 16px;
}

.ap-grid-overdue-chart { grid-area: overdueChart; }
.ap-grid-unpaid-chart { grid-area: unpaidChart; }
.ap-grid-kpi-overdue-value { grid-area: kpiOverdueValue; }
.ap-grid-kpi-overdue-vendor { grid-area: kpiOverdueVendor; }
.ap-grid-kpi-unpaid-value { grid-area: kpiUnpaidValue; }
.ap-grid-kpi-unpaid-vendor { grid-area: kpiUnpaidVendor; }

/* The 2 chart .card panels don't need their own margin-bottom here - .ap-report-grid's own gap already
   spaces every cell from every other cell in both directions, so the default .card margin-bottom would
   just double up the space directly below each chart on top of that. */
.ap-grid-overdue-chart, .ap-grid-unpaid-chart { margin-bottom: 0; }

@media (max-width: 768px) {
    /* Each chart now sits directly above its own 2 KPI cards rather than all 4 cards sitting below
       both charts - explicit request. The 2-column track is kept (not collapsed to 1) specifically so
       each chart's KPI pair still renders side by side rather than stacked, all the way down to phone
       widths (also explicit) - there's no narrower single-column override below this one. */
    .ap-report-grid {
        grid-template-columns: repeat(2, 1fr);
        grid-template-areas:
            "overdueChart overdueChart"
            "kpiOverdueValue kpiOverdueVendor"
            "unpaidChart unpaidChart"
            "kpiUnpaidValue kpiUnpaidVendor";
    }

    /* Restores the original, more generous 16px gap specifically for the one row transition that
       ISN'T a "belongs to" relationship on mobile - the overdue KPI row is immediately followed by the
       (unrelated) unpaid-amount chart, not by its own chart. .ap-report-grid's row-gap above is only
       8px now (tightened for the chart->its-own-KPIs pairs), so this adds the other 8px back just
       here. */
    .ap-grid-unpaid-chart {
        margin-top: 8px;
    }
}

/* AccountsReceivable.aspx's "By Salesperson Name" section - 2 horizontal bar charts (Number Days
   Overdue, Total Outstanding Amount) plus their 4 matching KPI cards - same named-area grid technique as
   .ap-report-grid below (explicit request: "in mobile view, the orange and green chart should be like
   the blue and yellow chart" - this used to be 2 independent grids, a chart row followed by a completely
   separate KPI row, so mobile showed both charts and THEN all 4 KPIs together instead of each chart
   directly above its own matching KPI pair the way .ap-report-grid's own mobile override already did;
   one grid with named areas instead of two sibling ones makes the 2 sections behave identically). See
   the comment above .ap-report-grid for the full column/row-gap and grid-area-naming reasoning - this is
   the exact same layout, just salesperson-flavored area names/colors (orange = overdue, green = amount)
   instead of customer-flavored ones. */
.ar-salesperson-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-areas:
        "salespersonOverdueChart salespersonOverdueChart salespersonUnpaidChart salespersonUnpaidChart"
        "kpiSalespersonOverdueValue kpiSalespersonOverdueVendor kpiSalespersonUnpaidValue kpiSalespersonUnpaidVendor";
    column-gap: 16px;
    row-gap: 8px;
    margin-bottom: 16px;
}

.ar-grid-salesperson-overdue-chart { grid-area: salespersonOverdueChart; }
.ar-grid-salesperson-outstanding-chart { grid-area: salespersonUnpaidChart; }
.ar-grid-kpi-salesperson-overdue-value { grid-area: kpiSalespersonOverdueValue; }
.ar-grid-kpi-salesperson-overdue-name { grid-area: kpiSalespersonOverdueVendor; }
.ar-grid-kpi-salesperson-outstanding-value { grid-area: kpiSalespersonUnpaidValue; }
.ar-grid-kpi-salesperson-outstanding-name { grid-area: kpiSalespersonUnpaidVendor; }

.ar-grid-salesperson-overdue-chart, .ar-grid-salesperson-outstanding-chart { margin-bottom: 0; }

@media (max-width: 768px) {
    .ar-salesperson-grid {
        grid-template-columns: repeat(2, 1fr);
        grid-template-areas:
            "salespersonOverdueChart salespersonOverdueChart"
            "kpiSalespersonOverdueValue kpiSalespersonOverdueVendor"
            "salespersonUnpaidChart salespersonUnpaidChart"
            "kpiSalespersonUnpaidValue kpiSalespersonUnpaidVendor";
    }

    /* Same "restore the other 8px" reasoning as .ap-grid-unpaid-chart's own mobile override below - the
       boundary between the overdue KPI row and the (unrelated) outstanding-amount chart isn't a
       "belongs to" relationship, so it should keep the original, more generous 16px rather than the
       tightened 8px row-gap used for the chart->its-own-KPIs pairs. */
    .ar-grid-salesperson-outstanding-chart {
        margin-top: 8px;
    }
}

/* Outer clip/scroll boundary for a vendor bar chart that has more rows than fit in the card's normal
   360px - the inner #wrapVendor...Chart div (.ap-chart-canvas-wrap) is the one that actually grows
   taller (see buildApVendorBarChart() in site.js, which sets its inline height directly), so Chart.js
   still measures a real, uncropped height for the canvas; this outer div is what visually caps and
   scrolls it. Kept as 2 nested elements rather than one for that reason - a single element can't both
   report its true (tall) size to Chart.js and be visually clipped to a shorter box at the same time.
   Explicit request: the scrollbar itself should be invisible (still scrollable - mouse wheel/trackpad/
   touch all still work, only the visual scrollbar track+thumb are hidden) - 3 vendor-prefixed rules
   below since there's no unprefixed standard for this yet. */
.vendor-chart-scroll {
    max-height: 360px;
    overflow-y: auto;
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* legacy Edge/IE */
}
.vendor-chart-scroll::-webkit-scrollbar {
    display: none; /* Chrome/Safari/Chromium Edge */
}

/* Each a "biggest value" callout rather than a data table row - background color alone tells the 2
   metrics apart (orange = overdue-days-related, green = amount-related); the bold value text stays the
   standard --text-dark (already theme-aware) so it doesn't need its own dark-mode override, only the
   backgrounds and the muted label text do (see below). Flex column centers the value/label pair as a
   block within the card both ways (explicit "height center, width center" ask) - a long vendor name
   wraps to 2-3 lines at .ap-kpi-value's font size while a plain number stays on 1, so without this the
   shorter cards' content would sit at the top instead of lining up with the taller ones. Sizing itself
   (width/height) now comes entirely from .ap-report-grid's own column tracks/row heights above, not
   from a flex-basis on this element the way it did before that grid existed. */
.ap-kpi-card {
    border-radius: 16px;
    padding: 22px 18px;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

/* Same size as .ap-kpi-label right below it (explicit request - the number/vendor-name cards' bold
   value used to be much larger, which read fine for a short number but made a long vendor name wrap
   awkwardly across several oversized lines) - still bold (font-weight unchanged) so it stays the
   visually dominant line of the two, just not a jump in font size to get there. */
.ap-kpi-value {
    font-size: 13px;
    font-weight: 800;
    color: var(--text-dark);
    line-height: 1.3;
    overflow-wrap: break-word;
}

/* Explicit request: 12px (down from matching .ap-kpi-value's 13px), and true white in dark mode rather
   than the theme's usual --text-muted (a muted blue-grey, not white - see below). */
.ap-kpi-label {
    margin-top: 6px;
    font-size: 12px;
    color: var(--text-muted);
}

.ap-kpi-card-orange { background: #F8D9C4; }
.ap-kpi-card-green { background: #DCF0C7; }

/* Blue/yellow variants - AccountsReceivable.aspx-specific, explicit request: the "By Customer Name" KPI
   cards should match the color of the chart directly above them (blue overdue chart, yellow
   outstanding-amount chart - see buildArCustomerBarChart() call sites in site.js), rather than reusing
   AP's orange/green convention the way every other KPI card on the site does. Same pastel-light/
   deepened-dark treatment as the orange/green pair above, just built from the same #4C8DF2/#E8C547 hues
   the 2 charts themselves use. */
.ap-kpi-card-blue { background: #D2E3FA; }
.ap-kpi-card-yellow { background: #F7EEC0; }

/* Dark mode: same desaturated-and-deepened treatment as the status badges above (html[data-theme="dark"]
   .status-approved etc.) - a pastel background reads as a glaring highlighter block against the dark
   page background, so this drops to a muted, low-saturation version of the same hue instead. The bold
   value text is untouched (still var(--text-dark), already theme-aware) - only the backgrounds and the
   muted label text (explicit request: true white, not the theme's usual --text-muted) get their own
   dark-mode override. */
html[data-theme="dark"] .ap-kpi-card-orange { background: #4A3018; }
html[data-theme="dark"] .ap-kpi-card-green { background: #223A18; }
html[data-theme="dark"] .ap-kpi-card-blue { background: #1B2E4D; }
html[data-theme="dark"] .ap-kpi-card-yellow { background: #4A4118; }
html[data-theme="dark"] .ap-kpi-label { color: #FFFFFF; }

/* ==========================================================================
   Responsive tweaks below 768px (off-canvas nav below - see .side-nav / .nav-overlay /
   .hamburger-btn-mobile above)
   ========================================================================== */

/* ---- AccountsPayable.aspx's mobile "Filters" bottom sheet (opt-in modifier - see the markup
   comment on that page's filter bar). Every rule below is scoped to .filters-bar-mobile-sheet
   specifically, so every OTHER page's plain .filters-bar keeps its normal (row/wrap, or whatever that
   page uses) layout completely unchanged. Explicit request: only the bottom half of the screen, not
   full-screen - anchored to the bottom edge with a fixed 50vh height, slides up into view rather than
   fading in place. z-index 209/210/211 (backdrop/sheet/FAB) sits above the app's modal overlay (200) -
   comparable UI tier, a full-screen-equivalent overlay - but below the full-page-navigation loading
   spinner (500) and toast snackbars (600), so both still visibly appear on top of this sheet if
   either fires while it's open (e.g. Search's real postback navigating away).
   Breakpoint is 1024px (not 768px like the rest of the mobile-only rules below) - explicit request:
   "for tablet view, use the same filter design as mobile view as well" - so this same bottom-sheet +
   floating-trigger treatment also kicks in across tablet widths, not just phones. Kept as its own
   dedicated media query (rather than just widening the 768px block below) so none of THAT block's
   other, genuinely phone-only rules (nav rail collapse, h1/card-grid/stat-card resizing, etc.) get
   pulled along with it. */
@media (max-width: 1024px) {
    /* overflow is "hidden" (not "auto") on this outer element specifically so the rounded top corners
       always clip cleanly - a border-radius doesn't reliably clip an element's OWN scrollbar in every
       browser (explicit bug report: the scrollbar was rendering outside the sheet's rounded edge,
       stretching up over the chart above it). The actual scrolling now happens one level down, on
       .filters-panel-scroll below - a plain rectangle with no border-radius of its own, so there's
       nothing for a scrollbar to visually escape from. */
    .filters-bar.filters-bar-mobile-sheet {
        position: fixed;
        left: 0;
        right: 0;
        bottom: -50vh;
        top: auto;
        height: 50vh;
        max-height: 50vh;
        z-index: 210;
        flex-direction: column;
        flex-wrap: nowrap;
        align-items: stretch;
        margin-bottom: 0;
        border-radius: 20px 20px 0 0;
        padding: 0;
        background: var(--card-bg);
        box-shadow: 0 -8px 28px rgba(0, 0, 0, 0.28);
        overflow: hidden;
        /* Hidden via visibility (not display:none) so there's something to transition from/to -
           toggled by initMobileFilterPanel() in site.js adding/removing .filters-panel-open. Slides up
           from fully off-screen below the viewport (bottom: -50vh -> 0, matching this element's own
           50vh height) rather than the full-screen version's simple fade, to read as a bottom sheet
           being pulled into view.
           Explicit bug report: animating this via `transform` instead (translateY(100%) -> 0) was the
           actual root cause of "I can see part of the filters text behind the back button" - ANY
           transform value other than the keyword `none` on an ancestor makes that ancestor establish a
           new containing block for position:fixed/sticky descendants, which broke
           .filters-panel-back's position:sticky (2 levels down, inside .filters-panel-scroll) in a way
           that let the scrolling title/subtitle paint on top of it instead of staying behind it.
           Animating `bottom` (a plain positional offset, not a transform) avoids that pitfall entirely
           while producing the identical slide-up visual. The visibility transition is delayed on the
           way in and instant on the way out, so it only ever becomes interactive/visible-to-screen-
           readers once the slide-in has actually finished. */
        visibility: hidden;
        transition: bottom .25s ease, visibility 0s linear .25s;
    }

    .filters-bar.filters-bar-mobile-sheet.filters-panel-open {
        visibility: visible;
        bottom: 0;
        transition: bottom .25s ease;
    }

    /* Dims the (now visible) top half of the screen behind the sheet and doubles as a tap-outside-to-
       close target - same discard-unconfirmed-filters behavior as Back (see closePanel() in
       initMobileFilterPanel(), which both share). Wasn't needed for the old full-screen version since
       there was no page content left showing behind it. */
    .filters-panel-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 209;
        background: rgba(0, 0, 0, 0.45);
        visibility: hidden;
        opacity: 0;
        transition: opacity .2s ease, visibility 0s linear .2s;
    }

    .filters-panel-backdrop.open {
        visibility: visible;
        opacity: 1;
        transition: opacity .2s ease;
    }

    /* Locks background scroll while the sheet is open - added/removed alongside .filters-panel-open,
       see openPanel()/closePanel() in initMobileFilterPanel(). */
    body.filters-panel-active { overflow: hidden; }

    /* Floating trigger lifts to sit just above the sheet instead of getting covered by it while open -
       only matters for a button still at its default right/bottom CSS position (an explicit inline
       left/top from being dragged - see initMobileFilterPanel() - always wins over this regardless). */
    body.filters-panel-active .filter-fab { bottom: calc(50vh + 20px); }

    /* Pinned bar - just the Back button, never scrolls. A plain, non-scrolling flex sibling that sits
       ABOVE .filters-panel-scroll (not a scrolling ancestor of it, not position:sticky) - explicit
       request: only Back stays put, the title/subtitle/fields all scroll underneath it. flex:none keeps
       it at its own natural height regardless of how tall the scrolling content below ends up being. */
    .filters-bar-mobile-sheet .filters-panel-backbar {
        display: flex;
        flex: none;
        padding: 20px 20px 8px;
    }

    .filters-panel-back {
        display: inline-flex;
        align-items: center;
        gap: 2px;
        border: none;
        background: none;
        padding: 0;
        margin: 0;
        color: var(--text-dark);
        font-size: 15px;
        font-weight: 600;
        cursor: pointer;
    }

    .filters-panel-back .material-icons { font-size: 22px; }

    /* The actual scrolling container - title, subtitle, every field, AND the button group all live in
       here (moved/built in by initMobileFilterPanel() in site.js), scrolling underneath the pinned
       .filters-panel-backbar above. flex:1 1 auto + min-height:0 is what lets this shrink to fill
       exactly the remaining space below that bar and scroll internally instead of pushing the sheet
       taller than its fixed 50vh (a plain flex child would otherwise just grow to fit all its content -
       min-height:0 overrides that default). No top padding of its own - .filters-panel-backbar above
       already provides that inset; carries the side/bottom padding the outer sheet used to have. */
    .filters-panel-scroll {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        display: flex;
        flex-direction: column;
        padding: 0 20px calc(20px + env(safe-area-inset-bottom, 0px));
    }

    .filters-bar-mobile-sheet .filters-panel-title {
        display: block;
        margin: 0 0 4px;
        font-size: 24px;
        font-weight: 700;
        color: var(--text-dark);
    }

    .filters-bar-mobile-sheet .filters-panel-subtitle {
        display: block;
        margin: 0 0 20px;
        font-size: 13px;
        color: var(--text-muted);
        line-height: 1.4;
    }

    /* .filters-panel-scroll's own flex column (default align-items: stretch, nothing extra needed) already
       makes every direct child - each .form-group, the button group - full width; no extra width rule
       needed here. margin-bottom is what actually separates each field from the next - explicit bug
       report: with 0 margin (the desktop/tablet .filters-bar rule below still applies, since it isn't
       viewport-scoped) and no gap of its own, every field was rendering flush against the next. */
    .filters-bar-mobile-sheet .form-group {
        min-width: 0;
        margin-bottom: 16px;
    }

    /* Explicit request: Clear and Search/Confirm side by side, Clear on the left, Search on the right -
       flex-direction stays the default row, which already puts them in that order since Clear renders
       before Search in markup (see AccountsPayable.aspx). */
    .filters-bar-mobile-sheet .filter-buttons-group {
        flex: none;
        margin-top: auto;
        padding-top: 16px;
        flex-direction: row;
        gap: 10px;
    }

    .filters-bar-mobile-sheet .filter-buttons-group .btn {
        flex: 1 1 0;
        justify-content: center;
    }

    /* Every "Search" button app-wide goes icon-only on mobile (see .btn-search-icon-mobile-icon
       below) - overridden back to its full text label here specifically, since inside this full-width
       full-screen sheet there's no crowding concern the icon-only treatment exists to solve. */
    .filters-bar-mobile-sheet .btn-search-icon-mobile-icon { display: none; }
    .filters-bar-mobile-sheet .btn-search-icon-mobile-text { display: inline; }
    .filters-bar-mobile-sheet .btn-search-icon-mobile { padding: 12px 16px; }

    /* Floating filter trigger - mobile/tablet only, fixed bottom-right by default. initMobileFilterPanel()
       in site.js makes it draggable: it only switches to explicit left/top pixel coordinates once the
       user actually drags it, so until then it stays anchored via this right/bottom rule. Same blue as
       the Search button (.btn-blue/var(--blue)) rather than the brand-red used elsewhere in this sheet,
       so the floating trigger reads as "the same action as Search" at a glance. */
    .filter-fab {
        display: flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        right: 20px;
        bottom: 20px;
        width: 52px;
        height: 52px;
        border-radius: 50%;
        border: none;
        padding: 0;
        background: var(--blue);
        color: #fff;
        box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
        z-index: 211;
        cursor: grab;
        /* Stops the browser's own touch-scroll/zoom gestures from fighting the drag handling in
           initMobileFilterPanel() (Pointer Events) while dragging this button specifically. */
        touch-action: none;
    }

    .filter-fab:active { cursor: grabbing; }

    .filter-fab .material-icons { font-size: 24px; }

    /* Scroll-to-top FAB - base rule above is display:none; only actually shown once
       initScrollToTopButton() (site.js) adds .visible (past a scroll-down threshold, same idea as
       "back to top" buttons elsewhere, just gated to mobile only per explicit request). Stacked
       directly above .filter-fab's own right:20px/bottom:20px spot (52px button + 12px gap + 20px =
       84px) so the two don't overlap on pages that have both (a mobile filter sheet AND enough
       scrolled content to trigger this button) at the same time.
       Frosted-glass look (explicit follow-up request, reference screenshots supplied of a Figma
       "Background Blur" tutorial) - replaces the earlier solid off-white/dark-card fill with a
       translucent white surface + backdrop-filter blur, so whatever's actually scrolled behind the
       button (page content, charts, nav rail) shows through it, softened - the same glassmorphism
       recipe as the reference: 20px background blur, 25%-opacity white fill, 35%-opacity 1px white
       stroke, a soft outer drop shadow for lift, and 2 inset shadows (a broader soft one for depth,
       plus a crisp 1px one right at the top edge standing in for the reference's separate "Top
       Highlight" layer - both easiest to express as one box-shadow list rather than a real extra
       layer, since this button has no nested elements to hang a highlight on). Deliberately the same
       translucent-white recipe in BOTH themes, not swapped per theme like the button's icon color
       below still is - unlike a flat fill, a blur-based glass surface already adapts its own apparent
       tone to whatever's behind it (dark page content reads as a darker pane of glass, light content
       as a lighter one), so there's no separate dark-theme override needed here at all, only for the
       icon's own color (kept theme-aware via var(--text-dark) so it stays legible against whichever
       tone the glass ends up reading as). "Keep the button circle" (explicit request) - border-radius
       is unchanged from the previous version.
       Draggable (explicit follow-up request: "allow it to be move around like the filter button as
       well") - see initScrollToTopButton() in site.js, same Pointer Events drag pattern as .filter-fab
       above. cursor: grab/touch-action: none below are the same drag-affordance/gesture-conflict-
       prevention pair .filter-fab uses for the same reason - stops the browser's own touch-scroll from
       fighting the drag handling while dragging this button specifically. */
    .scroll-top-fab {
        align-items: center;
        justify-content: center;
        position: fixed;
        right: 20px;
        bottom: 84px;
        width: 52px;
        height: 52px;
        border-radius: 50%;
        border: 1px solid rgba(255, 255, 255, 0.35);
        padding: 0;
        background: rgba(255, 255, 255, 0.25);
        backdrop-filter: blur(20px);
        -webkit-backdrop-filter: blur(20px);
        color: var(--text-dark);
        box-shadow:
            0 8px 30px rgba(0, 0, 0, 0.12),
            inset 0 1px 8px rgba(255, 255, 255, 0.2),
            inset 0 1px 0 rgba(255, 255, 255, 0.6);
        z-index: 211;
        cursor: grab;
        touch-action: none;
    }

    .scroll-top-fab.visible { display: flex; }

    .scroll-top-fab:active { cursor: grabbing; }

    .scroll-top-fab .material-icons { font-size: 24px; }
}

@media (max-width: 768px) {
    .header-username { display: none; }

    .hamburger-btn-mobile { display: flex; }

    /* Below 768px the rail is no longer a permanent part of the layout - it starts off-screen
       (slid fully left) and only slides into view as a full-width-labelled drawer when
       #btnHamburgerMobile toggles .expanded - see initNav() in site.js. .site-header's extra left
       padding / .content-area's / .site-footer's permanent left offset for the collapsed desktop
       rail (see var(--nav-rail-width) above) don't apply here since there's no permanently-docked
       rail taking up space to reserve or hide anything underneath. */
    .site-header { padding-left: 16px; }

    .side-nav {
        width: var(--nav-width);
        transform: translateX(-100%);
        transition: transform 0.25s ease;
    }

    .side-nav.expanded {
        transform: translateX(0);
    }

    /* Walkthrough tour on mobile - the rail is off-screen by default here (see .side-nav above), so
       without this a mobile user touring the left-nav icons would just see the tour spotlight/tooltip
       floating over nothing, with no actual icon underneath to point at. body.tour-nav-open is toggled
       per-step (not for the tour's whole duration) by goToStep() in initTour() (site.js) - true only
       while the CURRENT step's target actually lives inside #sideNav, so the rail slides into view for
       every left-nav icon step and slides back off-screen again the moment the tour moves on to a Home
       page step - explicit request, rather than staying open the whole time regardless of what's
       currently being explained. Back/Next/Skip/X/Escape all funnel through goToStep()/endTour(),
       which keep this class in sync, so no extra cleanup logic is needed here beyond the existing
       .side-nav transform/width rules above simply re-applying once the class is gone.
       Deliberately forced to the narrow icon-only --nav-rail-width, NOT .expanded's full --nav-width -
       same collapsed look as the permanently-docked desktop rail, so it visually matches the rest of
       the tour's icon-by-icon steps instead of popping open as a wide labelled drawer. This does NOT
       reuse .expanded (no .nav-label text shows, no #navOverlay dimming - the tour's own overlay
       already handles dimming everything else, see .tour-overlay above) - a separate, tour-specific
       state from the hamburger's normal open/close toggle. */
    body.tour-nav-open .side-nav {
        transform: translateX(0);
        width: var(--nav-rail-width);
    }

    .content-area { margin-left: 0; }
    .site-footer { left: 0; }

    /* Every page title (My Claims, Claims Pending Approval, Admin - Users, the claim number heading
       on ClaimsPendingApprovalDetail, ...) shrinks to 1.5em on mobile - they all share the plain h1
       element, so one rule covers every page at once instead of a per-page class. */
    h1 { font-size: 1.5em; }

    /* Explicit repeated bug report: even with .btn-icon-only-mobile collapsing the button itself down
       to a 40x40 square, it kept wrapping onto its own line below the page title. A first attempt at
       fixing this (min-width:0 + flex-grow on the badge group alone, flex-wrap left as "wrap") was
       verified - by actually rendering the real markup/CSS at a 375px viewport and reading back
       getBoundingClientRect() on both elements, not just reasoning about the CSS - to NOT work: with
       flex-wrap: wrap, the browser decides which line each item lands on using each item's
       HYPOTHETICAL (pre-shrink) main size, not its post-shrink size - the badge group's unshrunk
       content (title + pill) is wide enough to overflow the row by itself, so it alone claims the
       first line and the button is always bumped to a second line, regardless of any min-width/shrink
       rule on the badge group. Shrinking only happens AFTER line-assignment, which is too late.
       The actual fix: flex-wrap: nowrap here forces both the badge group and the button to stay on
       exactly one line no matter what; min-width: 0 on the badge group (below) is what then lets it
       actually shrink down to whatever space is left after the button's fixed 40px, with its own
       title/pill wrapping onto 2 sub-lines internally if they must (it's flex-wrap: wrap itself, see
       .page-cache-badge-group's own rule) - never spilling onto .page-header-row's own second line.
       Scoped to .page-header-row-tight specifically (not the base .page-header-row class) - every one
       of the 15 dashboard pages with this Clear All + cache-badge combo already uses the "-tight"
       modifier alongside .page-header-row (see AccountsPayable.aspx etc.), while the admin page that
       still needs real wrapping (Admin/UserForm.aspx - a title +
       a full-text "Back"/"Clear" link, not an icon-only button) use plain .page-header-row without
       "-tight", so they're untouched by this rule. */
    .page-header-row-tight { flex-wrap: nowrap; }
    .page-header-row-tight > .page-cache-badge-group { min-width: 0; }

    /* Every "+ Add New" style button in the app (Admin/Users' "+ Add New") collapses to a green
       "+"-only button on mobile, where screen space is tight - deliberately the exact same look (size,
       10px corner radius, flat green fill, no shadow) as the "+ New Claims" button in the left
       nav rail's own collapsed state (see .nav-new-claim-link) - one consistent "add" button
       design app-wide, not a separate mobile-only style. Staying in the same spot (top-right of
       the page, same row as the title, via .page-header-row's own flex space-between) rather than
       moving or stacking. The text label stays in the DOM (just visually hidden) so screen readers
       still announce it. */
    .btn-icon-only-mobile {
        width: 40px;
        height: 40px;
        padding: 0;
        border-radius: 10px;
        flex-shrink: 0;
        display: flex;
        align-items: center;
        justify-content: center;
    }
    /* The icon span is its own flex box (filling the button) rather than relying on the "+"
       glyph's own font metrics to sit centered within a line-height - fonts don't reliably center
       punctuation marks vertically within their em box, which was leaving the "+" looking
       off-center even though the button itself was centered correctly. Icon size matches
       .nav-new-claim-link's own icon (24px) for the same reason - same button, same icon. */
    .btn-icon-only-mobile .btn-icon-only-mobile-icon {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        height: 100%;
        font-size: 24px;
        line-height: 1;
    }
    .btn-icon-only-mobile .btn-icon-only-mobile-text { display: none; }

    /* ClaimsPendingApprovalDetail.aspx: on mobile, Back and the Approve/Reject tick/X icons share
       one row instead of stacking (the desktop behaviour in the base .header-actions-stack rule
       above) - Back stays on the left, the icons move to the right via justify-content: space-between,
       and width: 100% lets that spread happen across the full row instead of the narrow
       shrink-to-fit box the column layout produces. */
    .header-actions-stack {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        width: 100%;
    }

    /* The copyright line wraps to 2 lines on a narrow screen at the desktop 12px size - shrink it
       and force it onto one line (ellipsis rather than a second wrapped line, on the rare screen
       still too narrow even for the smaller size) so it reads as a clean single-line footer strip. */
    .site-footer {
        font-size: 10px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .content-inner { padding: 16px; }

    /* .card-grid's own gap (16px) and each .card's margin-bottom (16px) both add vertical space
       once cards stack into a single column here - together that's 32px between cards, which reads
       as too much dead space on a narrow screen. Shared .card/.card-grid classes, so this tightens
       the stacked gap on every card-based page at once. */
    .card-grid { grid-template-columns: 1fr; gap: 10px; }
    .card { margin-bottom: 10px; }

    /* Admin/Users.aspx: Created Date / Created User start collapsed on mobile to save space,
       revealed via the "View More" toggle (see initCardViewMore() in site.js). Username, the
       edit/deactivate icons, User ID, Email, and Account Status all stay outside .card-extra-rows
       so they're always visible regardless of this collapsed state. */
    .card-extra-rows { display: none; }
    .card-extra-rows.expanded { display: block; }

    .card-view-more {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 2px;
        width: 100%;
        margin: 4px 0 0;
        padding: 4px 0;
        background: none;
        border: none;
        color: var(--blue);
        font-size: 13px;
        font-weight: 600;
        font-family: inherit;
        cursor: pointer;
    }

    /* Google Material Icons' keyboard_arrow_down glyph rotates 180deg to point up once expanded,
       instead of swapping to a separate keyboard_arrow_up glyph - one icon, one transition. */
    .card-view-more-icon {
        font-size: 18px;
        transition: transform 0.2s ease;
    }

    .card-view-more.expanded .card-view-more-icon { transform: rotate(180deg); }

    /* Once stacked, spacing between fields should come from .form-group's own margin-bottom only
       (the same 12px used everywhere else on the page) - NOT from .form-row's flex "gap" as well,
       which was only ever meant to space columns apart horizontally on desktop. Leaving gap in
       here would double up the space between fields that used to share a row (e.g. Company Code /
       Cost Center) while fields between separate rows/sections (e.g. Cost Center -> Claim Type,
       or the last row -> the next standalone .form-group like Upload Receipt/Invoice) would only
       get whatever margin-bottom is left on the very last item - producing exactly the "some gaps
       wide, some near, some touching" inconsistency this is fixing. Zeroing the gap here makes
       every gap on the page, row or no row, come from the same single 12px margin-bottom rule. */
    .form-row { flex-direction: column; gap: 0; }

    /* .split-add-row is a grid (not a flex row), so the "gap: 0" reset just above - meant for
       stacked flex rows - would otherwise zero its column gap too, pushing the search box and
       "Add to split" button flush against each other with no space between them. */
    .split-add-row { gap: 10px; }

    /* The 280px floor exists to keep the two panel-box columns from getting too cramped side by
       side on desktop/tablet - once stacked full-width on mobile, that same floor can be wider
       than the phone screen itself (card padding included), pushing the panel past the card's own
       edge instead of staying contained inside it. */
    .form-row > .panel-box { min-width: 0; }

    .badge-row { justify-content: center; }
    .count-card { width: 140px; padding: 18px 12px; border-radius: 16px; }
    .count-card-icon-wrap { width: 46px; height: 46px; }
    .count-card-icon-wrap .material-icons { font-size: 22px; }
    .count-card-label { font-size: 12.5px; }
    .count-card-number { font-size: 24px; }

    .announcement-banner { padding: 20px 25px; border-radius: 16px; margin: 4px 0 20px; }
    .announcement-banner-graphic { width: 48px; height: 48px; }
    .announcement-banner-graphic .material-icons { font-size: 22px; }

    /* 2 equal columns instead of 3 - each stat card is 50% width on mobile per spec. */
    .stat-card-row { grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 20px; }
    .stat-card { padding: 14px; gap: 10px; border-radius: 16px; }
    .stat-ring-wrap { width: 52px; height: 52px; }
    .stat-ring-label { font-size: 12px; }
    .stat-card-number { font-size: 16px; }
    .stat-card-label { font-size: 10.5px; }
    .stat-card-limit { font-size: 9.5px; }
    .stat-card-icon-decor { width: 54px; height: 54px; top: -8px; right: -8px; }
    .stat-card-icon-decor .material-icons { font-size: 22px; }

    /* Claim Status and Month/Year share one row (wrapping, not stacking full-width); Search/Clear
       drop to their own full-width row below via flex-basis: 100%. */
    .filters-bar { flex-direction: row; flex-wrap: wrap; align-items: flex-end; }

    .filters-bar .form-group { min-width: 0; }

    /* max-width caps it at just enough to fit the longest status text ("Pending Resubmission")
       without truncating - previously flex-grow let it stretch to fill any leftover space in the
       row, making the box far wider than the text inside it ever needed. */
    .filters-bar .filter-status-group {
        flex: 1 1 45%;
        min-width: 0;
        max-width: 210px;
    }

    /* No flex-grow here (unlike Claim Status above) - the Month/Year box should stay at its 30%
       share (matching desktop - see the non-mobile .filter-monthyear-group rule above) instead of
       stretching to fill the row. */
    .filters-bar .filter-monthyear-group {
        flex: 0 1 30%;
        min-width: 0;
    }

    /* The panel's left offset is now positioned dynamically by positionPanel() in site.js instead
       of a fixed CSS anchor - the box's horizontal position varies too much page-to-page (different
       column widths, different siblings) for a single static left/right rule to keep it on-screen
       everywhere, so JS measures the actual on-screen position each time the panel opens and nudges
       it back into the viewport if needed. */
    .month-year-panel {
        width: min(240px, calc(100vw - 16px));
    }

    .filters-bar .filter-buttons-group {
        flex: 1 1 100%;
        display: flex;
        gap: 10px;
    }

    /* ---- AccountsPayable.aspx's mobile "Filters" bottom sheet (opt-in modifier - see the markup
       comment on that page's filter bar) used to live entirely in this 768px block. Explicit request:
       "for tablet view, use the same filter design as mobile view as well" - pulled out into its own
       @media (max-width: 1024px) block below (see just above this one) so it also activates across
       tablet widths, without dragging every OTHER unrelated rule in this 768px-only block (nav rail,
       h1 sizing, card-grid stacking, etc.) along with it. Search "filters-bar-mobile-sheet" for the
       relocated rules. */

    /* Admin/Users.aspx only - it has just one filter field (Username), so there's enough room to
       keep Clear/Search on the same row as the search box even on mobile, unlike the other filter
       bars (Claim Status + Month/Year + buttons) that need the buttons on their own row below to
       avoid crowding. The extra .filters-bar class in the selector outweighs the general
       .filters-bar .filter-buttons-group rule above regardless of declaration order. */
    .filters-bar.filters-bar-inline {
        flex-wrap: nowrap;
        align-items: flex-end;
    }
    .filters-bar.filters-bar-inline .form-group {
        min-width: 0;
    }
    .filters-bar.filters-bar-inline .form-group:first-child {
        flex: 1 1 auto;
    }
    .filters-bar.filters-bar-inline .filter-buttons-group {
        flex: 0 0 auto;
        width: auto;
    }

    /* Admin/Users.aspx master/detail layout - list-only by default; .master-detail-right (the detail
       card + Permissions card together - see Admin/Users.aspx) becomes a full-width fixed overlay that
       starts off-screen to the right and slides in only once .panel-open is added (an explicit
       selection - see RenderDetail in Users.aspx.cs and selectUser()/openPanel()/closePanel() in the
       page's own script block). Sits below the header (top: var(--header-height)) so the hamburger/
       theme toggle/user menu stay reachable while it's open. .master-detail-panel keeps
       its normal card look (background/rounded corners/shadow) inside this overlay. */
    .master-detail { display: block; }

    .master-detail-list { width: 100%; }

    .master-detail-right {
        position: fixed;
        top: var(--header-height);
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 45;
        overflow-y: auto;
        background: var(--page-bg);
        padding: 16px;
        transform: translateX(100%);
        transition: transform 0.28s ease;
    }

    .master-detail-right.panel-open { transform: translateX(0); }

    /* Explicit request: "dont allow user to scroll the background page" - while the detail overlay is
       open on mobile, the user-list page underneath it must not scroll too (the overlay's own
       overflow-y:auto above already lets the overlay's OWN content - both cards - scroll independently).
       Toggled on <body> by openPanel()/closePanel() in Users.aspx's own script block, alongside the
       .master-detail-right.panel-open toggle above, so they always turn on/off together. Scoped to this
       media query only - desktop's detail pane is inline, not an overlay, so there's no "background page"
       to lock there. */
    body.detail-panel-locked { overflow: hidden; }


    .master-detail-back-btn {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        color: var(--text-muted);
        text-decoration: none;
        font-weight: 600;
        font-size: 14px;
        margin-bottom: 14px;
    }

    .detail-grid { grid-template-columns: 1fr; gap: 14px; }

    /* Every "Search" button app-wide - icon-only on mobile instead of the "Search" text label,
       same icon/text-toggle pattern as .btn-icon-only-mobile (see the base .btn-search-icon-mobile-icon
       rule outside this media query for the desktop default). */
    .btn-search-icon-mobile-icon {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        font-size: 18px;
    }
    .btn-search-icon-mobile-text { display: none; }
    .btn-search-icon-mobile { padding: 10px 14px; }

    .upload-section { flex-direction: column; }

    .upload-dropzone-wrap, .upload-filelist-wrap { min-width: 0; width: 100%; }

    .mobile-section-divider { display: block; height: 1px; background: var(--card-border); margin: 20px 0; border: none; }

    .form-actions { justify-content: center; }
}

/* ==========================================================================
   AccountsReceivable.aspx - "clean & minimal" visual redesign
   Explicit request: "design it way more modern" (AR page only - AccountsPayable.aspx shares almost all
   the same class names below and is deliberately left on the original look). Every rule in this section
   is scoped under .ar-modern, the wrapper div around AccountsReceivable.aspx's whole MainContent (see
   the markup) - none of the shared selectors it overrides (.card, .ap-kpi-card, .filters-bar,
   table.data-table, .btn, ...) are touched globally, so no other page is affected.

   Direction chosen: whitespace-forward, thin 1px borders instead of heavy drop shadows, flat/muted
   colors, tighter typography hierarchy - closer to Linear/Notion than the rest of the app's current
   "floating card with soft shadow" style. Reuses all of --card-bg/--card-border/--text-dark/--text-muted
   etc. so light/dark mode both stay correct with no extra work.
   ========================================================================== */

/* ---- Page header: bigger breathing room, tighter/lighter title, ghost-style Clear All ---- */
.ar-modern .page-header-row { margin-bottom: 28px; }

.ar-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

/* Clear All - explicit request: match Company Overview's plain .btn-outline exactly (no ghosted
   transparent-until-hover override), so this button looks identical across AP/AR/Company Overview. */

/* ---- Filter bar: explicit request - no card/panel treatment, bare floating fields like Company
   Overview's filters-bar (falls through to the unscoped base .filters-bar rule). ---- */

.ar-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.ar-modern .filters-bar .form-control,
.ar-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.ar-modern .filters-bar .btn { border-radius: 10px; }

/* ---- Cards: thin border + faint shadow instead of the app-wide heavy floating shadow, more internal
   padding, more vertical rhythm between sections. Higher specificity than the base .card rule (2
   classes vs 1) so no !important is needed anywhere in this section. ---- */
.ar-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.ar-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .ar-modern .card { box-shadow: none; }

.ar-modern .card-header-row { margin-bottom: 16px; }
.ar-modern .card-title { font-size: 15px; font-weight: 600; }

.ar-modern .ap-report-grid,
.ar-modern .ar-salesperson-grid {
    margin-bottom: 24px;
}

/* ---- KPI cards: flat, bordered "stat tile" instead of a solid pastel fill - just a thin colored
   left-edge accent (still tied to the chart above each card - blue/yellow for the customer charts,
   orange/green for the salesperson charts, same hues as before) carrying the color instead of the
   whole card background. Overrides ap-kpi-card-blue/yellow/orange/green's solid `background` from the
   base rules with a transparent card + colored border-left. ---- */
.ar-modern .ap-kpi-card {
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-left-width: 3px;
    border-radius: 10px;
    padding: 18px 20px;
}

.ar-modern .ap-kpi-value { font-size: 22px; font-weight: 700; color: var(--text-dark); }
.ar-modern .ap-kpi-label { font-size: 12px; font-weight: 500; color: var(--text-muted); }
/* Overrides the app-wide "true white label text" dark-mode rule (meant for solid pastel-fill cards) -
   these cards no longer have a colored fill to contrast against, so the label goes back to the usual
   muted grey like everywhere else. */
html[data-theme="dark"] .ar-modern .ap-kpi-label { color: var(--text-muted); }

.ar-modern .ap-kpi-card-blue { border-left-color: #4C8DF2; background: var(--card-bg); }
.ar-modern .ap-kpi-card-yellow { border-left-color: #E8C547; background: var(--card-bg); }
.ar-modern .ap-kpi-card-orange { border-left-color: #F2764B; background: var(--card-bg); }
.ar-modern .ap-kpi-card-green { border-left-color: #8DC96B; background: var(--card-bg); }
html[data-theme="dark"] .ar-modern .ap-kpi-card-blue,
html[data-theme="dark"] .ar-modern .ap-kpi-card-yellow,
html[data-theme="dark"] .ar-modern .ap-kpi-card-orange,
html[data-theme="dark"] .ar-modern .ap-kpi-card-green {
    background: var(--card-bg);
}

/* ---- Transaction table: quieter header (no filled background, just a rule beneath it), a hover row
   highlight, and roomier cell padding - reads closer to a modern data-grid than the app's default
   filled-header table. ---- */
.ar-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .ar-modern table.data-table th { background: transparent; }

.ar-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.ar-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

/* ---- Buttons: match the cards'/filter bar's slightly larger 10px radius (was 8px app-wide). ---- */
.ar-modern .btn { border-radius: 10px; }

/* Explicit request: "Duplicate it and redesign the whole thing like the new Company Overview page."
   The redesigned KPI-card/trend-chart layout was originally trialled on a separate
   "AccountsReceivable2.aspx" page (reusing THIS SAME .ar-modern wrapper, not a separate .ar-modern
   class) and has since replaced AccountsReceivable.aspx outright (explicit follow-up request: "replace
   all the original pages with the new page. Keep the old page icon in the left menu bar.") - see the
   matching comment on .ap-modern below for the full reasoning (identical here, just for AR). */
.ar-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

/* ==========================================================================
   AccountsPayable.aspx - "clean & minimal" visual redesign
   Explicit request: "redesign the same in AP as well" - the exact same treatment as .ar-modern above
   (AccountsReceivable.aspx), just scoped to .ap-modern (the wrapper div around AccountsPayable.aspx's
   whole MainContent - see the markup) and with AP's own element IDs/classes. AP only ever had
   orange/green KPI cards (no blue/yellow customer-chart variant the way AR does) and has no
   salesperson-flavored second grid, so this section is shorter than .ar-modern's - otherwise identical
   rule-for-rule. Kept as its own separate block (not a shared ".ar-modern, .ap-modern" selector list)
   so either page's rules can keep evolving independently without touching the other.
   ========================================================================== */

.ap-modern .page-header-row { margin-bottom: 28px; }

.ap-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

/* Clear All - explicit request: match Company Overview's plain .btn-outline exactly (no ghosted
   transparent-until-hover override), so this button looks identical across AP/AR/Company Overview. */

/* ---- Filter bar: explicit request - no card/panel treatment, bare floating fields like Company
   Overview's filters-bar (falls through to the unscoped base .filters-bar rule). ---- */

.ap-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.ap-modern .filters-bar .form-control,
.ap-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.ap-modern .filters-bar .btn { border-radius: 10px; }

.ap-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.ap-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .ap-modern .card { box-shadow: none; }

.ap-modern .card-header-row { margin-bottom: 16px; }
.ap-modern .card-title { font-size: 15px; font-weight: 600; }

.ap-modern .ap-report-grid { margin-bottom: 24px; }

.ap-modern .ap-kpi-card {
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-left-width: 3px;
    border-radius: 10px;
    padding: 18px 20px;
}

.ap-modern .ap-kpi-value { font-size: 22px; font-weight: 700; color: var(--text-dark); }
.ap-modern .ap-kpi-label { font-size: 12px; font-weight: 500; color: var(--text-muted); }
html[data-theme="dark"] .ap-modern .ap-kpi-label { color: var(--text-muted); }

.ap-modern .ap-kpi-card-orange { border-left-color: #F2764B; background: var(--card-bg); }
.ap-modern .ap-kpi-card-green { border-left-color: #8DC96B; background: var(--card-bg); }
html[data-theme="dark"] .ap-modern .ap-kpi-card-orange,
html[data-theme="dark"] .ap-modern .ap-kpi-card-green {
    background: var(--card-bg);
}

.ap-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .ap-modern table.data-table th { background: transparent; }

.ap-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.ap-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.ap-modern .btn { border-radius: 10px; }

/* Explicit request: "Duplicate it and redesign the whole thing like the new Company Overview page."
   The redesigned KPI-card/trend-chart layout was originally trialled on a separate
   "AccountsPayable2.aspx" page (reusing THIS SAME .ap-modern wrapper, not a separate .ap-modern class,
   so it inherited every rule above for free) and has since replaced AccountsPayable.aspx outright
   (explicit follow-up request: "replace all the original pages with the new page. Keep the old page
   icon in the left menu bar."). It also reuses CompanyOverviewNetIncome.aspx's own bare/global
   .co-kpi-*/.co-card/.co-chart-* classes for its KPI row + charts - those classes read colors via
   var(--co-teal) etc., which only .co-modern defined until now, so the same 5 variables are added
   here too. */
.ap-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

/* ==========================================================================
   CompanyOverviewNetIncome.aspx - "professional dashboard" redesign
   Explicit request: "Duplicate Company Overview - Net Income page ... redesign all the charts and
   graphs ... more professional ... I give you free will to design in this new page." This design was
   originally trialled on a separate "CompanyOverviewNetIncome2.aspx" page and has since replaced
   CompanyOverviewNetIncome.aspx outright (explicit follow-up request: "replace all the original pages
   with the new page. Keep the old page icon in the left menu bar."). Scoped to .co-modern (this page's
   own wrapper div, same pattern as .ap-modern/.ar-modern) so none of this leaks onto any other page.
   Local --co-* accent variables (not added to :root) since this is the page that needs a wider color
   palette than the app-wide --green/--blue - kept page-scoped rather than global so no other page's
   styling shifts.
   ========================================================================== */

.co-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.co-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

/* Same "no card/panel treatment" flat filter bar as the AP/AR redesign (explicit request elsewhere in
   this project: "do not display the filters inside a card") - falls straight through to the unscoped
   base .filters-bar rule, just a touch more breathing room underneath before the KPI row starts. */
.co-modern .filters-bar { margin-bottom: 22px; }

/* ---- KPI summary row ---- */
.co-kpi-row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
    margin-bottom: 20px;
}

/* 5-card variant - CustomerSalesOverview.aspx's KPI row matches the reference dashboard's own 5-card
   layout (Total Sales Amount / Start Month_Year / End Month_Year / Highest Sales Amount / Highest Sales
   Month_Year) rather than the 4-card layout every other page's .co-kpi-row uses. Same class combined with
   .co-kpi-row (class="co-kpi-row co-kpi-row-5") so it still gets every other .co-kpi-row rule (card
   styling, gap, margin) for free - only grid-template-columns is overridden, here and in the 2 responsive
   breakpoints below. */
.co-kpi-row-5 { grid-template-columns: repeat(5, 1fr); }

/* 6-card variant - SalesOrdersOverview.aspx's KPI row (Total Orders / Total Outstanding Quantity /
   Highest Order Quantity by Part Code / Highest Order by Location / Highest Quantity by Salesperson /
   Highest Quantity by Customer - see the class comment on SoHighlightStats in
   SalesOrdersOverviewModels.cs) needs one more card than any existing page's own KPI row, so it gets
   3-per-row (2 rows of 3) rather than the 4/5-across single-row layouts above, which would leave each
   card too narrow for its longer labels/values at typical desktop widths. */
.co-kpi-row-6 { grid-template-columns: repeat(3, 1fr); }

.co-kpi-card {
    display: flex;
    align-items: center;
    gap: 14px;
    background: var(--card-bg);
    border-radius: 16px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.08);
    padding: 18px 20px;
    border-left: 4px solid transparent;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    /* Explicit request: "Each cards/charts should not display out of bound ... within the padding ...
       within the max width." .co-kpi-card is a grid item (child of .co-kpi-row) - grid/flex items
       default to min-width:auto, meaning the browser won't shrink them below their content's natural
       min-content width. Without this, a fixed-width grid column combined with .co-kpi-label's nowrap
       text (below) forced this card - and the whole row/page - wider than the viewport at exactly the
       widths where the row is still 4-across (900-1080px, common tablet/small-laptop sizes) or 2-across
       (520-900px). box-sizing:border-box (site-wide default) is what then makes this an accurate "shrink
       to fit, padding included" box once it's actually allowed to shrink. */
    min-width: 0;
    box-sizing: border-box;
}
html[data-theme="dark"] .co-kpi-card { border: 1px solid var(--card-border); border-left-width: 4px; }

.co-kpi-card:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.12); }

.co-kpi-teal { border-left-color: var(--co-teal); }
.co-kpi-orange { border-left-color: var(--co-orange); }
.co-kpi-indigo { border-left-color: var(--co-indigo); }
.co-kpi-violet { border-left-color: var(--co-violet); }
/* 5th KPI card color, added for CustomerSalesOverview.aspx's 5-card row - reads --co-blue, defined
   alongside the other 5 --co-* variables in each page wrapper (.ap-modern/.ar-modern/.co-modern/
   .cs-modern) rather than only .cs-modern, so it's available to any future page too. */
.co-kpi-blue { border-left-color: var(--co-blue); }
/* 6th KPI card color, added for SalesOrdersOverview.aspx's 6-card row - reads --co-rose, already defined
   (unused until now) alongside the other 5 --co-* variables in .sp-modern; added to .so-modern below too. */
.co-kpi-rose { border-left-color: var(--co-rose); }

.co-kpi-icon {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.co-kpi-teal .co-kpi-icon { background: rgba(20, 184, 166, 0.14); color: var(--co-teal); }
.co-kpi-orange .co-kpi-icon { background: rgba(249, 115, 22, 0.14); color: var(--co-orange); }
.co-kpi-indigo .co-kpi-icon { background: rgba(99, 102, 241, 0.14); color: var(--co-indigo); }
.co-kpi-violet .co-kpi-icon { background: rgba(139, 92, 246, 0.14); color: var(--co-violet); }
.co-kpi-icon .material-icons { font-size: 22px; }
.co-kpi-blue .co-kpi-icon { background: rgba(59, 130, 246, 0.14); color: var(--co-blue); }
.co-kpi-rose .co-kpi-icon { background: rgba(244, 63, 94, 0.14); color: var(--co-rose); }

.co-kpi-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }

.co-kpi-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    /* Was white-space:nowrap - a longer label ("Highest Unpaid Transaction") combined with a narrow
       card (see .co-kpi-card's own comment) forced the card wider than its grid column instead of
       wrapping to a 2nd line, pushing the row past the page's right edge. Wrapping is a plain,
       always-safe fix - these are short category labels, a 2-line wrap on a narrow card still reads
       fine (no ellipsis/truncation ambiguity about what got cut off). */
    overflow-wrap: break-word;
}

.co-kpi-value {
    font-size: 24px;
    font-weight: 700;
    color: var(--text-dark);
    line-height: 1.2;
    overflow-wrap: break-word;
}
.co-kpi-value-negative { color: var(--co-rose); }

/* Optional second line under .co-kpi-value - explicit request (AP/AR's redesigned KPI row): "Highest
   Days Overdue"/"Highest Unpaid Transaction" need a vendor name alongside the figure, unlike Company
   Overview 2's own 4 KPI cards (plain figures, no secondary line) - empty text content just collapses to
   nothing (no stray blank line) since this has no min-height of its own. */
.co-kpi-caption {
    font-size: 12px;
    font-weight: 500;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---- Chart cards ---- */
.co-card { margin-bottom: 20px; }

.co-card .card-header-row { align-items: baseline; }

.co-card-subtitle {
    font-size: 12.5px;
    color: var(--text-muted);
    font-weight: 500;
}

/* max-width:100% - explicit request: "Everything should be display within the max width." A Chart.js
   canvas is given explicit pixel width/height attributes by Chart.js itself (responsive:true just keeps
   those in sync with the wrap's own size on resize) - normally harmless, but belt-and-braces here since
   a canvas's own CSS default (like an <img>) doesn't shrink below its attribute size the way normal
   content does. */
.co-chart-wrap { position: relative; height: 260px; max-width: 100%; }
.co-chart-wrap canvas { max-width: 100%; }
.co-chart-wrap-tall { height: 320px; }
.co-chart-wrap-doughnut { height: 260px; max-width: 360px; margin: 0 auto; }

.co-chart-row {
    display: flex;
    gap: 20px;
    align-items: stretch;
}
.co-chart-row .co-card-half { flex: 1 1 0; min-width: 0; max-width: 100%; margin-bottom: 20px; }

/* Explicit request: "cater for all kind of viewport ... should not display out of bound ... within the
   padding ... within the max width." Widened from 900px/520px - a 4-across (or 2-across) KPI row was
   still getting uncomfortably tight right below the old 900px cutoff (roughly the 900-1080px tablet/
   small-laptop range), each card only just wide enough to avoid overflowing (see .co-kpi-card's own
   min-width:0 comment for the overflow bug this combination used to cause) but too cramped to read
   comfortably - collapsing a step earlier gives every card real breathing room at every width instead of
   just technically not overflowing. */
@media (max-width: 1080px) {
    .co-kpi-row, .co-kpi-row-5, .co-kpi-row-6 { grid-template-columns: repeat(2, 1fr); }
    .co-chart-row { flex-direction: column; }
}

@media (max-width: 600px) {
    .co-kpi-row, .co-kpi-row-5, .co-kpi-row-6 { grid-template-columns: 1fr; }
}

/* ==========================================================================
   CustomerSalesOverview.aspx - "professional dashboard" redesign
   New page (explicit request: "Create a new page with page name 'Customer Sales Overview' ... use your
   freewill to redesign and display the data in a way that is easier for any users to understand and
   digest"), built directly with the SAME KPI-card + trend-line/ranking-bar visual language as .ap-modern/
   .ar-modern/.co-modern (identical card/filter-bar/table treatment - see .ap-modern's own rules for the
   full reasoning) rather than staged on a separate "2" page first, since that pattern (build on a
   duplicate page, then replace/rename) was purely a migration path for 3 pages that already existed with
   an older look - this page has no older look to migrate away from. --co-blue (the 5th KPI card color,
   for the Highest Sales Month_Year card) is defined here alongside the other 5 --co-* variables, same as
   every other page wrapper now has (see the shared .co-kpi-blue rule above). Reuses the bare/global
   .co-kpi-row/.co-kpi-row-5/.co-card/.co-chart-wrap/.co-chart-row classes as-is.
   ========================================================================== */

.cs-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.cs-modern .page-header-row { margin-bottom: 28px; }

.cs-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

/* ---- Filter bar: same "no card/panel treatment" bare floating fields as every other redesigned page. ---- */
.cs-modern .filters-bar { margin-bottom: 22px; }

.cs-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.cs-modern .filters-bar .form-control,
.cs-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.cs-modern .filters-bar .btn { border-radius: 10px; }

.cs-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.cs-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .cs-modern .card { box-shadow: none; }

.cs-modern .card-header-row { margin-bottom: 16px; }
.cs-modern .card-title { font-size: 15px; font-weight: 600; }

.cs-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .cs-modern table.data-table th { background: transparent; }

.cs-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.cs-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.cs-modern .btn { border-radius: 10px; }

/* ==========================================================================
   Salesperson Sales Overview (SalespersonSalesOverview.aspx) - identical treatment to .cs-modern above
   (explicit request: "Create a new page with page title 'Salesperson Sales Overview' ... use your
   freewill in how to design the charts") - a salesperson-performance-focused sibling of
   CustomerSalesOverview.aspx over the same SALES_SUMMARY_ALL_PRODUCTS table (see the class-level comment
   on SalespersonSalesOverviewRepository.cs for how the two pages' filters/charts differ). Own scoping
   wrapper rather than reusing .cs-modern directly, same "each page owns its own wrapper class even when
   the rules are identical" convention as .ap-modern/.ar-modern/.co-modern/.cs-modern all follow. Reuses
   the bare/global .co-kpi-row/.co-card/.co-chart-wrap/.co-chart-row classes as-is (this page's 4 KPI
   cards fit the bare .co-kpi-row grid, not .co-kpi-row-5).
   ========================================================================== */

.sp-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.sp-modern .page-header-row { margin-bottom: 28px; }

.sp-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.sp-modern .filters-bar { margin-bottom: 22px; }

.sp-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.sp-modern .filters-bar .form-control,
.sp-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.sp-modern .filters-bar .btn { border-radius: 10px; }

.sp-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.sp-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .sp-modern .card { box-shadow: none; }

.sp-modern .card-header-row { margin-bottom: 16px; }
.sp-modern .card-title { font-size: 15px; font-weight: 600; }

.sp-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .sp-modern table.data-table th { background: transparent; }

.sp-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.sp-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.sp-modern .btn { border-radius: 10px; }

/* ==========================================================================
   Sales Orders Overview (SalesOrdersOverview.aspx) - identical treatment to .sp-modern above (explicit
   request: "Create a new page title 'Sales Orders Overview' ... Same as before. You have freewill to
   redesign and/or use other charts and graphs so that the data are easily understandable by
   non-technical people") - built over the new SALES_ORDERS_SUMMARY table (see the class-level comment on
   SalesOrdersOverviewRepository.cs). Own scoping wrapper rather than reusing .sp-modern directly, same
   "each page owns its own wrapper class even when the rules are identical" convention as
   .ap-modern/.ar-modern/.co-modern/.cs-modern/.sp-modern all follow. Reuses the bare/global
   .co-kpi-row-6/.co-card/.co-chart-wrap/.co-chart-row classes as-is (this page's 6 KPI cards use the
   .co-kpi-row-6 variant added above, not the bare 4-card .co-kpi-row).
   ========================================================================== */

.so-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.so-modern .page-header-row { margin-bottom: 28px; }

.so-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.so-modern .filters-bar { margin-bottom: 22px; }

.so-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.so-modern .filters-bar .form-control,
.so-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.so-modern .filters-bar .btn { border-radius: 10px; }

.so-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.so-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .so-modern .card { box-shadow: none; }

.so-modern .card-header-row { margin-bottom: 16px; }
.so-modern .card-title { font-size: 15px; font-weight: 600; }

.so-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .so-modern table.data-table th { background: transparent; }

.so-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.so-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.so-modern .btn { border-radius: 10px; }

/* ==========================================================================
   Sales Profits Overview (SalesProfitsOverview.aspx) - identical treatment to .so-modern above (explicit
   request: "Create a new page with page title 'Sales Profits Overview'. The table name is
   SALES_SUMMARY_ALL_PRODUCTS ... You have all freewill to redesign the current dashboard to something
   easier to digest") - a profitability-focused sibling of CustomerSalesOverview.aspx/
   SalespersonSalesOverview.aspx over the same SALES_SUMMARY_ALL_PRODUCTS table (see the class-level
   comment on SalesProfitsOverviewRepository.cs). Own scoping wrapper rather than reusing .cs-modern/
   .sp-modern directly, same "each page owns its own wrapper class even when the rules are identical"
   convention as .ap-modern/.ar-modern/.co-modern/.cs-modern/.sp-modern/.so-modern all follow. Reuses the
   bare/global .co-kpi-row/.co-card/.co-chart-wrap/.co-chart-row classes as-is (this page's 4 KPI cards fit
   the bare .co-kpi-row grid, not .co-kpi-row-5/-6).
   ========================================================================== */

.po-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.po-modern .page-header-row { margin-bottom: 28px; }

.po-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.po-modern .filters-bar { margin-bottom: 22px; }

.po-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.po-modern .filters-bar .form-control,
.po-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.po-modern .filters-bar .btn { border-radius: 10px; }

.po-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.po-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .po-modern .card { box-shadow: none; }

.po-modern .card-header-row { margin-bottom: 16px; }
.po-modern .card-title { font-size: 15px; font-weight: 600; }

.po-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .po-modern table.data-table th { background: transparent; }

.po-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.po-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.po-modern .btn { border-radius: 10px; }

/* ==========================================================================
   Inventory Summary (InventorySummary.aspx) - identical treatment to .po-modern above (explicit request:
   "Create a new with title 'Inventory Summary'... You have all freewill to redesign the current dashboard
   to something easier to digest"), over IC_SUMMARY (see the class-level comment on
   InventorySummaryRepository.cs). Own scoping wrapper rather than reusing another page's directly, same
   "each page owns its own wrapper class even when the rules are identical" convention every other page
   follows. Reuses the bare/global .co-kpi-row/.co-card/.co-chart-wrap/.co-chart-row classes as-is (this
   page's 4 KPI cards fit the bare .co-kpi-row grid, not .co-kpi-row-5/-6).
   ========================================================================== */

.is-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.is-modern .page-header-row { margin-bottom: 28px; }

.is-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.is-modern .filters-bar { margin-bottom: 22px; }

.is-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.is-modern .filters-bar .form-control,
.is-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.is-modern .filters-bar .btn { border-radius: 10px; }

.is-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.is-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .is-modern .card { box-shadow: none; }

.is-modern .card-header-row { margin-bottom: 16px; }
.is-modern .card-title { font-size: 15px; font-weight: 600; }

/* "Part Code by Quantity" chart's own Unit of Measure legend - explicit follow-up request: "Add in
   legends by Unit Of Measures", matching the reference dashboard's own "UNIT OF MEASURE  •BAG •BOX ..."
   legend row. Built by renderIsPartCodeQuantityLegend() in site.js from the same color map
   buildIsPartCodeQuantityChart() colors each bar with. Sits between the card title and the chart itself,
   same position as the reference screenshot's own legend. Empty (no children) when the Unit of Measure
   filter has no options yet, so it takes up no space rather than showing a blank row. */
.is-uom-legend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 14px;
    margin: -6px 0 16px;
}

.is-uom-legend-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    text-transform: uppercase;
    margin-right: 4px;
}

.is-uom-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    color: var(--text-dark);
    /* Clickable - explicit request: "i cant click on the legends in inventory summary page" - see
       wireIsUomLegendClick() in site.js. transition matches the opacity change below so dimming in/out
       reads as a soft fade rather than an abrupt cut. */
    cursor: pointer;
    border-radius: 4px;
    padding: 2px 4px;
    margin: -2px -4px;
    transition: opacity 0.15s ease, background 0.15s ease;
}

.is-uom-legend-item:hover { background: var(--surface-tint); }

/* Applied to every unit NOT currently selected, only once 1+ units ARE selected (see syncVisuals() in
   wireIsUomLegendClick()) - same "dim everything except what's selected" language as
   coHighlightBarSlice/coLegendPluginConfig's own legend dimming elsewhere in the app, just via a plain CSS
   class here since this legend isn't Chart.js-rendered. */
.is-uom-legend-item-dimmed { opacity: 0.35; }

.is-uom-legend-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    flex-shrink: 0;
}

/* Outer scroll region for the "View Details" pop-out (#isWarehousePartCodesModal) - one accordion group
   per warehouse (follow-up request: "group by Warehouse in an accordion"), built entirely client-side by
   renderIsWarehousePartCodesAccordion() in site.js. Capped so a long list of warehouses (collapsed
   headers) scrolls within the modal instead of pushing it off-screen; a touch shorter than a bare
   .modal-box would allow, leaving room for the card-header-row above it. */
.is-wh-modal-accordion-wrap {
    max-height: 75vh;
    overflow-y: auto;
    padding-right: 4px;
}

/* Small muted "N part codes - X total qty" summary shown inline after the warehouse name in each
   accordion header (see renderIsWarehousePartCodesAccordion() in site.js) - lets the user tell which
   warehouses are worth opening without expanding every one. */
.is-wh-accordion-meta {
    margin-left: 10px;
    font-size: 12.5px;
    font-weight: 400;
    color: var(--text-muted);
}

/* Per-warehouse scrollable table inside one accordion body (#isWarehousePartCodesModal) - unrestricted
   (not capped at 15 like the ranking charts, see GetPartCodeQuantitiesForWarehouse() in
   InventorySummaryRepository.cs) so a busy warehouse's list can run long. thead stays visible while the
   body scrolls, same "sticky header inside a capped-height scroll area" treatment as every other
   long-list table in the app. Shorter than the old single-table modal's 60vh since this now nests inside
   .is-wh-modal-accordion-wrap alongside the other (collapsed) warehouse headers. */
.is-warehouse-modal-table-wrap {
    max-height: 42vh;
    overflow-y: auto;
}

/* table.data-table's own border-collapse: collapse (site.css above) is what was causing the sticky header
   to visually overlap the scrolling rows behind it instead of staying opaque above them - a well-known
   browser limitation where position: sticky on a <th>/<tr> doesn't reserve/paint correctly inside a
   collapsed-border table. border-spacing: 0 keeps the exact same tight, gapless cell appearance
   border-collapse: collapse gave every other .data-table in the app, without triggering that bug - scoped
   to just this modal's table so no other table on the site is affected. */
.is-warehouse-modal-table-wrap table {
    border-collapse: separate;
    border-spacing: 0;
}

.is-warehouse-modal-table-wrap thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    /* !important: the real bug behind the header/row overlap - ".is-modern table.data-table th" below
       (2 classes) beats this selector's specificity (1 class) and comes later in the file, so it was
       winning and forcing "background: transparent" onto these header cells. A transparent sticky header
       has nothing opaque to paint over the rows scrolling underneath it, which is exactly what read as
       "overlapping" text. border-collapse was a red herring - this is the actual cause. */
    background: var(--card-bg, #fff) !important;
    /* Own bottom border (rather than relying on the row below's top border, which border-spacing: 0 above
       still renders independently) so the header/body boundary reads the same as it did under
       border-collapse: collapse. */
    box-shadow: inset 0 -1px 0 var(--card-border);
}

html[data-theme="dark"] .is-warehouse-modal-table-wrap thead th { background: var(--card-bg) !important; }

/* Thin, theme-aware scrollbar for both of this modal's scroll regions (the outer accordion list AND each
   warehouse's own per-table scroll area) - explicit request: "make the scrollbar as thin as possible and
   cater for both light and dark theme" (follow-up bug report: "in dark theme, the scroll bar is white
   color" - with no rule here at all, both regions were just rendering the browser/OS's own default
   scrollbar, which doesn't follow the app's theme on its own). scrollbar-color/-width cover Firefox; the
   ::-webkit-scrollbar-* pseudo-elements below cover Chrome/Edge/Safari. Both read var(--card-border) for
   the thumb, which already flips to a darker shade under html[data-theme="dark"] (see the :root/dark
   theme variable blocks at the top of this file), so no separate dark-mode override is needed here. */
.is-wh-modal-accordion-wrap,
.is-warehouse-modal-table-wrap {
    scrollbar-width: thin;
    scrollbar-color: var(--card-border) transparent;
}

.is-wh-modal-accordion-wrap::-webkit-scrollbar,
.is-warehouse-modal-table-wrap::-webkit-scrollbar {
    width: 6px;
}

.is-wh-modal-accordion-wrap::-webkit-scrollbar-track,
.is-warehouse-modal-table-wrap::-webkit-scrollbar-track {
    background: transparent;
}

.is-wh-modal-accordion-wrap::-webkit-scrollbar-thumb,
.is-warehouse-modal-table-wrap::-webkit-scrollbar-thumb {
    background-color: var(--card-border);
    border-radius: 4px;
}

.is-wh-modal-accordion-wrap::-webkit-scrollbar-thumb:hover,
.is-warehouse-modal-table-wrap::-webkit-scrollbar-thumb:hover {
    background-color: var(--text-muted);
}

.is-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .is-modern table.data-table th { background: transparent; }

.is-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.is-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.is-modern .btn { border-radius: 10px; }

/* ==========================================================================
   Maintenance Overview (MaintenanceOverview.aspx) - identical treatment to .is-modern above (explicit
   request: 'Create a new with title "Maintenance Overview"... You have all freewill to redesign the
   current dashboard to something easier to digest and easier to view by users'), over MAINTENANCE_SUMMARY
   (see the class-level comment on MaintenanceOverviewRepository.cs). Own scoping wrapper rather than
   reusing another page's directly, same "each page owns its own wrapper class even when the rules are
   identical" convention every other page follows. Reuses the bare/global .co-kpi-row/.co-card/
   .co-chart-wrap/.co-chart-row classes as-is (this page's 4 KPI cards fit the bare .co-kpi-row grid, not
   .co-kpi-row-5/-6).
   ========================================================================== */

.mo-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.mo-modern .page-header-row { margin-bottom: 28px; }

.mo-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.mo-modern .filters-bar { margin-bottom: 22px; }

.mo-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.mo-modern .filters-bar .form-control,
.mo-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.mo-modern .filters-bar .btn { border-radius: 10px; }

.mo-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.mo-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .mo-modern .card { box-shadow: none; }

.mo-modern .card-header-row { margin-bottom: 16px; }
.mo-modern .card-title { font-size: 15px; font-weight: 600; }

/* Small "0-7 days = A ... " key under the Completion Turnaround chart's own title - explicit request
   context: the source Power BI report's own "Completion Date Ranges" panel was a static text legend with
   no chart backing it at all (see the class-level markup comment on MaintenanceOverview.aspx); this page
   turns that into a real bar chart instead, but keeps a compact one-line version of the same bucket key
   directly above it so the A/B/C/D labels on the chart's own Y-axis still read as self-explanatory to a
   factory-floor user without a second card to cross-reference. */
.mo-turnaround-legend {
    font-size: 12px;
    color: var(--text-muted);
    margin: -6px 0 16px;
}

.mo-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .mo-modern table.data-table th { background: transparent; }

.mo-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.mo-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.mo-modern .btn { border-radius: 10px; }

/* ==========================================================================
   Maintenance Cost Overview (MaintenanceCostOverview.aspx)
   New page (explicit request: 'Create a new with title "Maintenance Cost Overview"... I want the whole
   dashboard to be clear and easy to understand charts, graphs and cards. So that non technical people and
   factory workers can understand easily... You have all freewill to redesign the current dashboard to
   something easier to digest and easier to view by users'), reading MAINTENANCE_COST_SUMMARY (see the
   class-level comment on MaintenanceCostOverviewRepository.cs). Own scoping wrapper rather than reusing
   .mo-modern directly, same "each page owns its own wrapper class even when the rules are identical"
   convention every other page follows - identical ruleset to .mo-modern above (this page's 4 KPI cards
   also fit the bare .co-kpi-row grid, not .co-kpi-row-5/-6).
   ========================================================================== */

.mc-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.mc-modern .page-header-row { margin-bottom: 28px; }

.mc-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.mc-modern .filters-bar { margin-bottom: 22px; }

.mc-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.mc-modern .filters-bar .form-control,
.mc-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.mc-modern .filters-bar .btn { border-radius: 10px; }

.mc-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.mc-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .mc-modern .card { box-shadow: none; }

.mc-modern .card-header-row { margin-bottom: 16px; }
.mc-modern .card-title { font-size: 15px; font-weight: 600; }

.mc-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .mc-modern table.data-table th { background: transparent; }

.mc-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.mc-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.mc-modern .btn { border-radius: 10px; }

/* ==========================================================================
   PO Overview (PurchasingPoOverview.aspx)
   New page (explicit request: 'Create a new with title "PO Overview"... I want the whole dashboard to be
   clear and easy to understand charts, graphs and cards. So that non technical people and factory workers
   can understand easily... You have all freewill to redesign the current dashboard to something easier to
   digest and easier to view by users'), reading PURCHASING_PO_SUMMARY (see the class-level comment on
   PurchasingPoOverviewRepository.cs). Own scoping wrapper rather than reusing .mc-modern/.ap-modern
   directly, same "each page owns its own wrapper class even when the rules are identical" convention every
   other page follows - identical ruleset to .mc-modern above (this page's 4 KPI cards also fit the bare
   .co-kpi-row grid).

   Naming note: "pu" (Purchasing), not "po" - this app's existing "po" short prefix already belongs to
   SalesProfitsOverview.aspx ("Profits Overview") - see the class-level markup comment on
   PurchasingPoOverview.aspx for why.
   ========================================================================== */

.pu-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.pu-modern .page-header-row { margin-bottom: 28px; }

.pu-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.pu-modern .filters-bar { margin-bottom: 22px; }

.pu-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.pu-modern .filters-bar .form-control,
.pu-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.pu-modern .filters-bar .btn { border-radius: 10px; }

.pu-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.pu-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .pu-modern .card { box-shadow: none; }

.pu-modern .card-header-row { margin-bottom: 16px; }
.pu-modern .card-title { font-size: 15px; font-weight: 600; }

.pu-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .pu-modern table.data-table th { background: transparent; }

.pu-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.pu-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.pu-modern .btn { border-radius: 10px; }

/* ==========================================================================
   PO - Delivery Performance (PurchasingDeliveryPerformance.aspx) - explicit request: 'Create a new with
   title "PO - Delivery Performance"... I want the whole dashboard to be clear and easy to understand
   charts, graphs and cards. So that non technical people and factory workers can understand easily...
   You have all freewill to redesign the current dashboard.' Byte-identical to .pu-modern above (which is
   itself byte-identical to .mc-modern) - same shared visual language across every dashboard page in this
   app, just scoped under its own page-wrapper class.

   Naming note: "dp" (Delivery Performance), not "pu" or "po" - this app's "pu" short prefix already
   belongs to PurchasingPoOverview.aspx and "po" belongs to SalesProfitsOverview.aspx - verified "dp" is
   unclaimed via grep before writing any of this page's code - see the class-level markup comment on
   PurchasingDeliveryPerformance.aspx for why.
   ========================================================================== */

.dp-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.dp-modern .page-header-row { margin-bottom: 28px; }

.dp-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.dp-modern .filters-bar { margin-bottom: 22px; }

.dp-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.dp-modern .filters-bar .form-control,
.dp-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.dp-modern .filters-bar .btn { border-radius: 10px; }

.dp-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.dp-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .dp-modern .card { box-shadow: none; }

.dp-modern .card-header-row { margin-bottom: 16px; }
.dp-modern .card-title { font-size: 15px; font-weight: 600; }

.dp-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .dp-modern table.data-table th { background: transparent; }

.dp-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.dp-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.dp-modern .btn { border-radius: 10px; }

/* "Late Deliveries by Vendor" + "Average Days Late by Vendor" - explicit request: "these 2 charts can
   display side by side" - paired UNCONDITIONALLY (every viewport/mode), not just in wide/fullscreen mode
   the way PurchasingPoOverview.aspx's own .pu-cc-partcode-row still is - same always-on
   display:flex/gap/align-items:stretch shape as the shared .co-chart-row, plus the same <=1080px
   stack-to-column breakpoint .co-chart-row itself uses (see its own @media rule above), reimplemented here
   under a page-scoped class rather than swapping in .co-chart-row/.co-card-half directly, since these 2
   cards use the dynamic-height .vendor-chart-scroll/.ap-chart-canvas-wrap ranking-bar sizing
   (buildMoRankingBarChart sets an explicit px height based on row count), not .co-chart-wrap's fixed-height
   donut sizing that .co-card-half assumes everywhere else on this page. */
.dp-modern .dp-vendor-row {
    display: flex;
    gap: 20px;
    align-items: stretch;
}
.dp-modern .dp-vendor-row .card.co-card {
    flex: 1 1 0;
    min-width: 0;
    max-width: 100%;
    margin-bottom: 20px;
}
@media (max-width: 1080px) {
    .dp-modern .dp-vendor-row { flex-direction: column; }
}

/* Explicit follow-up request: "stack all 4 [KPI] cards, then display the pie chart on the right of the
   card. Same for fullscreen mode as well." The 4 .co-kpi-card elements now live inside a .dp-kpi-stack
   wrapper, and the "Goods Received Status" donut card (.dp-kpi-donut-card) was moved to be the 2nd child of
   .co-kpi-row itself (see PurchasingDeliveryPerformance.aspx) rather than its own separate full-width card
   below - this override turns .co-kpi-row from the shared 4-column CSS-grid every other page uses into a
   2-item flex row (stack | donut) for this page only. */
.dp-modern .co-kpi-row {
    display: flex;
    align-items: stretch;
    gap: 20px;
}
.dp-modern .co-kpi-row .dp-kpi-stack {
    /* Explicit follow-up correction: "not like this. Keep the same height but change the card width
       only for this page." The 4 .co-kpi-card elements below are the plain, full-size, unmodified base
       .co-kpi-card style (same height as every other page's KPI cards) - no icon/padding/font shrinking
       here. Only the STACK's own width is narrowed: flex: 0 0 (fixed, no grow/shrink) at a set px width
       instead of the old flex: 1 1 340px (which grew to roughly split the row evenly with the donut) -
       so this column now takes a smaller, fixed share of the row and the donut (flex: 1 1 0 below) grows
       to fill the rest. .co-kpi-label's existing overflow-wrap: break-word (see its own rule) already
       handles a 2-line label wrap gracefully on a narrower card, same as it does on smaller viewports
       elsewhere in the app - no extra CSS needed for that. */
    display: flex;
    flex-direction: column;
    gap: 16px;
    flex: 0 0 220px;
    min-width: 180px;
}
.dp-modern .co-kpi-row .dp-kpi-donut-card {
    flex: 1 1 0;
    min-width: 0;
    margin-bottom: 0;
    display: flex;
    flex-direction: column;
}
.dp-modern .co-kpi-row .dp-kpi-donut-card .co-chart-wrap {
    flex: 1 1 auto;
}
@media (max-width: 1080px) {
    .dp-modern .co-kpi-row { flex-direction: column; }
    .dp-modern .co-kpi-row .dp-kpi-stack { flex: 1 1 auto; min-width: 0; }
}

/* "Late Deliveries by Vendor - Monthly Trend" 1Y/2Y/3Y range toggle - explicit request: "put a selection in
   the same row as the chart title with 1Y, 2Y, 3Y. Default to 3 years." Reuses the shared
   .chart-range-toggle/.chart-range-btn/.chart-range-btn-active classes (see their base rules above, used by
   AP/AR/CS/SP/SO/PO's own Trend-chart range toggles) purely for the pill-button visual style - the click
   behavior behind THIS toggle is page-scoped/client-side only (see initDpVendorTrendRangeToggle in site.js),
   not the shared initChartTrendRangeToggle()/AnalyticsMonthYearPicker mechanism, since this toggle must only
   affect this one chart and must not trigger a server re-fetch of the whole page. No extra CSS needed beyond
   the shared classes already provide. */

/* ==========================================================================
   Outstanding Requisition Overview (OutstandingRequisitionOverview.aspx) - explicit request: 'Create a new
   with title "Outstanding Requisition Overview"... I want the whole dashboard to be clear and easy to
   understand charts, graphs and cards. So that non technical people and factory workers can understand
   easily... You have all freewill to redesign the current dashboard.' Byte-identical to .dp-modern above
   (itself byte-identical to .pu-modern/.mc-modern) - same shared visual language across every dashboard
   page in this app, just scoped under its own page-wrapper class. The KPI-stack + donut combined-row layout
   (.co-kpi-row > .rq-kpi-stack + .rq-kpi-donut-card) is applied here from day one, not as a later follow-up
   the way it was added to .dp-modern - see that section's own comments for the full reasoning on why
   reusing .co-kpi-row itself (rather than a new wrapper) is what makes the donut sit beside the KPI stack
   in both normal and wide/fullscreen mode for free.

   Naming note: "rq" (Requisition) - this app's ap/ar/co/cs/sp/so/po/is/mo/mc/pu/dp short prefixes are all
   already claimed (see the class-level comments on those pages/tables) - verified "rq" is unclaimed via
   grep before writing any of this page's code - see the class-level markup comment on
   OutstandingRequisitionOverview.aspx for why.
   ========================================================================== */

.rq-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.rq-modern .page-header-row { margin-bottom: 28px; }

.rq-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.rq-modern .filters-bar { margin-bottom: 22px; }

.rq-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.rq-modern .filters-bar .form-control,
.rq-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.rq-modern .filters-bar .btn { border-radius: 10px; }

.rq-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.rq-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .rq-modern .card { box-shadow: none; }

.rq-modern .card-header-row { margin-bottom: 16px; }
.rq-modern .card-title { font-size: 15px; font-weight: 600; }

.rq-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .rq-modern table.data-table th { background: transparent; }

.rq-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.rq-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.rq-modern .btn { border-radius: 10px; }

/* "Top Vendors by Outstanding Requisitions" + "Top Vendors by Outstanding Value" - same "2 different
   measures of the same question" pairing as PurchasingDeliveryPerformance.aspx's own Late Deliveries/
   Average Days Late vendor charts, paired side by side unconditionally via the same always-on
   display:flex/gap/align-items:stretch shape as .dp-vendor-row, plus the same <=1080px stack-to-column
   breakpoint. Reimplemented under its own page-scoped class rather than reusing .co-chart-row/
   .co-card-half directly, for the same reason .dp-vendor-row is - these 2 cards use the dynamic-height
   .vendor-chart-scroll/.ap-chart-canvas-wrap ranking-bar sizing (buildMoRankingBarChart sets an explicit
   px height based on row count), not .co-chart-wrap's fixed-height donut sizing .co-card-half assumes
   everywhere else on this page. */
.rq-modern .rq-vendor-row {
    display: flex;
    gap: 20px;
    align-items: stretch;
}
.rq-modern .rq-vendor-row .card.co-card {
    flex: 1 1 0;
    min-width: 0;
    max-width: 100%;
    margin-bottom: 20px;
}
@media (max-width: 1080px) {
    .rq-modern .rq-vendor-row { flex-direction: column; }
}

/* KPI row + "Requisition Status Breakdown" donut, side by side - same .co-kpi-row flex override
   PurchasingDeliveryPerformance.aspx's own .dp-modern .co-kpi-row uses (see that section's full comment
   for the "why reuse .co-kpi-row itself" reasoning and the align-items:stretch-vs-center tradeoff already
   worked out there). Applied here from the start rather than as a later follow-up. */
.rq-modern .co-kpi-row {
    display: flex;
    align-items: stretch;
    gap: 20px;
}
.rq-modern .co-kpi-row .rq-kpi-stack {
    display: flex;
    flex-direction: column;
    gap: 16px;
    flex: 0 0 220px;
    min-width: 180px;
}
.rq-modern .co-kpi-row .rq-kpi-donut-card {
    flex: 1 1 0;
    min-width: 0;
    margin-bottom: 0;
    display: flex;
    flex-direction: column;
}
.rq-modern .co-kpi-row .rq-kpi-donut-card .co-chart-wrap {
    flex: 1 1 auto;
}
@media (max-width: 1080px) {
    .rq-modern .co-kpi-row { flex-direction: column; }
    .rq-modern .co-kpi-row .rq-kpi-stack { flex: 1 1 auto; min-width: 0; }
}

/* ==========================================================================
   Machine Summary (MachineSummary.aspx) - explicit request: 'Create a new with title "Machine Summary"...
   I want the whole dashboard to be clear and easy to understand charts, graphs and cards. So that non
   technical people and factory workers can understand easily... You have all freewill to redesign the
   current dashboard.' Byte-identical to .rq-modern above (itself byte-identical to .dp-modern/.pu-modern/
   .mc-modern) - same shared visual language across every dashboard page in this app, just scoped under its
   own page-wrapper class. The KPI-stack + donut combined-row layout (.co-kpi-row > .ms-kpi-stack +
   .ms-kpi-donut-card) is applied here from day one, same as .rq-modern's own from-day-one adoption - see
   that section's own comments for the full reasoning on why reusing .co-kpi-row itself (rather than a new
   wrapper) is what makes the donut sit beside the KPI stack in both normal and wide/fullscreen mode for
   free.

   Naming note: "ms" (Machine Summary) - this app's ap/ar/co/cs/sp/so/po/is/mo/mc/pu/dp/rq short prefixes
   are all already claimed (see the class-level comments on those pages/tables) - verified "ms" is unclaimed
   via grep before writing any of this page's code - see the class-level markup comment on
   MachineSummary.aspx for why.
   ========================================================================== */

.ms-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.ms-modern .page-header-row { margin-bottom: 28px; }

.ms-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.ms-modern .filters-bar { margin-bottom: 22px; }

.ms-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.ms-modern .filters-bar .form-control,
.ms-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.ms-modern .filters-bar .btn { border-radius: 10px; }

.ms-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.ms-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .ms-modern .card { box-shadow: none; }

.ms-modern .card-header-row { margin-bottom: 16px; }
.ms-modern .card-title { font-size: 15px; font-weight: 600; }

.ms-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .ms-modern table.data-table th { background: transparent; }

.ms-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.ms-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.ms-modern .btn { border-radius: 10px; }

/* "Runtime by Machine Code" + "Utilization % by Machine Code" (and, in the 2nd usage further down the
   page, "Machines Needing Attention - Lowest Utilization" + "Runtime by Job Number") - same "2 different
   measures of the same question" pairing as OutstandingRequisitionOverview.aspx's own vendor-ranking pair,
   paired side by side unconditionally via the same always-on display:flex/gap/align-items:stretch shape as
   .rq-vendor-row, plus the same <=1080px stack-to-column breakpoint. Reimplemented under its own
   page-scoped class rather than reusing .co-chart-row/.co-card-half directly, for the same reason
   .rq-vendor-row is - these cards use the dynamic-height .vendor-chart-scroll/.ap-chart-canvas-wrap
   ranking-bar sizing (buildMoRankingBarChart sets an explicit px height based on row count), not
   .co-chart-wrap's fixed-height donut sizing .co-card-half assumes everywhere else on this page. Named
   generically (.ms-machine-row, not .ms-vendor-row) since this page reuses the same row shape twice for 2
   different chart pairs, not just one. */
.ms-modern .ms-machine-row {
    display: flex;
    gap: 20px;
    align-items: stretch;
}
.ms-modern .ms-machine-row .card.co-card {
    flex: 1 1 0;
    min-width: 0;
    max-width: 100%;
    margin-bottom: 20px;
}
@media (max-width: 1080px) {
    .ms-modern .ms-machine-row { flex-direction: column; }
}

/* KPI row + "Machine Utilization Status" donut, side by side - same .co-kpi-row flex override every other
   page's own KPI-stack-plus-donut layout uses (see .rq-modern's own section above for the full "why reuse
   .co-kpi-row itself" reasoning). Applied here from the start rather than as a later follow-up. */
.ms-modern .co-kpi-row {
    display: flex;
    align-items: stretch;
    gap: 20px;
}
.ms-modern .co-kpi-row .ms-kpi-stack {
    display: flex;
    flex-direction: column;
    gap: 16px;
    flex: 0 0 220px;
    min-width: 180px;
}
.ms-modern .co-kpi-row .ms-kpi-donut-card {
    flex: 1 1 0;
    min-width: 0;
    margin-bottom: 0;
    display: flex;
    flex-direction: column;
}
.ms-modern .co-kpi-row .ms-kpi-donut-card .co-chart-wrap {
    flex: 1 1 auto;
}
@media (max-width: 1080px) {
    .ms-modern .co-kpi-row { flex-direction: column; }
    .ms-modern .co-kpi-row .ms-kpi-stack { flex: 1 1 auto; min-width: 0; }
}

/* ==========================================================================
   Manufacturing - Usage Variance (ManufacturingVariance.aspx) - explicit request: 'Create a new with title
   "Manufacturing - Usage Variance"... I want the whole dashboard to be clear and easy to understand charts,
   graphs and cards. So that non technical people and factory workers can understand easily... You have all
   freewill to redesign the current dashboard.' Byte-identical to .ms-modern above (itself byte-identical to
   .rq-modern/.dp-modern/.pu-modern/.mc-modern) - same shared visual language across every dashboard page in
   this app, just scoped under its own page-wrapper class. The KPI-stack + donut combined-row layout
   (.co-kpi-row > .mv-kpi-stack + .mv-kpi-donut-card) is applied here from day one, same as .ms-modern's own
   from-day-one adoption - see that section's own comments for the full reasoning on why reusing .co-kpi-row
   itself (rather than a new wrapper) is what makes the donut sit beside the KPI stack in both normal and
   wide/fullscreen mode for free.

   Naming note: "mv" (Manufacturing Variance) - this app's ap/ar/co/cs/sp/so/po/is/mo/mc/pu/dp/rq/ms short
   prefixes are all already claimed (see the class-level comments on those pages/tables) - verified "mv" is
   unclaimed via grep before writing any of this page's code - see the class-level markup comment on
   ManufacturingVariance.aspx for why.
   ========================================================================== */

.mv-modern {
    --co-teal: #14b8a6;
    --co-orange: #f97316;
    --co-indigo: #6366f1;
    --co-violet: #8b5cf6;
    --co-rose: #f43f5e;
    --co-blue: #3b82f6;
}

.mv-modern .page-header-row { margin-bottom: 28px; }

.mv-modern .page-header-row h1 {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-dark);
}

.mv-modern .filters-bar { margin-bottom: 22px; }

.mv-modern .filters-bar .form-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.mv-modern .filters-bar .form-control,
.mv-modern .filters-bar .custom-select-trigger {
    border-radius: 10px;
    border-color: var(--card-border);
    padding: 9px 12px;
    font-size: 13.5px;
}

.mv-modern .filters-bar .btn { border-radius: 10px; }

.mv-modern .card {
    border: 1px solid var(--card-border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    border-radius: 14px;
    padding: 22px;
    transition: box-shadow 0.15s ease;
}
.mv-modern .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
html[data-theme="dark"] .mv-modern .card { box-shadow: none; }

.mv-modern .card-header-row { margin-bottom: 16px; }
.mv-modern .card-title { font-size: 15px; font-weight: 600; }

.mv-modern table.data-table th {
    background: transparent;
    color: var(--text-muted);
    font-size: 10.5px;
    letter-spacing: 0.05em;
    padding: 12px 14px;
    border-bottom: 1px solid var(--card-border);
}
html[data-theme="dark"] .mv-modern table.data-table th { background: transparent; }

.mv-modern table.data-table td {
    padding: 12px 14px;
    font-size: 13px;
    color: var(--text-dark);
}

.mv-modern table.data-table tbody tr:hover td { background: var(--surface-tint); }

.mv-modern .btn { border-radius: 10px; }

/* "Wastage Quantity by Job Number" + "Wastage Cost by Job Number" (and, in the 2nd usage further down the
   page, "Wastage by Output Product" + "Wastage by Input Part Code") - same "2 different measures of the
   same question" pairing as MachineSummary.aspx's own .ms-machine-row (itself mirroring
   OutstandingRequisitionOverview.aspx's .rq-vendor-row), paired side by side unconditionally via the same
   always-on display:flex/gap/align-items:stretch shape, plus the same <=1080px stack-to-column breakpoint.
   Reimplemented under its own page-scoped class for the same reason .ms-machine-row is - these cards use
   the dynamic-height ranking-bar sizing, not .co-chart-wrap's fixed-height donut sizing .co-card-half
   assumes everywhere else on this page. Named generically (.mv-job-row, not .mv-vendor-row) since this page
   reuses the same row shape twice for 2 different chart pairs, not just one. */
.mv-modern .mv-job-row {
    display: flex;
    gap: 20px;
    align-items: stretch;
}
.mv-modern .mv-job-row .card.co-card {
    flex: 1 1 0;
    min-width: 0;
    max-width: 100%;
    margin-bottom: 20px;
}
@media (max-width: 1080px) {
    .mv-modern .mv-job-row { flex-direction: column; }
}

/* KPI row + "Job Wastage Status" donut, side by side - same .co-kpi-row flex override every other page's
   own KPI-stack-plus-donut layout uses (see .ms-modern's own section above for the full "why reuse
   .co-kpi-row itself" reasoning). Applied here from the start rather than as a later follow-up. */
.mv-modern .co-kpi-row {
    display: flex;
    align-items: stretch;
    gap: 20px;
}
.mv-modern .co-kpi-row .mv-kpi-stack {
    display: flex;
    flex-direction: column;
    gap: 16px;
    flex: 0 0 220px;
    min-width: 180px;
}
.mv-modern .co-kpi-row .mv-kpi-donut-card {
    flex: 1 1 0;
    min-width: 0;
    margin-bottom: 0;
    display: flex;
    flex-direction: column;
}
.mv-modern .co-kpi-row .mv-kpi-donut-card .co-chart-wrap {
    flex: 1 1 auto;
}
@media (max-width: 1080px) {
    .mv-modern .co-kpi-row { flex-direction: column; }
    .mv-modern .co-kpi-row .mv-kpi-stack { flex: 1 1 auto; min-width: 0; }
}

/* ---------------- NotFound.aspx (custom 404 page) ----------------
   Explicit request: "display the left menu bar, header and footer still. Then display this lottie
   file on the page to indicate to user that they hit a 404." Renders through Site.Master like every
   other page, so this only styles the centered empty-state content itself - the nav/header/footer
   chrome is untouched. See NotFound.aspx/.aspx.cs and initNotFoundLottie() in site.js. */
.not-found-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 40px 20px 60px;
    max-width: 620px;
    margin: 0 auto;
}

/* Fixed box the lottie animation renders its SVG into - matches the 950x847 aspect ratio of
   the source animation closely enough that it doesn't look squashed at this size. 560x500 = 280x250
   doubled (explicit request: "make the 404 lottie animation 2 times bigger"). */
.not-found-lottie {
    width: 560px;
    height: 500px;
    max-width: 100%;
}

.not-found-state h1 {
    margin: 8px 0 0;
    color: var(--text-dark);
}

.not-found-message {
    color: var(--text-muted);
    margin: 12px 0 24px;
    line-height: 1.5;
    word-break: break-word;
}

/* ==========================================================================
   Support Tickets (SupportTickets.aspx) - ticket list left, selected ticket right.
   ========================================================================== */
.st-layout {
    display: flex;
    align-items: stretch;
    gap: 20px;
    height: calc(100vh - var(--header-height) - var(--footer-height) - 64px);
    min-height: 480px;
}

/* Left list: 25% of the page, never narrower than 260px or wider than 340px. */
.st-list-panel {
    flex: 0 0 25%;
    min-width: 260px;
    max-width: 340px;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.st-list-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
}
.st-search-btn {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px;
    border: 1px solid var(--card-border);
    border-radius: 10px;
    background: var(--card-bg);
    color: var(--text-dark);
    cursor: pointer;
}
.st-search-btn:hover { background: var(--surface-tint); }

/* Staff Filters popup - floats over the ticket cards just under the toolbar. Same look as the
   upload popup (.pfu-dialog): 16px radius, card background, soft shadow, 18px bold title, X top right. */
.st-list-panel { position: relative; }
.st-search-btn { position: relative; }
.st-search-btn.active, .st-search-btn.open { border-color: var(--green); color: var(--green-dark); }
.st-filter-badge {
    position: absolute; top: -6px; right: -6px;
    min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 999px;
    background: var(--green); color: #fff;
    font-size: 11px; font-weight: 700; line-height: 18px; text-align: center;
}
/* Frosted-glass backdrop over the whole page (same as .pfu-overlay); the popup sits above it. */
.st-filter-backdrop {
    position: fixed;
    inset: 0;
    z-index: 2999;
    background: rgba(15, 23, 42, 0.28);
    backdrop-filter: blur(10px) saturate(140%);
    -webkit-backdrop-filter: blur(10px) saturate(140%);
    animation: stFilterFade .18s ease;
}
.st-filter-backdrop[hidden] { display: none; }
@keyframes stFilterFade { from { opacity: 0; } to { opacity: 1; } }
.st-filter-pop {
    position: absolute;
    top: 52px; left: 0; right: 0;
    z-index: 3000;
    display: flex; flex-direction: column;
    /* Sized to the screen, not the list panel - the panel can be short (few / no tickets, mobile),
       which squashed the popup down to just its title row. */
    max-height: calc(100vh - 160px);
    max-height: calc(100dvh - 160px);
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 16px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.25);
    animation: stFilterIn .16s ease;
}
.st-filter-pop[hidden] { display: none; }
/* Mobile: pin the popup to the screen (always fully visible, whatever the page scroll). */
@media (max-width: 768px) {
    .st-filter-pop {
        position: fixed;
        top: 16px; left: 12px; right: 12px;
        max-height: calc(100vh - 32px);
        max-height: calc(100dvh - 32px);
    }
}
@keyframes stFilterIn { from { opacity: 0; transform: translateY(-6px) scale(.98); } to { opacity: 1; transform: none; } }
.st-filter-head { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; padding: 14px 12px 4px 18px; }
.st-filter-title { flex: 1 1 auto; min-width: 0; margin: 0; font-size: 18px; font-weight: 700; color: var(--text-dark); }
.st-filter-save { flex: 0 0 auto; padding: 7px 16px; }
.st-filter-head .st-filter-close { position: static; flex: 0 0 auto; }
.st-filter-body { flex: 1 1 auto; min-height: 120px; overflow-y: auto; padding: 14px 18px 6px; }
.st-filter-body .form-group { margin-bottom: 12px; }
/* Dark mode: an open dropdown inside the Filters popup / Ticket Details is lifted off the fields behind it - slightly
   lighter panel, lighter edge and a deeper shadow (the default dark panel is the same colour as the popup). */
html[data-theme="dark"] .st-filter-pop .custom-select-panel,
html[data-theme="dark"] #stDetails .custom-select-panel,
html[data-theme="dark"] #stDetails .split-search-results {
    background: #26323C;
    border-color: #455462;
    box-shadow: 0 14px 36px rgba(0, 0, 0, 0.65), 0 4px 10px rgba(0, 0, 0, 0.45);
}
html[data-theme="dark"] .st-filter-pop .custom-select-search,
html[data-theme="dark"] #stDetails .custom-select-search { background: #1B242B; }

/* Field titles are plain text here - clicking them does nothing (explicit request). */
.st-filter-body .form-label { pointer-events: none; cursor: default; user-select: none; }

/* Ticket Created Date: native date picker follows the theme; the calendar icon is white in dark mode. */
.st-filter-body input[type="date"] { color-scheme: light; }
.st-filter-body input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .8; }
.st-filter-body input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
html[data-theme="dark"] .st-filter-body input[type="date"] { color-scheme: dark; }
html[data-theme="dark"] .st-filter-body input[type="date"]::-webkit-calendar-picker-indicator { filter: brightness(0) invert(1); opacity: 1; }
/* Reset + Save, right-aligned directly under the X. Reset is a borderless text button, underlined on hover. */
.st-filter-actions {
    flex: 0 0 auto;
    display: flex; align-items: center; justify-content: flex-end; gap: 14px;
    padding: 0 12px 10px 18px;
    border-bottom: 1px solid var(--card-border);
}
.st-filter-reset {
    border: none; background: none; padding: 4px 2px;
    font: inherit; font-size: 14px; font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
}
.st-filter-reset:hover, .st-filter-reset:focus-visible { color: var(--text-dark); text-decoration: underline; text-underline-offset: 3px; }

/* Chip input (email-style): chips + a borderless text box inside one form-control-looking box. */
.st-chip-input {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
    height: auto; min-height: 40px;
    padding: 5px 8px;
    cursor: text;
}
.st-chip-input:focus-within { outline: 2px solid var(--blue); outline-offset: 1px; } /* same as .form-control:focus */
.st-chip-input .st-chip { max-width: 100%; padding: 3px 5px 3px 10px; font-size: 12px; gap: 6px; }
.st-chip-input .st-chip-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-chip-input .chip-remove { flex: 0 0 auto; border: none; padding: 0; cursor: pointer; }
/* Dark mode: tag pills (Filters popup + Ticket Details) a lighter blue with a soft edge, so they stand
   out from the dark field instead of blending in. */
html[data-theme="dark"] .st-chip-input .st-chip {
    background: #36557F;
    color: #EEF4FB;
    box-shadow: inset 0 0 0 1px #5577A3;
}
html[data-theme="dark"] .st-chip-input .st-chip .chip-remove { background: rgba(255, 255, 255, 0.2); color: #EEF4FB; }
html[data-theme="dark"] .st-chip-input .st-chip .chip-remove:hover { background: var(--red); color: #fff; }

/* Chip input's suggestion list: keyboard-highlighted row (arrow keys). */
.st-chip-input + .split-search-results .split-search-result-item.active { background: var(--surface-tint); }
.st-chip-input + .split-search-results { max-height: 260px; overflow-y: auto; }
.st-chip-input-field {
    flex: 1 1 80px; min-width: 80px;
    border: none; outline: none; background: transparent;
    padding: 4px 2px;
    font: inherit; color: var(--text-dark);
}
.st-search-input { flex: 1 1 auto; min-width: 0; height: 40px; visibility: hidden; }
.st-search-input.open { visibility: visible; }
.st-create-btn { flex: 0 0 auto; margin-left: auto; text-decoration: none; }
.st-create-btn:hover { text-decoration: none; }

.st-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 2px 4px 2px 2px;
}
.st-list-empty { color: var(--text-muted); font-size: 13px; text-align: center; padding: 24px 8px; }

.st-card {
    display: flex;
    gap: 12px;
    padding: 14px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 12px;
    color: var(--text-dark);
    text-decoration: none;
    transition: border-color .15s, box-shadow .15s;
}
.st-card:hover { text-decoration: none; border-color: var(--blue); }
.st-card.selected { border-color: var(--blue); box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25); }
/* Unassigned tickets: faint yellow card background (hover/selected keep the blue border). */
.st-card.st-card-unassigned { background: #FFFBE6; border-color: #F3E3A6; }
.st-card.st-card-unassigned:hover,
.st-card.st-card-unassigned.selected { border-color: var(--blue); }
html[data-theme="dark"] .st-card.st-card-unassigned { background: rgba(234, 179, 8, 0.08); border-color: rgba(234, 179, 8, 0.28); }
html[data-theme="dark"] .st-card.st-card-unassigned:hover,
html[data-theme="dark"] .st-card.st-card-unassigned.selected { border-color: var(--blue); }

/* Ticket number - plain text (no box), full ID, not padded. */
.st-card-number {
    flex: 0 0 auto;
    align-self: flex-start;
    color: var(--text-muted);
    font-weight: 700;
    font-size: 13px;
    line-height: 22px;
    font-variant-numeric: tabular-nums;
}
.st-ticket-header .st-card-number { font-size: 16px; line-height: 26px; }

.st-card-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.st-card-meta { display: flex; align-items: center; gap: 6px; min-width: 0; }
.st-card-meta > .st-priority { flex: 0 0 auto; align-self: center; }
.st-card-sep { color: var(--text-muted); font-weight: 700; }
.st-card-company { font-size: 12px; font-weight: 700; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.st-card-title { font-weight: 700; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* 2 lines max (server also cuts it to 80 characters + "..."). */
.st-card-desc {
    font-size: 13px;
    color: var(--text-muted);
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.st-priority {
    align-self: flex-start;
    display: inline-flex; align-items: center; gap: 4px;
    padding: 1px 8px;
    border-radius: 6px;
    font-size: 11px; font-weight: 600;
    border: 1px solid transparent;
}
.st-priority::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.st-priority-low    { background: #E8F1FE; color: #2F6FD6; border-color: #C9DCFB; }
.st-priority-medium { background: #FFF4DE; color: #B7791F; border-color: #F6DDA8; }
.st-priority-high   { background: #FDE8E7; color: #C53030; border-color: #F6C7C4; }
.st-priority-other  { background: var(--surface-tint); color: var(--text-muted); }
html[data-theme="dark"] .st-priority-low    { background: #1B2B45; color: #7DAAF5; border-color: #27406A; }
html[data-theme="dark"] .st-priority-medium { background: #3A2E14; color: #F0C36B; border-color: #5A4520; }
html[data-theme="dark"] .st-priority-high   { background: #3F1C1B; color: #F28B85; border-color: #5E2A28; }

/* Right side */
.st-detail {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 12px;
    overflow: hidden;
}

.st-empty-state {
    margin: auto;
    text-align: center;
    padding: 40px 24px;
    max-width: 420px;
}
.st-empty-image { width: 220px; max-width: 100%; height: auto; display: block; margin: 0 auto 16px; }
.st-empty-title { font-size: 18px; margin: 0 0 6px; color: var(--text-dark); }
.st-empty-text { margin: 0; color: var(--text-muted); font-size: 14px; }

.st-ticket { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
/* Drag & drop files onto the open ticket card -> attached to the reply (SupportTickets.aspx). */
.st-detail { position: relative; }
.st-drop-overlay {
    position: absolute; inset: 0; z-index: 50;
    display: flex; align-items: center; justify-content: center;
    padding: 12px;
    background: rgba(255, 255, 255, 0.72);
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
    pointer-events: none;   /* the card underneath keeps getting the drag events */
}
.st-drop-overlay[hidden] { display: none; }
.st-drop-overlay-inner {
    flex: 1 1 auto; align-self: stretch;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
    border: 2px dashed var(--blue); border-radius: 10px;
    color: var(--blue); font-weight: 700; font-size: 16px;
}
.st-drop-overlay-inner .material-icons { font-size: 44px; }
html[data-theme="dark"] .st-drop-overlay { background: rgba(15, 20, 26, 0.72); }
.st-back-btn { display: none; }

.st-ticket-header {
    display: flex;
    gap: 14px;
    padding: 18px 20px;
    border-bottom: 1px solid var(--card-border);
}
.st-ticket-heading { min-width: 0; flex: 1 1 auto; }
.st-ticket-title { font-size: 18px; margin: 0 0 8px; overflow-wrap: anywhere; }
.st-ticket-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: 13px; color: var(--text-muted); }
.st-ticket-meta .st-priority { align-self: center; }

/* Body = relative box so the expanded Ticket Details panel can float over the conversation. */
.st-ticket-body { position: relative; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

.st-details { position: relative; z-index: 20; border-bottom: 1px solid var(--card-border); }
.st-details-toggle {
    width: 100%;
    display: flex; align-items: center; gap: 10px;
    padding: 10px 20px;
    border: none;
    background: var(--surface-tint);
    color: var(--text-dark);
    font: inherit; font-size: 13px;
    cursor: pointer;
    text-align: left;
}
.st-details-toggle-label { font-weight: 700; }
.st-details-summary { flex: 1 1 auto; min-width: 0; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.st-details-chevron { transition: transform .2s; }
.st-details.open .st-details-chevron { transform: rotate(180deg); }

/* Collapsed by default; when open it overlays the top of the conversation (absolute, not in flow). */
.st-details-panel {
    display: none;
    position: absolute;
    left: 0; right: 0; top: 100%;
    padding: 16px 20px 6px;
    background: var(--card-bg);
    border-bottom: 1px solid var(--card-border);
    box-shadow: 0 10px 24px rgba(0,0,0,0.12);
}
.st-details.open .st-details-panel { display: block; }
.st-details-row { display: flex; flex-wrap: wrap; align-items: flex-end; column-gap: 14px; }
.st-details-row > .form-group { flex: 1 1 180px; min-width: 0; }
.st-details-row > .st-details-cc { flex: 2 1 260px; }
.st-details-row > .st-details-save { flex: 0 0 auto; }
.st-details-row + .st-details-row { margin-top: 2px; }
.st-tag-new { color: var(--green-dark); font-style: italic; }

/* Conversation */
/* Conversation = the only scrolling part of an open ticket (header above, reply box pinned below).
   It opens scrolled to the newest message (script in SupportTickets.aspx); scroll up for older ones. */
.st-conversation {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Older messages are inserted above and the script keeps your place itself - switch off the
       browser's own scroll anchoring so the two don't both adjust. */
    overflow-anchor: none;
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    background: var(--page-bg);
}
/* Lazy loading: holder at the very top of the conversation - loading skeleton / retry for older messages. */
.st-older { display: flex; flex-direction: column; gap: 14px; }
.st-older:empty, .st-older > [hidden] { display: none; }
.st-older-skeleton { display: flex; flex-direction: column; gap: 14px; }
.st-skel-row { display: flex; }
.st-skel-row.st-skel-mine { justify-content: flex-end; }
.st-skel-bubble {
    display: flex; flex-direction: column; gap: 8px;
    padding: 14px 16px;
    border-radius: 14px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
}
.st-skel-line {
    display: block; height: 10px; width: 100%;
    border-radius: 6px;
    background: linear-gradient(90deg, var(--surface-tint) 0%, var(--card-border) 50%, var(--surface-tint) 100%);
    background-size: 200% 100%;
    animation: stSkelShimmer 1.2s ease-in-out infinite;
}
.st-skel-line.st-skel-short { width: 55%; }
@keyframes stSkelShimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
@media (prefers-reduced-motion: reduce) { .st-skel-line { animation: none; } }
.st-older-end { align-self: center; padding: 2px 0 4px; font-size: 12px; font-weight: 600; color: var(--text-muted); }
.st-older-retry {
    align-self: center;
    border: none; background: none; padding: 6px 10px;
    font: inherit; font-size: 13px; font-weight: 600; color: var(--blue);
    cursor: pointer;
}
.st-older-retry:hover { text-decoration: underline; }

/* Short conversation: messages sit at the bottom, next to the reply box, like a chat app. */
.st-conversation > :first-child { margin-top: auto; }
.st-date-divider { display: flex; align-items: center; gap: 12px; color: var(--text-muted); font-size: 12px; font-weight: 600; }
.st-date-divider::before, .st-date-divider::after { content: ""; flex: 1 1 auto; height: 1px; background: var(--card-border); }

.st-message { display: flex; }
.st-message-theirs { justify-content: flex-start; }
.st-message-mine { justify-content: flex-end; }

.st-bubble {
    max-width: min(640px, 78%);
    padding: 12px 14px 8px;
    border-radius: 14px;
    font-size: 14px;
    line-height: 1.5;
    overflow-wrap: anywhere;
    box-shadow: 0 1px 2px rgba(0,0,0,0.06);
}
.st-message-theirs .st-bubble { background: var(--card-bg); border: 1px solid var(--card-border); border-bottom-left-radius: 4px; color: var(--text-dark); }
.st-message-mine .st-bubble { background: #E3F6EE; border: 1px solid #BFE8D6; border-bottom-right-radius: 4px; color: var(--text-dark); }
html[data-theme="dark"] .st-message-mine .st-bubble { background: #173A2D; border-color: #245740; }

.st-bubble-content > :first-child { margin-top: 0; }
.st-bubble-content > :last-child { margin-bottom: 0; }
.st-bubble-content ul, .st-bubble-content ol { padding-left: 22px; margin: 4px 0; }
.st-bubble-content h2 { font-size: 16px; margin: 6px 0; }
.st-bubble-content h3 { font-size: 15px; margin: 6px 0; }

/* Bottom of each bubble: who wrote it + hh:mm AM/PM. */
.st-bubble-footer {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    margin-top: 8px;
    font-size: 11px;
    color: var(--text-muted);
}
.st-bubble-author { font-weight: 700; }
.st-message-mine .st-bubble-footer { justify-content: flex-end; }

@media (max-width: 768px) {
    .st-layout { display: block; height: auto; }
    .st-list-panel { max-width: none; min-width: 0; }
    .st-list { overflow: visible; }
    .st-layout .st-detail { display: none; }
    .st-layout.st-has-selection .st-list-panel { display: none; }
    /* Open ticket on mobile: fill the screen like desktop, so the reply box sits at the bottom and only
       the conversation scrolls (16px page padding top + bottom = 32px). */
    .st-layout.st-has-selection {
        height: calc(100vh - var(--header-height) - var(--footer-height) - 32px);
        height: calc(100dvh - var(--header-height) - var(--footer-height) - 32px);
        min-height: 420px;
    }
    .st-layout.st-has-selection .st-detail { display: flex; height: 100%; min-height: 0; }
    .st-back-btn { display: inline-flex; margin: 12px 16px 0; }
    .st-bubble { max-width: 90%; }
}

/* ---------------- Support Tickets: reply box (bottom of the conversation) ---------------- */
.st-composer {
    position: relative;
    flex: 0 0 auto;
    margin: 0 16px 16px;
    padding: 10px 12px 8px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 14px;
    box-shadow: 0 4px 14px rgba(0,0,0,0.06);
}
.st-composer:focus-within { border-color: var(--blue); }

.st-composer-editor {
    min-height: 44px;
    max-height: 200px;
    overflow-y: auto;
    padding: 4px 2px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--text-dark);
    outline: none;
    overflow-wrap: anywhere;
}
.st-composer-editor.is-empty::before { content: attr(data-placeholder); color: var(--text-muted); pointer-events: none; }
.st-composer-editor a, .st-bubble-content a { color: var(--blue); text-decoration: underline; }
.st-composer-editor ul, .st-composer-editor ol { padding-left: 22px; margin: 4px 0; }

.st-composer-format {
    display: flex; align-items: center; gap: 2px;
    padding-bottom: 6px; margin-bottom: 6px;
    border-bottom: 1px solid var(--card-border);
}
.st-composer-format[hidden] { display: none; }
.st-composer-divider { width: 1px; height: 18px; background: var(--card-border); margin: 0 4px; }

.st-composer-toolbar { display: flex; align-items: center; gap: 4px; margin-top: 6px; }
.st-composer-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px;
    border: none; border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}
.st-composer-btn .material-icons { font-size: 20px; }
.st-composer-btn:hover, .st-composer-btn.active { background: var(--surface-tint); color: var(--text-dark); }

.st-composer-count { margin-left: auto; font-size: 12px; color: var(--text-muted); }
.st-composer-count.over { color: var(--red); font-weight: 700; }

/* Send - green rounded square, same green as the Create New Tickets button (--green /
   --green-dark on hover). Greyed out and not clickable until a message is typed. */
.st-composer-send {
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px;
    margin-left: 8px;
    border-radius: 10px;
    background: var(--green);
    color: #fff;
    text-decoration: none;
    box-shadow: 0 2px 6px rgba(46, 158, 91, 0.35);
    transition: background .15s, opacity .15s;
}
.st-composer-send:hover { background: var(--green-dark); color: #fff; text-decoration: none; }
.st-composer-send .material-icons { font-size: 18px; }
.st-composer-send.disabled,
.st-composer-send.disabled:hover { background: var(--green); opacity: .35; cursor: not-allowed; box-shadow: none; }

.st-composer-popover {
    position: absolute;
    left: 12px;
    bottom: calc(100% + 8px);
    z-index: 30;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 12px;
    box-shadow: 0 10px 28px rgba(0,0,0,0.16);
    padding: 12px;
}
.st-composer-popover[hidden] { display: none; }
.st-emoji-popover { display: grid; grid-template-columns: repeat(8, 34px); gap: 2px; }
.st-emoji-popover[hidden] { display: none; }
.st-emoji {
    width: 34px; height: 34px;
    border: none; border-radius: 8px;
    background: transparent;
    font-size: 20px; line-height: 1;
    cursor: pointer;
}
.st-emoji:hover { background: var(--surface-tint); }

@media (max-width: 768px) {
    .st-composer { margin: 0 10px 10px; }
    .st-emoji-popover { grid-template-columns: repeat(6, 34px); }
}

/* ---------------- Reusable file chips / cards (PortalFileUpload in site.js, FileUploadHelper.cs) ---------------- */
/* Coloured file-type tile: PDF red, DOC blue, XLS green, image = thumbnail. */
.file-tile {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px;
    border-radius: 8px;
    overflow: hidden;
    color: #fff;
    font-size: 12px; font-weight: 800; letter-spacing: .3px;
    background: #6B7280;
}
.file-tile-pdf { background: #E53935; }
.file-tile-doc { background: #2B579A; }
.file-tile-xls { background: #217346; }
.file-tile-img { background: var(--surface-tint); border: 1px solid var(--card-border); }
.file-tile-img img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Picked-but-not-sent files (chips with an x). */
.file-chip-list { display: flex; flex-wrap: wrap; gap: 8px; }
.file-chip-list:empty { display: none; }
.file-chip {
    display: inline-flex; align-items: center; gap: 8px;
    max-width: 260px;
    padding: 6px 6px 6px 6px;
    border: 1px solid #9CCFEA;
    border-radius: 10px;
    background: var(--card-bg);
}
.file-chip .file-tile { width: 32px; height: 32px; font-size: 10px; border-radius: 6px; }
.file-chip-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.file-chip-name { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-chip-size { font-size: 11px; color: var(--text-muted); }
.file-chip-remove {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; border: none; border-radius: 6px;
    background: transparent; cursor: pointer; color: var(--text-muted);
}
.file-chip-remove .material-icons { font-size: 16px; }
.file-chip-remove:hover { background: var(--surface-tint); color: var(--text-dark); }
.file-chip-error { margin-top: 4px; font-size: 12px; color: var(--red); }
.file-chip-error[hidden] { display: none; }

/* Sent files (download cards) - light blue border, tile, name + size, blue download icon. */
.file-card {
    display: flex; align-items: center; gap: 12px;
    width: 250px; max-width: 100%;
    padding: 10px 12px;
    border: 1px solid #9CCFEA;
    border-radius: 12px;
    background: var(--card-bg);
    color: var(--text-dark);
    text-decoration: none;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    transition: border-color .15s, box-shadow .15s;
}
.file-card:hover { text-decoration: none; border-color: var(--blue); box-shadow: 0 2px 8px rgba(44,123,229,0.15); }
.file-card-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
.file-card-name { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-card-size { font-size: 12px; color: var(--text-muted); }
.file-card-download { flex: 0 0 auto; color: var(--blue); font-size: 22px; }
html[data-theme="dark"] .file-chip, html[data-theme="dark"] .file-card { border-color: #2C4F66; }

/* ---------------- Support Tickets: attachments beside the bubble ---------------- */
.st-composer-files:not(:empty) { margin: 4px 0 8px; }
.st-message { flex-wrap: wrap; align-items: flex-end; gap: 8px; }
.st-message-mine { flex-direction: row-reverse; justify-content: flex-start; }   /* reversed row: bubble at the right edge, cards on its left */
.st-attachments { display: flex; flex-direction: column; gap: 6px; }
.st-message-mine .st-attachments { align-items: flex-end; }

/* ---------------- Reusable upload pop-out (PortalFileUpload dialog option) ---------------- */
/* Frosted-glass backdrop: the page behind is blurred and softly tinted. */
.pfu-overlay {
    position: fixed;
    inset: 0;
    z-index: 3000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(15, 23, 42, 0.28);
    backdrop-filter: blur(10px) saturate(140%);
    -webkit-backdrop-filter: blur(10px) saturate(140%);
    opacity: 0;
    transition: opacity .18s ease;
}
.pfu-overlay[hidden] { display: none; }
.pfu-overlay.show { opacity: 1; }
body.pfu-open { overflow: hidden; }

.pfu-dialog {
    position: relative;
    width: min(440px, 100%);
    max-height: calc(100vh - 32px);
    overflow-y: auto;
    padding: 22px 20px 20px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 16px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.25);
    transform: translateY(8px) scale(.98);
    transition: transform .18s ease;
}
.pfu-overlay.show .pfu-dialog { transform: none; }

.pfu-close {
    position: absolute;
    top: 12px; right: 12px;
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px;
    border: none; border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}
.pfu-close:hover { background: var(--surface-tint); color: var(--text-dark); }

.pfu-title { margin: 0 40px 16px 0; font-size: 18px; font-weight: 700; color: var(--text-dark); }

.pfu-dropzone {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
    min-height: 130px;
    padding: 20px 16px;
    border: 2px dashed var(--card-border);
    border-radius: 12px;
    text-align: center;
    color: var(--text-muted);
    cursor: pointer;
    transition: border-color .15s, background .15s;
}
.pfu-dropzone:hover, .pfu-dropzone:focus { border-color: var(--blue); outline: none; }
.pfu-dropzone.dragover { border-color: var(--blue); background: var(--surface-tint); }
.pfu-drop-icon { font-size: 34px; color: var(--blue); }
.pfu-drop-text { font-size: 15px; color: var(--text-dark); }
.pfu-drop-link { color: var(--blue); text-decoration: underline; font-weight: 600; }
.pfu-drop-hint { font-size: 12px; }

.pfu-error { margin-top: 10px; }
.pfu-files-label { margin: 16px 0 8px; font-size: 14px; font-weight: 700; color: var(--text-dark); }
.pfu-files-empty { font-size: 13px; color: var(--text-muted); }
.pfu-files-empty[hidden] { display: none; }
.pfu-file-list { flex-direction: column; }
.pfu-file-list .file-chip { max-width: none; width: 100%; }
.pfu-file-list .file-chip-text { flex: 1 1 auto; }

/* Required-field marker + invalid state (Create New Tickets, reusable on any form). */
.required-mark { color: var(--red); font-weight: 700; margin-left: 2px; }
.form-control.input-invalid,
.rich-editor.input-invalid { border-color: var(--red); box-shadow: 0 0 0 2px rgba(216, 65, 47, 0.15); }
.field-error[hidden] { display: none; }

/* Support Tickets: the shared pagination bar pinned under the ticket list (outside the scrolling
   list), tightened to fit the narrow 25% column. */
.st-list-pager { flex: 0 0 auto; border-top: 1px solid var(--card-border); margin-top: 8px; }
.st-list-pager .pagination-bar { justify-content: space-between; gap: 8px 12px; padding: 8px 2px 0; }
@media (max-width: 768px) {
    .st-list-pager .pagination-bar { justify-content: flex-end; gap: 20px; }
}

/* Upload pop-out: file counter, locked drop zone at the limit, green Done button. */
.pfu-files-count { font-weight: 600; color: var(--text-muted); margin-left: 4px; }
.pfu-files-count.full { color: var(--red); }
.pfu-dropzone.disabled { cursor: not-allowed; opacity: .6; }
.pfu-dropzone.disabled:hover { border-color: var(--card-border); }
.pfu-dropzone.disabled .pfu-drop-icon { color: var(--text-muted); }
.pfu-actions { display: flex; justify-content: flex-end; margin-top: 18px; }
.pfu-done { min-width: 96px; }

/* Support Tickets: staff-only Status dropdown at the far right of the ticket title row. */
.st-ticket-status { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; margin-left: auto; align-self: flex-start; }
.st-ticket-status-label { font-size: 13px; font-weight: 700; color: var(--text-muted); }
.st-ticket-status .custom-select, .st-ticket-status .st-status-select { min-width: 170px; }
/* Desktop: the "more" box is just the right-hand column holding the Status dropdown; the mobile-only
   toggle and the second "Opened by" are hidden. */
.st-ticket-more { flex: 0 0 auto; margin-left: auto; align-self: flex-start; display: flex; }
.st-ticket-more .st-ticket-status { margin-left: 0; }
.st-ticket-opened-mobile, .st-ticket-more-toggle { display: none; }

/* Mobile: number + title on one row, chips under the title, then a "Ticket info" collapse
   (closed by default) with "Opened by" and the Status dropdown. */
@media (max-width: 768px) {
    .st-ticket-header { flex-wrap: wrap; align-items: flex-start; column-gap: 10px; row-gap: 0; }
    .st-ticket-header .st-card-number { flex: 0 0 auto; margin-top: 1px; }
    .st-ticket-heading { flex: 1 1 0; }
    .st-ticket-title { font-size: 17px; line-height: 1.35; margin-bottom: 8px; }
    .st-ticket-opened-desktop { display: none; }

    .st-ticket-more-toggle {
        display: flex; align-items: center; gap: 8px;
        width: 100%;
        margin-top: 12px;
        padding: 9px 12px;
        border: 1px solid var(--card-border);
        border-radius: 10px;
        background: transparent;
        color: var(--text-dark);
        font: inherit; font-size: 14px; font-weight: 700;
        cursor: pointer;
    }
    .st-ticket-more-toggle .material-icons { font-size: 20px; color: var(--text-muted); }
    .st-ticket-more-label { flex: 1 1 auto; text-align: left; }
    .st-ticket-more-chevron { transition: transform .18s ease; }
    .st-ticket-more-toggle.open .st-ticket-more-chevron { transform: rotate(180deg); }

    .st-ticket-more { display: none; width: 100%; margin-left: 0; flex-direction: column; gap: 12px; padding: 12px 2px 2px; }
    .st-ticket-more.open { display: flex; }
    .st-ticket-opened-mobile { display: block; font-size: 13px; color: var(--text-muted); }
    .st-ticket-status { width: 100%; margin-left: 0; }
    .st-ticket-status .custom-select, .st-ticket-status .st-status-select { flex: 1 1 auto; }
}

/* Ticket card: created date ("12 Feb") at the end of the description's 2nd line. The row is a
   flex line with bottom alignment, so the date sits level with the description's last line. */
.st-card-desc-row { display: flex; align-items: flex-end; gap: 10px; }
.st-card-desc-row .st-card-desc { flex: 1 1 auto; min-width: 0; }
.st-card-date { flex: 0 0 auto; font-size: 12px; line-height: 1.4; color: var(--text-muted); white-space: nowrap; }

/* Ticket card number in a rounded box: green = no assignee yet, faint grey = assigned.
   (Cards only - the number in the open ticket's header stays plain.) */
.st-card .st-card-number {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; height: 36px; padding: 0 10px;
    border-radius: 8px;
    border: 1px solid transparent;
    font-size: 13px; line-height: 1;
}
.st-card .st-card-number-unassigned { background: #DDF5EA; color: #1E7A4C; border-color: #B7E6CF; }
.st-card .st-card-number-assigned   { background: #F1F3F5; color: #8A94A0; border-color: #E2E6EA; }
html[data-theme="dark"] .st-card .st-card-number-unassigned { background: #143A2A; color: #5FD39A; border-color: #1F5A40; }
html[data-theme="dark"] .st-card .st-card-number-assigned   { background: #232B33; color: #8C97A3; border-color: #2E3842; }

/* ---------------- Home: staff ticket overview (under the announcement banner) ----------------
   KPI tiles (plain numbers in text colour - colour only on the icons), then three small panels.
   Status colours: fixed categorical order (blue, orange, aqua, yellow, ...) - validated for colour
   blindness in both themes; every colour also has its name + count beside it (legend). */
.dash {
    --dash-s1: #2a78d6; --dash-s2: #eb6834; --dash-s3: #1baf7a; --dash-s4: #eda100;
    --dash-s5: #e87ba4; --dash-s6: #008300; --dash-s7: #4a3aa7; --dash-s8: #e34948;
    margin: 0 0 24px;
}
html[data-theme="dark"] .dash {
    --dash-s1: #3987e5; --dash-s2: #d95926; --dash-s3: #199e70; --dash-s4: #c98500;
    --dash-s5: #d55181; --dash-s6: #008300; --dash-s7: #9085e9; --dash-s8: #e66767;
}
.dash-series-1 { background: var(--dash-s1); } .dash-series-2 { background: var(--dash-s2); }
.dash-series-3 { background: var(--dash-s3); } .dash-series-4 { background: var(--dash-s4); }
.dash-series-5 { background: var(--dash-s5); } .dash-series-6 { background: var(--dash-s6); }
.dash-series-7 { background: var(--dash-s7); } .dash-series-8 { background: var(--dash-s8); }

.dash-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.dash-title { margin: 0; font-size: 18px; font-weight: 700; color: var(--text-dark); }
.dash-link { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 600; color: var(--blue); text-decoration: none; white-space: nowrap; }
/* One straight underline under the text AND the arrow on hover (text-decoration drew two lines at
   different heights - one under the text, one under the icon). */
.dash-link { border-bottom: 1px solid transparent; line-height: 18px; }
.dash-link:hover { text-decoration: none; border-bottom-color: currentColor; }
.dash-link .material-icons { font-size: 16px; line-height: 18px; text-decoration: none; }

.dash-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; margin-bottom: 16px; }
.dash-kpi {
    display: flex; align-items: flex-start; gap: 14px;
    padding: 18px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 16px;
    box-shadow: 0 4px 14px rgba(0,0,0,0.08);
}
.dash-kpi-icon {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 42px; height: 42px; border-radius: 12px;
    font-size: 22px;
}
/* Higher specificity than the icon font's own ".material-icons { display: inline-block }" (the font
   stylesheet loads after site.css and was overriding the centring), so the glyph sits dead centre. */
.dash-kpi .dash-kpi-icon { display: inline-flex; align-items: center; justify-content: center; text-align: center; line-height: 1; }
.dash-kpi-icon-green  { background: rgba(46, 158, 91, 0.14);  color: #23824A; }
.dash-kpi-icon-amber  { background: rgba(237, 161, 0, 0.16);  color: #B7791F; }
.dash-kpi-icon-blue   { background: rgba(42, 120, 214, 0.14); color: #2a78d6; }
.dash-kpi-icon-violet { background: rgba(74, 58, 167, 0.14);  color: #4a3aa7; }
html[data-theme="dark"] .dash-kpi-icon-green  { background: rgba(63, 190, 120, 0.16); color: #5FD394; }
html[data-theme="dark"] .dash-kpi-icon-amber  { background: rgba(240, 195, 107, 0.16); color: #F0C36B; }
html[data-theme="dark"] .dash-kpi-icon-blue   { background: rgba(57, 135, 229, 0.18); color: #7DAAF5; }
html[data-theme="dark"] .dash-kpi-icon-violet { background: rgba(144, 133, 233, 0.18); color: #B3AAF2; }
.dash-kpi-body { min-width: 0; }
.dash-kpi-label { font-size: 13px; font-weight: 700; color: var(--text-muted); }
.dash-kpi-value { font-size: 34px; font-weight: 800; line-height: 1.1; margin: 4px 0 2px; color: var(--text-dark); font-variant-numeric: tabular-nums; }
.dash-kpi-hint { font-size: 12px; color: var(--text-muted); line-height: 1.35; }

.dash-panels { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.dash-panel {
    padding: 18px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 16px;
    box-shadow: 0 4px 14px rgba(0,0,0,0.08);
    min-width: 0;
}
.dash-panel-title { margin: 0 0 14px; font-size: 14px; font-weight: 700; color: var(--text-dark); }
.dash-empty { margin: 0 0 12px; font-size: 13px; color: var(--text-muted); }

/* Stacked bar: 2px surface gaps between segments, rounded ends. */
.dash-stack { display: flex; gap: 2px; height: 14px; border-radius: 7px; overflow: hidden; margin-bottom: 14px; }
.dash-stack-seg { flex-basis: 0; min-width: 4px; }
.dash-stack-seg:hover { filter: brightness(1.12); }
.dash-legend { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.dash-legend li { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-dark); }
.dash-swatch { flex: 0 0 auto; width: 10px; height: 10px; border-radius: 3px; }
.dash-legend-name { flex: 1 1 auto; min-width: 0; }
.dash-legend-count { font-weight: 700; font-variant-numeric: tabular-nums; }

/* Priority bars: one series, one hue. */
.dash-bars { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.dash-bars li { display: grid; grid-template-columns: 64px 1fr 32px; align-items: center; gap: 10px; font-size: 13px; }
.dash-bar-label { color: var(--text-dark); }
.dash-bar-track { height: 10px; border-radius: 5px; background: var(--surface-tint); overflow: hidden; }
.dash-bar-fill { display: block; height: 100%; border-radius: 5px; background: var(--dash-s1); min-width: 0; }
.dash-bar-count { text-align: right; font-weight: 700; color: var(--text-dark); font-variant-numeric: tabular-nums; }

.dash-mini-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 14px; }
.dash-mini { padding: 10px 8px; border-radius: 12px; background: var(--surface-tint); text-align: center; }
.dash-mini-value { font-size: 22px; font-weight: 800; color: var(--text-dark); font-variant-numeric: tabular-nums; }
.dash-mini-label { font-size: 12px; color: var(--text-muted); }
.dash-note { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; color: var(--text-muted); }
.dash-note .material-icons { font-size: 18px; }
.dash-note a { font-weight: 700; color: var(--blue); }

.dash-kpis.dash-kpis-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }   /* customer: 2 tiles */
@media (max-width: 1100px) {
    .dash-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .dash-panels { grid-template-columns: 1fr; }
}
/* Phone: still 2 tiles per row (all 4 numbers visible at a glance) - icon above the text. */
@media (max-width: 560px) {
    .dash-kpis { gap: 12px; }
    .dash-kpi { flex-direction: column; gap: 10px; padding: 14px; }
    .dash-kpi-icon { width: 36px; height: 36px; font-size: 20px; }
    .dash-kpi-value { font-size: 28px; }
}

/* Visually hidden, still read by screen readers. */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* ---------------- Admin > Token Revocation ---------------- */
.token-card { padding: 16px 16px 8px; }
.token-hint { margin: 0 0 12px; }
.token-filters { margin-bottom: 14px; }
.token-filters > .form-group:not(.filter-buttons-group) { flex: 1 1 240px; max-width: 360px; }
.token-filters > .filter-buttons-group { flex: 0 0 auto; }
/* Search button exactly as tall as the filter boxes (same fixed height, bottom-aligned in the row). */
.token-filters .form-control,
.token-filters .filter-buttons-group .btn { box-sizing: border-box; height: 42px; }
.token-filters .filter-buttons-group .btn { padding-top: 0; padding-bottom: 0; }
.token-table td { vertical-align: middle; }
.token-table .status-badge { margin: 2px 4px 2px 0; white-space: nowrap; }
.token-badge-staff { background: #E4EEFB; color: #2F6FD6; }
.token-badge-customer { background: #FFF4DE; color: #B7791F; }
.token-badge-current { background: rgba(46, 158, 91, 0.14); color: #23824A; }
html[data-theme="dark"] .token-badge-staff { background: #1B2B45; color: #7DAAF5; }
html[data-theme="dark"] .token-badge-customer { background: #3A2E14; color: #F0C36B; }
html[data-theme="dark"] .token-badge-current { background: rgba(63, 190, 120, 0.16); color: #5FD394; }
.token-col-date, .token-col-ip { white-space: nowrap; font-variant-numeric: tabular-nums; }
.token-col-email { overflow-wrap: anywhere; min-width: 180px; }
.token-table td:nth-child(2) { white-space: nowrap; }
.token-col-ua span { display: block; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-muted); }
.token-col-action { width: 1%; text-align: right; }
.token-revoke-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px;
    border-radius: 8px;
    background: var(--red); color: #fff;
    text-decoration: none;
    transition: filter .15s ease;
}
.token-revoke-btn:hover { filter: brightness(1.1); text-decoration: none; color: #fff; }
.token-revoke-btn .material-icons { font-size: 19px; }

/* Support Tickets (staff only): customer account banner under the status chip - no login / inactive. */
.st-account-banner {
    display: flex; align-items: center; flex-wrap: wrap; gap: 6px 8px;
    margin-top: 10px;
    padding: 8px 12px;
    border-radius: 10px;
    background: #FFF4DE;
    border: 1px solid #F6DDA8;
    color: #8A5A12;
    font-size: 13px;
    line-height: 1.4;
}
.st-account-banner-icon { font-size: 18px; color: #D08A1E; flex: 0 0 auto; }
.st-account-banner-text { min-width: 0; }
.st-account-banner-btn {
    display: inline-flex; align-items: center;
    padding: 3px 12px;
    border-radius: 999px;
    background: var(--green); color: #fff !important;
    font-weight: 700; font-size: 12px;
    text-decoration: none;
}
.st-account-banner-btn:hover { filter: brightness(1.08); text-decoration: none; }
html[data-theme="dark"] .st-account-banner { background: #3A2E14; border-color: #5A4520; color: #F0C36B; }
html[data-theme="dark"] .st-account-banner-icon { color: #F0C36B; }

/* ---------------- Closing a ticket ---------------- */
/* Customer: "Close ticket" button where staff have the Status dropdown. */
.st-close-ticket-btn { gap: 6px; white-space: nowrap; }
.st-close-ticket-btn .material-icons { font-size: 18px; color: var(--green); }

/* Customer on a Closed ticket: the reply box is replaced by this note. */
.st-composer[hidden] { display: none; }
.st-composer-closed {
    flex: 0 0 auto;
    display: flex; align-items: center; justify-content: center; gap: 8px;
    margin: 0 16px 16px;
    padding: 12px 14px;
    border: 1px dashed var(--card-border);
    border-radius: 14px;
    color: var(--text-muted);
    font-size: 14px; font-weight: 600;
}
.st-composer-closed .material-icons { font-size: 18px; }
@media (max-width: 768px) { .st-composer-closed { margin: 0 10px 10px; } }

/* Staff: closing-summary popup. Scrolls inside itself on short screens. */
.st-close-box {
    max-width: 520px;
    max-height: calc(100vh - 32px);
    max-height: calc(100dvh - 32px);
    overflow-y: auto;
    padding: 24px;
}
.st-close-title { margin: 0 0 4px; font-size: 18px; font-weight: 700; color: var(--text-dark); }
.st-close-intro { margin: 0 0 16px; font-size: 13px; color: var(--text-muted); }
.st-close-box .form-group { margin-bottom: 14px; }
.st-close-box textarea.form-control { min-height: 76px; font-family: inherit; font-size: 14px; }
.st-close-optional { font-weight: 400; color: var(--text-muted); }
.st-close-actions { display: flex; align-items: center; justify-content: flex-end; gap: 16px; margin-top: 18px; }
/* Cancel: no border or background - only an underline on hover. */
.st-close-cancel {
    border: none; background: none; padding: 6px 2px;
    font: inherit; font-size: 14px; font-weight: 700;
    color: var(--text-dark);
    cursor: pointer;
}
.st-close-cancel:hover, .st-close-cancel:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
.st-close-submit { min-width: 96px; }
@media (max-width: 768px) {
    .st-close-box { width: auto; margin: 0 12px; padding: 20px 16px; }
}

/* Closing summary in the conversation (staff + customers), centred at the time it was closed. */
.st-closure { display: flex; justify-content: center; }
.st-closure-card {
    width: min(560px, 100%);
    padding: 12px 16px 14px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-left: 4px solid var(--green);
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.st-closure-head { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.st-closure-head .material-icons { font-size: 20px; color: var(--green); }
.st-closure-title { font-size: 14px; font-weight: 700; color: var(--text-dark); }
.st-closure-meta { font-size: 12px; color: var(--text-muted); }
.st-closure-list { margin: 0; display: grid; gap: 8px; }
.st-closure-item dt { font-size: 12px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.3px; }
.st-closure-item dd { margin: 2px 0 0; font-size: 14px; color: var(--text-dark); white-space: pre-wrap; overflow-wrap: anywhere; }

/* ---------------- Home: announcements (one per customer company) ---------------- */
/* Staff only: the company the announcement is for, under the "Announcement" title. */
.announcement-banner-company { margin: -4px 0 8px; font-size: 14px; font-weight: 700; color: var(--text-dark); }
html[data-theme="dark"] .announcement-banner-company { color: #fff; }

/* Staff carousel: [<] banner [>] with the dots underneath. The arrows sit outside the banner. */
.announcement-carousel {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "prev view next" ". dots .";
    align-items: center;
    column-gap: 10px;
    margin: 4px 0 24px;
}
.announcement-viewport { grid-area: view; min-width: 0; }
.announcement-carousel .announcement-banner { margin: 0; }
.announcement-slide { display: none; }
.announcement-slide.active { display: block; animation: annFade .35s ease; }
@keyframes annFade { from { opacity: 0; } to { opacity: 1; } }
.announcement-nav {
    width: 36px; height: 36px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    border: 1px solid var(--card-border);
    background: var(--card-bg);
    color: var(--text-dark);
    cursor: pointer;
    box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}
.announcement-nav:hover { background: var(--surface-tint); }
.announcement-nav:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.announcement-nav .material-icons { font-size: 22px; }
.announcement-prev { grid-area: prev; }
.announcement-next { grid-area: next; }
.announcement-dots { grid-area: dots; display: flex; justify-content: center; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.announcement-dot {
    width: 9px; height: 9px; padding: 0;
    border: none; border-radius: 999px;
    background: var(--card-border);
    cursor: pointer;
    transition: width .2s ease, background .2s ease;
}
.announcement-dot:hover { background: var(--text-muted); }
.announcement-dot.active { width: 22px; background: var(--green); }
.announcement-dot:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
html[data-theme="dark"] .announcement-dot { background: #455462; }
html[data-theme="dark"] .announcement-dot.active { background: var(--green); }
@media (max-width: 768px) {
    .announcement-carousel { column-gap: 6px; margin: 4px 0 20px; }
    .announcement-nav { width: 30px; height: 30px; }
    .announcement-nav .material-icons { font-size: 20px; }
}
@media (prefers-reduced-motion: reduce) { .announcement-slide.active { animation: none; } }

/* Admin > Customer Company: announcement card under the company details card. */
.company-announcement-card { margin-top: 16px; }
.company-announcement-card[hidden] { display: none; }
.company-announcement-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.company-announcement-head .material-icons { font-size: 20px; color: var(--green); }
.company-announcement-title { margin: 0; font-size: 15px; font-weight: 700; color: var(--text-dark); }
.company-announcement-note { margin: 0 0 10px; }
.company-announcement-note[hidden], .company-announcement-head .status-badge[hidden] { display: none; }
.company-announcement-message { font-size: 14px; line-height: 1.6; color: var(--text-dark); word-break: break-word; }
.company-announcement-message p { margin: 0 0 8px; }
.company-announcement-message p:last-child { margin-bottom: 0; }
.company-announcement-message ul, .company-announcement-message ol { margin: 0 0 8px; padding-left: 22px; }
.company-announcement-message h2 { font-size: 18px; margin: 0 0 8px; }
.company-announcement-message h3 { font-size: 16px; margin: 0 0 8px; }

/* ==========================================================================
   AI Q&A - chat page (AiChat.aspx) and Admin > AI Documents (Admin/AiDocuments.aspx)
   ========================================================================== */
.ai-chat-header { align-items: flex-end; }
.ai-chat-header .btn .material-icons { font-size: 18px; }
.ai-chat-eyebrow {
    display: flex; align-items: center; gap: 6px; margin-bottom: 4px;
    font-size: 12px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase; color: var(--green);
}
.ai-chat-eyebrow .material-icons { font-size: 18px; }

.ai-chat {
    display: flex; flex-direction: column;
    height: calc(100vh - var(--header-height) - var(--footer-height) - 150px);
    height: calc(100dvh - var(--header-height) - var(--footer-height) - 150px);
    min-height: 420px;
    padding: 0;
    overflow: hidden;
}
.ai-chat-messages { flex: 1 1 auto; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 14px; }
.ai-msg { display: flex; gap: 10px; align-items: flex-start; }
.ai-msg-user { justify-content: flex-end; }
/* .ai-avatar.material-icons (not just .ai-avatar): Google's own .material-icons rule (display:
   inline-block, font-size 24px, line-height 1) loads later and was winning, so the robot sat in the
   top-left of its green circle instead of the middle. */
.ai-avatar,
.ai-avatar.material-icons {
    flex: 0 0 auto; width: 32px; height: 32px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(46,158,91,0.15); color: var(--green);
    font-size: 20px; line-height: 1; text-align: center; overflow: hidden;
}
.ai-bubble {
    max-width: min(760px, 85%);
    padding: 10px 14px;
    border-radius: 14px;
    background: var(--surface-tint);
    color: var(--text-dark);
    font-size: 14.5px; line-height: 1.55;
    overflow-wrap: anywhere;
}
.ai-msg-user .ai-bubble { background: var(--blue); color: #fff; white-space: pre-wrap; border-bottom-right-radius: 4px; }
.ai-msg-bot .ai-bubble { border-top-left-radius: 4px; }
html[data-theme="dark"] .ai-msg-bot .ai-bubble { background: #26323C; }
.ai-bubble p { margin: 0 0 8px; }
.ai-bubble p:last-child { margin-bottom: 0; }
.ai-bubble ul, .ai-bubble ol { margin: 4px 0 8px; padding-left: 22px; }
.ai-bubble code { font-size: 13px; background: rgba(0,0,0,0.06); padding: 1px 4px; border-radius: 4px; }
.ai-bubble-error { background: rgba(216,65,47,0.10) !important; color: var(--red) !important; }
.ai-muted { color: var(--text-muted); font-size: 13px; }
.ai-cite { font-size: 11px; font-weight: 700; color: var(--blue-dark); margin-left: 1px; }
html[data-theme="dark"] .ai-cite { color: var(--blue); }
.ai-sources { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--card-border); }
.ai-sources-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px; color: var(--text-muted); margin-right: 2px; }
.ai-source {
    display: inline-block; max-width: 100%;
    padding: 3px 9px; border-radius: 999px;
    background: var(--card-bg); border: 1px solid var(--card-border);
    color: var(--text-dark); font-size: 12px; text-decoration: none;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ai-source:hover { border-color: var(--blue); color: var(--blue-dark); text-decoration: none; }
button.ai-source { font-family: inherit; line-height: inherit; cursor: pointer; text-align: left; }
/* Picture sources: a small thumbnail in front of the label. */
button.ai-source.ai-source-pic { display: inline-flex; align-items: center; gap: 7px; padding: 3px 10px 3px 3px; }
.ai-source-thumb {
    width: 40px; height: 30px; flex: 0 0 auto; object-fit: cover; border-radius: 999px 6px 6px 999px;
    border: 1px solid var(--card-border); background: var(--surface-tint);
}
.ai-source-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* View-only PDF pop-out (PortalPdfViewer in site.js) - pages drawn on canvas, zoom in/out only. */
.pdfv-tools { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.pdfv-btn { color: var(--text-muted); width: 34px; height: 34px; border-radius: 8px; }
.pdfv-btn:hover:not(:disabled) { background: var(--surface-tint); color: var(--text-dark); }
.pdfv-btn:disabled { opacity: .35; cursor: default; }
.pdfv-btn .material-icons { font-size: 22px; }
.pdfv-zoom { min-width: 48px; text-align: center; font-size: 13px; font-weight: 700; color: var(--text-dark); font-variant-numeric: tabular-nums; }
.pdfv-tools .doc-viewer-close { margin-left: 6px; }
.doc-viewer-body.pdfv-body {
    display: block; position: relative; overflow: auto; padding: 16px;
    -webkit-user-select: none; user-select: none;
}
.pdfv-pages { display: flex; flex-direction: column; align-items: center; gap: 14px; min-width: min-content; }
.pdfv-page { flex: 0 0 auto; background: #fff; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18); }
.pdfv-page canvas { display: block; }
.pdfv-msg { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 24px; text-align: center; color: var(--text-muted); font-size: 14px; }
.pdfv-msg[hidden] { display: none; }
/* Picture mode (a picture source in the AI Q&A chat). */
.pdfv-image-wrap { background: transparent; box-shadow: none; }
.pdfv-image { display: block; max-width: none; height: auto; border-radius: 6px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18); }
.pdfv-pdflink { display: inline-flex; align-items: center; gap: 4px; margin-right: 6px; white-space: nowrap; }
.pdfv-pdflink[hidden] { display: none; }
.pdfv-pdflink .material-icons { font-size: 18px; }
@media (max-width: 600px) { .pdfv-pdflink-text { display: none; } }
/* "Thinking" dots while the answer is being written. */
.ai-typing { display: inline-flex; gap: 4px; padding: 4px 0; }
.ai-typing span { width: 7px; height: 7px; border-radius: 50%; background: var(--text-muted); animation: aiDot 1.2s infinite ease-in-out; }
.ai-typing span:nth-child(2) { animation-delay: .15s; }
.ai-typing span:nth-child(3) { animation-delay: .3s; }
@keyframes aiDot { 0%, 80%, 100% { opacity: .25; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-3px); } }

.ai-chat-composer {
    position: relative; flex: 0 0 auto;
    display: flex; align-items: flex-end; gap: 8px;
    margin: 0 16px; padding: 8px 8px 8px 14px;
    border: 1px solid var(--card-border); border-radius: 14px; background: var(--card-bg);
}
.ai-chat-composer:focus-within { border-color: var(--blue); }
.ai-input {
    flex: 1 1 auto; min-width: 0; resize: none; border: none; outline: none; background: transparent;
    color: var(--text-dark); font: inherit; font-size: 14.5px; line-height: 1.45; padding: 6px 0; max-height: 160px;
}
.ai-count { font-size: 11px; color: var(--text-muted); align-self: center; }
.ai-send {
    flex: 0 0 auto; width: 38px; height: 38px; border: none; border-radius: 10px;
    background: var(--green); color: #fff; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
}
.ai-send:hover { background: var(--green-dark); }
.ai-send:disabled { opacity: .45; cursor: not-allowed; background: var(--green); }
.ai-send .material-icons { font-size: 20px; }
.ai-disclaimer { margin: 6px 16px 12px; font-size: 11.5px; color: var(--text-muted); }
@media (max-width: 768px) {
    .ai-chat { height: calc(100dvh - var(--header-height) - var(--footer-height) - 130px); min-height: 360px; }
    .ai-chat-messages { padding: 14px 12px; }
    .ai-bubble { max-width: 92%; }
    .ai-chat-composer { margin: 0 10px; }
    .ai-disclaimer { margin: 6px 10px 10px; }
}

/* ---- Admin > AI Documents ---- */
.ai-admin-pagebar .field-hint { margin: 6px 0 0; }
.ai-admin-pagebar-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.ai-admin-page-select { flex: 1 1 280px; max-width: 420px; margin-bottom: 0; }
/* Same line as the dropdown: the row is bottom-aligned (the dropdown has its label above it), and the
   buttons are exactly the dropdown's height, so everything shares one centre line. */
.ai-admin-page-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-height: 38px; }
.ai-admin-page-actions .btn { height: 38px; padding-top: 0; padding-bottom: 0; }
.ai-admin-page-actions .btn .material-icons { font-size: 18px; }
.ai-admin-open-chat { display: inline-flex; align-items: center; gap: 4px; }
.ai-admin-docs { display: flex; gap: 16px; align-items: flex-start; margin-bottom: 16px; }
/* Top row: page picker card + Companies card side by side, half each (stacked on small screens). */
.ai-admin-top { display: flex; gap: 16px; align-items: stretch; margin-bottom: 16px; }
.ai-admin-top > .card { flex: 1 1 0; min-width: 0; margin-bottom: 0; }
.ai-admin-companies .field-hint { margin: 6px 0 0; }
@media (max-width: 900px) {
    .ai-admin-top { flex-direction: column; }
}
/* Shared multi-select dropdown extras (data-select-all / data-max-shown, see site.js). */
.custom-select-actions { display: flex; gap: 4px; padding: 4px 8px; border-bottom: 1px solid var(--card-border); }
.custom-select-actions button {
    border: none; background: none; cursor: pointer; font: inherit; font-size: 12.5px; font-weight: 700;
    color: var(--blue-dark); padding: 4px 6px; border-radius: 6px;
}
.custom-select-actions button:hover { background: var(--surface-tint); }
html[data-theme="dark"] .custom-select-actions button { color: var(--blue); }
.custom-select-option-more { padding: 7px 12px; font-size: 12px; color: var(--text-muted); font-style: italic; border-top: 1px solid var(--card-border); }
.ai-admin-docs > .card { margin-bottom: 0; }
.ai-admin-upload { flex: 0 0 380px; max-width: 100%; }
.ai-admin-list { flex: 1 1 auto; min-width: 0; }
.ai-admin-docs .card-title { display: flex; align-items: center; gap: 8px; margin: 0 0 14px; }
.ai-doc-count {
    display: inline-flex; align-items: center; justify-content: center; min-width: 22px; height: 22px; padding: 0 7px;
    border-radius: 999px; background: var(--surface-tint); color: var(--text-muted); font-size: 12px; font-weight: 700;
}
.ai-dropzone { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 28px 20px; }
.ai-dropzone:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.ai-dropzone-icon { font-size: 34px; color: var(--blue); }
.ai-dropzone-hint { font-size: 12px; color: var(--text-muted); }
.ai-upload-list { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.ai-upload-item { display: flex; align-items: center; gap: 10px; padding: 6px 8px; border: 1px solid var(--card-border); border-radius: 10px; }
.ai-upload-item .ai-doc-tile { width: 32px; height: 32px; font-size: 10px; }
.ai-upload-name { flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ai-upload-size { font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.ai-upload-remove { border: none; background: none; cursor: pointer; color: var(--text-muted); display: inline-flex; padding: 2px; border-radius: 50%; }
.ai-upload-remove:hover { color: var(--red); background: rgba(216,65,47,0.10); }
.ai-upload-remove .material-icons { font-size: 18px; }
.ai-upload-actions { display: flex; justify-content: flex-end; margin-top: 12px; }
.ai-upload-note { margin: 10px 0 0; }
.ai-doc-rows { list-style: none; margin: 0; padding: 0; }
.ai-doc-row { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-top: 1px solid var(--card-border); }
.ai-doc-row:first-child { border-top: none; padding-top: 0; }
.ai-doc-tile { width: 40px; height: 40px; font-size: 11px; }
.ai-doc-info { flex: 1 1 auto; min-width: 0; }
.ai-doc-name { font-weight: 700; font-size: 14px; color: var(--text-dark); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ai-doc-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.ai-doc-error { font-size: 12px; color: var(--red); margin-top: 4px; }
/* Document type badge (Text / Image / Text + Image), picture counts and live progress. */
.ai-type-badge {
    display: inline-block; margin-right: 6px; padding: 0 7px; border-radius: 999px;
    font-size: 11px; font-weight: 700; line-height: 18px; vertical-align: 1px;
    background: rgba(44, 123, 229, 0.12); color: var(--blue-dark);
}
html[data-theme="dark"] .ai-type-badge { background: rgba(91, 155, 240, 0.16); color: var(--blue); }
.ai-doc-images { display: flex; align-items: center; gap: 4px; }
.ai-doc-images .material-icons { font-size: 14px; }
.ai-doc-progress { font-size: 12px; font-weight: 600; color: #9A6A12; margin-top: 3px; }
html[data-theme="dark"] .ai-doc-progress { color: var(--yellow); }
.ai-doc-actions { display: flex; gap: 6px; flex: 0 0 auto; }
.ai-status {
    flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 700; white-space: nowrap;
}
.ai-status-ready { background: rgba(46,158,91,0.14); color: var(--green-dark); }
.ai-status-processing { background: rgba(232,169,58,0.18); color: #9A6A12; }
.ai-status-failed { background: rgba(216,65,47,0.12); color: var(--red); }
html[data-theme="dark"] .ai-status-ready { color: var(--green); }
html[data-theme="dark"] .ai-status-processing { color: var(--yellow); }
.ai-spin { width: 10px; height: 10px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; animation: aiSpin .8s linear infinite; }
@keyframes aiSpin { to { transform: rotate(360deg); } }
.ai-empty { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 30px 10px; color: var(--text-muted); text-align: center; }
.ai-empty .material-icons { font-size: 34px; }
.ai-modal-title { margin: 0 0 14px; font-size: 18px; font-weight: 700; }
.ai-modal-actions { display: flex; justify-content: flex-end; align-items: center; gap: 16px; margin-top: 18px; }
.ai-modal-cancel { border: none; background: none; padding: 6px 2px; font: inherit; font-size: 14px; font-weight: 700; color: var(--text-dark); cursor: pointer; }
.ai-modal-cancel:hover, .ai-modal-cancel:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 900px) {
    .ai-admin-docs { flex-direction: column; align-items: stretch; }
    .ai-admin-upload { flex: 1 1 auto; }
}
@media (max-width: 560px) {
    .ai-doc-row { flex-wrap: wrap; }
    .ai-doc-info { flex-basis: calc(100% - 52px); }
    .ai-status { margin-left: 52px; }
    .ai-doc-actions { margin-left: auto; }
}
@media (prefers-reduced-motion: reduce) { .ai-typing span, .ai-spin { animation: none; } }

/* AI chat: model warm-up status next to "New chat". */
.ai-chat-header-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: flex-end; }
.ai-model-status { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--text-muted); max-width: 420px; line-height: 1.35; }
.ai-model-dot { flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%; background: var(--yellow); }
.ai-model-loading .ai-model-dot { animation: aiPulse 1.2s ease-in-out infinite; }
.ai-model-ready .ai-model-dot { background: var(--green); }
.ai-model-ready { color: var(--green-dark); }
html[data-theme="dark"] .ai-model-ready { color: var(--green); }
.ai-model-offline .ai-model-dot { background: var(--red); }
.ai-model-offline { color: var(--red); }
@keyframes aiPulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
@media (max-width: 768px) { .ai-chat-header { flex-wrap: wrap; gap: 8px; } .ai-model-status { max-width: 100%; } }
@media (prefers-reduced-motion: reduce) { .ai-model-loading .ai-model-dot { animation: none; } }

/* ---------------- Header / left menu / footer blend into the background ----------------
   Transparent, no dividing borders - the header, left menu and footer read as one piece with the
   site-wide gradient. A light blur (no tint) only softens page content scrolling underneath the
   fixed header/footer, so text never clashes; over the plain gradient it's invisible.
   The left menu only gets a frosted tint while it's OPEN over the page (expanded / mobile / tour),
   so its labels stay readable on top of cards. */
.site-header,
.site-footer,
.side-nav {
    background: transparent;
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    border: none;
    box-shadow: none;
}
.side-nav.expanded,
body.tour-nav-open .side-nav {
    background: var(--nav-glass);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    backdrop-filter: blur(18px) saturate(150%);
    box-shadow: 2px 0 18px rgba(0, 0, 0, 0.12);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .site-header, .site-footer { background: var(--app-bg-color); }
    .side-nav.expanded, body.tour-nav-open .side-nav { background: var(--page-bg); }
}
/* Mobile ticket detail panel slides over the list - give it the same gradient instead of flat grey. */
@media (max-width: 768px) {
    .master-detail-right { background-color: var(--app-bg-color); background-image: var(--app-bg-image); }
}
