/* input(512,5): run-time error CSS1062: Expected semicolon or closing curly-brace, found '&'
input(603,15): run-time error CSS1019: Unexpected token, found 'selector('
input(603,43): run-time error CSS1025: Expected comma or open brace, found ')'
input(761,5): run-time error CSS1062: Expected semicolon or closing curly-brace, found '&'
input(3531,1): run-time error CSS1019: Unexpected token, found '}'
input(3814,15): run-time error CSS1034: Expected closing parenthesis, found '>'
input(6361,21): run-time error CSS1030: Expected identifier, found '#pmAdoPat'
input(6361,30): run-time error CSS1025: Expected comma or open brace, found ')'
input(6367,21): run-time error CSS1030: Expected identifier, found '#pmAdoPat'
input(6367,30): run-time error CSS1025: Expected comma or open brace, found ')'
input(6371,25): run-time error CSS1030: Expected identifier, found '#pmAdoPat'
input(6371,34): run-time error CSS1025: Expected comma or open brace, found ')'
input(6375,25): run-time error CSS1030: Expected identifier, found '#pmAdoPat'
input(6375,34): run-time error CSS1025: Expected comma or open brace, found ')'
input(6381,25): run-time error CSS1030: Expected identifier, found '#pmAdoPat'
input(6381,34): run-time error CSS1025: Expected comma or open brace, found ')'
input(7797,15): run-time error CSS1019: Unexpected token, found 'selector('
input(7797,43): run-time error CSS1025: Expected comma or open brace, found ')'
input(8119,1): run-time error CSS1019: Unexpected token, found '}'
input(9286,15): run-time error CSS1034: Expected closing parenthesis, found '>'
input(13368,1): run-time error CSS1019: Unexpected token, found '}'
input(13570,15): run-time error CSS1034: Expected closing parenthesis, found '>'
input(15359,36): run-time error CSS1030: Expected identifier, found ' '
input(15359,37): run-time error CSS1031: Expected selector, found '395px'
input(15359,37): run-time error CSS1025: Expected comma or open brace, found '395px'
input(15359,55): run-time error CSS1030: Expected identifier, found ' '
input(15359,60): run-time error CSS1031: Expected selector, found ';'
input(15359,60): run-time error CSS1025: Expected comma or open brace, found ';'
input(15593,68): run-time error CSS1031: Expected selector, found '('
input(15593,68): run-time error CSS1025: Expected comma or open brace, found '('
input(16865,11): run-time error CSS1019: Unexpected token, found 'selector('
input(16865,43): run-time error CSS1025: Expected comma or open brace, found ')'
input(17034,15): run-time error CSS1034: Expected closing parenthesis, found '>'
input(23744,21): run-time error CSS1030: Expected identifier, found '#screen-login'
input(23744,34): run-time error CSS1025: Expected comma or open brace, found ')'
input(23757,21): run-time error CSS1030: Expected identifier, found '#screen-login'
input(23757,34): run-time error CSS1025: Expected comma or open brace, found ')' */
/* input(475,5): run-time error CSS1062: Expected semicolon or closing curly-brace, found '&'
input(566,15): run-time error CSS1019: Unexpected token, found 'selector('
input(566,43): run-time error CSS1025: Expected comma or open brace, found ')'
input(724,5): run-time error CSS1062: Expected semicolon or closing curly-brace, found '&'
input(3494,1): run-time error CSS1019: Unexpected token, found '}'
input(3777,15): run-time error CSS1034: Expected closing parenthesis, found '>'
input(6324,21): run-time error CSS1030: Expected identifier, found '#pmAdoPat'
input(6324,30): run-time error CSS1025: Expected comma or open brace, found ')'
input(6330,21): run-time error CSS1030: Expected identifier, found '#pmAdoPat'
input(6330,30): run-time error CSS1025: Expected comma or open brace, found ')'
input(6334,25): run-time error CSS1030: Expected identifier, found '#pmAdoPat'
input(6334,34): run-time error CSS1025: Expected comma or open brace, found ')'
input(6338,25): run-time error CSS1030: Expected identifier, found '#pmAdoPat'
input(6338,34): run-time error CSS1025: Expected comma or open brace, found ')'
input(6344,25): run-time error CSS1030: Expected identifier, found '#pmAdoPat'
input(6344,34): run-time error CSS1025: Expected comma or open brace, found ')'
input(7760,15): run-time error CSS1019: Unexpected token, found 'selector('
input(7760,43): run-time error CSS1025: Expected comma or open brace, found ')'
input(8082,1): run-time error CSS1019: Unexpected token, found '}'
input(9249,15): run-time error CSS1034: Expected closing parenthesis, found '>'
input(13331,1): run-time error CSS1019: Unexpected token, found '}'
input(13533,15): run-time error CSS1034: Expected closing parenthesis, found '>'
input(15322,36): run-time error CSS1030: Expected identifier, found ' '
input(15322,37): run-time error CSS1031: Expected selector, found '395px'
input(15322,37): run-time error CSS1025: Expected comma or open brace, found '395px'
input(15322,55): run-time error CSS1030: Expected identifier, found ' '
input(15322,60): run-time error CSS1031: Expected selector, found ';'
input(15322,60): run-time error CSS1025: Expected comma or open brace, found ';'
input(15556,68): run-time error CSS1031: Expected selector, found '('
input(15556,68): run-time error CSS1025: Expected comma or open brace, found '('
input(16828,11): run-time error CSS1019: Unexpected token, found 'selector('
input(16828,43): run-time error CSS1025: Expected comma or open brace, found ')'
input(16997,15): run-time error CSS1034: Expected closing parenthesis, found '>'
input(23707,21): run-time error CSS1030: Expected identifier, found '#screen-login'
input(23707,34): run-time error CSS1025: Expected comma or open brace, found ')'
input(23720,21): run-time error CSS1030: Expected identifier, found '#screen-login'
input(23720,34): run-time error CSS1025: Expected comma or open brace, found ')' */
body {
    /*padding-top: 80px;*/
    padding-bottom: 20px;
}

/* Set padding to keep content from hitting the edges */
.body-content {
    padding-left: 15px;
    padding-right: 15px;
}

/* Override the default bootstrap behavior where horizontal description lists 
   will truncate terms that are too long to fit in the left column 
*/
.dl-horizontal dt {
    white-space: normal;
}

/* Removed: input/select/textarea { max-width: 280px } — was capping
   every input across the entire portal at 280px, including the
   redesign's New Case modal Title field. Modern layouts pin width
   per-component via .form-stack / .form-grid / etc. */

/*#fileuploader-container {
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    margin: 20px 20px 0 20px;
}*/

#form h3 {
    margin-left: 20px;
    font-weight: normal;
    font-size: 22px;
}


@font-face {
    font-family: 'TwemojiMozilla';
    src: url('/fonts/Twemoji.Mozilla.ttf?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo') format('truetype');
    font-weight: normal;
    font-style: normal;
}

:root {
    /* Brand red — official SigmaTEK brand-guide values (spec §4.1).
       Every --main-color* reference across the site inherits these automatically. */
    --main-color: #D42F3B;
    --main-color-dark: #8A1820;
    --main-color-bright: #F7403A;
    --main-color-outline: #D45F67;
    --main-color-highlight: #FCE4E5;
    --main-color-disabled: #E18B91;

    --warning-color: #F78B37;
    --success-color: #34AE2C;
    --success-color-dark: #298722;
    --success-color-darker: #1C5E17;
    --error-color: #F44336;
    --error-color-dark: #B52A21;
    --error-color-light: #FBC4C0;

    /* State TEXT tokens — theme-aware, and the only state colors permitted in a
       `color:` declaration. The plain --success/--warning/--error-color hues above
       are FILLS: at full strength they measure 2.9:1, 2.4:1 and 3.9:1 on white, all
       below the 4.5:1 floor. These flip with the theme; the fill hues deliberately
       do not. Verified on white and on a 15-18% wash of their own hue, both themes:
       light 7.90:1 / 5.33:1 / 6.38:1, dark 10.1:1 / 9.10:1 / 9.04:1. */
    --success-text: #1C5E17;
    --warning-text: #A85405;
    --error-text: #B52A21;
    /* Toast surface. Toasts sit over arbitrary page content, so they stay dark in
       both themes; the dark side lifts to the nav-pane grey so the toast still
       separates from the #141414 canvas. The state rides on a left rail. */
    --toast-bg: #1E1E1E;
    /* Brand red as TEXT. --main-color (#D42F3B) is a fill at 3.6:1 and
       --main-color-dark does not flip with the theme, so neither can carry text
       on a tinted chip. Dark value is a light tint of the brand hue, pitched at
       the same lightness as --error-text so the two reds don't fight. */
    --brand-text: #8A1820;

    --purple-color: #7E57C2;
    --purple-color-dark: #5E35B1;
    --purple-color-rgb: 126, 87, 194;
    --yellow-color: #EAB308;
    --yellow-color-dark: #854D0E;
    /* Global-search match highlight (::highlight(search-hit), layout.css). Amber, not
       brand red: red already means error or active state here, and yellow is what
       people read as "found". The text keeps its own colour in light mode (#141414 on
       the tinted fill measures about 15:1); dark mode sets a light amber text,
       --search-hit-fg, in the .dark-mode block. */
    --search-hit-bg: rgba(var(--yellow-color-rgb), .35);
    /* Outline for inputs whose value matches; a Range can't paint inside a field. */
    --search-hit-ring: var(--yellow-color);
    --yellow-color-rgb: 234, 179, 8;
    --main-text-color: #141414;
    --main-background-color: #FFFFFF;
    --main-background-color-subtle: #F4F5F6;
    --main-background-color-subtle-highlight: #DDDDDD;
    --main-background-color-subtle-lowlight: #D0D0D0;
    /* Surface for list rows and list cards that sit directly on the page canvas —
       the Support Cases cards, the Customer Search company/people rows and the A–Z
       grid cards. Dark mode paints the nav pane's #2A2E33 (--sidebar-bg, layout.css)
       so a row reads as a raised surface against the #141414 canvas. Light mode keeps
       white on purpose: the nav pane's light grey (#F4F5F6) IS the light-mode canvas
       (--workspace-bg aliases --sidebar-bg there), so a row painted with it would
       vanish into the page instead of lifting off it. */
    --list-surface-bg: var(--main-background-color);
    --main-border-color: #E2E3E5;
    --chat-message-background-color: #FFFFFF;
    --light-grey-color: #6D6E70;
    --dark-grey-color: #1E1E1E;
    /* Monogram-tile fills. Every avatar in the app is now one of exactly two flat
       colours: brand dark red for a person (user / role) and branding charcoal for a
       company / account. This replaced the per-name hash palettes that used to tint
       them (.u-avatar.b/.g/.p, .contact-card-avatar.avatar-c1..c6, the AV_PALETTE in
       company-search.js, .pp-tone-* on contact avatars) — a wall of assorted hues
       read as decoration and made two lists of the same people look unrelated.

       The user fill is theme-STABLE, unlike --dark-grey-color (which flips to #101010):
       a monogram tile is an identity mark, not a surface, so brand dark red holds in both
       themes. The company fill cannot — #1E1E1E is only a shade off the #141414 card it
       sits on, so in dark mode the tile disappears and the initials float unattached; it
       lifts to the brand's DF Mid there (see .dark-mode). Both carry --white-color text —
       8.9:1 and 15.9:1 respectively. */
    --avatar-user-bg: #8A1820;
    --avatar-company-bg: #1E1E1E;
    /* --blue-color stays #2D8FD2: it is a FILL / chart hue, where the 3:1 non-text
       floor applies. It measures only 3.52:1 on white, so it must never carry text.
       The link token is a step darker so inline links clear the 4.5:1 text floor
       (5.23:1), with hover darker again (7.42:1). Corrected 2026-08-03 — the link
       token previously shared the fill value and failed AA everywhere it was used. */
    --blue-color: #2D8FD2;
    --blue-link-color: #2372A4;
    --blue-link-hover-color: #1B5A82;
    --notification-select-color: #D9D9D957;

    --chart-color-red: #CD3B44;
    --chart-color-yellow: #F78B37;
    --chart-color-green: #298722;
    --chart-color-blue: #2D8FD2;


    /* only update these if the hex colors are modified */
    --main-color-rgb: 212, 47, 59;
    --blue-color-rgb: 45, 143, 210;
    --success-color-rgb: 52, 174, 44;
    --warning-color-rgb: 247, 139, 55;
    --error-color-rgb: 244, 67, 54;
    --blue-color-rgb: 45, 143, 210;
    --main-background-color-subtle-rgb: 241, 241, 241;
    --main-border-color-rgb: 224, 224, 224;
    --black-color-rgb: 0, 0, 0;
    --white-color-rgb: 255, 255, 255;
    --modal-background-color-rgb: 0, 0, 0;
    /* ------------------------------------------------ */

    /* Tag / chip palette — the single source of truth for every badge, pill and
       tag fill in the app. Rules consume ONLY these pairs, never a raw rgba() or
       a state fill token, so re-hueing a tag is a one-line edit here.
       Each pair is built from tokens that already flip with the theme, which is
       why there is no .dark-mode counterpart to this block — --brand-text is the
       lone exception and is overridden there. */
    --tag-blue-bg: rgba(var(--blue-color-rgb), .15);
    --tag-blue-fg: var(--blue-link-hover-color);
    --tag-purple-bg: rgba(var(--purple-color-rgb), .15);
    --tag-purple-fg: var(--purple-color-dark);
    --tag-green-bg: rgba(var(--success-color-rgb), .15);
    --tag-green-fg: var(--success-text);
    --tag-red-bg: rgba(var(--error-color-rgb), .12);
    --tag-red-fg: var(--error-text);
    /* For tags on the surfaces that stay dark in both themes (the company hero, the
       licence server header): the dark-mode --success-text / --error-text, so a state
       tag there matches the same tag everywhere else in dark mode. */
    --tag-green-fg-on-dark: #7CD474;
    --tag-red-fg-on-dark: #FF9A90;
    --tag-orange-bg: rgba(var(--warning-color-rgb), .15);
    --tag-orange-fg: var(--warning-text);
    --tag-orange-fg-on-dark: #F2A65A;
    /* Every chip carries a soft edge in its own hue: currentColor resolves on the chip
       that uses this, so one value serves every colour, theme and surface. */
    --tag-border: 1px solid color-mix(in srgb, currentColor 45%, transparent);
    --tag-brand-bg: rgba(var(--main-color-rgb), .12);
    --tag-brand-fg: var(--brand-text);
    --tag-neutral-bg: var(--gray-bg);
    --tag-neutral-fg: var(--text-sub);

    --white-color: white;
    --white-color-a50: rgba(255, 255, 255, 0.5);
    --black-color: black;
    --main-text-color-a25: rgba(30, 30, 30, 0.25);
    --main-text-color-a50: rgba(30, 30, 30, 0.5);
    --main-text-color-a75: rgba(30, 30, 30, 0.75);

    --font-extra-bold: 700;
    --font-bold: 600;
    --font-semi-bold: 500;
    --font-regular: 400;

    --box-shadow-color: rgba(0, 0, 0, 0.25);
    --box-shadow-40-color: rgba(0, 0, 0, 0.4);
    --box-shadow-user-details: 0 0 0 .25rem rgba(13, 110, 253, .25);

    --notification-button: #1E1E1E;
    --notification-button-text: #FFFFFF;
    --notification-button-hover: #444444;

    --nav-progress-bar-height: 16px;
    --bs-primary: var(--main-color);
    --bs-primary-rgb: 212, 47, 59;

    /* ════════════════════════════════════════════════════════════════
       NEW DESIGN-SYSTEM TOKENS (MySigmaTEK migration — spec §3)
       ────────────────────────────────────────────────────────────────
       Single source of truth for the redesign palette. Everything below
       is additive: the short new names alias the existing --main-* tokens
       (new = var(old)) so all 500+ existing references keep working and the
       transition stays smooth. Component CSS should consume these new names.

       Dark-mode values come for free: because each alias points at a
       --main-* token that .dark-mode already overrides, the new names
       resolve to the correct dark value without a separate dark block here.
       (DF surface migration of the dark hexes themselves is a later task.)
       ════════════════════════════════════════════════════════════════ */

    /* Brand red */
    --red: var(--main-color);
    --red-dark: var(--main-color-dark);
    --red-light: var(--main-color-bright);
    --red-tint: var(--main-color-highlight);

    /* Neutrals / surfaces */
    --surface: var(--main-background-color);
    --gray-bg: var(--main-background-color-subtle);
    --border: var(--main-border-color);
    --text: var(--main-text-color);
    --text-sub: var(--light-grey-color);

    /* Dark Forge (DF) neutral ramp — brand steel-grey palette (spec §3.3).
       Global (identical in light & dark); consumed by the dark-mode surface
       migration and the new shell (site header/footer, sidebar). */
    --df-jet: #141414;
    --df-dark: #2A2E33;
    --df-mid: #42474D;
    --df-highlight: #52575E;
    --df-light: #7A7F85;
    --df-fog: #C5C8CC;
    --df-snow: #FAFAFA;

    /* Selected row inside an open dropdown / option list — one token for every
       dropdown in the app (.sh-select menus, the searchable variant, the language
       picker and every DevExtreme SelectBox/Lookup popup), so the selection reads
       the same wherever it appears and matches the MySigmaTEK prototype.
       It was a 10% wash of the text colour, which on a white menu came out ~#E8E8E8
       — barely distinguishable from a hover, so the current value was hard to spot.
       DF Fog is the ramp's light-metal step: solid enough to read at a glance and
       still carries #141414 text at 11.5:1. Neutral by design — selection never
       uses brand red (see .sh-select-result.selected, which did).
       HOVER USES THE SAME FILL — deliberately one tone, not a ladder of three.
       A dropdown highlights the row under the pointer exactly the way it marks the
       chosen one, so the band tracks the mouse and lands on the current value when
       you stop: that is how the native popup in the prototype behaves. A separate,
       fainter hover shade next to a solid selected band read as two unrelated
       states. There is therefore no --select-selected-hover-bg. */
    --select-selected-bg: var(--df-fog);

    /* The --df-forge-gradient token (steel → jet → deep red, 135°) lived here and
       is gone: every surface that used it now takes --df-facet-tile, so it had no
       consumers left. Recover it from git history if the red gradient is ever
       wanted back rather than reintroducing it by hand. */

    /* Facet texture — the brand's low-poly charcoal fill, and the single fill for
       every dark chrome surface in the app, replacing the red forge gradient on
       all of them: the page banners (dashboard, company and search heroes), every
       dialog header (.modal-header, .dx-popup-title and the license-server popup's
       own header), the download bundle card (.dl-bundle) and the Knowledge Base
       support callout. One token, so a dialog header and a hero band read as the
       same material.
       The tile is authored seamless on both axes (its edge polygons mirror across
       x=800 / y=400), so it repeats without a visible seam at any width. It is
       drawn at its native 800×400 rather than `cover` for two reasons: facets
       never stretch on a short, wide band, and the facet size stays physically
       identical across surfaces — a 52px dialog header shows a slice of the same
       texture a 180px banner does, instead of a shrunken copy of the whole tile.
       Always-dark: identical in light & dark mode, like the gradient it replaces. */
    --df-facet-tile: url('../Images/Patterns/facet-tile-dark.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo') 0 0 / 800px 400px repeat #1C1C1E;

    /* ── Dialog surfaces ─────────────────────────────────────────────
       One rule governs every dialog in the app, whichever shell renders it
       (.modal-overlay > .modal, .pm-modal, .patches-modal, .ump-modal, a
       DevExtreme dxPopup or a Bootstrap .modal-content): THE FIELD IS THE
       RECESSED SURFACE AND THE DIALOG IS THE RAISED ONE. Light mode and dark
       mode are the same idea mirrored, not two separate palettes.

       These are deliberately NOT --page-bg, which the dialog shells used to
       read. That token is also the fill for card headers and table rows
       (--table-row-bg), so the dialog surface could not move without dragging
       those with it. Dialogs get their own tokens instead.

       Light: the shell is white and the field keeps the topbar-search grey, so
       for the first time the field is DARKER than the dialog holding it — it
       previously matched the shell to within 1.5% (#FAFAFA shell / #F4F5F6
       field on an #F4F5F6 canvas), which is why a dialog read as a patch of
       page rather than as an object on top of it.
       Dark: the shell takes the nav pane's #2A2E33 and the field drops to the
       #141414 the workspace canvas uses. The old dark shell was #141414 —
       byte-identical to the canvas behind it, separated by nothing but a
       shadow. */
    --dialog-bg: var(--main-background-color);
    --dialog-field-bg: var(--main-background-color-subtle);
    /* Resting edge on every dialog field. Fields are outlined, not borderless:
       a dialog stacking five or six of them reads as five explicit boxes rather
       than five slabs of fill, and the box does not change weight when it takes
       focus — only its colour. Also carried by Cancel, so the action row stays in
       step with the form above it. */
    --dialog-field-border: var(--main-border-color);
    /* Focus is brand red in BOTH themes, edge + halo, matching
       .topbar-search:focus-within. The halo is heavier on the dark chassis for
       the same reason the topbar's is: a 12% red wash disappears on #2A2E33. */
    --dialog-focus-ring: 0 0 0 3px rgba(var(--main-color-rgb), .12);
    /* Hairline ring around the shell, drawn as the second layer of the dialog's
       box-shadow. A dialog needs an EDGE and not only a drop shadow: the shadow
       is nearly invisible where a white dialog meets the light canvas, and it
       vanishes entirely in a screenshot or a PDF export. */
    --dialog-frame: rgba(0, 0, 0, .10);
    /* There is deliberately no footer token. A banded action bar was tried and
       removed: the dialog surface runs unbroken from under the facet header to
       the bottom edge, with no fill change and no rule above the buttons. */

    /* Typography — Segoe UI brand standard with Arial fallback (spec §5.1).
       Defined here as the single font token; the actual html/body font swap
       and Google-Fonts removal are a later task. */
    --font: 'Segoe UI', Arial, sans-serif;
}

/* Global (Base Elements + Templates) */

.dark-mode {
    /* Dark theme aligned 1:1 to the MySigmaTEK prototype (body.dark):
       brand red stays #D42F3B, cards #141414, workspace #111113,
       border rgba(255,255,255,.09), text #C5C8CC, sub-text rgba(220,220,230,.5). */
    --main-color: #D42F3B;
    --main-color-bright: #F7403A;
    --main-color-highlight: #553739;
    --main-color-outline: #963d45;
    /* Search match, dark side: a slightly thinner amber fill with light amber text.
       On the #141414 surface the fill composites to roughly #59470F, and #FDE68A
       holds about 7:1 on it. */
    --search-hit-bg: rgba(var(--yellow-color-rgb), .32);
    --search-hit-fg: #FDE68A;
    /* Dropdown selection, dark side: DF Mid rather than light mode's Fog — Fog is
       the dark theme's TEXT colour, so it would invert the row. #42474D sits clearly
       above the #141414 menu surface and holds #C5C8CC text at 5.4:1. */
    --select-selected-bg: var(--df-mid);
    --purple-color: #B39DDB;
    --purple-color-dark: #D1C4E9;
    --purple-color-rgb: 179, 157, 219;
    --yellow-color: #FBBF24;
    --yellow-color-dark: #FDE68A;
    --yellow-color-rgb: 251, 191, 36;

    /* State text lightens for the dark chassis. The fill hues above/below stay put —
       only these three flip, which is why a `color:` must never borrow a fill token. */
    --success-text: #7CD474;
    --warning-text: #F2A65A;
    --error-text: #FF9A90;
    --toast-bg: #2A2E33;
    /* The only --tag-* input that needs a dark value: --main-color-dark is
       re-declared identical below, so brand-as-text would stay #8A1820. */
    --brand-text: #F79098;

    --main-text-color: #C5C8CC;
    /* Dark elevation ladder, darkest → lightest: page #0e0e10 (painted on
       body.shell.dark-mode) < card/surface #141414 < raised header/'subtle'
       #202024 < lowlight #2a2a2c. 'subtle' is now LIGHTER than the card so
       table/section headers, popovers and hover states read as raised (matching
       the prototype and mirroring how light mode behaves), instead of the old
       #111113 which was darker than the card and made headers recede. */
    --main-background-color: #141414;
    --main-background-color-subtle: #202024;
    --main-background-color-subtle-highlight: #0a0a0c;
    --main-background-color-subtle-lowlight: #2a2a2c;
    /* Nav-pane grey (#2A2E33): list rows and list cards lift off the #141414 canvas
       the way a white card lifts off light mode's #F4F5F6. Full note at :root. */
    --list-surface-bg: var(--sidebar-bg);
    /* Dialog surfaces, dark side — see the block at :root for the full note.
       The shell borrows the nav pane's #2A2E33 (same source as --list-surface-bg
       above: a dialog is a raised object on the canvas, exactly like a list card)
       and the field drops to the #141414 the canvas itself uses. That inverts the
       old arrangement, where the shell WAS #141414 and the field sat lighter at
       #202024 — a field that reads as raised above the dialog containing it. */
    --dialog-bg: var(--sidebar-bg);
    --dialog-field-bg: var(--main-background-color);
    /* Not --main-border-color: that token is .08 alpha, tuned as the frame weight
       for a card sitting on #141414. On the lighter #2A2E33 dialog it composites to
       almost nothing, so the rich-text block and any field that opts into a resting
       edge would lose it. .12 lands on the reference design's field edge. */
    --dialog-field-border: rgba(255, 255, 255, .12);
    /* 12% red washes out on #2A2E33 — the same reason #topbar's focus halo is .25
       against the always-dark bar while the theme-aware one is .12. */
    --dialog-focus-ring: 0 0 0 3px rgba(var(--main-color-rgb), .28);
    --dialog-frame: rgba(255, 255, 255, .10);
    /* Border alpha history: .09 → .15 (to separate cards, tables and dialogs from the
       chassis) → .10 → .08 on 2026-09-22. .15 drew a visible grid over the dark
       surfaces; .08 is measured off the reference design, where the card/panel frame
       composites to #262626 on the #141414 surface — (0x26-0x14)/(0xFF-0x14) = .08.
       This token is the FRAME weight: card, panel and dialog edges, and the rule under
       a table header. Interior row rules are one step softer again — see --table-border
       in pages.css. Don't collapse the two: the reference draws the frame heavier than
       the rules inside it, which is what stops a table reading as a grid. */
    --main-border-color: rgba(255, 255, 255, 0.08);
    /* Bootstrap draws its own hairlines from --bs-border-color (#dee2e6) and never
       flipped it for dark mode — the app only ever remapped --bs-primary. So every
       control Bootstrap styles itself (.table, .list-group, .dropdown-divider,
       .nav-tabs, .modal-footer, the .border-* utilities) painted a near-white line on
       the #141414 chassis, which is why some lines stayed loud no matter how far the
       app's own token came down. Pointing both at --main-border-color means one
       hairline everywhere, Bootstrap's components included.
       --bs-table-border-color is NOT settable here: Bootstrap declares it on .table
       itself, so an inherited value loses — see the body.dark-mode .table rule below. */
    --bs-border-color: var(--main-border-color);
    --bs-border-color-translucent: var(--main-border-color);
    /* Alpha lifted .5 → .6 on 2026-08-03. At .5 this composited to roughly 4.05:1
       over the #141414 card — below the 4.5:1 text floor, and --text-sub aliases it,
       so every caption and hint in dark mode was failing AA. At .6 it measures
       5.45:1, closely matching light mode's 5.19:1 so the two themes read alike. */
    --light-grey-color: rgba(220, 220, 230, 0.6);
    --dark-grey-color: #101010;
    /* Company monogram fill — DF Mid from the SigmaTEK palette. The light-mode #1E1E1E is
       within a shade of the dark card it sits on, so the tile read as a hole rather than a
       mark. DF Mid is the palette's mid-metal step: it separates cleanly from both the
       #141414 card and the #202024 raised header, and still carries white initials at
       9.4:1. The red user tile is unchanged — it already stands off the dark chassis. */
    --avatar-company-bg: #42474D;
    --chat-message-background-color: #383838;
    --blue-link-color: #75c7ff;
    --blue-link-hover-color: #66b1e3;

    /* only update these if the hex colors are modified */
    --main-background-color-rgb: 20, 20, 20;
    --main-background-color-subtle-rgb: 32, 32, 36;
    --main-border-color-rgb: 255, 255, 255;
    --black-color-rgb: 255, 255, 255;
    --white-color-rgb: 0, 0, 0;
    /* ------------------------------------------------ */

    --main-text-color-a25: rgba(197, 200, 204, 0.25);
    --main-text-color-a50: rgba(197, 200, 204, 0.5);
    --main-text-color-a75: rgba(197, 200, 204, 0.75);
    --white-color: white;
    --white-color-a50: rgba(255, 255, 255, 0.5);
    --black-color: black;

    --box-shadow-color: rgba(0, 0, 0, 0.6);
    --box-shadow-40-color: rgba(0, 0, 0, 0.75);
    --box-shadow-user-details: 0 0 0 .25rem rgba(13, 110, 253, .65);
    
    --notification-button: #FFFFFF;
    --notification-button-text: #000000;
    --notification-button-hover: #ddd;
    --notification-button-shadow: 0, 0, 0;
}

/* Reseller palette unified to red per feedback_reseller_no_green — was previously green.
   Brand-red values aligned to the official SigmaTEK guide (spec §4.1). */
.reseller-mode {
    --main-color: #D42F3B;
    --main-color-dark: #8A1820;
    --main-color-bright: #F7403A;
    --main-color-outline: #D45F67;
    --main-color-highlight: #FCE4E5;
    --main-color-disabled: #E18B91;
    --bs-primary: var(--main-color);
    --bs-primary-rgb: 212, 47, 59;

    &.dark-mode {
        --main-color: #F24652;
        --main-color-bright: #FF6062;
        --main-color-highlight: #553739;
        --main-color-outline: #963d45;
    }
}

/* Reduced-motion override — collapses every transition and animation
   for users with vestibular sensitivity. PRODUCT.md commits to this;
   single block here covers all four redesign files via the * selector.
   Scroll-behavior: auto so anchor jumps don't smooth-scroll either. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        transition-duration: .001ms !important;
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        scroll-behavior: auto !important;
    }
}

/* ── Icon system ────────────────────────────────────────────────────
   Rendered by the global <icon> tag helper (wwwroot/icons/{set}/{name}.svg).
   The wrapper is inline and sizes the SVG; colour is inherited via
   stroke/fill: currentColor, so an icon takes the colour of its context. */
.svg-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    flex: 0 0 auto;
}
.svg-icon > svg {
    width: 100%;
    height: 100%;
    display: block;
}
.nav-icon {
    width: 18px;
    height: 18px;
}
/* Fallback so a bare <icon> (no sizing class) is still visible. */
.svg-icon:not(.nav-icon):not(.ui-icon) > svg {
    width: 1.1em;
    height: 1.1em;
}
/* Missing/unimplemented icon — deliberately obvious placeholder. */
.icon-missing {
    width: 18px;
    height: 18px;
    border: 1px dashed var(--error-color);
    border-radius: 2px;
    box-sizing: border-box;
}

html {
    font: normal 16px var(--font) !important;
    height: 100%;
}

h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
    font-family: var(--font) !important;
}

html, body {
    margin: 0;
    padding-bottom: 0;
    font-family: var(--font) !important;
}

.twemoji {
    font-family: TwemojiMozilla, var(--font) !important;
}

body {
    display: flex;
    min-height: 100vh;
    flex-direction: column;
    background-color: var(--main-background-color) !important;
    color: var(--main-text-color);
}

/* ── Global scrollbar styling ───────────────────────────────────────
   Thin, theme-tinted scrollbars everywhere (not just the few components that
   previously styled their own) — scrollbar-width/scrollbar-color are inherited,
   so setting them once on :root covers every scrollable element site-wide unless
   something more specific overrides them. */
/* Firefox only: in Chrome/Edge these properties override the square ::-webkit-scrollbar
   styling below with the browser's rounded native bar. */
@supports not selector(::-webkit-scrollbar) {
    :root {
        scrollbar-width: thin;
        scrollbar-color: color-mix(in srgb, var(--text-sub) 55%, transparent) transparent;
    }
}

::-webkit-scrollbar {
    width: 7px;
    height: 5px;
}

::-webkit-scrollbar-track {
    background: color-mix(in srgb, var(--text-sub) 10%, transparent);
}

::-webkit-scrollbar-corner {
    background: transparent;
}

/* Square arrow buttons at both ends of every scrollbar. A data-URI triangle, because a
   pseudo-element scrollbar button can't take an inline <icon>; mid-grey reads on both themes. */
::-webkit-scrollbar-button:single-button {
    display: block;
    width: 7px;
    height: 7px;
    background-color: transparent;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 7px 7px;
}

::-webkit-scrollbar-button:single-button:hover {
    background-color: color-mix(in srgb, var(--text-sub) 25%, transparent);
}

/* Horizontal bars are one step thinner than vertical ones; their buttons follow. */
::-webkit-scrollbar-button:horizontal {
    height: 5px;
    background-size: 5px 5px;
}

/* Chrome can render a pair at each end; keep only the outer one (start: decrement,
   end: increment) so each end has a single arrow. */
::-webkit-scrollbar-button:start:increment,
::-webkit-scrollbar-button:end:decrement {
    display: none;
}

::-webkit-scrollbar-button:horizontal:decrement {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath d='M6 0.5v7L1.5 4z' fill='%23888'/%3E%3C/svg%3E");
}

::-webkit-scrollbar-button:horizontal:increment {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath d='M2 0.5v7L6.5 4z' fill='%23888'/%3E%3C/svg%3E");
}

::-webkit-scrollbar-button:vertical:decrement {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath d='M0.5 6h7L4 1.5z' fill='%23888'/%3E%3C/svg%3E");
}

::-webkit-scrollbar-button:vertical:increment {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath d='M0.5 2h7L4 6.5z' fill='%23888'/%3E%3C/svg%3E");
}

::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--text-sub) 45%, transparent);
    border-radius: unset;
}

::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--text-sub) 65%, transparent);
}

.indicators {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

main {
    flex: 1;
}

h1 {
    font-size: 32px;
    font-weight: var(--font-bold);
}

h2 {
    font-size: 28px;
    font-weight: var(--font-semi-bold);
}

h2 a {
    text-decoration: none;
}

h3 {
    font-size: 24px;
    font-weight: 300 !important;
}

h4 {
    font-size: 20px;
    font-weight: 300 !important;
}

h5 {
    font-size: 18px;
    font-weight: var(--font-semi-bold);
    margin: .5em 0 0;
}

h6 {
    font-size: 16px;
    font-weight: var(--font-semi-bold);
    margin: .5em 0 0;
}

code {
    color: var(--main-color);
}

.text-muted {
    color: var(--main-text-color-a75) !important;
}

.default-shadow {
    box-shadow: 1px 2px 4px 0 var(--box-shadow-color);
}

.main-padding {
    margin-bottom: 4%;
    padding-top:2%;
}

.underline {
    text-decoration: underline;
}

.container {
    padding-left: 10px;
    padding-right: 10px;
    min-height: 50px;
}

.navbar-horizontal {
    margin: 3% 6%;
}

.language-dropdown-menu.dropdown-menu {
    left: auto;
    right: 0;
    max-height: calc(100vh - 114px);
    overflow-y: scroll;
    
    & hr {
        margin-top: 2px;
        margin-bottom: 2px;
    }
}

/* ── Page-load progress bar — slim 3px determinate top bar ──────────
   Sits at the top of the content area; #progress-bar width is driven by
   JS (0 → 95% while loading, then completes). Brand red, no shadow. */
#progress-container {
    height: 3px;
    /* Pinned just under the fixed topbar so the loading bar stays visible
       while the page is scrolled, instead of scrolling away with content. */
    position: fixed;
    top: calc(var(--site-header-h) + var(--topbar-h));
    /* Spans the full viewport, starting under the brand mark rather than at the
       sidebar's inner edge — the topbar runs edge to edge now, so the bar tracks
       the whole width of the chrome above it instead of just the content column. */
    left: 0;
    right: 0;
    width: auto;
    box-sizing: border-box;
    pointer-events: none;
    /* Must clear #sidebar (1100): the bar now crosses the sidebar's column, and at
       the old 1080 that leading 240px would have been painted over by the sidebar
       and never seen. Still below #topbar (1200), so it tucks under the bar. */
    z-index: 1150;
    overflow: hidden;
    opacity: 1;
    transition: opacity .25s ease;
}

#progress-bar {
    height: 100%;
    width: 0;
    background: var(--main-color);
    transition: width .15s ease;
}

@media (max-width: 380px) {
    .login-container {
        margin-top: -10%;
    }

    .container {
        width: 95% !important;
    }

    #navbar-brand-img {
        margin-right: 0 !important;
    }
}

@media (max-width: 400px) {
    .dropdown-menu {
        width: 85% !important;
        margin-left: 15% !important;
    }
}

@media (max-width: 435px) {
    .center {
        margin-left: 0;
        margin-right: 0;
    }

    a.dropdown-item {
        font-size: 14px;
    }

    .login-site-branding-header {
        display: flex;
        justify-content: center;
    }

    .container {
        width: 85% !important;
    }
}

@media (max-width: 780px) {
    .navbar-nav {
        flex-direction: column;
    }

    .navbar-buttons {
        padding-left: 5% !important;
        margin-left: 0 !important;
    }

    a.dropdown-item {
        text-wrap: wrap;
    }
}

@media (min-width: 780px) {
    .navbar-img {
        height: 50px;
    }
}

@media (max-width: 855px) {
    a.navbar-brand {
        font-size: 14px !important;
    }

    .dropdown-item {
        padding: 0 10px !important;
        font-size: 12px !important;
    }
}

@media (max-width: 990px) {
    .dropdown-item {
        padding: 0 10px !important;
        font-size: 14px;
    }

    .container {
        width: 75%;
    }

    .navbar-nav {
        text-align: right;
    }

    .dropdown-menu {
        text-align: right;
        width: 60%;
        margin-left: 40%;
    }

    #navbar-collapsible {
        margin-top: 10px;
    }

    .navbar-horizontal {
        margin: 10px 12px !important;
    }
}

@media (min-width: 1200px) {
    .container {
        max-width: 1380px !important;
        padding-left: 38px !important;
        padding-right: 38px !important;
    }

    #padding-anchor {
        padding-right: 0;
        padding-left: 0;
    }

    .navbar-nav {
        margin: 0;
        justify-content: flex-end;
        display: flex;
        float: none;
        align-items: center;
    }
}

@media (max-width: 1200px) {
    .language-dropdown-menu.dropdown-menu {
        max-height: calc(100vh - 320px);
    }
}

@media (min-width: 1400px) {
    .container {
        max-width: 1600px !important;
    }
}

@media (min-width: 1600px) {
    .container {
        max-width: 2200px !important;
        padding-left: 38px !important;
        padding-right: 78px !important;
    }
    
    .center {
        width: 80%;
    }

    .navbar-nav {
        margin: 0;
        justify-content: flex-end;
        display: flex;
        float: none;
        align-items: center;
    }
}

a.navbar-brand {
    font-size: 16px;
    font-weight: var(--font-bold);
    position: relative;
}

img.navbar-brand {
    margin-right: 20px;
    height: 38px;
    padding: 0;
}

@media (min-width:993px) {
    .nav > li > a {
        margin-left: 20px !important;
    }
}

.navbar-brand {
    margin-right: 0 !important;
}

.dropdown-menu {
    background-color: var(--main-background-color);
    border-color: var(--main-text-color-a25);
    color: var(--main-text-color);
}

a.dropdown-item {
    color: var(--main-text-color);
}

a.dropdown-item:hover,
a.dropdown-item:active,
a.dropdown-item:focus {
    color: var(--main-color);
    background-color: transparent;
}

/* messages styling*/
.case-message-container {
    min-height: 400px;
    max-height: 530px;
    border-radius: var(--radius);
    display: flex;
    flex-direction: column;
    gap: 14px;
    overflow-y: auto;
    padding: 4px 4px 4px 0;
}

.case-message {
    max-width: 100%;
    border-radius: var(--radius);
    background-color: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

#NewMessage .dx-htmleditor-content {
    font-size: 16px;
}

.case-message-header {
    padding: 8px 14px;
    background-color: rgba(var(--main-color-rgb), .12);
    border-bottom: 1px solid var(--main-border-color);
}

.case-message-header p {
    font-weight: var(--font-bold);
    margin-bottom: 0;
}

.case-message-content {
    padding: 10px;
}

.case-message-content p,
.case-message-content ul {
    margin-bottom: 0;
}

.final-case-message {
    text-align: center;
}

.final-case-message p {
    margin-bottom: 0;
    font-size: 14px;
    color: var(--text-sub);
}

.case-message-content table {
    box-shadow: none;
}

.case-message-content a {
    color: var(--main-color);
}

.case-message-content p {
    color: var(--main-text-color);
}

.case-message-content img {
    max-width: 100%;
    height: auto;
}

.case-message-content * {
    max-width: 100%;
    box-sizing: border-box;
}

.message-container {
    background-color: var(--main-background-color-subtle);
    padding: 20px;
    width: 100%;
    border-radius: var(--radius);
    border: 1px solid var(--main-border-color);
}

/* case assignment history */

.assignment-history-container {
    border-radius: var(--radius);
    max-height: 530px;
    overflow-y: auto;
}


/* customer styling*/

.searchErrorStyle {
    display: block !important;
    color: var(--error-text);
}

.new-description {
    height: 100px !important;
}

#netsuitedata > div,
#connectdata > div,
.label-format > div {
    margin-bottom: 4px;
}

.label-format > span {
    color: var(--main-color);
    margin-top: 3%;
    margin-bottom: 3%;
}

.label-format.multi-line > span {
    margin-top: 0;
    margin-bottom: 0;
}

.label-format.multi-line > span:first-of-type {
    margin-top: 3%;
}

.label-format.multi-line > span:last-of-type {
    margin-bottom: 3%;
}

.label-format > span > a {
    color: var(--main-color);
}

p a, h2 a, li a {
    color: var(--main-color)
}

.label-format {
    display: flex;
    flex-direction: column;
}

.label-format > label {
    margin: unset;
}

#additional-details-accordion .dx-accordion-item-title-caption {
    font-size: 16px;
}

.additional-details-data {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 12px 0;
}

.detail-group-label:first-child,
.detail-group-value:first-child {
    margin-top: 0;
}

.detail-group-value:first-child {
    margin-top: 0;
}

.detail-group-label {
    margin: 0;
    display: inline;
    font-size: 16px;
}

.detail-group-value {
    color: var(--main-color);
    font-size: 16px;
}

.detail-group-value a {
    color: var(--main-color);
}

.detail-group-value > a {
    font-size: 16px !important;
    font-weight: normal !important;
}

.additional-details-data .detail-group-label,
.additional-details-data .detail-group-value {
    flex-basis: 35% !important;
}

.additional-details-data .detail-group-label {
    flex: 1;
}

.additional-details-data .detail-group-value {
    flex: 10;
}

.details-header-container a {
    color: var(--main-color);
    font-size: 12px;
    font-weight: var(--font-bold);
}

.details-header-container h1 {
    color: var(--main-color);
    margin-top: 18px;
    margin-bottom: 30px;
    font-weight: var(--font-bold);
}

.details-header-container h2 {
    font-style: italic;
    font-weight: var(--font-bold);
}

.details-header-text a {
    color: var(--main-text-color);
    font-size: 28px;
    text-decoration: underline;
}

.details-header-subtitle-container {
    display: flex;
    justify-content: space-between;
}

@media (max-width: 1000px) {
    .details-header-subtitle-container {
        flex-direction: column;
        gap: 10px;
    }
}

.details-header-subtitle-button-container {
    display: flex;
    gap: 20px;
    max-height: 40px;
}

.details-header-subtitle-button-container button {
    min-width: 200px;
    font-size: 18px;
    box-shadow: 1px 2px 4px 0 var(--box-shadow-color);
    border-radius: 0;
}

.details-headers {
    color: var(--main-text-color);
    font-weight: var(--font-extra-bold) !important;
    font-size: 16px;
    margin-top: 32px;
    margin-bottom: 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--main-border-color);
}

.details-body-container {
    max-width: 100%;
    box-sizing: border-box;
}

.details-body {
    display: flex;
    gap: 60px;
}

.details-body-main {
    flex-grow: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

#user-details-information {
    display: flex;
    flex-direction: row;
    gap: 15%;
}

@media (max-width: 650px) {
    #user-details-information {
        flex-direction: column;
    }
}

.details-body-main.full-width {
    max-width: 100%;
}

.details-body-main-element:first-child h3.details-headers {
    margin-top: 0;
}

.details-body-main-element {
    max-width: 100%;
    margin-bottom: 20px;
}

form#create-case-form div.inline-item-group {
    display: flex;
    justify-content: space-between;
    gap: 20px;
    margin-top: 25px;
    align-items: center;
}

form#create-case-form div.inline-item-group:first-of-type {
    margin-top: 0;
}

form#create-case-form div.inline-item-group label.control-label {
    margin: 0;
    width: 100px;
}

.speechrec-microphone-button {
    background: none;
    border: none;
    color: var(--main-text-color);
    cursor: pointer;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.speechrec-microphone-button.recording {
    color: var(--main-color);
}

#actions-dropdown .dx-dropdownbutton-action {
    height: 40px;
}

#actions-dropdown .dx-button-content {
    font-size: 16px;
}

#actions-dropdown .dx-button-text {
    text-transform: none;
}

#additional-details-accordion {
    margin: 0;
    background-color: transparent;
    width: 100%;
}

@media (max-width: 1000px) {
    #additional-details-accordion {
        width: 75%;
    }

}

@media (max-width: 650px) {
    #additional-details-accordion {
        width: 100%;
    }
}

/* utility styling*/

/* case styling*/

/* ── Error system ("Rail") ───────────────────────────────────────────────
   One treatment per situation, shared by every page: a field message, a form
   summary banner, the invalid-input edge, and the full-page error card. Panel
   notices (.notice-error), toasts (.dx-toast-error / .toast-error) and the
   DevExtreme message follow the same rail in pages.css and dx.alterations.css.

   Colour rule: --error-color is a FILL (rails, badges, borders). --error-text is
   the only red allowed in a `color:` declaration, because it is the one that
   flips with the theme (see the state TEXT tokens in :root). */

/* Bootstrap 5.3's .text-danger is `color: #DC3545 !important`, so a plain rule
   here never applied and every validation span rendered an off-palette red with
   no dark value. !important is the only way past it; :root lifts specificity
   above Bootstrap's single class so this wins whichever sheet loads last —
   _LoginLayout links Bootstrap AFTER the css bundle, _Layout before it. */
:root .text-danger {
    color: var(--error-text) !important;
}

/* Field message. MVC stamps field-validation-error on a span holding an error and
   adds field-validation-valid when there is none; jQuery Unobtrusive toggles the
   same classes client side. The other selectors are script-driven field messages
   (profile modal, partner profile, notification schedule) that predate the shared
   class. The badge is inline-block rather than a flex item so it still renders on
   spans that scripts reveal with .show() (which writes display:inline).
   vertical-align:middle centres the badge on the text (flex layouts ignore it). */
.field-validation-error,
.pm-field-msg-error,
.pp-row-error,
.pp-form-error,
.sched-start-error,
.invite-field-error {
    color: var(--error-text);
    font-size: 13px;
    line-height: 1.5;
}

.field-validation-error:not(.field-validation-valid):not(.validation-summary-errors):not(.validation-summary-valid):not(:empty)::before,
.pm-field-msg-error:not(:empty)::before,
.pp-row-error:not(:empty)::before,
.pp-form-error:not(:empty)::before,
.sched-start-error:not(:empty)::before,
.invite-field-error:not(:empty)::before,
.validation-summary-errors::before,
.error-banner::before {
    content: "!";
    display: inline-block;
    flex-shrink: 0;
    width: 15px;
    height: 15px;
    margin-right: 6px;
    vertical-align: middle;
    background: var(--error-color);
    color: #fff;
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    line-height: 15px;
    text-align: center;
}

/* Form summary: ValidationSummary output, and .error-banner for script-raised
   form failures. Wash + hairline + solid 3px rail. Body text stays in the normal
   text colour; the rail and badge carry the state. Declared after the field rule
   because a summary rendered with the field class must still read as a banner. */
.validation-summary-errors,
.error-banner {
    display: flex;
    align-items: flex-start;
    gap: 4px;
    padding: 12px 14px;
    margin-bottom: 16px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--main-text-color);
    background: rgba(var(--error-color-rgb), .08);
    border: 1px solid rgba(var(--error-color-rgb), .32);
    border-left: 3px solid var(--error-color);
}

.validation-summary-errors::before,
.error-banner::before {
    width: 18px;
    height: 18px;
    margin-top: 1px;
    margin-right: 8px;
    font-size: 12px;
    line-height: 18px;
}

.validation-summary-errors ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

.validation-summary-valid {
    display: none;
}

/* Invalid input: the state border plus a 2px inner bottom rail, so the field
   reads as invalid even where a theme recolours its border on focus. */
input.input-validation-error,
select.input-validation-error,
textarea.input-validation-error {
    border-color: var(--error-color);
    box-shadow: inset 0 -2px 0 var(--error-color);
}

/* Full-page error card (ErrorPage/Error, NotFound, AccessDenied, Shared/Error).
   Same rail, thicker, with the status code as the mark. */
.error-page {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 16px 24px;
    padding: 22px 24px;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-left: 4px solid var(--error-color);
}

.error-page-code {
    min-width: 80px;
    font-family: ui-monospace, Consolas, 'Courier New', monospace;
    font-size: 34px;
    font-weight: var(--font-bold);
    line-height: 1;
    color: var(--error-text);
    font-variant-numeric: tabular-nums;
}

.error-page-body {
    flex: 1 1 260px;
    min-width: 0;
}

.error-page-body p {
    margin: 0 0 8px;
}

.error-page-detail {
    font-size: 13px;
    color: var(--light-grey-color);
    overflow-wrap: anywhere;
}

.error-page-actions {
    margin-top: 16px;
}

.error-page-ref {
    display: block;
    margin-top: 12px;
    font-size: 12px;
    color: var(--light-grey-color);
}

.form-width {
    max-width: none;
}

.caseStatus,
.case-assigned-to {
    margin-top: 8px;
    display: flex;
    align-items: center;
    gap: 5px;
}

.caseStatus select {
    -moz-appearance: none; /* Disables default Firefox styling */
    -webkit-appearance: none; /* For consistency on WebKit browsers */
    appearance: auto;
    border: 1px solid var(--main-color-dark);
    border-radius: var(--radius);
    width: 355px;
    padding: .175rem .75rem;
    background: var(--main-color);
    color: var(--white-color);
}
/* flex box */
.flex-row {
    display: flex;
    flex-direction: row;
    justify-content: space-evenly;
    margin-bottom: 5%;
}

.flex-row-case {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
}

.flex-row-search-bar {
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
    align-items: center;
    min-height: 48px;
    margin-bottom: 30px;
}

.search-checkbox-container {
    padding-left: 20px;
}

/* DevExtreme field-value default width is 50% — override inside search filters */
.search-checkbox-container .dx-field-value {
    width: unset;
}

.flex-row-details {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
}

.flex-row-container {
    display: flex;
    flex-wrap: wrap;
    margin-bottom: 2%;
}

.flex-column {
    display: flex;
    flex-direction: column;
    justify-content: space-evenly;
}

.button-style {
    display: inline-block;
    margin-top: 2em;
    text-transform: none;
    padding: .75em 1em;
    border-radius: var(--radius);
    border: none;
    color: var(--white-color);
}

.button-margin-top {
    margin-top: 20px;
}

#button-margin {
    margin-bottom: 0.5em;
}

.button-margin {
    margin-bottom: 0.5em;
}

.multiColumn {
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    align-items: stretch;
    align-content: stretch;
    flex-wrap: wrap;
}

/* table styling*/
table {
    border-radius: 0;
    box-shadow: var(--shadow-sm);
}

/*Invoice PDF Table*/

.row-padding td, th {
    padding: 8px;
}

.row-border {
    border: 1px solid var(--main-text-color);
}

.header-border {
    border-right: none;
    border-left: none;
}

.paragraph-margin p {
    margin-bottom: 0;
}

/*Pay Table*/
.fieldset-style {
    margin-inline-start: 2px;
    margin-inline-end: 2px;
    padding-block-start: 0.35em;
    padding-inline-start: 0.75em;
    padding-inline-end: 0.75em;
    padding-block-end: 0.625em;
    min-inline-size: min-content;
    border-width: 2px;
    border-style: groove;
    border-color: threedface;
    border-image: initial;
}

.multiColumn > * {
    flex-grow: 1;
}

label {
    display: block;
    font-weight: var(--font-bold);
    margin-top: 1em;
}

input:not([type="checkbox"]):not([type="radio"]), select, textarea {
    width: 100%;
    background-color: var(--main-background-color-subtle);
}

/* ============================================================================
   Browser autofill, app-wide (task 370063)
   Chrome and Edge paint autofilled / remembered values with their own pale
   background (near-white in dark mode) and FieldText colour, through UA
   `!important` declarations that no author `background` can override. A running
   transition outranks every cascade origin, so the long background-color / color
   transition holds whatever the field already had (filled, transparent search
   bar, DevExtreme editor) and prefilled text looks exactly like typed text.
   Wrapped in :is() so one selector carries 0-10-1: enough to outrank the
   page-level field rules (.form-group-sh, .searchArea, auth .field) whose own
   `transition` would otherwise replace this one, while the dialog (pages.css)
   and topbar (layout.css) autofill rules stay more specific and keep their
   context-specific covers. border-color / box-shadow are restated so focus
   edges still animate on an autofilled field.
   No text colour is forced here on purpose: the color transition keeps each
   field's own colour, so white-on-dark fields (the Customers hero search) stay
   white. Forcing --main-text-color painted them near-black on the dark band.
   ============================================================================ */
:is(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]), select, textarea):-webkit-autofill {
    transition:background-color 9999s ease-in-out 0s, color 9999s ease-in-out 0s, border-color .15s, box-shadow .15s;
}

/* Dark mode fallback: if a browser ever skips the transition above (e.g. a field
   restored already-filled from the back/forward cache), its autofill tint is the
   dark one rather than the near-white light one. Same text-like set as above. */
.dark-mode input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]),
.dark-mode textarea {
    color-scheme: dark;
}

#invoiceLinesPanel {
    display: table;
}

    #invoiceLinesPanel > div {
        display: table-row;
    }

        #invoiceLinesPanel > div > span {
            display: table-cell;
        }


/* Impersonation banner — fixed bottom strip while impersonating (matches prototype) */
.impersonate-banner {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    /* Above dialog overlays (5000) and the bell drawer, below DevExtreme/select popups (6000). */
    z-index: 5500;
    background: var(--main-color-dark);
    color: var(--white-color);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 10px 24px;
    font-size: 13px;
    font-weight: 500;
    gap: 16px;
    /* Flat-Chrome Rule: the impersonation banner is fixed chrome. Top hairline
       instead of the former `box-shadow: 0 -2px 12px`. */
    border-top: 1px solid rgba(var(--white-color-rgb), .15);
    height: var(--imp-banner-h, 48px);
    box-sizing: border-box;
}

.impersonate-banner-left {
    display: flex;
    align-items: center;
    gap: 10px;
}

.impersonate-banner-icon {
    width: 28px;
    height: 28px;
    border-radius: var(--radius);
    background: rgba(255, 255, 255, .18);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: 700;
    flex-shrink: 0;
    color: var(--white-color);
}

.impersonate-stop {
    padding: 6px 14px;
    border-radius: 0;
    background: var(--white-color);
    color: var(--main-color-dark);
    border: none;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
    transition: background .15s;
    white-space: nowrap;
}

.impersonate-stop:hover { background: rgba(255, 255, 255, .92); }

/* Phone: keep the bar at its fixed height by truncating the name rather than wrapping. */
@media (max-width: 575.98px) {
    .impersonate-banner {
        padding: 6px 12px;
        gap: 8px;
    }

    .impersonate-banner-left {
        flex: 1 1 auto;
        min-width: 0;
        gap: 8px;
    }

    .impersonate-banner-left > div:last-child {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .impersonate-stop {
        flex-shrink: 0;
        padding: 6px 10px;
    }
}

.impersonate-stop:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(255, 255, 255, .65);
}

/* Reserve space at the bottom of the content area so the fixed banner
   doesn't overlap the last row of the page, and pull the sidebar up
   so the banner sits below the sidebar footer instead of covering it
   (banner still spans full screen width — sidebar bottom edge meets
   the top edge of the banner exactly via the shared --imp-banner-h). */
body.impersonating {
    --imp-banner-h: 48px;
}

body.impersonating .shell-main-wrap {
    padding-bottom: var(--imp-banner-h);
}

body.impersonating #sidebar {
    bottom: var(--imp-banner-h);
}

.other-products-footer {
    gap: 50px;
}

.other-products-footer > div {
    flex-basis: 100%;
}

/*product styling*/
.products-container {
    max-width: 1200px;
    width: 90%;
}

.products-wrapper {
    display: flex;
    flex-direction: column;
    padding-left: 10em;
    padding-right: 10em;
}

@media (min-width: 1200px) {
    .products-wrapper {
        flex-direction: row;
        justify-content: space-evenly;
    }
}

#products .product {
    padding: 0;
    text-align: center;
    text-decoration: none;
    transition: .1s ease;
    border-radius: 0;
    width: 400px;
}

#products.login-no-brand-products .product {
    width: unset;
}

#products .product:hover {
    transform: scale(1.02);
}

.product img {
    max-width: 100%;
}

.line-title > h2 {
    font-size: 18px;
    color: var(--main-color);
}

.product-text {
    color: var(--main-color);
}

.topProductLeft {
    width: 64px;
    display: inline-block;
    vertical-align: top;
    margin-left: 3%;
}

.productLeft {
    width: 44px;
    display: inline-block;
    vertical-align: top;
    margin-left: 5%;
}

.productRight {
    margin-left: 5%;
    display: inline-block;
    vertical-align: top;
    position: relative;
    height: 100%;
}

.employeeLeft {
    width: 5em;
    display: inline-block;
}

.employeeRight {
    margin-left: 2em;
    display: inline-block;
    position: relative;
    height: 100%;
}

.productImage {
    max-width: 100%;
    max-height: 100%;
}

.productFullName {
    font-size: 20px;
    display: inline-block;
    font-weight: var(--font-semi-bold);
}

.productSize {
    margin-top: .5em;
}

.productsAll {
    position: absolute;
    left: -74px;
    top: 60px;
    font-weight: var(--font-bold);
    /*width: 180px; comment this out if you cant fix*/
    min-width: 180px;
    /*max-width: 180px; comment this out if you cant fix*/
    height: 40px;
    min-height: 40px;
    max-height: 40px;
}

.productDownload {
    padding: .5em 1em;
    color: var(--white-color);
    font-weight: var(--font-bold);
    box-shadow: 0 0 5px 1px var(--box-shadow-color);
    text-decoration: none;
    position: absolute;
    bottom: 0;
    right: 0;
}

.productsDescription {
    margin-right: 5%;
    max-width: 1000px;
}

.content-wrapper {
    padding-left: 12%;
    padding-right: 12%;
}

@media (max-width: 767.98px) {
    .content-wrapper {
        padding-left: 0;
        padding-right: 0;
    }
}

.page-header-dividing-line {
    margin-top: 2%;
    margin-bottom: 4rem;
}

.version-section {
    margin-bottom: 2em;
}

.version-name-and-date,
.release-notes-url {
    color: var(--main-color);
}

.release-message {
    margin-bottom: 1em;
}

.product-link {
    margin-bottom: 2em;
}

/***Release Candidate Styling***/

.release-container {
    margin: 0 auto;
    margin-top: 2em !important;
    max-width: 1200px;
}

.releaseMessage {
    font-size: clamp(18px, 5vw, 28px);
    text-transform: uppercase;
    font-weight: var(--font-bold);
}

.releasePadding {
    padding-left: 3em;
}

@media (max-width: 600px) {
    .releasePadding {
        padding-left: 0;
    }
}

.release-title {
    flex: 1;
    margin-left: 1em;
}

    .release-title h1 {
        font-weight: var(--font-bold);
        font-size: 20px;
    }

    .release-title h2 {
        font-weight: var(--font-bold);
        font-size: 16px;
    }

.release-image {
    max-height: 75px;
    max-width: 75px;
    margin: auto;
}

@media (max-width: 999px) {
    .release-title h1 {
        font-size: 18px;
    }

    .release-title h2 {
        font-size: 14px;
    }

    .release-image {
        max-height: 60px;
        max-width: 60px;
    }
}

.productMessage {
    font-size: 18px;
}

.productLink {
    font-size: 28px;
    font-weight: var(--font-bold);
}

.disable-product-cursor {
    cursor: not-allowed;
}

.flex-container {
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.image-wrapper {
    display: flex;
    flex-direction: row;
    justify-content: space-evenly;
    margin-top: 4em;
    margin-left: 5em;
    margin-right: 5em;
}

.product-image {
    height: 150px;
}

/**Login page */

.login-main {
    width: 100%;
    display: flex;
    background: var(--main-background-color);
}

.login-left-side {
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: 0;
    width: 0;
}

.saml-login-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
}

.login-content {
    display: flex;
    justify-content: center;
    align-items: center;
}

/*only include 80% height on pages that aren't register pages*/
.login-content:not(:has(.register-container)) {
    height: 80%;
}


.login-content > div {
    max-width: 100%;
}

.account-span {
    font-size: 20px;
    font-style: normal;
    font-weight: var(--font-regular);
}

.placeholder-text > div {
    font-size: 18px;
}

.login-links {
    font-size: 20px !important;
}

.login-header {
    display: flex;
    flex-direction: column;
    height: 30vh;
}

.login-header-banner {
    display: flex;
    flex-direction: column;
    flex-grow: 0.4;
    align-items: center;
    justify-content: center;
    background: url(../Images/banner-backgroud.jpg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo) no-repeat center;
    width: 100%;
    min-height: 290px;
    max-height: 350px;
    background-size: cover;
}

.login-header-tagline {
    color: #FFFFFF;
    text-align: center;
    font-weight: var(--font-bold);
    font-size: 48px;
    text-shadow: 1px 1px black;
    max-width: 900px;
}

.login-container {
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: start;
    width: 100%;
    max-width: 500px;
}

.login-wrapper {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
}

.login-form {
    margin-left: 20px;
    max-width: 620px;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-bottom: 40px;
    padding-left: 40px;
    padding-right: 40px;
}

    .login-form h2 {
        color: var(--main-text-color);
        font-size: 36px;
        font-weight: 800;
        line-height: 31px;
    }

    .login-form h4 {
        color: var(--main-text-color);
        font-size: 20px;
        font-weight: 300;
        line-height: 31px;
    }

    .login-form .login-form-bottom-container {
        display: flex;
        flex-direction: column;
        align-items: center;
    }

.login-hr {
    border-radius: 0;
    stroke-width: 2px;
    stroke: var(--main-border-color);
    align-self: normal;
}

.login-container input {
    max-width: 100%;
    min-width: 400px;
}

.login-button {
    width: 200px;
    max-width: 200px;
    min-width: 100px;
    padding: 0 0;
}

.login-label {
    font-weight: var(--font-semi-bold);
    font-size: 20px;
    font-style: normal;
    color: var(--main-text-color);
    margin-bottom: 0.5em;
}

.control-label {
    font-size: 11px;
    color: var(--text-sub);
    margin-bottom: 4px;
    font-weight: var(--font-semi-bold);
    margin-top:0px;
}

.bg-primary {
    border-radius:unset !important;
}

.password-toggle {
    position: absolute;
    top: 50%;
    right: 10px;
    transform: translateY(-50%);
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
}

.password-toggle:focus-visible {
    outline: none;
    border-radius: 0;
    box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), .45);
}

.password-toggle img {
    width: 20px;
    height: 20px;
}

.copilot-icon-field {
    background-image: url('../Images/Branding/Copilot/Copilot_icon_round button_white_128px.png?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo');
}

/* The SigmaTEK Copilot mark inline beside a label (sidebar, page title, buttons, popup
   title). Sized in em so it follows the text it sits next to; callers override the size. */
.copilot-brand-icon {
    display: inline-block;
    flex-shrink: 0;
    width: 1.25em;
    height: 1.25em;
    vertical-align: middle;
    background: url('../Images/Branding/Copilot/Copilot_icon_round button_white_128px.png?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo') center / contain no-repeat;
}

.nav-icon.copilot-brand-icon {
    width: 18px;
    height: 18px;
}

.large-icon-container {
    background-repeat: no-repeat;
    background-size: 100%;
    width: 40px;
    height: 40px;
    min-width: 40px;
    min-height: 40px;
    display: block;
}

@media only screen and (min-width: 1000px) {
    .login-form {
        margin-top: .5%;
    }
}


@media only screen and (max-width: 999px) {
    .login-header-tagline {
        font-size: 32px;
    }

    .login-container {
        flex-direction: column;
        align-items: center;
    }

    .login-form {
        max-width: 485px;
        order: 1;
        margin-left: 0;
    }

    .login-container input {
        max-width: 100%;
        min-width: 240px;
    }
}

/** Register page */
.register-container {
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: start;
}

.register-form {
    margin-right: 20px;
    width: 600px;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-bottom: 50px;
}

.register-container hr {
    width: 300px;
    border-top: 4px solid var(--main-color);
    margin-left: 0;
    margin-right: 0;
}

.register-container input {
    max-width: 100%;
    min-width: 400px;
}

@media (max-width: 900px) {
    .register-container input {
        min-width: 260px;
    }
}

.register-hr.sigmanest {
    width: 300px;
    border-top: 4px solid var(--main-color);
    margin-left: 0;
    margin-right: 0;
    opacity: 1;
}

.home-link-disabled {
    color: var(--text-sub) !important;
    opacity: .65;
    cursor: not-allowed;
}

@media (max-width: 576px) {
    #home-page {
        padding-left: 3%;
        padding-right: 3%;
    }
}

.package-container {
    background-color: var(--main-background-color-subtle);
    box-shadow: 0 3px 3px 1px var(--box-shadow-40-color);
    border-radius: 0;
    padding: 1em;
    display: flex;
    flex-direction: row;
    max-width: 467px;
    min-height: 6.5em;
    min-width: min(270px, 100%);
}

.lines-container {
    display: flex;
    flex-wrap: wrap;
    margin-top: 3%;
}

.line {
    flex: 1 1 auto;
    margin-top: 1em;
    margin-right: 1em;
    margin-bottom: 1em;
}

.line-title {
    word-wrap: break-word;
    margin-bottom: 1em;
}

.productBlock {
    font-size: 10px;
    font-weight: var(--font-semi-bold);
    border-radius: 0;
    margin: 30px 0;
    box-shadow: 0 3px 3px 1px var(--box-shadow-color);
    padding: 2.5em 1em;
    background-color: var(--main-background-color-subtle);
    /* Grows with a two-line name or the Patches note instead of overflowing into the next tile. */
    min-height: 9.5em;
    max-width: 302px;
    min-width: 270px;
}

.productBlock .patch-note {
    position: relative;
    background-color: var(--main-background-color-subtle-lowlight);
    text-align: center;
    margin-top: 10px;
    margin-left: -10px;
    min-width: inherit;
    max-width: inherit;
    border-bottom-right-radius: 6px;
    border-bottom-left-radius: 6px;
    margin-right: -10px;
}

.patch-note {
    color: var(--main-text-color) !important;
}

.patch-note:hover {
    cursor: text;
}

.patch-note span {
    cursor: pointer;
    color: var(--main-color);
    text-decoration: underline;
}

p.patch-info-disclaimer {
    margin-bottom: 50px;
}

#patch-info-modal .hidden {
    display: none;
}

div#patches-loading {
    width: 100%;
    height: min-content;
    text-align: center;
    margin: 40% 0;
    font-size: 18px;
}

div#patches-loading.error {
    color: var(--error-text);
}

.patch-info-block {
    font-size: 14px;
}

.patch-info-block label {
    color: var(--main-color);
    font-weight: bold;
    margin: 0;
}

a.download-link {
    display: contents;
    max-width: 467px;
}

a.download-link, a.download-link:hover, a.download-link:active {
    color: inherit;
    text-decoration: none;
}

a.download-link:hover {
    text-decoration: none;
    color: var(--main-color);
}

.package-image {
    max-height: 100%;
    max-width: 100%;
}

.package-title {
    font-weight: var(--font-bold);
}

    .package-title h1 {
        font-size: 14px;
        font-weight: var(--font-bold);
    }

    .package-title h2 {
        font-size: 12px;
        font-weight: var(--font-semi-bold);
    }

.package-size {
    font-size: 14px;
    margin-top: 0;
}

.version-wrapper {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    margin-top: 6em;
}

@media (max-width: 999px) {
    .version-wrapper {
        flex-direction: column;
        justify-content: space-between;
    }
}

.version {
    margin-bottom: 2em;
}

.version-lower {
    margin-bottom: 2em;
    margin-right: 1em;
}

.version-title {
    font-size: 25px;
    font-weight: var(--font-bold);
    margin-bottom: 2%;
}

.version-release-type {
    font-weight: var(--font-bold);
    font-size: 18px;
    display: inline-block;
    color: var(--main-color);
}

.version-title h3 {
    display: inline-block;
}

.version hr {
    border: none;
    height: 1px;
    color: var(--main-text-color);
    background-color: var(--main-text-color);
    margin: .5em 0 3em;
}

.page-header-dividing-line {
    border: none;
    height: 1px;
    color: var(--main-border-color);
    background-color: var(--main-border-color);
    margin: 0 auto 4em;
}

/*Search box area*/
.searchArea {
    position: relative;
    width: 50%;
    max-width: 518px;
}

/* Half width leaves ~160px for the box on a phone; its parents are wrapping flex rows. */
@media (max-width: 600px) {
    .searchArea {
        width: 100%;
        flex: 1 1 100%;
    }
}

.searchArea input {
    width: 100%;
    max-width: 100%;
    display: block;
    font-size: 14px;
    height: 40px;
    padding-left: 40px;
    border: 1.5px solid var(--main-border-color);
    border-radius: 0;
    background-color: var(--main-background-color);
    color: var(--main-text-color);
    transition: border-color .15s, box-shadow .15s;
}

.searchArea input:focus,
.searchArea input:focus-within {
    outline: none;
    border-color: var(--main-color);
    box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .1);
}

.searchIcon button {
    background: transparent;
    border: none;
    cursor: pointer;
    display: inline-block;
    font-size: 20px;
    position: absolute;
    top: 0;
    left: 40px;
}

.search-button {
    background: transparent;
    border: none;
    outline: none;
    margin-left: -33px;
}

.search-button:focus-visible {
    outline: none;
    border-radius: 0;
    box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), .45);
}

.search-button img {
    width: 20px;
    height: 20px;
    object-fit: cover;
}

.search-checkbox {
    margin: 0;
    font-size: 16px;
}

/*User Things*/

.user-label > label {
    font-weight: var(--font-bold);
    font-size: 18px;
    margin-bottom: 3%;
}

.user-form .form-group {
    min-width: 0;
    max-width: 500px;
}

.user-form .form-control {
    max-width: 500px;
}

.role-form {
    display: flex;
    flex-direction: column;
}

.role-form div {
    padding-top: 5px;
}


.noneMaint {
    color: var(--main-text-color);
}

.expiredMaint {
    color: var(--main-color) !important;
    font-weight: var(--font-extra-bold);
}

.active30Maint {
    color: var(--warning-color) !important;
    font-weight: var(--font-extra-bold);
}

.active60Maint,
.active90Maint,
.activeMaint {
    color: var(--success-color) !important;
    font-weight: var(--font-extra-bold);
}

.form-check-input {
    accent-color: var(--bs-primary);
    border: 2px solid var(--main-color);
}

.form-check-input:checked {
    background-color: var(--main-color);
    border-color: var(--main-color);
  }

/* Custom Checkbox Styling  */

/* Hide the original checkbox */
.custom-checkbox input {
    display: none;
}

/* Custom style for the fake checkbox */
.custom-checkbox label {
    position: relative;
    padding-left: 25px;
    cursor: pointer;
    display: inline-block;
}

/* Box for the custom checkbox */
.custom-checkbox label:before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 18px;
    height: 18px;
    border: 2px solid var(--main-color);
    border-radius: 0;
    background-color: var(--main-background-color);
}

/* When the checkbox is checked, add a red background */
.custom-checkbox input:checked + label:before {
    background-color: var(--main-color);
}

/* Checkmark to show when checked */
.custom-checkbox input:checked + label:after {
    content: '';
    position: absolute;
    left: 6px;
    top: 2px;
    width: 6px;
    height: 12px;
    border: solid white;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

/* Style for the fake checkbox when the actual checkbox is disabled */
.custom-checkbox input:disabled + label {
    color: var(--text-sub);
}

/* Style for the box of the custom checkbox when it's disabled */
.custom-checkbox input:disabled + label:before {
    border-color: var(--main-color-disabled); /* Lighter border to indicate it's disabled */
}

/* Optionally, if you want a specific style for the checkmark when disabled */
.custom-checkbox input:disabled:checked + label:after {
    background-color: var(--main-color-disabled); /* Lighter background for disabled and checked state */
    border-color: var(--white-color); /* Lighter checkmark for disabled and checked state */
}

/* Optionally, if you want a specific style for the checkmark when disabled and checked */
.custom-checkbox input:disabled:checked + label:before {
    background-color: var(--main-color-disabled); /* Lighter background for disabled and checked state */
}

/* Start of new custom branding elements */

.btn.wide {
    width: 100%;
    max-width: unset;
}

.btn.branded {
    border-width: 2px;
    border-radius: 0;
}

    .btn.branded > p {
        margin: 5px 0;
        font-size: 20px;
    }

.btn-primary {
    background-color: var(--main-color) !important;
    border-color: var(--main-color) !important;
}

.btn-secondary.branded {
    color: var(--main-text-color);
    border-color: var(--main-color);
    background-color: transparent;
}

.btn-success {
    background-color: var(--success-color) !important;
    border-color: var(--success-color) !important;
}

input[type=password].branded,
input[type=email].branded,
input[type=text].branded {
    background-color: transparent;
    padding: 12px;
    border-radius: 0;
    border: 2px solid var(--main-border-color);
}

/* File Manager Styling */
.header-cell {
    border-left: solid 1px var(--main-border-color) !important;
}

.suspicious-file {
    background-color: var(--error-color-light);
}

.locked-file .dx-filemanager-details-item-name::after {
    content: "";
    display: inline-block;
    width: 14px;
    height: 14px;
    vertical-align: -2px;
    background-color: currentColor;
    -webkit-mask: url("/icons/ui/lock.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo") center / contain no-repeat;
    mask: url("/icons/ui/lock.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo") center / contain no-repeat;
    margin-left: 4px;
}

/* Loading Bar Styling */

.loading-bar {
    background-color: var(--main-color);
    border-color: var(--main-color);
    box-shadow: var(--shadow-sm);
    transition: width 0.2s ease-out;
}

/* Applied just for the reset-to-0 step so the bar snaps back instead of visibly rewinding */
.loading-bar.no-transition {
    transition: none;
}

/* Invoice Table Styling */
.styled-table {
    border-collapse: collapse;
    margin: 25px 0;
    font-family: var(--font);
    min-width: 100%;
    box-shadow: var(--shadow-sm);
    border-radius: var(--radius);
}

.styled-table th,
.styled-table td {
    padding: 12px 15px;
}

.table-background.sigmanest {
    background-color: var(--main-color);
}

.table-header-color {
    color: var(--white-color);
}

.cell-alignment {
    text-align: end;
}

#invoice-table-header {
    background-color: var(--main-background-color-subtle-lowlight);
}

#invoice-table-body {
    border-collapse: collapse;
    border: 2px solid var(--main-text-color);
    letter-spacing: 1px;
}

a.receipt-download-link[disabled="disabled"] {
    cursor: not-allowed;
}

#file-manager {
    box-shadow: 1px 2px 4px 0 var(--box-shadow-color);
    height: 418.72px;
    margin-top: 10px;
}

.file-uploader-storage-remaining-container {
    background-color: var(--main-background-color-subtle);
    margin-top: -4px; /* move background up slightly to be cohesive with DX element */
    border: 1px solid var(--main-border-color);
    border-top: 0;
    padding: 10px 30px;
    color: var(--main-color);
    font-size: 14px;
    border-radius: 0;
    box-shadow: 1px 2px 4px 0 var(--box-shadow-color);
}

.file-uploader-storage-remaining .file-uploader-storage-remaining-space {
    font-weight: var(--font-extra-bold); /* intentionally using true bold instead of 600 bold to better match mockups*/
    /*margin-bottom: 10px;*/
}


.file-uploader-storage-bar {
    width: 100%;
    height: 8px;
    background: var(--main-background-color-subtle);
    border-radius: 0;
    overflow: hidden;
    margin: 5px 0px 4px;
}

.file-uploader-storage-bar-fill {
    height: 100%;
    width: 0;
    background: var(--main-color);
    border-radius: 0;
    transition: width .3s ease;
}

.file-uploader-storage-remaining .file-uploader-storage-location {
    font-weight: var(--font-semi-bold);
}

.form-dropdown-container {
    display: flex;
    gap: 20px;
}

.form-dropdown-container.form-dropdown-container-wide {
    justify-content: space-evenly;
}

.form-dropdown-option {
    display: flex;
    gap: 5px;
    align-items: center;
}

.form-dropdown-option-vertical {
    display: flex;
    flex-direction: column;
    gap: 5px;
    align-items: center;
}

.form-dropdown-option label,
.form-dropdown-option-vertical label {
    margin-top: 0;
    font-size: 16px;
}

/* User Button Styling */

.user-button {
    width: 235px;
    font-size: 18px;
    border-radius: 0;
    box-shadow: 0px 2px 4px 0 rgb(50 50 50/50%);
}

.close-button {
    background-color: var(--gray-bg, var(--main-background-color-subtle));
    color: var(--text-sub);
    border-color: var(--main-border-color);
}

/* Notifications Styling */

.spaced-columns-container {
    display: flex;
    flex-direction: row;
    gap: 15%;
}

.form-action-buttons-container {
    margin-top: 5em;
    padding-left: 20%;
    padding-right: 20%;
    display: flex;
    justify-content: space-evenly;
    gap: 25px
}

@media (max-width: 950px) {
    .spaced-columns-container {
        flex-direction: column;
    }
    
    .form-action-button {
        width: 160px;
    }
    
    .form-action-buttons-container {
        padding-left: 0;
        padding-right: 0;
    }
}

@media (max-width: 1050px) {
    .spaced-columns-container {
        gap: 5%;
    }
}

#notification-acknowledge-button {
    background-color: var(--notification-button);
    color: var(--notification-button-text);
    box-shadow: 0 1px 3px rgba(var(--notification-button-shadow), 0.24);
}

#notification-acknowledge-button:hover {
    background-color: var(--notification-button-hover);
}

#notification-remind-me-later-button {
    box-shadow: 0 1px 3px rgba(var(--notification-button-shadow), 0.24);
    background-color: var(--main-color);
}

.language-container {
    display: flex;
    flex-direction: column;
    width: 100%;
}

.language-container select {
    width: 25%;
    max-width: 100%;
    border-radius: 0;
    border-color: var(--main-border-color);
    padding: 5px;
    background-color: var(--notification-select-color);
}

.language-container input {
    width: 90%;
    max-width: 100%;
    border-radius: 0;
    border-color: var(--main-border-color);
    padding: 10px
}

@media (max-width: 500px) {
    .language-container input {
        width: 80%;
    }
}

.message-row {
    display: flex;
    align-items: center;
    margin-bottom: 10px;
}

.message-row select,
.message-row input {
    margin-right: 10px;
}

.title-text {
    margin-top: 1em;
    height: 49px;
}

.message-text {
    margin-top: 1em;
    height: 49px;
}

.message-row button {
    background: var(--main-color);
    color: var(--white-color);
    border: none;
    border-radius: var(--radius);
    padding: 5px 10px;
}

#add-language {
    color: var(--main-text-color);
    background: none;
    font-size: 16px;
    font-style: italic;
    font-weight: var(--font-extra-bold);
    text-decoration: underline;
    margin-top: 2em;
}

/* Partner Styling */

.edit-partner-wrapper {
    width: 100%;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    padding: 20px 22px;
    box-shadow: var(--shadow-sm);
    margin-bottom: 16px;
}

.edit-partner-wrapper legend.details-headers {
    width: auto;
    margin: 0 0 12px;
    padding: 0;
    float: none;
}

.edit-partner-wrapper input {
    max-width: 90%;
}

.edit-partner-wrapper textarea {
    max-width: 90%;
}

.partner-headers {
    color: var(--main-color);
    font-weight: var(--font-bold) !important;
    margin-top: 30px;
    margin-bottom: 3%;
}

.dx-treelist-headers .dx-treelist-text-content {
    color: var(--main-text-color);
}

body.reseller-mode .form-check-input {
    accent-color: var(--bs-primary);
    border: 2px solid var(--main-color);
}

body.reseller-mode .form-check-input:checked {
    background-color: var(--main-color);
    border-color: var(--main-color);
}


.site-banner {
    width: 100%;
    /* Stays put under the topbar instead of scrolling away with the page --
       same fixed-below-topbar pattern #sidebar uses (layout.css). */
    position: sticky;
    top: var(--topbar-h);
    z-index: 190;
}

.site-banner-content {
    text-align: center;
    font-size: 14px;
    line-height: 1.5;
    word-wrap: break-word;
    overflow-wrap: break-word;
    width: 100%;
}

.site-banner-content a {
    color: currentColor;
    text-decoration: underline;
    font-weight: 600;
}

.site-banner-content a:hover {
    color: currentColor;
    text-decoration: none;
}

.dark-mode .site-banner-content a {
    color: var(--white-color);
}

.dark-mode .site-banner-content a:hover {
    color: var(--main-color-highlight);
}

.site-banner-content strong {
    font-weight: 700;
}
.card-header {
    background-color: var(--main-background-color-subtle, var(--gray-bg));
    color: var(--main-text-color);
    border-bottom: 1px solid var(--main-border-color);
}

body.dark-mode .card {
    background-color: var(--main-background-color);
    border: 1px solid var(--main-border-color);
}

/* Bootstrap sets --bs-table-border-color on .table itself, so the .dark-mode
   remap above cannot reach it by inheritance — a declaration on the element always
   beats an inherited one. Restated here so a plain Bootstrap .table draws the same
   soft-white hairline as .sh-table / .data-table (see --table-border in pages.css). */
body.dark-mode .table {
    --bs-table-border-color: var(--main-border-color);
}

body.dark-mode .card-header {
    background-color: var(--main-background-color);
    color: var(--main-text-color);
}

body.dark-mode .card-body {
    background-color: var(--main-background-color);
    color: var(--main-text-color);
}

/* Bootstrap alerts, dark side. The theme is the .dark-mode class, not
   data-bs-theme, so Bootstrap keeps its pale light-theme washes and dark text
   (e.g. alert-warning's #fff3cd) on the dark canvas. Remapped onto the tag pairs,
   which already flip with the theme. Only the variants the views use are listed. */
body.dark-mode .alert-info {
    --bs-alert-bg: var(--tag-blue-bg);
    --bs-alert-color: var(--tag-blue-fg);
    --bs-alert-border-color: rgba(var(--blue-color-rgb), .35);
    --bs-alert-link-color: var(--tag-blue-fg);
}

body.dark-mode .alert-success {
    --bs-alert-bg: var(--tag-green-bg);
    --bs-alert-color: var(--tag-green-fg);
    --bs-alert-border-color: rgba(var(--success-color-rgb), .35);
    --bs-alert-link-color: var(--tag-green-fg);
}

body.dark-mode .alert-warning {
    --bs-alert-bg: var(--tag-orange-bg);
    --bs-alert-color: var(--tag-orange-fg);
    --bs-alert-border-color: rgba(var(--warning-color-rgb), .35);
    --bs-alert-link-color: var(--tag-orange-fg);
}


.site-banner .container {
    max-width: 100%;
    padding-left: 15px;
    padding-right: 15px;
    align-content: center;
}

@media (max-width: 768px) {
    .site-banner-content {
        font-size: 12px;
    }
}

@media (max-width: 576px) {
    /* Not sticky on phones: it would permanently take up to ~76px under the topbar. */
    .site-banner {
        position: static;
    }

    /* .banner-item, not .site-banner: the sticky wrapper is transparent now that the
       banner-type color lives on .banner-item, so padding on .site-banner itself
       shows through as a gap between the topbar and the visible banner instead of
       shrinking it. */
    .banner-item {
        padding-top: 8px;
        padding-bottom: 8px;
    }

    .site-banner-content {
        font-size: 11px;
    }
}

/* ============================================================================
   Dropdown / disclosure carets drawn as an SVG chevron instead of a text glyph
   (the old markup used the ▾ ▼ characters). The element stays empty; its size
   follows the font-size of whichever class it carries and its colour follows
   `color`, so each caret keeps its existing sizing, colour and rotation rules.
   ============================================================================ */
.sh-select-caret::before,
.role-category-chevron::before,
.perm-module-caret::before,
.tag-select-caret::before,
.lang-drop-caret::before,
.tb-user-caret::before,
.tab-more-caret::before,
.co-hero-toggle-chevron::before,
.az-collapse-btn::before,
.btn-caret::before {
    content: "";
    display: inline-block;
    width: 1.3em;
    height: 1.3em;
    vertical-align: -0.25em;
    background-color: currentColor;
    -webkit-mask: url("/icons/ui/chevron-down.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo") center / contain no-repeat;
    mask: url("/icons/ui/chevron-down.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo") center / contain no-repeat;
}

/* ══════════════════════════════════════════════════════════════════
   layout.css — Fixed sidebar + topbar shell.
   Sidebar is always dark. Topbar + main honour .dark-mode.
   ══════════════════════════════════════════════════════════════════ */

:root {
    --sidebar-w: 240px;
    /* 56px, up from 40px: the topbar now carries the brand lockup as well as the
       page title and the utility cluster, and a 28px-tall lockup needs the room. */
    --topbar-h: 56px;
    /* #siteFooter's height, shared so things pinned above the fixed footer clear it. */
    --site-footer-h: 36px;
    /* The separate #siteHeader is gone (folded into #topbar). The token is kept at
       zero rather than deleted because page-level sticky offsets still compute
       against it — Style.css and HelpKnowledgeBase/ViewAll both position sticky
       chrome at calc(--site-header-h + --topbar-h), and those stay correct as-is. */
    --site-header-h: 0px;
    /* Sidebar is theme-aware (matches MySigmaTEK prototype):
       light (soft neutral gray + dark text) in light mode, DF-dark in dark mode. */
    --sidebar-bg: #F4F5F6;
    /* Contrast tone — one step off the workspace canvas. Table/grid header rows,
       filter controls, tooltips and bordered side panels all used to read
       --sidebar-bg for this, back when the nav pane and the page canvas were
       different colours. They are the same colour now, so those surfaces need a
       token of their own or they dissolve into the page. #EBEDF0 is exactly what
       every one of them rendered before this change, so tables, filters and
       tooltips are pixel-identical — only the nav pane and canvas moved. */
    --contrast-bg: #EBEDF0;
    /* Form-control fill — selects, and anything else that must read as an
       interactive control rather than page. White in light mode, matching the
       .cs-filter-pill / .cs-view-btn buttons these controls sit beside.
       It was --main-background-color-subtle, which is #F4F5F6 — the colour the
       page canvas now is, so every enhanced select had the same fill as the page
       behind it and read as bare text with a hairline around it. */
    --control-bg: var(--main-background-color);
    --page-bg: #FAFAFA; /* Snow — card headers, dialog bodies & table rows (light) */
    /* Workspace canvas — the surface the page content sits ON (html/body, #main,
       #siteFooter). Light mode deliberately aliases --sidebar-bg so the workspace
       and the navigation pane are the same grey and read as one continuous
       chassis, with only the sidebar's right border (--sidebar-border) between them.
       This is NOT --page-bg: that token is the Snow tone used INSIDE components
       (card headers, dialog bodies, table rows). Table rows in particular must
       stay a step lighter than their header, which paints --sidebar-bg — folding
       the two together would erase that contrast. */
    --workspace-bg: var(--sidebar-bg);
    --sidebar-text: #141414;
    --sidebar-text-dim: #42474D;
    --sidebar-hover-bg: rgba(0, 0, 0, 0.06);
    --sidebar-active-bg: rgba(0, 0, 0, 0.09);
    --sidebar-active-text: #141414;
    --sidebar-border: #e0e0e5;
    --sidebar-focus-ring: rgba(var(--main-color-rgb), 0.55);
    /* Row states app-wide — grid rows, tree rows, list rows — now read the SAME fill
       the dropdowns use (--select-selected-bg, Style.css), so a hovered user row, a
       hovered grid row and the highlighted row in an open dropdown are one colour and
       the app has a single selection language. They previously aliased the sidebar's
       own washes (rgba(0,0,0,.06) / .09), which at 6-9% were a different, much fainter
       grey than the menus' band — the same state reading two ways depending on where
       you were. The nav pane keeps --sidebar-hover-bg / --sidebar-active-bg: it is a
       separate chassis painted on its own surface, not a row on the page.
       Still deliberately NOT the brand tint — a highlighted row is a working state,
       and the theme's red wash read as an error flag.

       Hover and selected are the same value on purpose, matching the dropdowns. The
       cost is inside a grid: hovering row B while row A is selected paints both the
       same, so the selection is not distinguishable under the pointer. Split hover to
       a lighter step here if that ever matters more than the uniformity. */
    --row-hover-bg: var(--select-selected-bg);
    --row-selected-bg: var(--select-selected-bg);
}

.dark-mode {
    /* Sidebar panel: #2A2E33 (--df-dark) — a distinct raised surface. */
    --sidebar-bg: var(--df-dark);
    /* Dark keeps the header/control tone on the sidebar panel shade, which is
       what these surfaces already rendered — dark mode is untouched by this. */
    --contrast-bg: var(--sidebar-bg);
    /* Dark keeps the raised #202024 fill these controls already had — dark cards
       are #141414, so a control cannot borrow the surface colour there. */
    --control-bg: var(--main-background-color-subtle);
    --page-bg: #141414; /* Jet — card headers, dialog bodies & table rows (dark) */
    /* Dark keeps the workspace one step below the #2A2E33 sidebar panel, so cards
       and tables still separate from the canvas by shade — unchanged behaviour. */
    --workspace-bg: var(--page-bg);
    --sidebar-text: rgba(255, 255, 255, 0.6);
    --sidebar-text-dim: rgba(255, 255, 255, 0.28);
    --sidebar-hover-bg: rgba(255, 255, 255, 0.07);
    --sidebar-active-bg: rgba(255, 255, 255, 0.10);
    --sidebar-active-text: #ffffff;
    --sidebar-border: rgba(255, 255, 255, 0.10);
    --sidebar-focus-ring: rgba(255, 255, 255, 0.7);
}

html,
body.shell {
    margin: 0;
    padding: 0;
    /* min-height, never a fixed height: body is a flex column (Style.css), so a
       definite 100% height turns tall pages into overflow out of the body box
       instead of a taller body. Body-level siblings then keep their original
       slots — which is how the unauthenticated footer ended up painted over the
       bottom of the page (e.g. across the embedded EULA PDF). */
    min-height: 100%;
    background: var(--workspace-bg); /* Sidebar grey (light) / Jet (dark) — page canvas */
    color: var(--main-text-color);
    font-family: var(--font);
}

/* ── Motion polish: page-enter + smooth in-page scrolling ───────────
   Native-friendly only — no scroll hijacking. Anchor jumps land clear of the
   fixed topbar, and page content fades/rises in on load. All motion is gated
   behind prefers-reduced-motion so motion-sensitive users get an instant UI. */
html {
    /* --site-banner-h is set by _SiteBanner.cshtml; the banner is sticky below the topbar. */
    scroll-padding-top: calc(var(--topbar-h) + var(--site-banner-h, 0px) + 8px);
}

/* The banner is not sticky on phones (Style.css), so it adds no offset there. */
@media (max-width: 576px) {
    html {
        scroll-padding-top: calc(var(--topbar-h) + 8px);
    }
}

/* The bounce at the top and bottom of a page is scripted (initPageBounce,
   motion.js), because Chrome on Windows gives no native bounce on the document
   scroller. The browser's own end-of-page effect (the macOS elastic pull,
   Chrome's glow) is switched off so the two never stack. Pointer-gated: the
   script responds to wheel input only, and on touch this property would also
   take away pull-to-refresh. */
@media (pointer: fine) {
    html {
        overscroll-behavior-y: none;
    }
}

@media (prefers-reduced-motion: no-preference) {
    html {
        scroll-behavior: smooth;
    }

    /* backwards fill = no lingering transform after the run, so position:fixed
       descendants (modals, full-screen pages, grid toolbars) are unaffected.
       The container carries the fade; its top-level blocks rise in just behind
       it, each a frame later, so a page assembles rather than appearing whole.
       Pages that wrap everything in a single root div simply keep the one
       entrance they have today. */
    #main-content {
        animation: page-fade .28s cubic-bezier(.22, .61, .36, 1) backwards;
    }

    #main-content > * {
        animation: page-enter .34s cubic-bezier(.22, .61, .36, 1) backwards;
    }

    #main-content > *:nth-child(2) { animation-delay: .04s; }
    #main-content > *:nth-child(3) { animation-delay: .08s; }
    #main-content > *:nth-child(4) { animation-delay: .12s; }
    /* Stops at four: past that the delay is longer than the eye reads as one
       movement, and a long page would stagger content the user is already
       looking at. */

    @keyframes page-fade {
        from {
            opacity: 0;
        }

        to {
            opacity: 1;
        }
    }

    @keyframes page-enter {
        from {
            opacity: 0;
            transform: translateY(8px);
        }

        to {
            opacity: 1;
            transform: translateY(0);
        }
    }

    /* ── Sheet drop: menus that hang off the topbar ──────────────────
       The language menu, the user menu and the search results all used to be a
       display:none → block flip, so they arrived between two frames while every
       dialog in the app faded in. They are attached to the bar they drop out of,
       which is what picks the treatment: a scaleY from a top origin unfurls the
       menu downward out of the bar, and --ease-sheet's hard stop keeps it
       reading as hinged to the bar rather than floating over it.

       Travel is small enough not to need a clipping wrapper, and the whole thing
       is opacity + transform, so it composites without touching layout.

       Re-rendering search results does not replay this: layout.js re-adds .open
       on an element that already carries it, which is a no-op, so the menu does
       not flicker once per keystroke. */
    .lang-drop-menu.open,
    .tb-user-drop.open,
    .search-drop.open {
        transform-origin: top center;
        animation: sheet-drop var(--dur-base) var(--ease-sheet);
    }

    @keyframes sheet-drop {
        from {
            opacity: 0;
            transform: translateY(-6px) scaleY(.96);
        }

        to {
            opacity: 1;
            transform: translateY(0) scaleY(1);
        }
    }
}

/* Dark workspace sits one step below the cards (which stay #141414) so every
   card, table and panel separates from the page by shade, not just a border. */
html.dark-mode,
body.shell.dark-mode {
    background: var(--workspace-bg); /* Jet #141414 */
}

#app-shell {
    display: flex;
    min-height: 100vh;
    min-height: 100dvh;
}

.shell-main-wrap {
    flex: 1;
    min-width: 0;
    margin-left: var(--sidebar-w);
    padding-top: var(--topbar-h);
    display: flex;
    flex-direction: column;
    position: relative; /* anchors the page loader to the content area, not the viewport */
}

/* ── Sidebar ────────────────────────────────────────────────────── */
#sidebar {
    width: var(--sidebar-w);
    flex-shrink: 0;
    background: var(--sidebar-bg);
    color: var(--sidebar-text);
    position: fixed;
    /* Starts below the topbar, which now spans the full width across the top. */
    top: var(--topbar-h);
    bottom: 0;
    left: 0;
    display: flex;
    flex-direction: column;
    z-index: 1100;
    pointer-events: auto;
    transition: transform .25s ease;
    /* Flat-Chrome Rule: fixed chrome carries no shadow — a 1px border does the
       separating. The previous `box-shadow: 1px 0 4px` was a deliberate choice to
       avoid a hard line, but it contradicted the rule; ruled in favour of the
       sheet on 2026-08-03. --sidebar-border is theme-aware, so the dark-mode
       override this replaced is no longer needed. */
    border-right: 1px solid var(--sidebar-border);
}

/* .sidebar-logo / -text / -name / -sub removed with the sidebar logo block — the
   brand lockup lives in #topbar now. */

.sidebar-nav {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 8px 0;
}

/* The nav list scrolls with no visible scrollbar. */
.sidebar-nav {
    scrollbar-width: none;
}

.sidebar-nav::-webkit-scrollbar {
    display: none;
}

.nav-section {
    margin-bottom: 18px;
}

.nav-section-label {
    padding: 8px 20px 4px;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: var(--sidebar-text-dim);
    font-weight: var(--font-extra-bold);
}

.nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 20px;
    border-left: 3px solid transparent;
    border-radius: 0;
    color: var(--sidebar-text);
    font-size: 13px;
    cursor: pointer;
    text-decoration: none;
    /* padding and gap ride along so the icon glides to the rail's centre with the panel
       rather than snapping there the moment the collapsed class lands. */
    transition: background .12s, color .12s, border-left-color .12s, padding .18s ease, gap .18s ease;
    margin-bottom: 1px;
}

    .nav-item:hover {
        background: var(--sidebar-hover-bg);
        color: var(--sidebar-active-text);
        font-weight: var(--font-bold);
    }

    .nav-item.active {
        border-left-color: var(--sidebar-active-text);
        background: var(--sidebar-active-bg);
        color: var(--sidebar-active-text);
        font-weight: var(--font-extra-bold);
    }

    /* Sidebar focus ring — white-tinted because chassis is always-dark.
   Inset so the ring lands inside the item's padding box rather than
   colliding with adjacent items. */
    .nav-item:focus-visible {
        outline: none;
        color: var(--sidebar-active-text);
        box-shadow: inset 0 0 0 2px var(--sidebar-focus-ring);
    }

.nav-icon {
    width: 18px;
    display: inline-flex;
    justify-content: center;
    font-size: 14px;
    flex-shrink: 0;
}

.nav-badge {
    margin-left: auto;
    background: var(--main-color);
    color: var(--white-color);
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    padding: 1px 7px;
    border-radius: 0;
    /* Needed for the pop below: an inline box ignores transform. */
    display: inline-block;
}

/* Pop when the count goes up. The badges are filled by fetch after the shell
   paints (loadNavBadges in layout.js), so a new case used to raise the number
   with nothing to catch the eye — the user found out on their next full page
   load. --ease-spring's overshoot is allowed here and nowhere else: the value is
   changing under the user's eye, which is the one case where a slight bounce
   reads as emphasis rather than as a wobble.

   layout.js adds .nav-badge-pop only when the count is higher than the one this
   session last showed, so a plain reload with unchanged counts stays still. */
.nav-badge.nav-badge-pop,
.bell-count.nav-badge-pop {
    animation: nav-badge-pop 260ms var(--ease-spring);
}

@keyframes nav-badge-pop {
    0% { transform: scale(1); }
    45% { transform: scale(1.32); }
    100% { transform: scale(1); }
}

/* Attention badges are positioned relative to the item so the collapsed rail can pin a
   dot to the icon (below) — the number itself has no room there. */
.nav-item.has-badge {
    position: relative;
}

.nav-item.hidden[data-placeholder="true"] {
    display: none;
}

/* Tooltip source content — hidden inline; DevExtreme tooltip widget reads and displays it */
.prospect-tooltip {
    display: none;
}

.sidebar-footer {
    padding: 12px 18px;
    border-top: 1px solid var(--sidebar-border);
    font-size: 11px;
    color: var(--sidebar-text-dim);
    text-align: center;
    position: relative;
    flex-shrink: 0;
}

.app-version {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 12px;
    opacity: 0.6;
    color: #ccc;
    white-space: nowrap;
}

.sidebar-footer a,
.sidebar-footer .footer-link {
    color: var(--sidebar-text-dim);
    text-decoration: none;
    cursor: pointer;
}

    .sidebar-footer a:hover,
    .sidebar-footer .footer-link:hover {
        color: var(--white-color);
    }

.sep-dot {
    margin: 0 6px;
    opacity: .5;
}

/* ── Sidebar collapse (icon rail) ───────────────────────────────────
   Manual, user-driven collapse, independent of the viewport-driven off-canvas
   behaviour at the 900px breakpoint further down. body.sidebar-collapsed is
   rendered server-side from AppUser.SidebarCollapsed, so the rail is already
   narrow on first paint instead of snapping in once layout.js runs.

   The state works by re-pointing --sidebar-w itself rather than overriding each
   consumer: #sidebar, .shell-main-wrap, #siteFooter, .modal-overlay (pages.css)
   and .help-kb-page (HelpKnowledgeBase/ViewAll) all derive their offset from that
   one token and follow the rail for free. .topbar-brand is the single deliberate
   exception — its width is --sidebar-w minus the topbar's own chrome, which would
   shrink the lockup to 32px, so it falls back to its intrinsic width and the
   brand/title divider stops tracking the sidebar edge while collapsed. */
/* Caption row of the first nav section, which also carries the collapse button — the
   button used to sit in a strip of its own above the nav, spending a whole row on one
   32px control. The caption keeps its own left padding so it still lines up with every
   other section label; only the button is pulled to the right edge. */
.nav-section-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-right: 12px;
}

.sidebar-collapse-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Pushed to the panel's right edge, where this control already lived before it moved
       onto the caption row. */
    margin-left: auto;
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--sidebar-text-dim);
    cursor: pointer;
    border-radius: var(--radius);
    transition: background .12s, color .12s;
    /* The inline SVG inside a bare <icon> is sized in em by the .svg-icon fallback in
       Style.css, so the glyph's actual size comes from font-size here — the width/height
       below only reserve the box. Same arrangement .hamburger already relies on. */
    font-size: 16px;
}

    .sidebar-collapse-btn:hover {
        background: var(--sidebar-hover-bg);
        color: var(--sidebar-active-text);
    }

    .sidebar-collapse-btn:focus-visible {
        outline: none;
        color: var(--sidebar-active-text);
        box-shadow: inset 0 0 0 2px var(--sidebar-focus-ring);
    }

    .sidebar-collapse-btn .svg-icon {
        width: 18px;
        height: 18px;
    }

/* The mobile off-canvas opener carries the same side-panel glyph, so it needs the
   same icon box now that it is an <icon> rather than the ☰ character. */
.hamburger .svg-icon {
    width: 20px;
    height: 20px;
}

@media (width > 900px) {
    #sidebar,
    .shell-main-wrap,
    #siteFooter {
        transition: width .18s ease, margin-left .18s ease, left .18s ease, transform .25s ease;
    }

    body.sidebar-collapsed {
        --sidebar-w: 64px;
    }

    /* Centred in the rail rather than pinned right, where it would sit off-axis
       from the icons below it. The caption beside it is hidden by the rule below, so
       the button is all that is left in the row. */
    body.sidebar-collapsed .nav-section-head {
        justify-content: center;
        padding: 6px 0 0;
    }

    body.sidebar-collapsed .sidebar-collapse-btn {
        margin-left: 0;
    }

    /* Item labels fade out in step with the panel instead of blinking out: at display:none
       they vanished on the first frame, while the rail was still 240px wide, which read as
       the text being deleted rather than the panel closing.

       max-width, not width, drives the squeeze, and it has to be capped at zero while
       collapsed: `overflow: hidden` only drops the label's automatic minimum size to zero,
       which lets flex-shrink stop the moment the line fits the 64px rail — leaving the
       label ~39px wide and every nav icon pinned to the rail's left edge, off-axis from the
       collapse button centred above them. The cap forces the label to nothing so
       justify-content can actually centre the glyph.

       The open cap of 200px is above the widest label the 240px panel can show (200px of
       content box, less the 18px icon and 10px gap = 172px), so no translation is clipped
       while expanded; it exists only to give max-width a definite value to animate from.
       The tradeoff of `nowrap` is that a label too long for the panel clips instead of
       wrapping to two lines — worth it, since a wrapping label reflows line by line
       through the whole animation.

       Section captions and badges stay display:none — a caption is a block and would hold
       its ~25px of height for the duration, and the badge hands over to the dot below, so
       neither has anything to gain from fading. */
    .nav-label {
        max-width: 200px;
        overflow: hidden;
        white-space: nowrap;
        transition: opacity .12s ease, max-width .18s ease;
    }

    body.sidebar-collapsed .nav-label {
        opacity: 0;
        max-width: 0;
        pointer-events: none;
    }

    body.sidebar-collapsed .nav-section-label,
    body.sidebar-collapsed .nav-badge {
        display: none;
    }

    /* The glyph is a side-panel mark, so flipping it points at what the button will do
       next — panel closing while open, opening while collapsed. */
    .sidebar-collapse-btn .svg-icon {
        transition: transform .18s ease;
    }

    body.sidebar-collapsed .sidebar-collapse-btn .svg-icon {
        transform: rotate(180deg);
    }

    /* The count can't be read in a 64px rail, but "something needs looking at" still has to
       survive the collapse — so the badge becomes a dot on the icon's top-right corner. */
    body.sidebar-collapsed .nav-item.has-badge::after {
        content: "";
        position: absolute;
        top: 7px;
        right: 16px;
        width: 7px;
        height: 7px;
        /* Square, not a circle: the rail dot is the collapsed form of .nav-badge,
           which is a sharp-cornered chip, so the two states have to agree. */
        border-radius: 0;
        background: var(--main-color);
        box-shadow: 0 0 0 2px var(--sidebar-bg);
    }

    body.sidebar-collapsed .nav-section {
        margin-bottom: 8px;
        padding-bottom: 8px;
        border-bottom: 1px solid var(--sidebar-border);
    }

        body.sidebar-collapsed .nav-section:last-child {
            border-bottom: none;
        }

    /* padding-right offsets the 3px active border on the left, so the glyph lands on
       the rail's true centre instead of 1.5px shy of it. */
    body.sidebar-collapsed .nav-item {
        justify-content: center;
        gap: 0;
        padding: 9px 3px 9px 0;
    }

    body.sidebar-collapsed .topbar-brand {
        width: auto;
    }
}

@media (prefers-reduced-motion: reduce) {
    #sidebar,
    .shell-main-wrap,
    #siteFooter,
    .nav-item,
    .nav-label,
    .sidebar-collapse-btn .svg-icon {
        transition: none;
    }
}

/* ── Topbar ─────────────────────────────────────────────────────── */
/* Full-width and always dark, in both themes — it absorbed the old #siteHeader,
   so it is now the app's single band of top chrome and carries the brand. It
   spans over the sidebar, hence the z-index above #sidebar's 1100. */
#topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--topbar-h);
    background: var(--df-jet);
    display: flex;
    align-items: center;
    padding: 0 20px;
    z-index: 1200;
    gap: 12px;
    border-bottom: 1px solid rgba(255, 255, 255, .07);
    transition: box-shadow var(--dur-base, 180ms) var(--ease-out, ease);
}

/* Content has scrolled under the bar (body.is-scrolled, set by motion.js), so the
   bar takes on a shadow and reads as sitting above the page — the same cue a
   native title bar gives once its view scrolls. At the top of the page there is
   nothing underneath it and the shadow is gone. */
body.is-scrolled #topbar {
    box-shadow: 0 2px 14px rgba(0, 0, 0, .30);
}

/* ── Topbar brand lockup ────────────────────────────────────────── */
.topbar-brand {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    text-decoration: none;
    /* Width is set so that the .topbar-sep that follows lands exactly on the
       sidebar's right edge, making the divider and the sidebar boundary read as
       one continuous vertical line down the page.

           #topbar padding-left (20) + brand width + #topbar gap (12) = --sidebar-w

       so the brand is --sidebar-w minus those 32px of chrome. The lockup itself
       still starts at x=20, matching the 20px left padding of the sidebar's
       .nav-section-label below it. Keep this in step if the topbar's padding or
       gap ever change. */
    width: calc(var(--sidebar-w) - 32px);
}

    .topbar-brand:hover {
        text-decoration: none;
    }

    /* 36px, not the 28px this started at. The lockup carries ~23% built-in vertical
       padding, so a declared 28px was only 21.6px of actual ink — which put the
       wordmark's cap height at 9.3px against the 11.2px of the "Dashboard" title
       sitting right next to it. The brand was optically smaller than a page title.
       At 36px the wordmark caps land at 11.9px, just above the title, and the mark
       still leaves 14px of clearance top and bottom inside the 56px bar.
       Width is 180px, comfortably inside .topbar-brand's 208px box, so the divider
       alignment with the sidebar edge is unaffected. */
    .topbar-brand img {
        height: 36px;
        width: auto;
        max-width: 100%;
        object-fit: contain;
        display: block;
    }

    .topbar-brand:focus-visible {
        outline: 2px solid #fff;
        outline-offset: 3px;
    }

/* Hairline divider between the brand and the page title, and again between the
   utility links and the controls. */
.topbar-sep {
    width: 1px;
    height: 20px;
    background: rgba(255, 255, 255, .12);
    flex-shrink: 0;
}

/* Currently unused: the Website / Contact Us links these style are parked behind a
   Razor comment in _Topbar.cshtml. Kept live so uncommenting that block is the only
   step needed to bring them back. */
.topbar-link {
    font-size: 12px;
    color: rgba(255, 255, 255, .55);
    text-decoration: none;
    white-space: nowrap;
    transition: color .15s;
}

    .topbar-link:hover {
        color: rgba(255, 255, 255, .9);
        text-decoration: none;
    }

    .topbar-link:focus-visible {
        outline: 2px solid #fff;
        outline-offset: 2px;
    }

.hamburger {
    display: none;
    width: 36px;
    height: 36px;
    border: none;
    background: transparent;
    color: var(--main-text-color);
    font-size: 18px;
    cursor: pointer;
    border-radius: var(--radius);
}

    .hamburger:hover {
        background: var(--gray-bg);
    }

    .hamburger:focus-visible {
        outline: none;
        background: var(--gray-bg);
        box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .35);
    }

/* min-width:0 so the title can actually shrink and ellipsis; a flex item's
   default auto min-size would push .topbar-right off the viewport instead. */
.topbar-page {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    min-width: 0;
    overflow: hidden;
}

.topbar-page-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--main-text-color);
    flex-shrink: 0;
}

    .topbar-page-icon .svg-icon {
        width: 20px;
        height: 20px;
    }

    .topbar-page-icon .copilot-brand-icon {
        width: 20px;
        height: 20px;
    }

.topbar-breadcrumb {
    font-size: 16px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
    letter-spacing: -.02em;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Order of collapse when the bar runs short: the search box shrinks first (from
   260px to a 120px floor), then the page title truncates, and the bell and user
   menu never shrink.
   - The cluster grows to fill the bar's free space and packs its items to the
     right, so it always has a definite width to lay the search box out in.
   - The very high flex-shrink makes it absorb nearly all of any shortfall
     before .topbar-page gives anything up.
   - It can't go below its min-content (the search floor plus the fixed
     controls), so nothing in it ever clips. */
.topbar-right {
    margin-left: auto;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
    flex: 1 1000 auto;
}

    .topbar-right > :not(.topbar-search-wrap) {
        flex-shrink: 0;
    }

.topbar-btn.topbar-search-toggle {
    display: none;
}

.topbar-search {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 5px 12px;
    background: var(--gray-bg);
    border-radius: 0;
    /* Width comes from .topbar-search-wrap, which owns the shrink. */
    min-width: 0;
    border: 1.5px solid transparent;
    transition: border-color .15s, box-shadow .15s;
}

    .topbar-search:focus-within {
        border-color: var(--main-color);
        box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .12);
    }

    /* The magnifier needs the same treatment .topbar-page-icon gets, and for the
       same two reasons:

       1. Size. <icon> emits only .svg-icon, so with no nav-icon/ui-icon class it
          falls to the `.svg-icon:not(.nav-icon):not(.ui-icon) > svg` fallback of
          1.1em — which resolves against the *inherited* font size, not the input's
          13px. The glyph came out ~17.6px, visibly larger than the text beside it.
       2. Baseline. The wrapper was a bare inline <span>, so the inline-flex
          .svg-icon inside it sat on the text baseline and the span's line box
          carried descender space underneath. .topbar-search centres that whole
          box, which put the visible glyph a couple of px above the text's centre.
          inline-flex on the wrapper drops the baseline and centres the glyph. */
    .topbar-search-icon {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
    }

        .topbar-search-icon .svg-icon {
            width: 16px;
            height: 16px;
        }

    .topbar-search input {
        border: none;
        outline: none;
        background: transparent;
        flex: 1;
        /* An input's intrinsic width would otherwise stop the box shrinking. */
        min-width: 0;
        font-size: 13px;
        color: var(--main-text-color);
    }

        .topbar-search input::placeholder {
            color: var(--text-sub);
        }

.topbar-btn {
    width: 36px;
    height: 36px;
    border: 1px solid var(--main-border-color);
    background: var(--gray-bg);
    border-radius: 50%;
    color: var(--main-text-color);
    font-size: 16px;
    cursor: pointer;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

    .topbar-btn:hover {
        border-color: rgba(var(--main-color-rgb), .4);
        color: var(--main-color);
    }

    .topbar-btn:focus-visible {
        outline: none;
        background: var(--gray-bg);
        box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .35);
    }

/* Dark mode: lift the control fill above the #141414 topbar for contrast
   (workspace --gray-bg is too close to the topbar), matching the prototype. */
.dark-mode .topbar-btn,
.dark-mode .lang-drop-toggle {
    background: var(--df-dark);
    border-color: rgba(255, 255, 255, .09);
}

.dark-mode .topbar-btn:hover {
    border-color: rgba(var(--main-color-rgb), .4);
}

/* ── Always-dark topbar chassis ─────────────────────────────────────
   The bar is Jet in both themes now, so every control inside it has to be
   light-on-dark even in light mode — the theme tokens these controls normally
   read (--main-text-color, --gray-bg, --main-border-color) resolve dark-on-light
   there and would vanish against the bar. Same treatment .unauth-header and the
   auth brand panel already get. Scoped to #topbar so the identical components
   keep their theme-aware styling everywhere else on the page.

   Panels that hang *below* the bar — the user, bell, language and search
   dropdowns — are deliberately excluded: they sit on the page, not on the bar,
   and stay theme-aware. */
#topbar .hamburger {
    color: #fff;
}

    #topbar .hamburger:hover,
    #topbar .hamburger:focus-visible {
        background: rgba(255, 255, 255, .08);
    }

#topbar .topbar-page-icon {
    color: rgba(255, 255, 255, .85);
}

#topbar .topbar-breadcrumb {
    color: #fff;
}

#topbar .topbar-search {
    background: rgba(255, 255, 255, .06);
    border-color: rgba(255, 255, 255, .12);
    color: #fff;
}

    #topbar .topbar-search:focus-within {
        border-color: var(--main-color);
        box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .25);
    }

    /* Muted rather than inheriting the wrapper's #fff: at full white the glyph
       read heavier than the placeholder next to it, so the two didn't scan as one
       control. Sits just above the placeholder's .45 so it still reads as an
       affordance. */
    #topbar .topbar-search-icon {
        color: rgba(255, 255, 255, .55);
    }

    /* background MUST be restated here. The global
       `input:not([type=checkbox]):not([type=radio])` rule in Style.css carries
       specificity 0-2-1 and paints every input --main-background-color-subtle,
       which outranks the plain `.topbar-search input { background: transparent }`
       base rule at 0-1-1. The result was a light-filled box on the dark bar with
       white text on it — invisible in light mode. This selector is 1-1-1, so it
       wins. */
    #topbar .topbar-search input {
        background: transparent;
        color: #fff;
    }

        #topbar .topbar-search input::placeholder {
            color: rgba(255, 255, 255, .45);
        }

        /* Autofill paints its own background through an internal UA layer that
           `background` cannot touch — the inset shadow is the only way to cover it,
           and -webkit-text-fill-color the only way to recolour the text. Without
           this the same white-box-with-white-text symptom returns the moment the
           browser autofills a remembered search. */
        #topbar .topbar-search input:-webkit-autofill,
        #topbar .topbar-search input:-webkit-autofill:hover,
        #topbar .topbar-search input:-webkit-autofill:focus {
            -webkit-text-fill-color: #fff;
            caret-color: #fff;
            box-shadow: inset 0 0 0 100px var(--df-jet);
            transition: background-color 9999s ease-in-out 0s;
        }

/* The .lang-drop-toggle / -code / -caret companions to these rules are gone with
   the language dropdown itself, which no longer renders inside #topbar. The
   component still exists for the login and unauthenticated layouts, where it keeps
   its own theme-aware styling further down this file. */
#topbar .topbar-btn {
    background: rgba(255, 255, 255, .06);
    border-color: rgba(255, 255, 255, .12);
    color: #fff;
}

    #topbar .topbar-btn:hover {
        background: rgba(255, 255, 255, .1);
        border-color: rgba(var(--main-color-rgb), .55);
        color: #fff;
    }

    #topbar .topbar-btn:focus-visible {
        background: rgba(255, 255, 255, .1);
        border-color: var(--main-color);
    }

/* The user chip is transparent on the bar in both themes. This also cancels the
   `.dark-mode .tb-user { background: var(--sidebar-bg) }` rule below, which in
   light mode would paint an #ebedf0 chip onto the now-black bar. */
#topbar .tb-user,
.dark-mode #topbar .tb-user {
    background: transparent;
}

    #topbar .tb-user:hover,
    #topbar .tb-user:focus-visible,
    .dark-mode #topbar .tb-user:hover {
        background: rgba(255, 255, 255, .08);
    }

#topbar .tb-user-name {
    color: #fff;
}

#topbar .tb-user-role,
#topbar .tb-user-caret {
    color: rgba(255, 255, 255, .5);
}

/* Pinned to the button's outer corner rather than inside it: at top/right 4px the
   chip landed on top of the bell glyph and read as part of the icon. Overhanging by
   3px keeps the glyph legible, and the light ring separates the chip from both the
   button fill and the topbar behind it. */
.bell-count {
    position: absolute;
    top: -3px;
    right: -3px;
    min-width: 15px;
    height: 15px;
    padding: 0 3px;
    background: var(--main-color);
    color: var(--white-color);
    font-size: 9px;
    line-height: 1;
    font-weight: var(--font-extra-bold);
    border: 1.5px solid var(--white-color);
    border-radius: 0;
    display: none;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

/* ── Language dropdown (topbar) ─────────────────────────────────── */
.lang-drop {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.lang-drop-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    width: auto;
    padding: 8px 12px;
    border-radius: 0;
    background: var(--main-background-color-subtle);
    border: 1.5px solid var(--main-border-color);
    color: var(--main-text-color);
    font-size: 13px;
    font-family: inherit;
    cursor: pointer;
}

    .lang-drop-toggle:hover {
        border-color: rgba(var(--main-color-rgb), .4);
    }

    .lang-drop-toggle:focus-visible {
        outline: none;
        background: var(--main-background-color-subtle);
        border-color: var(--main-color);
        box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .35);
    }

.lang-drop-code {
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    white-space: nowrap;
}

.lang-drop-caret {
    font-size: 10px;
    color: var(--text-sub);
    transition: transform .15s;
}

.lang-drop-toggle[aria-expanded="true"] .lang-drop-caret {
    transform: rotate(180deg);
}

.lang-drop-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    min-width: 200px;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    box-shadow: var(--shadow-lg);
    display: none;
    z-index: 1200;
    max-height: min(260px, 50vh);
    overflow-y: auto;
}

    .lang-drop-menu.open {
        display: block;
    }

.lang-drop-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 12px;
    background: none;
    border: none;
    border-radius: 0;
    color: var(--main-text-color);
    font-size: 13px;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
}

    .lang-drop-item:hover {
        background: var(--select-selected-bg);
    }

    .lang-drop-item:focus-visible {
        outline: none;
        background: var(--select-selected-bg);
        box-shadow: inset 0 0 0 2px rgba(var(--main-color-rgb), .45);
    }

    .lang-drop-item.selected {
        background: var(--select-selected-bg);
        font-weight: var(--font-bold);
    }

.lang-drop-flag {
    font-size: 16px;
    flex-shrink: 0;
}

.lang-drop-sep {
    border: none;
    border-top: 1px solid var(--main-border-color);
    margin: 4px 0;
}

.bell-count.show {
    display: inline-flex;
}

/* Carries on after the pop, while the panel holds a row the user has not looked at
   yet - syncBellIndicator in layout.js adds and removes the class. The pop alone is
   260ms and is missed by anyone who happened to be reading at the time, and a bell
   that has already stopped moving by the time you glance up has told you nothing.

   Three iterations, then silence: the rule on the motion tokens is that nothing loops
   outside a genuine loading state, and a badge pulsing all afternoon becomes wallpaper
   long before it becomes useful. The count stays as the standing signal. The halo is
   drawn with box-shadow so it costs no layout and cannot nudge the topbar. */
.bell-count.has-new {
    animation: bell-count-pulse 1.4s var(--ease-out) 3;
}

@keyframes bell-count-pulse {
    0% {
        box-shadow: 0 0 0 0 var(--main-color-outline);
    }

    70% {
        box-shadow: 0 0 0 5px transparent;
    }

    100% {
        box-shadow: 0 0 0 0 transparent;
    }
}

/* ── Bell notification dropdown ─────────────────────────────────── */
.bell-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
}

/* Notifications open as a full-height right-side drawer (matches prototype),
   not a small dropdown. Slides in via transform; data logic unchanged. */
.bell-drop {
    position: fixed;
    top: 0;
    right: 0;
    bottom: var(--imp-banner-h, 0px);
    width: 380px;
    max-width: 90vw;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* A dismissing row slides out to the right (.sh-dismissing in pages.css). overflow-y
       alone would leave overflow-x computing to auto and give the drawer a horizontal
       scrollbar for the length of the transition. */
    overflow-x: hidden;
    background: var(--main-background-color);
    border-left: 1px solid var(--main-border-color);
    border-radius: 0;
    box-shadow: -8px 0 32px rgba(0, 0, 0, .18);
    transform: translateX(100%);
    /* A panel arriving from off-screen gets the long decelerate, not a linear-ish
       `ease`: it leaves the edge fast and settles, so the eye catches up with a
       surface that was not on the page a moment ago. On the app's --dur-slow
       tempo. */
    transition: transform var(--dur-slow) var(--ease-decel);
    z-index: 2000;
    padding: 0;
}

    .bell-drop.open {
        transform: translateX(0);
    }

.bell-drop-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    font-size: 16px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
    padding: 18px 20px;
    border-bottom: 1px solid var(--main-border-color);
    position: sticky;
    top: 0;
    background: var(--main-background-color);
    z-index: 1;
}

.bell-drop-title {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.bell-drop-count {
    background: var(--main-color);
    color: #fff;
    font-size: 12px;
    font-weight: var(--font-extra-bold);
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: var(--radius);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.bell-drop-close {
    width: 30px;
    height: 30px;
    border: none;
    background: var(--gray-bg);
    color: var(--text-sub);
    border-radius: var(--radius);
    cursor: pointer;
    font-size: 15px;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

    .bell-drop-close:hover {
        background: var(--main-border-color);
        color: var(--main-text-color);
    }

.bell-drop-section {
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .8px;
    color: var(--text-sub);
    padding: 14px 20px 6px;
}

/* The bell dropdown's empty state now uses the shared .empty-state
   (empty-state-sm) from pages.css. */

.bell-drop-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 20px;
    cursor: default;
    border-radius: 0;
    border-bottom: 1px solid var(--main-border-color);
    transition: background .12s;
}

/* Only rows with somewhere to go (an alert's ActionUrl) promise a click. */
.bell-drop-item.is-link {
    cursor: pointer;
}

    .bell-drop-item:hover,
    .bell-drop-item:focus-visible {
        background: var(--select-selected-bg);
        outline: none;
    }

/* Severity marker: a small square in the priority's colour, left of the text. Replaces the
   old left-edge accent - the repo bans side stripes, and a square survives the stacked
   layout, where a stripe would run into the stack edges below the row. */
.bell-drop-item-sev {
    width: 8px;
    height: 8px;
    flex-shrink: 0;
    margin-top: 5px;
    background: var(--text-sub);
}

/* Unread: a filled square ahead of the title, the same mark the old dot carried. */
.bell-drop-item.is-unread .bell-drop-item-title::before {
    content: "";
    display: inline-block;
    width: 6px;
    height: 6px;
    margin-right: 6px;
    vertical-align: 2px;
    background: var(--main-text-color);
}

.bell-drop-item-body {
    flex: 1;
    min-width: 0;
}

.bell-drop-item-title {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    white-space: normal;
}

.bell-drop-item-msg {
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 3px;
    line-height: 1.5;
    white-space: normal;
}

/* ── Notification priority colour coding ─────────────────────────────
   Carried by the severity square only; the chip on each group names the module. */
.bell-drop-item.prio-medium .bell-drop-item-sev {
    background: var(--blue-color);
}

.bell-drop-item.prio-high .bell-drop-item-sev {
    background: var(--warning-color);
}

.bell-drop-item.prio-critical .bell-drop-item-sev {
    background: var(--error-color);
}

/* ── Per-row dismiss button ──────────────────────────────────────────
   Revealed on hover only, but always rendered: opacity rather than display
   keeps it in the tab order and keeps the row from reflowing as it appears.
   Keyboard focus anywhere in the row reveals it too, and devices with no
   hover (touch) always show it - there would be no way to reveal it there. */
.bell-drop-item .bell-drop-item-dismiss {
    opacity: 0;
    transition: opacity var(--dur-fast, .12s) var(--ease-out);
}

.bell-drop-item:hover .bell-drop-item-dismiss,
.bell-drop-item:focus-within .bell-drop-item-dismiss {
    opacity: 1;
}

/* The group header's ✕ follows the same rule, revealed by hovering the header itself.
   The expand chevron stays visible - it is how a collapsed stack is opened. */
.bell-group-head .bell-group-dismiss {
    opacity: 0;
    transition: opacity var(--dur-fast, .12s) var(--ease-out);
}

.bell-group-head:hover .bell-group-dismiss,
.bell-group-head:focus-within .bell-group-dismiss {
    opacity: 1;
}

@media (hover: none) {
    .bell-drop-item .bell-drop-item-dismiss,
    .bell-group-head .bell-group-dismiss {
        opacity: 1;
    }
}

.bell-drop-item-dismiss {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid transparent;
    background: none;
    color: var(--text-sub);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

    .bell-drop-item-dismiss:hover {
        color: var(--main-color);
    }

    .bell-drop-item-dismiss:disabled {
        opacity: .4;
        cursor: default;
    }

/* A row that arrived since the user last opened the panel. Two parts, deliberately:
   the ring pulses twice to catch the eye of someone who has just opened the drawer,
   then stops, and the pale wash stays behind as the standing "this is the new one"
   marker for as long as the row counts as unseen. Opening the panel is what clears
   it - see markBellSeen in layout.js - so a notification that arrived while the user
   was on another page is still pointed out when they finally look.

   The wash is --main-color-highlight, which has its own dark-mode value in Style.css.
   The severity square is deliberately left alone: severity is what it encodes, and
   overwriting it would trade one signal for another. Under
   prefers-reduced-motion the global override in Style.css drops the pulse and leaves
   the wash, which is the part that carries the meaning anyway. */
.bell-drop-item.is-new {
    background: var(--main-color-highlight);
    animation: bell-row-glow 1.4s var(--ease-out) 2;
}

@keyframes bell-row-glow {
    0%, 100% {
        box-shadow: inset 0 0 0 0 transparent;
    }

    50% {
        box-shadow: inset 0 0 0 2px var(--main-color-outline);
    }
}

/* Dismissed rows stay listed under their own section but read as resolved:
   muted, and with the priority accent dropped since it no longer needs action. */
.bell-drop-item.is-dismissed .bell-drop-item-sev {
    background: var(--main-border-color);
}

/* The other half of a panel dismissal. The row does not leave the drawer - it moves
   down into the Dismissed section - so it slides out of Active (.sh-dismissing), the
   panel re-renders, and the row it reappears as fades in here. Without this the row
   collapses in one place and pops into existence in another, which reads as two
   unrelated events rather than one move. Only the row just dismissed carries the
   class; renderBellItem in layout.js stamps it by id. */
.bell-drop-item.is-landing {
    animation: bellRowLand var(--dur-base) var(--ease-out);
}

@keyframes bellRowLand {
    from {
        opacity: 0;
        transform: translateY(-6px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

    .bell-drop-item.is-dismissed .bell-drop-item-title {
        font-weight: var(--font-regular);
        color: var(--text-sub);
    }

/* ── Windows-style module stacks ─────────────────────────────────────
   Active rows are grouped by module under a header carrying the module chip. A group
   with several rows collapses to its most pressing row plus a "+N more" link; expanding
   lists every row. All corners are square,
   to match the rest of the shell (--radius is 0). */
.bell-glyph > svg {
    width: 14px;
    height: 14px;
    display: block;
}

.bell-drop-header-actions {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* No close button on desktop: clicking anywhere outside the drawer closes it. Phones get it
   back below, because there the drawer is a full-width sheet with no outside to click. */
.bell-drop-close {
    display: none;
}

.bell-drop-clear {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    border: 1px solid transparent;
    background: none;
    padding: 5px 8px;
    font-size: 12px;
    font-weight: var(--font-bold);
    color: var(--text-sub);
    cursor: pointer;
}

    .bell-drop-clear:hover,
    .bell-drop-clear:focus-visible {
        color: var(--main-text-color);
        border-color: var(--main-border-color);
    }

.bell-group {
    border-bottom: 1px solid var(--main-border-color);
}

.bell-group-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px 8px 20px;
    background: var(--gray-bg);
    border-bottom: 1px solid var(--main-border-color);
}

.bell-group-actions {
    display: inline-flex;
    gap: 2px;
    margin-left: auto;
}

.bell-icon-btn {
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid transparent;
    background: none;
    color: var(--text-sub);
    cursor: pointer;
}

    .bell-icon-btn:hover,
    .bell-icon-btn:focus-visible,
    .bell-drop-item-dismiss:focus-visible {
        color: var(--main-text-color);
        border-color: var(--main-border-color);
        background: var(--main-background-color);
    }

.bell-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: var(--font-bold);
    line-height: 1.4;
    color: var(--main-text-color);
    white-space: nowrap;
}

/* Icon and name sit bare on the band; only the count gets a box, in a grey a step up from
   the group header's own. The two themes need different stops: light's lowlight is too
   heavy, and dark's highlight is darker than the page. */
.bell-chip-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 18px;
    padding: 0 6px;
    background: var(--main-background-color-subtle-highlight);
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    color: var(--main-text-color);
    font-variant-numeric: tabular-nums;
}

.dark-mode .bell-chip-count {
    background: var(--main-background-color-subtle-lowlight);
}

/* The rows inside a stack lose their own bottom rule; the group draws the divider. */
.bell-group .bell-drop-item:last-of-type {
    border-bottom: none;
}

/* Expand / collapse: toggleBellGroup in layout.js pins the rebuilt group at its old height
   and eases it to the new one; the rows that appear on opening fade down into place. */
.bell-group.is-resizing {
    transition: height var(--dur-slow, .3s) var(--ease-decel, ease-out);
}

.bell-group.is-opening .bell-drop-item ~ .bell-drop-item {
    animation: bellRowLand var(--dur-slow, .3s) var(--ease-out);
}

.bell-group-more {
    display: block;
    width: 100%;
    /* Lines up with the row text: row padding + severity square + gap. */
    padding: 0 20px 10px 38px;
    border: none;
    background: none;
    text-align: left;
    font-size: 12px;
    color: var(--text-sub);
    cursor: pointer;
}

    .bell-group-more:hover,
    .bell-group-more:focus-visible {
        color: var(--main-text-color);
        text-decoration: underline;
    }

.bell-drop-item-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
}

.bell-drop-item-time {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    color: var(--text-sub);
}

    .bell-drop-item-time .bell-glyph > svg {
        width: 12px;
        height: 12px;
    }

.bell-drop-section {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.bell-dismissed-toggle,
.bell-drop-restore-all {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 0;
    border: none;
    background: none;
    font: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
    color: inherit;
    cursor: pointer;
}

    .bell-dismissed-toggle .bell-glyph > svg {
        transform: rotate(-90deg);
        transition: transform var(--dur-base) var(--ease-out);
    }

    .bell-dismissed-toggle[aria-expanded="true"] .bell-glyph > svg {
        transform: none;
    }

    .bell-dismissed-toggle:hover,
    .bell-drop-restore-all:hover,
    .bell-dismissed-toggle:focus-visible,
    .bell-drop-restore-all:focus-visible {
        color: var(--main-text-color);
    }

.bell-drop-item-restore {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 9px;
    font-size: 11px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    cursor: pointer;
    white-space: nowrap;
}

    .bell-drop-item-restore:hover,
    .bell-drop-item-restore:focus-visible {
        background: var(--gray-bg);
    }

    .bell-drop-item-restore:disabled {
        opacity: .4;
        cursor: default;
    }

.bell-drop .empty-state-icon .bell-glyph > svg {
    width: 24px;
    height: 24px;
}

/* Phones: the drawer becomes a full-width sheet and every control meets the 44px
   tap-target floor at <=600px (responsive spec §15.4, see pages.css); desktop keeps
   the tighter 28px. */
/* On a phone the notifications drawer keeps its desktop shape instead of covering
   the whole screen. It is 85% wide (capped at the desktop 380px) and starts below
   the topbar, so the bell stays visible. A strip of the dimmed page stays showing
   on the left, and tapping it closes the drawer through the existing outside-click
   handler. body.bell-drawer-open is set by toggleBellDrop / closeBellDrop in
   layout.js. */
@media (max-width: 600px) {
    .bell-drop {
        top: var(--topbar-h);
        height: auto;
        width: 85vw;
        max-width: 380px;
    }

    body.bell-drawer-open::after {
        content: "";
        position: fixed;
        top: var(--topbar-h);
        left: 0;
        right: 0;
        bottom: 0;
        background: rgba(0, 0, 0, .5);
        /* Just under .bell-drop (2000), above the page and the fixed footer. */
        z-index: 1999;
    }

    body.bell-drawer-open {
        overflow: hidden;
    }

    .bell-drop-close {
        display: inline-flex;
    }

    .bell-drop-item {
        padding-left: 16px;
        padding-right: 12px;
    }

    .bell-group-head {
        padding-left: 16px;
        padding-right: 8px;
    }
}

/* Touch targets follow the shell breakpoint (tablet portrait included), not just phones. */
@media (max-width: 900px) {
    .bell-icon-btn,
    .bell-drop-item-dismiss,
    .bell-drop-close {
        width: 44px;
        height: 44px;
    }

    .bell-drop-clear,
    .bell-drop-item-restore {
        min-height: 44px;
        padding-left: 12px;
        padding-right: 12px;
    }

    .bell-group-more {
        min-height: 44px;
        padding-left: 34px;
    }
}

/* ── Global search dropdown ──────────────────────────────────────── */
.topbar-search-wrap {
    position: relative;
    display: inline-flex;
    flex-direction: column;
    /* 260px when there's room, down to a 120px floor before the title truncates
       (see .topbar-right). The results dropdown matches this width. A basis, not
       a width: a width would also raise the cluster's minimum size to 260px and
       stop it shrinking. */
    flex: 0 1 260px;
    min-width: 120px;
}

.search-drop {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    z-index: 1300;
    display: none;
    max-height: 420px;
    overflow-y: auto;
    padding: 6px 0;
}

    .search-drop.open {
        display: block;
    }

.search-drop-group-label {
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .9px;
    color: var(--text-sub);
    padding: 8px 14px 4px;
}

.search-drop-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    text-decoration: none;
    cursor: pointer;
    transition: background .1s;
    border-radius: 0;
}

    /* Same band as a dropdown option and a table row — see --select-selected-bg
       (Style.css). --gray-bg is the page colour now, so it read as no hover at all. */
    .search-drop-item:hover,
    .search-drop-item.active,
    .search-drop-item:focus-visible {
        background: var(--select-selected-bg);
        outline: none;
    }

.search-drop-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 22px;
    color: var(--text-sub, currentColor);
}

.search-drop-icon .svg-icon > svg {
    width: 18px;
    height: 18px;
}

/* Result icons are uncoloured: every group uses the neutral colour set on
   .search-drop-icon above. */
.search-drop-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* ── Search-result highlight ──────────────────────────────────────────
   After a global-search result is clicked, the page it opens marks the searched
   words (initSearchHighlight, layout.js). Text is painted through the CSS Custom
   Highlight API, so no markup is added to the page. Only color and background
   apply to ::highlight. Light mode leaves the text colour alone. */
::highlight(search-hit) {
    background-color: var(--search-hit-bg);
}

.dark-mode ::highlight(search-hit) {
    color: var(--search-hit-fg);
}

/* Input and textarea values sit outside the DOM text a Range can cover, so a
   matching field is outlined instead. Outline, not border: no layout shift. */
.search-hit-field {
    outline: 2px solid var(--search-hit-ring);
    outline-offset: 1px;
}

.search-drop-label {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.search-drop-sub {
    font-size: 11px;
    color: var(--text-sub);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* .search-drop-empty was folded into the shared .empty-state; the loading
   row keeps this rule. Flex so the shared 5-dot .cd-spinner and the label sit on
   one centred line — the row used to hold text only. */
.search-drop-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 18px 16px;
    font-size: 13px;
    color: var(--text-sub);
}

    /* .cd-spinner-sm carries its own margin-right for in-button use; here the
       flex gap does the spacing, so cancel it rather than getting 16px. */
    .search-drop-loading .cd-spinner-sm {
        margin-right: 0;
    }

.tb-user {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 4px 10px 4px 4px;
    border-radius: 0;
    cursor: pointer;
    position: relative;
}

    .tb-user:hover {
        background: var(--gray-bg);
    }

    .tb-user:focus-visible {
        outline: none;
        background: var(--gray-bg);
        box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .35);
    }

/* Dark mode: give the user-info card the sidebar panel color so it stands out
   as a raised card against the darker topbar. */
.dark-mode .tb-user {
    background: var(--sidebar-bg);
}

    .dark-mode .tb-user:hover {
        background: var(--sidebar-active-bg);
    }

.tb-user-av {
    width: 32px;
    height: 32px;
    border-radius: var(--radius);
    background: var(--avatar-user-bg);
    color: var(--white-color);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: var(--font-extra-bold);
    flex-shrink: 0;
}

.tb-user-info {
    line-height: 1.2;
    text-align: left;
    min-width: 0;
    max-width: 160px;
}

.tb-user-name,
.tb-user-role {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tb-user-name {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.tb-user-role {
    font-size: 11px;
    color: var(--text-sub);
}

.tb-user-caret {
    font-size: 9px;
    color: var(--text-sub);
    margin-left: 2px;
}

/* Fixed, not absolute: #topbar's overflow makes it a clipping container for
   absolutely-positioned descendants, which hid this dropdown entirely (same
   reason .bell-drop is fixed). 20px matches #topbar's own padding. */
.tb-user-drop {
    position: fixed;
    top: calc(var(--topbar-h) + 6px);
    right: 20px;
    min-width: 200px;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    /* Square and unpadded, like the company Actions menu: the header band and the rows'
       hover fill run to the menu's edge instead of sitting inset from it. */
    border-radius: 0;
    box-shadow: var(--shadow-md);
    padding: 0;
    overflow: hidden;
    display: none;
    z-index: 200;
}

    .tb-user-drop.open {
        display: block;
    }

    .tb-user-drop a,
    .tb-user-drop button {
        display: block;
        width: 100%;
        text-align: left;
        padding: 8px 12px;
        background: transparent;
        border: none;
        font-size: 13px;
        color: var(--main-text-color);
        border-radius: var(--radius);
        cursor: pointer;
        text-decoration: none;
    }

        .tb-user-drop a:hover,
        .tb-user-drop button:hover {
            background: var(--select-selected-bg);
        }

        .tb-user-drop a:focus-visible,
        .tb-user-drop button:focus-visible {
            outline: none;
            background: var(--select-selected-bg);
            box-shadow: inset 0 0 0 2px rgba(var(--main-color-rgb), .45);
        }

    .tb-user-drop .divider {
        height: 1px;
        background: var(--main-border-color);
        margin: 4px 0;
    }

/* ── Topbar user-drop — richer header panel ─────────────────────── */
.tb-drop-head {
    padding: 14px 16px;
    display: flex;
    align-items: center;
    gap: 11px;
    background: var(--gray-bg);
    border-radius: 0;
    cursor: pointer;
}

    .tb-drop-head:hover {
        background: var(--main-background-color-subtle-lowlight);
    }

    .tb-drop-head-static,
    .tb-drop-head-static:hover {
        cursor: default;
        background: var(--gray-bg);
    }

    .tb-drop-head:focus-visible {
        outline: none;
        box-shadow: inset 0 0 0 2px rgba(var(--main-color-rgb), .45);
    }

    /* When the header is a link (to the user's profile), the generic .tb-user-drop a
       rule would turn it into a padded block row; keep the header's own layout. */
    .tb-user-drop a.tb-drop-head {
        display: flex;
        padding: 14px 16px;
        border-radius: 0;
        background: var(--gray-bg);
        color: inherit;
    }

        .tb-user-drop a.tb-drop-head:hover,
        .tb-user-drop a.tb-drop-head:focus-visible {
            background: var(--main-background-color-subtle-lowlight);
        }

.tb-drop-av {
    width: 38px;
    height: 38px;
    border-radius: var(--radius);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--avatar-user-bg);
    color: var(--white-color);
    font-size: 13px;
    font-weight: var(--font-extra-bold);
    flex-shrink: 0;
}

.tb-drop-name {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    line-height: 1.3;
}

.tb-drop-email {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 1px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 185px;
}

.tb-drop-divider {
    height: 1px;
    background: var(--main-border-color);
    margin: 4px 0;
}

.tb-drop-section {
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .8px;
    color: var(--text-sub);
    padding: 8px 16px 3px;
}

.tb-drop-item {
    display: flex !important;
    align-items: center;
    gap: 9px;
    width: 100%;
    text-align: left;
    padding: 9px 16px !important;
    font-size: 13px !important;
    color: var(--main-text-color) !important;
    background: transparent !important;
    border: none;
    cursor: pointer;
    text-decoration: none !important;
    border-radius: 0;
    white-space: nowrap;
    transition: background .12s;
}

    .tb-drop-item:hover {
        background: var(--select-selected-bg) !important;
    }

    .tb-drop-item:focus-visible {
        outline: none;
        background: var(--select-selected-bg) !important;
        box-shadow: inset 0 0 0 2px rgba(var(--main-color-rgb), .45);
    }

    /* Hairline between rows, as between the Actions menu's rows (.ca-item). */
    .tb-user-drop .tb-drop-item {
        border-top: 1px solid var(--main-border-color);
    }

    .tb-drop-item.tb-drop-danger {
        color: var(--error-text) !important;
    }

        .tb-drop-item.tb-drop-danger:hover {
            background: rgba(var(--error-color-rgb), 0.07) !important;
        }

/* ── Main content wrapper ───────────────────────────────────────── */
#main {
    flex: 1;
    background: var(--workspace-bg); /* Sidebar grey (light) / Jet (dark) */
}

/* Dark mode keeps its own darker workspace (#0e0e10 from body.shell), not the
   sidebar panel color, so cards/tables still separate by shade. */
.dark-mode #main {
    background: transparent;
}

/* ── Full-screen page mode ──────────────────────────────────────────
   Opt-in via body.fullscreen-page (set from ViewBag.FullScreenPage) — hides the
   global sidebar/topbar chrome. The page's own root element (e.g. Cases/Details.cshtml's
   .cd-page) is expected to position:fixed itself over the full viewport rather than
   relying on percentage-height propagation through #app-shell/.shell-main-wrap/#main —
   that chain turned out fragile (broke page scrolling entirely) and isn't needed once
   sidebar/topbar are out of the way. */
/* #topbar carries the z-index 1200 the cross-portal header used to, so hiding it
   here is what keeps .cd-page (z-index 10) clear of the chrome on these pages. */
body.fullscreen-page #sidebar,
body.fullscreen-page #topbar,
body.fullscreen-page #progress-container,
body.fullscreen-page #siteFooter {
    display: none;
}

/* .shell-main-wrap still reserves space for the hidden #sidebar/#topbar; .cd-page
   escapes both itself via position:fixed, so cancel them here too. */
body.fullscreen-page .shell-main-wrap {
    margin-left: 0;
    padding-top: 0;
}

/* .site-banner sticks below --topbar-h (Style.css), but there's no topbar to clear here. */
body.fullscreen-page .site-banner {
    top: 0;
}

/* Site-wide banner bar rendered above content */
#siteBannerBar {
    padding: 0;
}

/* ── Sidebar overlay for mobile ─────────────────────────────────── */
#sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    /* Above the sticky site banner (190), just under #sidebar (1100). */
    z-index: 1090;
}

    #sidebar-overlay.open {
        display: block;
    }

/* ── Responsive ─────────────────────────────────────────────────── */
@media (max-width: 900px) {
    #sidebar {
        transform: translateX(-100%);
    }

        #sidebar.open {
            transform: translateX(0);
        }

    /* Last-resort backstop so no control is ever pushed off-screen and lost.
       Scoped to this breakpoint deliberately: above 900px .search-drop is
       position:absolute inside #topbar and an overflow value would clip it. */
    #topbar {
        overflow-x: auto;
        overflow-y: auto;
        scrollbar-width: none;
    }

    #topbar::-webkit-scrollbar {
        display: none;
    }

    /* #progress-container needs no override here any more — it spans the full
       width at every breakpoint now. */

    .shell-main-wrap {
        margin-left: 0;
    }

    .hamburger {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 44px;
        height: 44px;
    }

    /* The sidebar is off-canvas here, so the brand no longer has a 240px column to
       line up with — let it take only the lockup's own width. */
    .topbar-brand {
        width: auto;
    }

    /* The brand/title divider goes on small screens — the bar has to fit
       hamburger + lockup + title + controls on a phone. .topbar-link is listed
       for when the parked utility links come back; they'd be the first thing to
       drop here too. */
    .topbar-link,
    .topbar-sep {
        display: none;
    }

    /* Search collapses to the toggle button; the field opens as a full-width bar
       under the topbar (layout.js sets body.topbar-search-open). */
    .topbar-search-wrap {
        display: none;
    }

    .topbar-btn.topbar-search-toggle {
        display: inline-flex;
    }

    body.topbar-search-open .topbar-search-wrap {
        display: flex;
        position: fixed;
        top: var(--topbar-h);
        left: 0;
        right: 0;
        min-width: 0;
        padding: 8px 12px;
        background: var(--df-jet);
        border-bottom: 1px solid rgba(255, 255, 255, .07);
    }

    body.topbar-search-open .topbar-search {
        display: flex;
    }

    /* The open bar replaces the toggle; blur closes it (layout.js). */
    body.topbar-search-open .topbar-btn.topbar-search-toggle {
        display: none;
    }

    /* The user button is squared to 44px below, so it shows the avatar only; the
       caret would otherwise sit outside the button, next to the avatar. */
    .tb-user-info,
    .tb-user-caret {
        display: none;
    }

    .tb-user {
        padding: 0;
        gap: 0;
        justify-content: center;
    }

    /* The page behind the open drawer stays put instead of scrolling under it
       (layout.js toggles the class together with .open). */
    body.nav-drawer-open {
        overflow: hidden;
    }

    /* Touch-target overrides — desktop chrome is intentionally compact (32–36px),
       but on tablet/phone every primary action needs to clear WCAG 2.5.5's 44px floor. */
    .nav-item {
        padding: 12px 20px;
    }

    .topbar-btn,
    .tb-user,
    .sidebar-collapse-btn {
        width: 44px;
        height: 44px;
    }

    .lang-drop-toggle {
        /* Overrides .topbar-btn's shared width:44px, which squashed this pill. */
        width: auto;
        height: 44px;
        padding: 0 12px;
    }

    .lang-drop-item {
        padding: 12px;
    }
}

/* On a phone the page name matters more than the logo: the logo is also at the
   top of the drawer behind the hamburger, but nothing else says which page this
   is. So the brand lockup and the page icon go, and the title keeps the space,
   truncating with an ellipsis. */
@media (max-width: 480px) {
    .topbar-brand,
    .topbar-page-icon {
        display: none;
    }
}

/* ── Unauthenticated layout ─────────────────────────────────────── */
.unauth-header {
    height: var(--topbar-h);
    background: var(--auth-brand-bg, #1c1c1e);
    color: var(--white-color);
    display: flex;
    align-items: center;
    padding: 0 24px;
    gap: 16px;
    position: sticky;
    top: 0;
    z-index: 50;
}

/* Drop sticky on phones — combined with .site-banner, the persistent
   chrome ate ~120px of viewport. Header still renders at top, just
   scrolls with content instead of consuming permanent vertical space. */
@media (max-width: 576px) {
    .unauth-header {
        position: static;
    }

    /* The loading bar sat 56px down for the sticky header; with the header scrolling away it would float over content. */
    .unauth-header ~ #progress-container {
        position: absolute;
        top: 0;
    }
}

.unauth-brand {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    color: var(--white-color);
    text-decoration: none;
    font-weight: var(--font-extra-bold);
    font-size: 14px;
}

    .unauth-brand img {
        height: 28px;
        width: auto;
    }

.unauth-nav {
    margin-left: auto;
    list-style: none;
    padding: 0;
    margin-top: 0;
    margin-bottom: 0;
}

/* Language toggle keeps its default light-pill styling (.lang-drop-toggle in
   this file) instead of the always-dark header's chassis treatment. */

.unauth-main {
    min-height: calc(100vh - var(--topbar-h) - 80px);
    background: var(--gray-bg);
}

.unauth-footer {
    background: var(--auth-brand-bg, #1c1c1e);
    color: rgba(255, 255, 255, 0.62);
    padding: 18px 24px;
    text-align: center;
    font-size: 12px;
}

    .unauth-footer a {
        color: rgba(255, 255, 255, 0.85);
        text-decoration: none;
        margin: 0 6px;
    }

        .unauth-footer a:hover {
            color: var(--main-color);
        }

.unauth-copy {
    margin-top: 6px;
    color: rgba(255, 255, 255, 0.4);
}

/* ─────────── SETTINGS MODAL  ─────────────────────── */

/* Overlay shell is .modal-overlay itself (pages.css, see #profileModal's markup) —
   .pm-modal here is just the dialog's own (wider) box.*/

.pm-modal {
    --pm-control-width: 200px;
    /* Anchor for the dialog's centred .cd-loading-overlay while a Save request is
       in flight (saveProfile, layout.js) - the dots sit in the middle of the modal
       rather than inside the footer button. */
    position: relative;
    background: var(--main-background-color);
    width: 760px;
    max-width: min(94vw, calc(100vw - var(--sidebar-w) - 48px));
    max-height: min(90vh, 100%);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--main-border-color);
    box-shadow: 0 12px 32px rgba(0,0,0,.18);
}

@media (max-width: 900px) {
    .pm-modal {
        max-width: 94vw;
    }
}

.pm-modal,
.pm-modal * {
    border-radius: 0 !important;
}

/* The blanket square-corner rule above also lands on the shared 5-dot loader's
   dots, so the Save overlay inside this dialog rendered squares while every other
   loader in the app shows circles. The dots are not a surface — exempt them. */
.pm-modal .cd-spinner .dot {
    border-radius: 50% !important;
}


.pm-modal-body {
    flex: 1 1 493px;
    display: flex;
    align-items: stretch;
    height: min(493px, 66vh) !important;
    /* 0, not the pinned height: on a short landscape phone the dialog's own max-height wins
       and the body shrinks, instead of pushing the footer out of the dialog. */
    min-height: 0;
    max-height: min(493px, 66vh) !important;
    overflow: hidden;
}

/* TAB RAIL */

.pm-tab-rail {
    flex: 0 0 auto;
    width: fit-content;
    min-width: 160px;
    max-width: 200px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 12px 8px;
    overflow-y: auto;
    /* Flush with the dialog surface: no fill of its own and no divider. It was
       --main-background-color-subtle, picked when the shell was #141414 and the rail
       therefore sat a step LIGHTER; once the shell moved to the nav-pane #2A2E33 the
       same token made the rail the darkest block in the dialog — a hole beside the
       content rather than a nav pane. The active-tab fill marks the rail now, and the
       recessed cards on the right (see .pm-card) separate the two halves by tone, so
       a vertical rule between them is deliberately absent. The mobile horizontal
       strip keeps its bottom rule — see the max-width: 600px block. */
    background: transparent;
}

.pm-tab-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 10px 12px;
    border: none;
    background: transparent;
    color: var(--main-text-color);
    font-size: 13px;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
}

.pm-tab-btn.tab-item {
    display: flex;
    justify-content: flex-start;
    padding: 10px 12px;
    white-space: normal;
    border-bottom: none;
}

    /* The profile rail borrows .tab-item for its shared metrics, but it is a filled
       vertical nav, not an underline tab bar — so it keeps the sidebar's active shade
       while .tab-item.active went brand red (pages.css). The .dark-mode selector is
       repeated here only to outrank pages.css's equally-specific dark override, which
       would otherwise win on source order and put red text on the grey fill. */
    .pm-tab-btn.tab-item.active,
    .dark-mode .pm-tab-btn.tab-item.active {
        border-bottom-color: transparent;
        color: var(--sidebar-active-text);
    }

    .pm-tab-btn:hover {
        background: var(--sidebar-hover-bg);
        color: var(--sidebar-active-text);
    }

    .pm-tab-btn.active {
        background: var(--sidebar-active-bg);
        color: var(--sidebar-active-text);
    }

    .pm-tab-btn .svg-icon {
        flex-shrink: 0;
    }

/* TAB CONTENT */

.pm-tab-content {
    flex: 1;
    min-width: 0;
    overflow-y: auto;
    padding: 16px;
}

.pm-tab-panel {
    display: none;
    flex-direction: column;
    gap: 10px;
    min-height: 100%;
}

    .pm-tab-panel.active {
        display: flex;
    }

    .pm-tab-panel > .pm-card {
        flex-shrink: 0;
    }

/* FOOTER */

.pm-modal-footer {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 12px;
    padding: 12px 16px;
    /* No rule and no band above the buttons — see .modal-footer (pages.css). */
}

.pm-footer-actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* CARDS */

/* Cards are the recessed wells of this dialog (#F4F5F6 / #141414) on the raised
   #FFFFFF / #2A2E33 shell — the app-wide dialog rule, where the field is recessed
   and the dialog raised, applied at card scale. This is what separates the tab rail
   from the content now that the rail has no fill and no divider. One rule for both
   themes: the tokens flip, so there is no .dark-mode .pm-card counterpart. */
.pm-card {
    background: var(--dialog-field-bg);
    border: 1px solid var(--dialog-field-border);
    margin-bottom: 0;
}

/* Section label, not a title bar: a bordered 14px row with a red icon on every
   card turned four short settings lists into a stack of competing headings. The
   card's own border already says where a group starts, so the header only has to
   name it - small, uppercase, --text-sub, no rule underneath. The danger card
   below keeps a real bar, because there the emphasis is the point. */
.pm-card-header {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 10px 14px 0;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-sub);
    background: transparent;
    border-bottom: none;
}

.pm-card-header-sub {
    margin-left: auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 11px;
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
    color: var(--text-sub);
}

.pm-card-body {
    padding: 10px 14px;
    /* Inside a card the surfaces swap roles. The card itself is the recessed tone,
       so a field painted --dialog-field-bg would land on a surface its own colour
       and lose its fill (it did, in both themes). Re-pointing the token here lifts
       every control in the card body onto the dialog surface instead — the shared
       field rules in pages.css (native inputs, .sh-select-toggle, DevExtreme
       editors) all read --dialog-field-bg, so they follow with no per-control
       overrides and no specificity fight. Set on the BODY, not on .pm-card: the
       card's own background reads this same token, and a custom property declared
       on an element applies to that element's var() too. Every control in the
       dialog sits inside a .pm-card-body (checked when this was written). */
    --dialog-field-bg: var(--dialog-bg);
}

/* PROFILE */

.pm-profile-summary {
    display: flex;
    align-items: center;
    gap: 12px;
    padding-bottom: 8px;
    margin-bottom: 0;
    border-bottom: 1px solid var(--main-border-color);
}

.pm-avatar-lg {
    width: 52px;
    height: 52px;
    background: var(--main-color);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    font-weight: 700;
    flex-shrink: 0;
}

.pm-profile-info {
    min-width: 0;
}

.pm-profile-name {
    font-size: 16px;
    font-weight: 700;
    color: var(--main-text-color);
    line-height: 1.3;
    overflow-wrap: break-word;
}

.pm-profile-email {
    margin-top: 2px;
    margin-bottom: 6px;
    font-size: 13px;
    color: var(--text-sub);
    overflow-wrap: break-word;
}

/* ROLE CHIPS */

.pm-role-chip {
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    padding: 3px 9px;
    font-size: 11px;
    font-weight: 600;
    color: var(--main-color);
    background: rgba(var(--main-color-rgb,205,59,68),.08);
    border: 1px solid rgba(var(--main-color-rgb,205,59,68),.18);
    overflow-wrap: break-word;
}

.pm-role-list {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    margin-top: 4px;
    margin-bottom: 0;
}

/* SETTINGS ROWS */

.pm-field-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px 14px;
    padding: 4px 0;
}

.pm-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

    .pm-field > label {
        font-size: 12px;
        font-weight: 400;
        color: var(--main-text-color);
    }

    .pm-field .pm-input {
        width: 100%;
    }

.pm-field-value {
    display: flex;
    align-items: center;
    height: 30px;
    padding: 6px 10px;
    font-size: 12px;
    /* Read-only twin of .pm-input, which sits beside it in the same grid — so it
       reads the same field tokens and follows the .pm-card-body re-point with it.
       Its old --main-background-color-subtle fill IS the light card colour now,
       which would have left these as bare outlines next to filled inputs. */
    border: 1px solid var(--dialog-field-border);
    background: var(--dialog-field-bg);
    color: var(--main-text-color);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
}

/* Read-only fields look exactly like editable ones - same box, same border - so the
   only way to find out used to be clicking and getting a toast. A lock beside the
   label says it up front, and the wrapper's native title explains why on hover: the
   plain browser tooltip, same as the collapsed sidebar uses for its nav labels
   (syncNavTooltips, layout.js). The click toast stays for touch, where there is no
   hover to reveal a title. */
.pm-field-locked > label {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.pm-field-lock {
    color: var(--text-sub);
}

.pm-field-locked .pm-field-value {
    cursor: help;
}

.pm-setting-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    padding: 9px 0;
    border-bottom: 1px solid var(--main-border-color);
}

    .pm-setting-row > .btn-primary,
    .pm-setting-row > .btn-ghost,
    .pm-setting-row > .btn-outline {
        flex-shrink: 0;
        white-space: nowrap;
    }

    /* Security tab: Change Password, Sign Out All Devices and Request Deactivation
       stacked in one column, so they share one width rather than each hugging its
       label. min-width, so a longer translation still grows the button. */
    #pmPanelSecurity .pm-setting-row > .btn-primary,
    #pmPanelSecurity .pm-setting-row > .btn-outline {
        min-width: 170px;
    }

    .pm-setting-row:last-child {
        border-bottom: none;
    }

    .pm-setting-row label {
        font-size: 12px;
        font-weight: 400;
        color: var(--main-text-color);
        margin: 0;
    }

    .pm-setting-row > span,
    .pm-setting-row > div:not(.pm-input-action):not(.pm-ranking-team-copy):not(.pm-theme-group):not(.sh-select) {
        font-size: 13px;
        color: var(--main-text-color);
        /* Lets the label/description shrink and wrap instead of pushing the
           action button off the edge of the row once flex-wrap has nowhere
           left to give — a flex item's default min-width:auto refuses to
           shrink below its own content width otherwise. */
        flex: 1 1 160px;
        min-width: 0;
    }

    .pm-setting-row > div:not(.pm-input-action):not(.pm-ranking-team-copy) > div:first-child {
        font-size: 12px;
        font-weight: 400;
    }

.pm-ranking-team-copy {
    flex: 1 1 160px;
    min-width: 0;
}

.pm-row-sub {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 3px;
    line-height: 1.4;
}

.pm-accent-link {
    color: var(--main-color);
}

    .pm-accent-link:hover {
        color: var(--main-color-dark);
    }

.usage-analytics-tooltip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    height: 14px;
    margin-left: 2px;
    border: 1px solid var(--main-color);
    border-radius: var(--radius);
    color: var(--main-color);
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    text-decoration: none;
    vertical-align: middle;
}

    .usage-analytics-tooltip:hover {
        background: rgba(var(--main-color-rgb,205,59,68),.08);
    }

/* INPUTS */

.pm-input {
    height: 30px;
    padding: 6px 10px;
    font-size: 12px;
    /* Matches the 16px content box (30px minus padding/border) - inherited
       line-height was taller and overflow:hidden clipped descenders. */
    line-height: 16px;
    border: 1px solid var(--main-border-color);
    overflow: hidden;
    text-overflow: ellipsis;
}

    .pm-input:focus {
        outline: none;
        border-color: var(--main-color);
        box-shadow: 0 0 0 3px rgba(var(--main-color-rgb,205,59,68),.1);
    }

/* Every enhanced select in the dialog is one width (--pm-control-width on
   .pm-modal) so they agree with each other down the column. The row is
   space-between, so they end flush right against the theme group above them
   without having to match its width - matching it made a two-word dropdown as
   wide as three buttons.

   They shrink no further than the row, and flex-wrap drops them onto their own
   line on narrow viewports, where max-width takes over. */
.pm-setting-row > .sh-select {
    flex: 0 0 auto;
    width: var(--pm-control-width);
    max-width: 100%;
    min-width: 0;
}

.pm-setting-row > .pm-theme-group {
    flex: 0 0 auto;
    max-width: 100%;
    min-width: 0;
}

/* Preferred Language is the one select carrying emoji, and flag emoji are
   regional-indicator pairs that Windows' default emoji font does not draw — it
   falls back to bare letters or nothing. The bundled TwemojiMozilla @font-face
   (top of Style.css) is what renders them, which is why the topbar dropdown put a
   .twemoji class on its flags.

   That class can't come along here: the flags live inside <option> elements, and
   shEnhanceSelect copies only option.text() into the widget it builds, so any
   markup or class on the option is dropped. Setting the font on the enhanced
   wrapper instead reaches everything — .sh-select-toggle and .sh-select-option are
   both font-family:inherit, so the label and every menu row pick it up.

   The open menu needs its own selector: shEnhanceSelect detaches it to <body>, so
   it is not inside the `+ .sh-select` wrapper and was rendering bare country codes
   while the closed toggle showed the flag. data-sh-select-for is stamped with the
   source select's id for exactly this (utils.js).

   The native #pmPreferredLanguage is included for the un-enhanced fallback, in case
   the select is read before shEnhanceSelect runs. */
#pmPreferredLanguage,
#pmPreferredLanguage + .sh-select,
.sh-select-menu[data-sh-select-for="pmPreferredLanguage"] {
    font-family: TwemojiMozilla, var(--font);
}

.pm-input-action {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    width: 100%;
    max-width: 260px;
}

    .pm-input-action .pm-input {
        flex: 1;
    }

.pm-field-msg {
    font-size: 12px;
    margin-top: 4px;
    min-width: 0;
    white-space: normal;
    overflow-wrap: break-word;
}

    .pm-field-msg:empty {
        margin-top: 0;
    }

    .pm-input-action .pm-field-msg {
        flex-basis: 100%;
    }

    /* Colour, badge and size come from the shared field-message rule in Style.css;
       restated here only because .pm-field-msg above is later in the cascade. */
    .pm-field-msg-error {
        color: var(--error-text);
        font-size: 13px;
    }

    .pm-input-action > span {
        font-size: 13px;
        color: var(--main-text-color);
        cursor: pointer;
    }

.pm-setting-row:has(#pmAdoPat) {
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
}

.pm-setting-row:has(#pmAdoPat) > div {
    flex: 0 1 auto;
}

    .pm-setting-row:has(#pmAdoPat) > div:first-child {
        max-width: none;
    }

    .pm-setting-row:has(#pmAdoPat) .pm-input-action {
        width: 100%;
        max-width: none;
        flex-wrap: nowrap;
    }

    .pm-setting-row:has(#pmAdoPat) .pm-input {
        flex: 1;
        min-width: 0;
    }


/* THEME BUTTONS */

.pm-theme-group {
    display: flex;
    gap: 10px;
}

.pm-theme-btn {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 8px;
    flex: 1;
    /* Raised onto the dialog surface, like every other control on a recessed card
       (see .pm-card-body). A button, not a field, so it takes --dialog-bg directly
       rather than through the re-pointed --dialog-field-bg. */
    border: 1px solid var(--dialog-field-border);
    background: var(--dialog-bg);
    padding: 5px 10px;
    cursor: pointer;
    font-size: 12px;
    font-weight: 400;
    color: var(--main-text-color);
}

    .pm-theme-btn .svg-icon,
    .pm-theme-btn > span[aria-hidden] {
        font-size: 15px;
        width: 15px;
        height: 15px;
    }

    .pm-theme-btn > span[aria-hidden] {
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Filled, not outlined: a red border with red text on a pale red fill is this
       app's error styling, so the selected theme read as a validation failure. */
    .pm-theme-btn.active {
        border-color: var(--main-color);
        color: var(--white-color);
        background: var(--main-color);
    }

/* TOGGLE SWITCH — the profile modal's settings rows use the shared .toggle /
   .toggle-slider component in pages.css. The .pm-switch / .pm-slider rules that used
   to live here were a second implementation of the same control and had drifted:
   still pill-shaped (border-radius 99px, circular knob) at 42x22 with a hardcoded
   #cfcfcf track, so it never picked up the square redesign or the theme tokens. One
   definition now, so these switches cannot look different from every other one again. */

/* DANGER ZONE */

.pm-card-danger {
    border-color: rgba(var(--error-color-rgb), .35);
}

    /* Alpha wash rather than a fixed tint, so the header composites correctly over
       the card surface in both themes. Was #d64545 on #fff8f8 — 4.18:1. */
    .pm-card-danger .pm-card-header {
        color: var(--error-text);
        background: rgba(var(--error-color-rgb), .06);
        padding: 8px 14px;
        border-bottom: 1px solid rgba(var(--error-color-rgb), .35);
    }

    /* The only icon left in a card header, and the one place it carries meaning. */
    .pm-card-danger .pm-card-header .svg-icon {
        color: var(--error-text);
    }

/* YOUR PARTNER CARD */
.pm-partner-hero {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 20px;
    background: var(--df-facet-tile);
}

.pm-partner-avatar {
    width: 52px;
    height: 52px;
    background: var(--main-color);
    color: var(--white-color);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    font-weight: 700;
    flex-shrink: 0;
}

.pm-partner-details {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.pm-partner-name {
    font-size: 17px;
    font-weight: 700;
    line-height: 1.2;
    color: var(--white-color);
    overflow-wrap: break-word;
}

.pm-partner-subtitle {
    margin-top: 3px;
    font-size: 13px;
    color: rgba(255,255,255,.7);
    line-height: 1.35;
    overflow-wrap: break-word;
}

.pm-partner-link {
    color: var(--main-color);
    text-decoration: underline;
    font-size: 12px;
    font-weight: 600;
}

    .pm-partner-link:hover {
        color: var(--main-color-dark);
    }

.pm-partner-section-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    font-weight: 700;
    color: var(--main-color);
}

.pm-partner-detail-info {
    margin-top: 10px;
    margin-bottom: 20px;
}

.pm-partner-website {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
}

.pm-partner-about {
    margin: 8px 0 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--main-text-color);
}

.pm-partner-contact-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 10px;
    margin-bottom: 10px;
}

.pm-partner-contact-box {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 16px;
    /* A raised panel on the recessed card, like every control there. Its old
       --main-background-color-subtle fill IS the light card colour now. */
    background: var(--dialog-bg);
    border: 1px solid var(--dialog-field-border);
}

.pm-partner-contact-avatar {
    width: 44px;
    height: 44px;
    background: var(--main-color);
    color: var(--white-color);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    font-weight: 700;
    flex-shrink: 0;
}

.pm-partner-contact-info {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.pm-partner-contact-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--main-text-color);
    margin-bottom: 4px;
}

.pm-partner-contact-rows {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.pm-partner-contact-row {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--main-text-color);
    text-decoration: none;
    max-width: 100%;
    overflow-wrap: anywhere;
}

    .pm-partner-contact-row:hover {
        color: var(--main-color);
        text-decoration: underline;
    }

    .pm-partner-contact-row .svg-icon {
        color: var(--main-color-dark);
        flex-shrink: 0;
    }

/* Settings dialog on a phone: the 200px tab rail sits beside the panel and
   only leaves ~150px for it once the modal is capped at 94vw — not enough
   room for the field grid or a setting row. Below 600px the rail becomes a
   horizontal, scrollable strip above the panel instead of a sidebar beside
   it, the same collapse .ump-tab-bar's tab strip already uses. */
@media (max-width: 600px) {
    .pm-overlay {
        padding: 12px;
    }

    .pm-modal-body {
        flex-direction: column;
    }

    .pm-tab-rail {
        flex: 0 0 auto;
        width: 100%;
        max-width: none;
        flex-direction: row;
        overflow-x: auto;
        overflow-y: hidden;
        border-right: none;
        border-bottom: 1px solid var(--main-border-color);
        padding: 0 8px;
    }

    /* Once initTabBarOverflow (layout.js, wired up unconditionally now — its own
       ResizeObserver handles the breakpoint, not a gate at wire-up time) has fitted
       the rail, it no longer needs its own scroll — and can't have one: the More
       dropdown is position:absolute under the trigger, and overflow-y:hidden above
       would clip it since it extends below the rail's own box. #pmTabRail can't
       reuse the shared .tab-bar.tab-bar-overflow rule for this (it deliberately
       isn't in the sitewide .tab-bar auto-init — see the JS-side comment), so the
       same override is restated here under its own id. */
    #pmTabRail.tab-bar-overflow {
        flex-wrap: nowrap;
        overflow: visible;
    }

    .pm-tab-btn {
        width: auto;
        white-space: nowrap;
        flex-shrink: 0;
    }

    /* As a horizontal strip the rail is a tab bar, not a sidebar, so the selected
       tab uses the sitewide underline (.tab-item.active) instead of the sidebar
       pill — the same look the More trigger gets when the active tab is inside it,
       so the row never mixes two selection styles. Vertical padding is dropped
       above so the underline sits on the rail's bottom border. */
    .pm-tab-btn.tab-item {
        border-bottom: 2px solid transparent;
    }

        .pm-tab-btn.tab-item:hover {
            background: transparent;
            color: var(--main-text-color);
        }

        .pm-tab-btn.tab-item.active {
            background: transparent;
            color: var(--main-color);
            border-bottom-color: var(--main-color);
        }

    .pm-tab-content {
        min-height: 0;
    }

    .pm-field-grid {
        grid-template-columns: 1fr;
    }
}

/* DARK MODE */

/* .pm-card was also listed here; it now takes one token-driven rule for both
   themes (see .pm-card), so a dark override would only pin it back to #141414. */
.dark-mode .pm-modal {
    background: var(--main-background-color);
    border-color: var(--main-border-color);
}

.dark-mode .pm-card-header {
    background: transparent;
}

.dark-mode .pm-profile-name,
.dark-mode .pm-setting-row label {
    color: var(--main-text-color);
}

.dark-mode .pm-row-sub,
.dark-mode .pm-profile-email {
    color: var(--text-sub);
}

/* The dark-only resting fills for .pm-input, .sh-select-toggle and .pm-theme-btn
   that lived here are gone — all three pinned #202024, which on a #141414 card
   barely lifted, and the .pm-input one had been dead outright: the shared
   `.modal-overlay input:not(...)` rule in pages.css outranks it (0,11,1 against
   0,2,0). The problem they were patching — a field landing on a surface its own
   colour — is now solved at the source by the --dialog-field-bg re-point on
   .pm-card-body, which covers both themes and every control type at once. */

    /* One step up the DF ramp from the #2A2E33 resting fill. The old value
       (--main-background-color-subtle-lowlight, #2A2A2C) sat within a shade of
       the new resting colour, so hovering would have produced no visible change. */
    .dark-mode .pm-theme-btn:hover {
        background: var(--df-mid);
    }

    .dark-mode .pm-theme-btn.active {
        border-color: var(--main-color);
        color: var(--white-color);
        background: var(--main-color);
    }

/* The cross-portal #siteHeader and its .sh-* styles are gone: the header was
   folded into #topbar, which is now the single full-width always-dark bar. The
   shell offsets that shifted #sidebar/#topbar/main down past it went with it —
   --site-header-h is 0 and #sidebar/#topbar carry their own top values now. */

/* Sidebar right border to separate from workspace */
#sidebar {
    border-right: 1px solid var(--sidebar-border);
}

/* ── Site footer (fixed bottom bar, theme-aware) ───────────────────── */
#siteFooter {
    position: fixed;
    bottom: 0;
    /* Sit beside the sidebar (not under it) so the sidebar's bottom-right corner
       meets the footer's top-left corner cleanly — no overlap/crossing at the seam. */
    left: var(--sidebar-w);
    right: 0;
    height: var(--site-footer-h);
    /* Match the workspace canvas (sidebar grey light / Jet dark) with no top
       border so the footer blends into the page — no visible line/seam. */
    background: var(--workspace-bg);
    border-top: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 28px;
    z-index: 89;
    font-size: 11px;
    gap: 16px;
    /* Neither half wraps or shrinks, so the bar scrolls rather than letting the
       two overlap. overflow-y must be explicit: per spec, setting only
       overflow-x makes the other axis compute to auto as well. */
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
}

/* The page scrolls with no visible scrollbar (wheel, touch, keyboard still work). */
html {
    scrollbar-width: none;
}

html::-webkit-scrollbar {
    display: none;
}

#siteFooter::-webkit-scrollbar {
    display: none;
}

.sf-links {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
}

.sf-link {
    color: #4a4a52;
    text-decoration: none;
    cursor: pointer;
    transition: color .15s;
    background: none;
    border: none;
    font-size: 11px;
    font-family: inherit;
    padding: 0;
    line-height: 1;
    font-weight: 500;
    white-space: nowrap;
}

    .sf-link:hover {
        color: var(--red);
    }

.sf-sep {
    color: #a0a0aa;
    font-size: 10px;
}

.sf-version {
    color: #8a8a95;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: .02em;
    white-space: nowrap;
    flex-shrink: 0;
}

/* Content clears the fixed footer */
#main {
    padding-bottom: 36px;
}

body.impersonating #siteFooter {
    /* Matches the banner's actual height (--imp-banner-h, set in Style.css) instead
       of a hardcoded value — the two had drifted apart and left a gap between the
       footer and the banner below it. */
    bottom: var(--imp-banner-h, 48px);
}

.dark-mode #siteFooter {
    background: #141414;
    border-top-color: rgba(255, 255, 255, .07);
}

    .dark-mode .sf-link {
        color: rgba(220, 220, 230, .55);
    }

        .dark-mode .sf-link:hover {
            color: var(--red-light);
        }

@media (max-width: 900px) {
    #siteFooter {
        padding: 0 24px;
        left: auto;
        right:auto;
        width: 100%;
    }
}

/* On a phone the one-line fixed bar can't hold the links and the version: it
   scrolled sideways with no visible scrollbar, so the version and copyright were
   simply out of reach. Here the footer joins the page flow at the bottom and
   wraps, centred. That also stops it permanently taking 36px of a small screen.
   #main drops the bottom padding that only existed to clear the fixed bar, and
   the links get 44px tap targets. */
@media (max-width: 600px) {
    /* The footer is in flow here, so the shell shares the screen with it instead of taking a full 100dvh and pushing the page into a scroll. */
    body.shell {
        min-height: 100dvh;
    }

    #app-shell {
        min-height: 0;
        flex: 1 0 auto;
    }

    #siteFooter {
        position: static;
        height: auto;
        flex-wrap: wrap;
        justify-content: center;
        column-gap: 16px;
        row-gap: 0;
        padding: 8px 16px 12px;
        overflow: visible;
        text-align: center;
    }

    #siteFooter .sf-links {
        flex-wrap: wrap;
        justify-content: center;
        flex-shrink: 1;
    }

    #siteFooter .sf-link {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    #siteFooter .sf-version {
        white-space: normal;
        flex-shrink: 1;
    }

    #main {
        padding-bottom: 0;
    }

    /* The footer is in flow here, so the banner clearance moves from the content
       wrapper to the footer (the last thing on the page). */
    body.impersonating .shell-main-wrap {
        padding-bottom: 0;
    }

    body.impersonating #siteFooter {
        margin-bottom: var(--imp-banner-h);
    }
}

/* ══════════════════════════════════════════════════════════════════
   pages.css — SigmaHQ prototype design system
   Utility classes + shared component styles used by every interior page.
   Portal shell / sidebar / topbar live in layout.css.
   Brand colour pulls from Style.css root (--main-color).
   ══════════════════════════════════════════════════════════════════ */

/* ── Global box-sizing reset ────────────────────────────────────── */
/* Bootstrap 3 only sets border-box on form controls; the redesign
   relies on padding-counts-inside-width sizing so add it everywhere. */
*,
*::before,
*::after {
    box-sizing: border-box;
}

/* ── Layout tokens layered onto Style.css ───────────────────────── */
/* Colour tokens (--text-sub, --gray-bg, --red, --df-*, …) are defined once in
   Style.css as the single source of truth. Only non-colour layout tokens
   (radius/shadow, kept angular per SigmaNEST brand) live here. */
:root {
    --radius: 0;
    --radius-lg: 0;
    --shadow-sm: 0 2px 12px rgba(0, 0, 0, 0.08);
    --shadow-md: 0 4px 24px rgba(0, 0, 0, 0.12);
    --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.18);
    /* Motion. Every transition and animation in the app references these six
       values, so the whole UI shares one tempo instead of each page inventing its
       own 0.3s ease. Feedback (hover, colour, press) is fast enough to feel
       instantaneous; entrances are slower and eased-out, so things arrive rather
       than snap. --ease-out is deliberately the same curve as the #main-content
       page-enter animation in layout.css, so a card revealing and the page it sits
       on decelerate identically.

       Two rules that keep this from turning into noise: animate only `opacity` and
       `transform` (never width/height/top — the sidebar rail's max-width is the one
       deliberate exception), and nothing loops outside a genuine loading state.

       No prefers-reduced-motion guard is needed on anything built from these: the
       `*` override in Style.css already collapses every transition and animation
       app-wide. Only JS-driven motion (see initCountUps in utils.js) has to check
       the media query itself. */
    --dur-fast: 120ms;
    --dur-base: 180ms;
    --dur-slow: 340ms;
    --ease-out: cubic-bezier(.22, .61, .36, 1);
    /* Slight overshoot — reserved for a value changing under the user's eye (the nav
       badge). Never for entrances, where it reads as a wobble. */
    --ease-spring: cubic-bezier(.34, 1.36, .64, 1);
    /* Long decelerate: leaves fast, settles slowly. For a surface arriving from
       off-screen, where the eye has to catch up with something that was not on the
       page a moment ago — the notifications drawer, a toast. */
    --ease-decel: cubic-bezier(.16, 1, .3, 1);
    /* Sheet curve: near-linear through the middle, hard stop, no overshoot. For a
       surface that unfurls from an edge it stays attached to — a menu dropping out
       of the topbar, a pane pushing in over the one it replaces. The stop is what
       reads as "attached"; a spring here would read as detached and floating. */
    --ease-sheet: cubic-bezier(.32, .72, 0, 1);
}

/* Native date/time input pickers: the browser's own calendar/clock glyph is hidden
   and replaced with the site's SVG icons (calendar, calendar-time, clock), coloured
   by --text-sub so it reads in both light and dark mode without an invert filter. */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator {
    background: none;
    background-color: var(--text-sub, currentColor);
    -webkit-mask: url("/icons/ui/calendar.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo") center / 18px 18px no-repeat;
    mask: url("/icons/ui/calendar.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo") center / 18px 18px no-repeat;
    filter: none;
    cursor: pointer;
}

input[type="datetime-local"]::-webkit-calendar-picker-indicator {
    -webkit-mask-image: url("/icons/ui/calendar-time.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo");
    mask-image: url("/icons/ui/calendar-time.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo");
}

input[type="time"]::-webkit-calendar-picker-indicator {
    -webkit-mask-image: url("/icons/ui/clock.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo");
    mask-image: url("/icons/ui/clock.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo");
}

/* ── Checkboxes: square, brand-coloured, theme-aware ──────────────────────
   Hand-drawn (native boxes ignore border-radius); tick is an SVG since <input>
   pseudo-elements are unreliable. :where() = zero specificity, so page rules win. */
:where(input[type="checkbox"]) {
    -webkit-appearance: none;
    appearance: none;
    display: inline-block;
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    vertical-align: middle;
    border: 1.5px solid var(--text-sub);
    border-radius: 0;
    background-color: var(--control-bg);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 11px 11px;
    cursor: pointer;
}

:where(input[type="checkbox"]):checked {
    background-color: var(--main-color);
    border-color: var(--main-color);
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}

/* Partly-selected "select all": a dash. */
:where(input[type="checkbox"]):indeterminate {
    background-color: var(--main-color);
    border-color: var(--main-color);
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-width='3' d='M5 10h10'/%3e%3c/svg%3e");
}

/* appearance:none drops the native focus ring. */
:where(input[type="checkbox"]):focus-visible {
    outline: 2px solid var(--main-color-outline);
    outline-offset: 1px;
}

:where(input[type="checkbox"]):disabled {
    opacity: .45;
    cursor: not-allowed;
}

/* ── Typography utilities ───────────────────────────────────────── */
.fw-extra-bold { font-weight: var(--font-extra-bold); }

/* ── State utility ──────────────────────────────────────────────── */
.is-disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* ── Whitespace utility ─────────────────────────────────────────── */
.ws-pre-line {
    white-space: pre-line;
}

/* ── Section action button — positions an action button next to a section header */
.section-action-btn {
    margin-top: 50px;
    margin-bottom: 1%;
}

/* ── Embedded dx-grid table sized to fit a section card ────────── */
.dx-grid-section {
    max-height: 576px;
}

.dx-grid-medium {
    height: 400px;
}

.dx-grid-tall {
    height: 500px;
}

.dx-grid-xl {
    height: 600px;
}

/* ── Page shell ─────────────────────────────────────────────────── */
.page {
    padding: 28px;
    width: 100%;
    box-sizing: border-box;
}

.page-header {
    margin-bottom: 24px;
}

/* Page title type lives in one declaration. Use the .page-header wrapper where
   the title owns its own row; use .page-title directly where a wrapper doesn't
   fit (e.g. a flex header row with an icon beside the title) so the size and
   weight still come from here rather than being retyped per page. */
.page-header h2,
.page-title {
    font-size: 22px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
}

.page-header h2 {
    margin: 0 0 4px;
}

.page-header p {
    font-size: 14px;
    color: var(--text-sub);
    margin: 0;
    line-height: 1.5;
}

/* Section header within a Configuration card (e.g. "News Feed Items", "Patch
   Manager", "In-App Notifications") — a plain <h3> here would otherwise pick up
   the global thin h3 {font-weight:300} and look unintentionally light. Bold
   title, small subtitle below it, same rhythm as .page-header above. */
.section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: 18px;
    font-weight: var(--font-extra-bold) !important;
    color: var(--main-text-color);
}

.section-subtitle {
    margin: 4px 0 0;
    font-size: 13px;
    color: var(--text-sub);
    line-height: 1.5;
}

.cfg-section-hdr > :first-child {
    flex: 1 1 260px;
    min-width: 0;
}

@media (max-width: 600px) {
    .cfg-section-hdr > .btn-primary-sh {
        flex: 1 1 100%;
    }
}

/* cd-page — Case Detail surface: full-bleed white panel sitting on the workspace grey */
.cd-page {
    padding: 0;
    background: var(--main-background-color);
}

/* cd-topbar — detail page back-button bar (matches prototype: flush border-bottom, button-style back link) */
.cd-topbar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 28px;
    border-bottom: 1px solid var(--main-border-color);
    background: var(--main-background-color);
    flex-shrink: 0;
}

.cd-topbar-title {
    font-size: 14px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cd-topbar-actions {
    display: flex;
    gap: 8px;
    flex-shrink: 0;
}

.cd-back-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--text-sub);
    text-decoration: none;
    padding: 6px 12px;
    border: 1px solid var(--main-border-color);
    border-radius: 0px;
    background: none;
    white-space: nowrap;
    cursor: pointer;
    transition: border-color .15s, color .15s, transform .1s;
}

.cd-back-btn:hover {
    border-color: var(--main-color);
    color: var(--main-color);
}

.cd-back-btn:focus-visible {
    outline: none;
    border-color: var(--main-color);
    color: var(--main-color);
    box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .35);
}

.cd-back-btn:active {
    transform: scale(.98);
}

/* Icon-only variant: a square arrow button instead of the bordered "Go Back" pill.
   Used by the detail pages whose topbar already names the record beside it (Projects
   ViewProject, Cases Details, Invoice Details); MyCompanies, Notifications and
   Invoice/Pay keep the labelled pill, which is why this is a modifier rather than a
   change to .cd-back-btn itself.
   Written as .cd-back-btn.cd-back-btn-icon (specificity 0,2,0) on purpose: the
   responsive block near the end of this file repads a lone .cd-back-btn (0,1,0) and,
   being later in the file, would otherwise win and put the padding back on mobile. */
.cd-back-btn.cd-back-btn-icon {
    width: 30px;
    height: 30px;
    padding: 0;
    justify-content: center;
    gap: 0;
}

/* Separates the arrow from the page title beside it. */
.cd-topbar-divider {
    width: 1px;
    height: 20px;
    background: var(--main-border-color);
    flex-shrink: 0;
}

/* The arrow is the only visible navigation below 900px, where the sidebar goes
   off-canvas (layout.css), and a touch target has no hover tooltip to fall back on. */
@media (max-width: 900px) {
    .cd-back-btn.cd-back-btn-icon {
        width: 40px;
        height: 40px;
        padding: 0;
    }
}

/* Invoice Details is the only topbar carrying full buttons rather than an empty
   .cd-topbar-actions, and .cd-topbar itself does not wrap. Three labelled buttons
   (Download Receipt / Download Invoice / Pay Invoice) overrun a phone-width bar, and
   the mobile touch-target block near the end of this file repads them taller still.
   Allowing a wrap on narrow viewports lets the actions drop under the title instead
   of pushing off the edge; the divider is dropped there because a wrapped row no
   longer reads as "arrow, then title". */
@media (max-width: 700px) {
    .cd-topbar-inv {
        flex-wrap: wrap;
    }

    .cd-topbar-inv .cd-topbar-divider {
        display: none;
    }

    .cd-topbar-inv .cd-topbar-actions {
        flex-basis: 100%;
    }
}

/* cd-header — case number + title + badges block under cd-topbar */
.cd-header {
    margin-bottom: 20px;
}

/* ── Card ───────────────────────────────────────────────────────── */
.card {
    position: relative;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    margin-bottom: 16px;
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    transition: box-shadow var(--dur-base) var(--ease-out);
}

/* Hover response is the shadow step alone. The 2px rise this used to carry moved
   the panel under the pointer, which reads as a press target — but most .card
   panels are static containers, so the movement promised a click that isn't
   there. The deeper shadow still separates the hovered panel without motion. */
.card:hover {
    box-shadow: var(--shadow-md);
}

/* Title and action wrap as whole units instead of breaking mid-phrase; the
   action drops to its own line only when the two genuinely don't both fit. */
.card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    row-gap: 4px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--main-border-color);
    /* Card header uses the page tone: Snow (#FAFAFA) light / Jet (#141414) dark. */
    background: var(--page-bg);
}

/* Bootstrap's .card-header:first-child rounds the top corners (and outranks .card-header); the brand is angular. */
.card > .card-header:first-child {
    border-radius: 0;
}

.card-title {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    white-space: nowrap;
    flex-shrink: 0;
}

.ct-icon {
    display: inline-flex;
    align-items: center;
    font-size: 16px;
    color: var(--main-color);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.ct-icon .svg-icon > svg {
    width: 16px;
    height: 16px;
}

.card-action {
    font-size: 12px;
    color: var(--main-color);
    cursor: pointer;
    font-weight: var(--font-bold);
    text-decoration: none;
    white-space: nowrap;
    margin-left: auto;
}

.card-action:hover {
    text-decoration: underline;
}

@media (max-width: 600px) {
    .card-action {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }
}

.card-action:focus-visible {
    outline: none;
    text-decoration: underline;
    border-radius: 0;
    box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), .35);
}

/* Clickable text that is neither a button nor a whole-surface card link: plain
   until hovered or focused, then underlined — the same affordance .card-action
   gives "Manage ›" / "View all →". Use this in place of Bootstrap's
   .text-decoration-none on text links inside data tables (dashboard case ids,
   Latest Downloads), which suppressed the underline outright and left them with
   no hover feedback at all. :focus-visible mirrors :hover so the affordance is
   reachable by keyboard, not only by pointer. */
.link-hover-underline {
    text-decoration: none;
}

.link-hover-underline:hover,
.link-hover-underline:focus-visible {
    text-decoration: underline;
}

.card-body {
    padding: 20px;
}

.card-body-flush {
    padding: 0;
}

.card-header-brand {
    background-color: var(--main-color);
    color: var(--white-color);
}

/* ── Buttons ────────────────────────────────────────────────────── */
/* Non-suffixed .btn-primary/.btn-ghost/.btn-outline are the new class names
   (UIUX migration spec §8.1, drops the -sh suffix) — added here as additional
   selectors alongside the -sh originals so both names style identically.
   Migrate a page's markup to the plain name incrementally; -sh stays valid
   until every view has moved off it. */
.btn-primary-sh,
.btn-primary,
.btn-ghost-sh,
.btn-ghost,
.btn-outline-sh,
.btn-outline,
.btn-dl {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 9px 18px;
    font-size: 13px;
    font-weight: var(--font-bold);
    font-family: inherit;
    cursor: pointer;
    border: 1.5px solid transparent;
    transition: background var(--dur-fast), border-color var(--dur-fast), color var(--dur-fast), transform var(--dur-fast);
    text-decoration: none;
    line-height: 1.2;
}

/* Press feedback for the whole family. Only .btn-primary used to have it, which
   meant a click on Cancel, Export or Download felt dead next to a click on Save —
   and those are the majority of clicks in the app. translateY over a bare scale:
   pushing down is what a button does, whereas shrinking reads as the control
   itself changing size. The .99 keeps a trace of the old scale(.98) so primary
   buttons don't lose the weight they had. */
.btn-primary-sh:active,
.btn-primary:active,
.btn-ghost-sh:active,
.btn-ghost:active,
.btn-outline-sh:active,
.btn-outline:active,
.btn-dl:active {
    transform: translateY(1px) scale(.99);
}

/* Matches the :not() chain of Style.css's bare-<icon> fallback, which targets
   the <svg> directly — sizing the .svg-icon wrapper alone is a silent no-op. */
.btn-primary-sh .svg-icon:not(.nav-icon):not(.ui-icon) > svg,
.btn-primary .svg-icon:not(.nav-icon):not(.ui-icon) > svg,
.btn-ghost-sh .svg-icon:not(.nav-icon):not(.ui-icon) > svg,
.btn-ghost .svg-icon:not(.nav-icon):not(.ui-icon) > svg,
.btn-outline-sh .svg-icon:not(.nav-icon):not(.ui-icon) > svg,
.btn-outline .svg-icon:not(.nav-icon):not(.ui-icon) > svg,
.btn-dl .svg-icon:not(.nav-icon):not(.ui-icon) > svg {
    width: 14px;
    height: 14px;
}

.btn-primary-sh .svg-icon,
.btn-primary .svg-icon,
.btn-ghost-sh .svg-icon,
.btn-ghost .svg-icon,
.btn-outline-sh .svg-icon,
.btn-outline .svg-icon,
.btn-dl .svg-icon {
    flex-shrink: 0;
}

.btn-primary-sh,
.btn-primary {
    background: var(--main-color);
    color: var(--white-color);
}

.btn-primary-sh:hover,
.btn-primary:hover {
    background: var(--main-color-dark);
    color: var(--white-color);
}

/* (primary's own scale(.98) :active rule folded into the shared press rule above) */

.btn-ghost-sh,
.btn-ghost {
    background: transparent;
    color: var(--main-text-color);
    border-color: var(--main-border-color);
}

.btn-ghost-sh:hover,
.btn-ghost:hover {
    background: var(--gray-bg);
    border-color: rgba(var(--main-color-rgb), .4);
}

.btn-outline-sh,
.btn-outline {
    background: transparent;
    color: var(--main-color);
    border-color: var(--main-color);
}

.btn-outline-sh:hover,
.btn-outline:hover {
    background: rgba(var(--main-color-rgb), .08);
}

.btn-dl {
    width: 100%;
    background: var(--main-color);
    color: var(--white-color);
}

.btn-dl:hover {
    background: var(--main-color-dark);
    color: var(--white-color);
}

/* Focus rings — keyboard visibility for the -sh button family.
   Two patterns: filled-brand buttons get an inset-white halo + outer
   tinted ring (so the ring is legible regardless of surrounding bg);
   transparent / hairline-bordered buttons get a single tinted ring. */
.btn-primary-sh:focus-visible,
.btn-primary:focus-visible,
.btn-dl:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px var(--white-color),
                0 0 0 3px rgba(var(--main-color-rgb), .55);
}

.btn-ghost-sh:focus-visible,
.btn-ghost:focus-visible,
.btn-outline-sh:focus-visible,
.btn-outline:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .35);
}

.btn-sh-compact {
    font-size: 12px;
    padding: 0 12px;
    min-height: 22px;
}

/* ── Download button progress (ConnectDownloads.startButton) ──────
   The button keeps its own idle look; while busy it turns dark brand red and
   fills with brand red up to --dl-pct, with a cancel cross on the right.
   The state colours are !important because the host buttons come from several
   families (ghost, .btn-primary-sh, Bootstrap .btn, the Stripe page's .sr-btn),
   some of which force their own :disabled look with !important. Scoped to a
   .dl-wrap state, so nothing outside a running download is affected. */
.dl-wrap {
    position: relative;
    display: inline-flex;
}

.dl-wrap .dl-btn {
    font-variant-numeric: tabular-nums;
}
.dl-wrap[data-dl-status="downloading"] .dl-btn {
    padding-right: 32px;
    background: linear-gradient(to right, var(--main-color) var(--dl-pct, 0%), var(--main-color-dark) var(--dl-pct, 0%)) !important;
    border-color: var(--main-color-dark) !important;
    color: var(--white-color) !important;
    box-shadow: none !important;
    opacity: 1 !important;
    cursor: progress;
}

.dl-wrap[data-dl-status="complete"] .dl-btn {
    background: var(--main-background-color) !important;
    border-color: var(--main-border-color) !important;
    color: var(--main-text-color) !important;
    box-shadow: none !important;
    cursor: default;
}

.dl-wrap[data-dl-status="complete"] .dl-btn .svg-icon {
    color: var(--main-color);
}

.dl-wrap[data-dl-status="failed"] .dl-btn {
    background: transparent !important;
    border-color: var(--main-color) !important;
    color: var(--main-color) !important;
    box-shadow: none !important;
}

.dl-cancel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--white-color);
    cursor: pointer;
}

.dl-cancel[hidden] {
    display: none;
}

.dl-cancel:hover {
    background: rgba(0, 0, 0, .18);
}

.dl-cancel:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px var(--white-color);
}

.dl-cancel .svg-icon:not(.nav-icon):not(.ui-icon) > svg {
    width: 12px;
    height: 12px;
}

/* Text-link variant (.dl-wrap-link, set in buttonWrap): a link in a table row stays a
   link while running. Brand-red label, a 3px track with the fill under it, and a small
   grey cancel cross beside the label instead of a filled block. The doubled class
   outranks the filled-button state rules above. */
.dl-wrap.dl-wrap-link {
    align-items: center;
    gap: 6px;
    padding-bottom: 6px;
    margin-bottom: -6px;
    vertical-align: middle;
}

.dl-wrap.dl-wrap-link[data-dl-status] .dl-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 0;
    background: none !important;
    border-color: transparent !important;
    box-shadow: none !important;
    color: var(--main-color) !important;
    white-space: nowrap;
    text-decoration: none;
}

.dl-wrap.dl-wrap-link[data-dl-status="complete"] .dl-btn {
    color: var(--main-text-color) !important;
}

.dl-wrap.dl-wrap-link[data-dl-status="downloading"]::before,
.dl-wrap.dl-wrap-link[data-dl-status="downloading"]::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    height: 3px;
}

.dl-wrap.dl-wrap-link[data-dl-status="downloading"]::before {
    right: 0;
    background: rgba(var(--main-color-rgb), .18);
}

.dl-wrap.dl-wrap-link[data-dl-status="downloading"]::after {
    width: var(--dl-pct, 0%);
    background: var(--main-color);
    transition: width 90ms linear;
}

.dl-wrap.dl-wrap-link .dl-cancel {
    position: static;
    width: auto;
    padding: 2px;
    color: var(--text-sub);
}

.dl-wrap.dl-wrap-link .dl-cancel:hover {
    background: none;
    color: var(--main-text-color);
}

.dl-wrap.dl-wrap-link .dl-cancel:focus-visible {
    box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), .45);
}

@media (prefers-reduced-motion: reduce) {
    .dl-wrap.dl-wrap-link[data-dl-status="downloading"]::after {
        transition: none;
    }
}

@media (max-width: 600px) {
    .btn-sh-compact {
        min-height: 44px;
    }
}

/* ── Stats / stat card ──────────────────────────────────────────── */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

/* --list-surface-bg, not --main-background-color: in dark mode the latter is the
   #141414 canvas itself, so the tiles showed only as outlines while Contacts,
   Downloads and System Health cards sat raised at #2A2E33. Light mode is unchanged
   (both tokens are white there). */
.stat-card {
    position: relative;
    background: var(--list-surface-bg);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    padding: 18px 20px;
    overflow: hidden;
    transition: box-shadow var(--dur-base) var(--ease-out);
}

/* Shadow only, same as .card — no lift. The tiles in .stat-row are mostly
   cursor:default summaries, and a tile that jumps toward the pointer reads as a
   button. */
.stat-card:hover {
    box-shadow: var(--shadow-md);
}

.stat-card::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: var(--main-color);
    transition: height var(--dur-fast) var(--ease-out);
}

/* The accent strip thickening is the lift gesture at tile scale: it reads as the
   card leaning toward the pointer without any of the text moving. */
.stat-card:hover::before {
    height: 5px;
}

.stat-card.stat-green::before { background: var(--success-color); }
.stat-card.stat-orange::before { background: var(--warning-color); }
.stat-card.stat-red::before { background: var(--error-color); }
.stat-card.stat-blue::before { background: var(--blue-color); }
.stat-card.stat-purple::before { background: var(--purple-color); }

.stat-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .8px;
    color: var(--text-sub);
    font-weight: var(--font-bold);
    margin-bottom: 8px;
    padding-right: 26px;
}

.stat-value {
    font-size: 28px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
    line-height: 1.1;
}

/* Status words ("No Active Licenses") render smaller than counts; min-height keeps them
   aligned with the numbers on neighbouring cards. */
.stat-value.stat-value-text {
    display: flex;
    align-items: flex-end;
    min-height: calc(28px * 1.1);
    font-size: 25px;
    line-height: 1.2;
}

.stat-hint {
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 4px;
}

/* ── Tabs ───────────────────────────────────────────────────────── */
.tab-bar {
    display: flex;
    border-bottom: 1px solid var(--main-border-color);
    margin: 24px 0 20px;
    overflow-x: auto;
}

/* Slim scrollbar tinted to match the (inactive) tab text color, not the brand accent. Firefox
   only: elsewhere these properties would override the square ::-webkit-scrollbar below.
   --text-sub has no -rgb companion variable, so color-mix() (not rgba(var(...))) blends it. */
@supports not selector(::-webkit-scrollbar) {
    .tab-bar {
        scrollbar-width: thin;
        scrollbar-color: color-mix(in srgb, var(--text-sub) 55%, transparent) transparent;
    }
}

/* Customer Details: inset the tabs + their panels so the column lines
   up with the hero's internal padding (28px hero internal — same as
   .page horizontal padding) instead of running edge-to-edge. */
.details-body-main > .tab-bar,
.details-body-main > .cd-panel {
    margin-left: 28px;
    margin-right: 28px;
}

.tab-item {
    padding: 10px 18px;
    font-size: 13px;
    font-weight: var(--font-semi-bold);
    color: var(--text-sub);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    transition: color .15s, border-color .15s;
    white-space: nowrap;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

.tab-item:hover {
    color: var(--main-text-color);
}

/* Selected tab carries the brand red, label + icon (currentColor) + underline, per
   the MySigmaTEK prototype. This reverses the earlier "selection = the nav pane's
   active shade, never the accent" rule for tab bars specifically: a tab bar is a
   wayfinding control rather than a row selection, and the prototype pitches it at
   #D42F3B. Row/menu selection elsewhere (--row-selected-bg, --select-selected-bg)
   is unchanged and still neutral.
   Dark mode swaps to --main-color-bright: #D42F3B measures 3.5:1 on the #141414
   card, under the 4.5:1 text floor, while #F7403A clears it at 4.6:1. */
.tab-item.active {
    color: var(--main-color);
    border-bottom-color: var(--main-color);
}

.dark-mode .tab-item.active {
    color: var(--main-color-bright);
    border-bottom-color: var(--main-color-bright);
}

.tab-item:focus-visible {
    outline: none;
    color: var(--main-text-color);
    border-radius: 0;
    box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), .35);
}

/* Count badge inside a tab ("Not Started 5"). Deliberately the same square red chip as
   the sidebar's .nav-badge so a count reads identically wherever it appears; square
   corners because every badge in the app is angular. An empty badge means zero and is
   hidden rather than rendered as "0", which would add noise on the statuses with no
   cases - the JS writes '' for a zero count. */
.tab-count {
    background: var(--main-color);
    color: var(--white-color);
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    line-height: 1.6;
    padding: 0 6px;
    border-radius: 0;
    min-width: 18px;
    text-align: center;
}

.tab-count:empty {
    display: none;
}

/* Warning-toned variant. The Unregistered tab counts NetSuite contacts who have yet
   to register — something to act on, not something broken — so it carries the warning
   amber of the alert-triangle already in that tab's label instead of the brand red
   every other count uses. Fill only: the size, weight and white numerals come from
   .tab-count, which the element keeps alongside this class. */
.tab-count-warn {
    background: var(--warning-color);
}

/* Tabs pushed into the More dropdown keep their badge (initTabBarOverflow clones the
   tab's child nodes); right-align it there so the numbers line up in the menu. */
.tab-more-item .tab-count {
    margin-left: auto;
}

/* ── Tab bar overflow: the "More" tab ────────────────────────────
   Opt-in, added by initTabBarOverflow (utils.js) — the bar keeps only the tabs
   that fit and lists the rest under More, so the row never wraps or scrolls
   sideways. clientWidth is what the measure phase reads, so this bar must stay
   free of horizontal padding, borders and gap.*/
.tab-bar.tab-bar-overflow {
    flex-wrap: nowrap;
    overflow: visible;
}

.tab-more {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    flex-shrink: 0;
    user-select: none;
}

.tab-more-caret {
    font-size: 16px;
    line-height: 1;
    opacity: .6;
}

.tab-more-drop {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    z-index: 350;
    min-width: 200px;
    padding: 4px 0;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: unset;
    box-shadow: var(--shadow-md);
}

.tab-more.open .tab-more-drop {
    display: block;
}

.tab-more-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.tab-more-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 9px 16px;
    border: 0;
    background: none;
    font-family: inherit;
    font-size: 13px;
    font-weight: var(--font-semi-bold);
    color: var(--main-text-color);
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}

.tab-more-item:hover {
    background: var(--main-background-color-subtle);
}

.tab-more-item.active {
    color: var(--main-color);
}

/* Cases: partner-only outer view switcher ("Your Cases" / "Customer Cases"). Sits above
   .tab-bar/.tab-item with heavier weight so the two tab rows read as a hierarchy. */
.pcase-view-bar {
    display: flex;
    border-bottom: 2px solid var(--main-border-color);
    margin: 24px 0 0;
}

.pcase-view-tab {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 10px 22px;
    font-size: 13px;
    font-weight: var(--font-semi-bold);
    color: var(--text-sub);
    cursor: pointer;
    border-bottom: 3px solid transparent;
    /* Pulls the active underline down over the bar's own 2px border. */
    margin-bottom: -2px;
    transition: color .15s, border-color .15s;
    white-space: nowrap;
    user-select: none;
}

.pcase-view-tab:hover {
    color: var(--main-text-color);
}

/* Stays on the nav pane's active shade. These are sub-tabs inside a control, not a
   page-level tab bar, so they did NOT follow .tab-item.active to brand red — the red
   there is wayfinding for the page's main sections. */
.pcase-view-tab.active {
    color: var(--sidebar-active-text);
    border-bottom-color: var(--sidebar-active-text);
}

.pcase-view-tab:focus-visible {
    outline: none;
    color: var(--main-text-color);
    box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), .35);
}

/* Both tabs fit a 375px phone with longer translations. */
@media (max-width: 600px) {
    .pcase-view-tab {
        padding-left: 12px;
        padding-right: 12px;
    }
}

/* padding-top, not margin-top: margin is transparent on a sticky element and would
   let scrolled cases show through the seam under the switcher above. */
.pcase-view-bar ~ div > .cases-sticky-header {
    padding-top: 8px;
    margin-top: 0;
}

/* ============================================================================
   Cases scope toggle: two/three side-by-side buttons that pick what the list shows
   (Your Cases / Your Team's Cases / Customer Cases). Single-select; sits under the page
   header and above the status tabs.
   ---------------------------------------------------------------------------- */
.case-scope-toggle {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: 6px;
    margin: 0 0 16px;
}

.case-scope-btn {
    padding: 9px 16px;
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    background: var(--main-background-color);
    color: var(--text-sub);
    font-family: "Segoe UI", "Noto Sans", sans-serif;
    font-size: 14px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color .15s;
}

.case-scope-btn:hover {
    background: var(--row-hover-bg);
}

.case-scope-btn.is-active {
    background: var(--main-color);
    border-color: var(--main-color);
    color: var(--white-color);
}

.case-scope-btn.is-active:hover {
    background: var(--main-color-dark);
}

.case-scope-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .55);
}

.tab-panel {
    display: none;
}

.tab-panel.active {
    display: block;
}

/* ── Tab pane push ───────────────────────────────────────────────────
   Switching a tab swapped two display values, so the new pane's content
   appeared already in place and the only thing that told you anything had
   happened was the tab strip. The incoming pane now pushes in over the one it
   replaces: a short slide against --ease-sheet, whose hard stop reads as the
   pane arriving at a fixed edge rather than floating into position.

   Four panel families, one rule. .pm-tab-panel (Settings) and .ump-tab-panel
   (the user profile dialog) are styled in layout.css and in that view's own
   <style> respectively; neither sets `animation`, so this does not collide with
   them and the panels keep their own display/flex behaviour.

   Deliberately a short slide and not the full-width push the pattern normally
   uses: the outgoing pane is display:none the same frame, so there is no second
   surface to parallax against, and a 100% translate inside panes that are not
   all overflow-clipped would hand some pages a horizontal scrollbar for the
   length of the animation.

   No fill mode, for the reason the dialog entrance documents: a pane holding a
   transform at rest becomes the containing block for the position:fixed layers
   DevExtreme opens from fields inside it. */
@media (prefers-reduced-motion: no-preference) {
    .tab-panel.active,
    .pm-tab-panel.active,
    .ump-tab-panel.active {
        animation: pane-push var(--dur-base) var(--ease-sheet);
    }

    @keyframes pane-push {
        from {
            opacity: 0;
            transform: translateX(12px);
        }

        to {
            opacity: 1;
            transform: translateX(0);
        }
    }
}

/* ── Pills / badges ─────────────────────────────────────────────── */
.pill {
    border: var(--tag-border);
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 9px;
    font-size: 11px;
    font-weight: var(--font-regular);
    line-height: 1.4;
    background: var(--gray-bg);
    color: var(--main-text-color);
}

.pill-green  { background: var(--tag-green-bg); color: var(--tag-green-fg); }
.pill-orange { background: var(--tag-orange-bg); color: var(--tag-orange-fg); }
.pill-red    { background: var(--tag-red-bg); color: var(--tag-red-fg); }
.pill-blue   { background: rgba(var(--blue-color-rgb), .15); color: var(--blue-link-hover-color); }
.pill-gray {
    background: var(--gray-bg);
    color: var(--text-sub);
}
.pill-purple { background: rgba(var(--purple-color-rgb), .15); color: var(--purple-color-dark); }
.pill-yellow { background: rgba(var(--yellow-color-rgb), .15); color: var(--yellow-color-dark); }
.pill-brand  { background: var(--tag-brand-bg); color: var(--tag-brand-fg); }

/* Deeper solid-fill red for Critical, so it groups with High (.pill-red) but
   still reads as more severe. Reuses --error-color-rgb, no new hue. */
.pill-red-dark {
    background: rgba(var(--error-color-rgb), .9);
    color: var(--white-color);
}

.chip-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    flex-shrink: 0;
    background: var(--text-sub);
}

/* ── Attachment list ─────────────────────────────────────────────────
   Flat list of files, used by the Case Details sidebar and the New Product
   Backlog dialog's uploaded-files list. Shared here (was scoped to
   Cases/Details.cshtml) so both render identically. */
.cd-attachments-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* Bordered so each attachment reads as its own card, not just a hover-highlighted row. */
.cd-attachment-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border: 1px solid var(--main-border-color);
    border-radius: unset;
    text-decoration: none;
    color: inherit;
    transition: background .15s, border-color .15s;
}

/* Only the anchor form (Case Details, where the row downloads the file) reacts to hover —
   the dialog's rows are plain divs for files that aren't downloadable until the item saves. */
a.cd-attachment-row:hover {
    background: var(--row-hover-bg);
    border-color: rgba(var(--main-color-rgb), .3);
}

.cd-attachment-icon {
    flex-shrink: 0;
    font-size: 18px;
    color: var(--text-sub);
}

.cd-attachment-name {
    flex: 1;
    min-width: 0;
    font-size: 12px;
    color: var(--main-text-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cd-attachment-size {
    flex-shrink: 0;
    font-size: 11px;
    color: var(--text-sub);
}

.chip-dot.active-green { background: var(--success-color); }
.chip-dot.inactive     { background: var(--text-sub); }
.chip-dot.warning      { background: var(--warning-color); }
.chip-dot.error        { background: var(--error-color); }

/* ── Hero bands ─────────────────────────────────────────────────── */
/* Welcome hero — always-dark brand facet card (matches MySigmaTEK
   prototype), identical in light and dark mode. */
.hero-band {
    background: var(--df-facet-tile);
    border-radius: var(--radius);
    padding: 24px;
    margin-bottom: 20px;
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    position: relative;
    overflow: hidden;
}

/* (Removed the red hexagon watermark ::before — its clip-path edge created a
   visible vertical colour band over the gradient. The banner now shows the clean
   smooth gradient, matching the dialog headers.) */
.hero-band > * {
    position: relative;
    z-index: 1;
}

    /* Keep hero content above the watermark. */
    .hero-band > .hero-left,
    .hero-band > .hero-actions {
        position: relative;
        z-index: 1;
    }

.hero-left h1 {
    font-size: 22px;
    font-weight: var(--font-extra-bold);
    color: var(--white-color);
    margin: 0 0 6px;
}

.hero-left h1 span {
    color: var(--red-light);
}

.hero-left h1 span#dashboardGreeting {
    color: var(--white-color);
}

.hero-left p {
    font-size: 13px;
    color: rgba(255, 255, 255, .55);
    margin: 0;
}

.hero-meta {
    display: flex;
    gap: 12px;
    margin-top: 10px;
    flex-wrap: wrap;
}

.hero-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    background: rgba(255, 255, 255, .08);
    border: 1px solid rgba(255, 255, 255, .12);
    color: rgba(255, 255, 255, .85);
    border-radius: 0;
    font-size: 12px;
}

.hero-actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    flex-shrink: 0;
}

/* Downloads + Feature Requests share one row; the New Case button above them
   stretches to that combined width so the hero action block stays compact. */
.hero-actions-secondary {
    display: flex;
    flex-direction: row;
    gap: 8px;
}

.hero-actions-secondary .btn-ghost-sh {
    flex: 1 1 0;
    min-width: 0;
}

.hero-actions .btn-primary-sh,
.hero-actions .btn-ghost-sh {
    justify-content: center;
    white-space: nowrap;
}

.hero-actions .btn-ghost-sh {
    color: rgba(255, 255, 255, .85);
    border-color: rgba(255, 255, 255, .18);
}

.hero-actions .btn-ghost-sh:hover {
    background: rgba(255, 255, 255, .08);
    border-color: rgba(255, 255, 255, .3);
}

/* On narrow viewports the hero-left/hero-actions row cramps the greeting,
   chips and buttons into two squeezed columns. Stack them full-width instead,
   matching the MySigmaTEK mobile prototype. */
@media (max-width: 768px) {
    .hero-band {
        flex-direction: column;
        align-items: stretch;
    }

    .hero-actions {
        width: 100%;
    }

    .hero-actions .btn-primary-sh,
    .hero-actions .btn-ghost-sh {
        width: 100%;
    }

    /* Labels get tight at this width; trim the horizontal padding so both
       buttons keep fitting side by side. */
    .hero-actions-secondary .btn-ghost-sh {
        padding-left: 8px;
        padding-right: 8px;
    }
}

/* Primary mobile band: tighter padding, smaller greeting, denser chip row. */
@media (max-width: 600px) {
    .hero-band {
        padding: 16px;
    }

    .hero-left h1 {
        font-size: 18px;
    }

    .hero-meta {
        gap: 6px;
    }

    /* Chips grow to fill the row: long ones take a full row, short ones share. */
    .hero-chip {
        flex: 1 1 auto;
    }
}

@media (max-width: 480px) {
    .hero-band {
        padding: 14px 12px;
    }

    /* Both labels no longer fit side by side; their boxes shrink below the
       nowrap text and it collides. Stack full-width instead. */
    .hero-actions-secondary {
        flex-direction: column;
    }

    .hero-actions-secondary .btn-ghost-sh {
        flex: 0 0 auto;
        width: 100%;
        padding-left: 18px;
        padding-right: 18px;
    }
}

/* ── Company/User hero (co-hero) ────────────────────────────────── */
.co-hero {
    background: var(--df-facet-tile);
    padding: 24px 28px;
    margin-bottom: 24px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 18px 24px;
    position: relative;
    overflow: hidden;
    color: var(--white-color);
}

.co-right {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    flex-shrink: 0;
    gap: 12px;
}

/* The red hexagon watermark is gone with the forge gradient: its clip-path edge
   laid a soft red wedge over the top-right corner, which on the flat charcoal
   facet fill reads as a colour cast rather than texture. The facet pattern is
   the banner's texture now — same call already made for .hero-band above. */

.co-hero > * {
    position: relative;
    z-index: 1;
}

.co-logo {
    width: 64px;
    height: 64px;
    /* Grey in both themes. --avatar-company-bg is #1E1E1E in light mode, which sinks
       into the charcoal hero banner; --df-mid is theme-stable and matches the dark-mode
       company fill. Scoped to the Company Details hero — other company tiles keep the token. */
    background: var(--df-mid);
    color: var(--white-color);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    font-weight: var(--font-extra-bold);
    letter-spacing: 1px;
    flex-shrink: 0;
}

/* ── Three-character monograms ───────────────────────────────────────
   companyMonogram() (utils.js) / MonogramHelper.Company return up to three
   characters — "AMW" for Acme Metal Works, "360" for 360 Metalworks. At the tile's
   normal type size a third character overflows or touches the edges, so every tile
   that can carry a monogram gets a stepped-down size and negative tracking under
   .mono-3. The class is added by companyMonogramClass() / MonogramHelper.CssClass,
   so a tile never has to know how long its own monogram is. */
.co-logo.mono-3 {
    font-size: 16px;
    letter-spacing: -.02em;
}

.cs-row-av.mono-3,
.pcs-avatar.mono-3 {
    font-size: 10px;
    letter-spacing: -.02em;
}

.pcs-detail-avatar.mono-3 {
    font-size: 12px;
    letter-spacing: -.02em;
}

.co-meta {
    flex: 1 1 260px;
    min-width: 220px;
}

.co-meta h2 {
    font-size: 20px;
    font-weight: var(--font-extra-bold);
    color: var(--white-color);
    margin: 0 0 4px;
}

.co-meta h2 a {
    color: var(--white-color);
    text-decoration: none;
}

.co-meta h2 a:hover {
    color: var(--white-color);
    text-decoration: underline;
}

.co-meta p {
    font-size: 13px;
    color: rgba(255, 255, 255, .55);
    margin: 0 0 8px;
}

/* The NetSuite and Salesforce logo links moved up to the title line (.co-name-row,
   in the view's own style block), so this is back to carrying only the account
   type / member-since / city line. */
.co-meta p.co-subtitle {
    display: flex;
    align-items: center;
    gap: 14px;
}

.co-chips {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.co-chip {
    border: var(--tag-border);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    background: rgba(255, 255, 255, .08);
    font-size: 12px;
    color: rgba(255, 255, 255, .85);
    line-height: 1.4;
}

.co-chip.co-chip-active {
    background: var(--tag-green-bg);
    color: var(--tag-green-fg);
}

.co-chip.co-chip-inactive {
    background: var(--tag-red-bg);
    color: var(--tag-red-fg);
}

/* The hero stays dark in both themes, where the light-mode foregrounds are too dark.
   (The same chips also sit on light cards in user search, so this is scoped.) */
.co-chips .co-chip.co-chip-active {
    color: var(--tag-green-fg-on-dark);
}

.co-chips .co-chip.co-chip-inactive {
    color: var(--tag-red-fg-on-dark);
}

.co-chip.co-chip-warn {
    background: rgba(var(--warning-color-rgb), .15);
    color: var(--warning-color);
}

.co-chip.co-chip-accent {
    background: rgba(var(--main-color-rgb), .1);
    color: var(--main-color);
}

/* Mobile-only toggle for .co-hero-extras — shown at max-width:760px below. */
.co-hero-toggle {
    display: none;
    align-items: center;
    gap: 4px;
    background: rgba(255, 255, 255, .08);
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: var(--radius);
    color: rgba(255, 255, 255, .75);
    font-family: var(--font);
    font-size: 11px;
    font-weight: var(--font-bold);
    padding: 4px 9px;
    margin-top: 6px;
    cursor: pointer;
    transition: background .15s;
}

.co-hero-toggle:hover {
    background: rgba(255, 255, 255, .13);
}

.co-hero-toggle-chevron {
    font-size: 8px;
    transition: transform .15s;
}

.co-hero-toggle[aria-expanded="true"] .co-hero-toggle-chevron {
    transform: rotate(180deg);
}

.co-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
    row-gap: 14px;
    align-items: center;
}

.co-stat {
    text-align: center;
}

.co-stat .cs-val {
    color: var(--white-color);
    font-size: 22px;
    font-weight: var(--font-extra-bold);
    line-height: 1;
}

.co-stat .cs-lbl {
    color: rgba(255, 255, 255, .4);
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .6px;
    margin-top: 3px;
}

.co-stat-divider {
    width: 1px;
    background: rgba(255, 255, 255, .12);
    align-self: stretch;
    margin: 0 4px;
}

.co-stat .cs-val.expiring { color: var(--warning-color); }
.co-stat .cs-val.expired  { color: var(--error-color-light); }

/* ── Actions dropdown — the last item in the hero row. It used to be absolutely
   positioned at the top-right, which laid it on top of the right-hand stats (the
   Maintenance stat disappeared behind it once enough stats were shown). In flow —
   after .co-meta's flex-grow eats the leftover space — it never overlaps them,
   and just as naturally, sits at the row's right edge. ───────────────────── */
.co-hero > .co-actions-wrap {
    flex-shrink: 0;
    z-index: 20;
}

/* The widget root must hug the button. It used to be forced to 100px in JS while
   the button rendered wider, so the dropdown — which anchors to the root — sat
   short of the button's right edge. */
#actions-dropdown {
    display: inline-block;
    width: auto;
}

/* Hero behaviour on a narrow viewport lives at the end of this section, after every
   base .co-* rule — media queries add no specificity, so an override placed here
   would lose to the base rules that follow it. */

/* ── Additional Details — full-width strip on the hero's bottom edge. flex-basis
   of 100% is wider than any leftover space on the row above, so it always wraps
   onto a row of its own; cancels the hero's own 24/28px padding with negative
   margins, so the divider reaches both card edges. ──────────────────────── */
.co-hero > .co-more {
    flex: 1 1 100%;
    margin: 4px -28px -24px;
    border-top: 1px solid rgba(255, 255, 255, .12);
}

.co-more-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 11px 28px;
    background: transparent;
    border: 0;
    color: rgba(255, 255, 255, .75);
    font-family: var(--font);
    font-size: 13px;
    font-weight: var(--font-bold);
    text-align: left;
    cursor: pointer;
    transition: background .15s, color .15s;
}

.co-more-toggle:hover {
    background: rgba(255, 255, 255, .06);
    color: var(--white-color);
}

.co-more-toggle:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px rgba(var(--main-color-rgb), .55);
}

/* The glyph takes the label's own colour, not red — the toggle is chrome on the
   dark hero, and brand red here competed with the red Actions button beside it. */
.co-more-label {
    display: inline-flex;
    align-items: center;
    gap: 10px;
}

    .co-more-label .svg-icon {
        color: currentColor;
    }

    .co-more-label .svg-icon svg {
        width: 15px;
        height: 15px;
    }

.co-more-chevron {
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid currentColor;
    transition: transform .15s;
}

.co-more-toggle[aria-expanded="true"] .co-more-chevron {
    transform: rotate(180deg);
}

/* Horizontal padding only. The vertical padding moved inside, onto .co-det-grid:
   it is the block's own height that animates, and padding on the animated element
   survives the sweep — height would reach 0 with 22px of padding still holding the
   block open, so the collapse ended on a jump when `hidden` finally applied. Inside
   the box it is part of the height being animated, and part of the scrollHeight the
   open target is measured from.

   No background of its own, and neither do the cards: the hero's dark facet fill
   shows through the whole block, so it stays one dark company card. The padding
   matches the hero's own 28px inset.

   The facet fill is always dark — identical in light and dark mode — so the cards
   carry their own on-dark palette rather than the theme tokens (--text would be
   #141414 on it in light mode). One set, no .dark-mode counterpart. */
.co-more-body {
    --co-det-text: rgba(255, 255, 255, .92);
    --co-det-sub: rgba(255, 255, 255, .55);
    --co-det-line: rgba(255, 255, 255, .14);
    --co-det-head-bg: rgba(255, 255, 255, .06);
    padding: 0 28px;
    color: var(--co-det-text);
    /* For the stacked-row @container rule (tracks nav collapse and zoom). */
    container: co-more / inline-size;
}

/* ── Additional Details open/close ────────────────────────────────────
   The block's natural height isn't knowable from CSS — it depends on how many
   groups the viewer's permissions leave visible and how many lines the address
   wraps to — so the height is driven from JS (see the coMoreToggle block in
   Details.cshtml) and .is-animating is what turns that from a jump into a sweep.
   The class is only present while a transition is in flight: at rest the block
   keeps height:auto, so a later reflow (window resize, a re-wrapped address) is
   never clipped by a stale pixel height. */
.co-more-body.is-animating {
    overflow: hidden;
    transition: height .24s ease;
}

/* The cards ride in behind the height sweep rather than being revealed by it —
   a plain height animation on its own reads as the content being wiped in from
   the top edge. Staggered so the row assembles left to right, which also gives
   the eye the reading order (Account first). */
.co-more-body .co-det-card {
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity .22s ease, transform .22s ease;
}

.co-more-body.is-open .co-det-card {
    opacity: 1;
    transform: none;
}

    .co-more-body.is-open .co-det-card:nth-child(2) { transition-delay: .05s; }
    .co-more-body.is-open .co-det-card:nth-child(3) { transition-delay: .1s; }

/* The hero tightens its bottom margin while the block is collapsed (rule further
   down), which without this snapped 12px the moment the collapse finished. */
.co-hero {
    transition: margin-bottom .24s ease;
}

/* Motion is decoration here — the state is carried by aria-expanded and the
   chevron, both of which still work with every transition off. */
@media (prefers-reduced-motion: reduce) {
    .co-more-body.is-animating,
    .co-more-body .co-det-card,
    .co-hero {
        transition: none;
    }

    .co-more-body .co-det-card {
        opacity: 1;
        transform: none;
    }
}

/* Gap between the hero and the tab bar below it.
   On the company page .page wraps the hero alone and the real body follows in a
   sibling .details-body-container, so three spacings stacked under the hero with
   nothing between them: the hero's margin, .page's 28px bottom padding, and the tab
   bar's 24px top margin — about 64px of empty band that read as a slot held open for
   the collapsed Additional Details. The outer two are dropped so the hero's own
   margin is the only gap, and that tightens while the details are collapsed, so the
   space tracks the toggle instead of sitting reserved.

   Scoped through :has(.co-hero), which exists only on this page, so no other page's
   .page padding or tab bar spacing moves. Browsers without :has() keep today's
   spacing rather than breaking. */
.page:has(.co-hero) {
    padding-bottom: 0;
}

.page:has(.co-hero) ~ .details-body-container .tab-bar {
    margin-top: 0;
}

.co-hero:has(.co-more-body[hidden]) {
    margin-bottom: 12px;
}

/* Three columns, and the cards always sit on ONE row. grid-auto-flow:column +
   grid-auto-columns is what holds that: auto-fit/auto-fill would reflow to a
   second row at narrow widths, and a fixed repeat(3, …) would leave a dead
   column when a group is hidden. The markup renders at most three groups
   (Account / Configuration / Location) so nothing can flow past the third.
   minmax(0, 1fr) rather than 1fr so a long value (an address line, a Salesforce
   id) wraps inside its card instead of widening the column past its share. */
.co-det-grid {
    display: grid;
    grid-auto-flow: column;
    /* All cards share ONE width — the widest card's content width — and the group
       is centred. The mechanism: equal 1fr tracks inside a fit-content grid. Sizing
       the grid to its content resolves 1fr to the largest card's max-content width,
       so every track takes that; margin-inline:auto then centres the group, putting
       the leftover width evenly on the two outer edges. When the three don't fit,
       max-width caps the grid at the row and the minmax(0, …) floor lets the equal
       tracks shrink together, wrapping values instead of overflowing.
       The gap scales with the viewport (20px floor, 56px cap).
       min-width holds the group to at least 90% of the row, so short content still
       gets roomy cards and the outer margins stay small (5% a side at most); the
       equal 1fr tracks share that width, keeping every card the same size. */
    grid-auto-columns: minmax(0, 1fr);
    width: fit-content;
    min-width: 90%;
    max-width: 100%;
    margin-inline: auto;
    gap: clamp(20px, 4vw, 56px);
    /* The block's vertical padding lives here rather than on .co-more-body — see the
       note on that rule: it has to be inside the element whose height animates.
       16px at the top so the cards' red top edge stands clear of the toggle strip's
       divider instead of merging with it; the bottom matches the hero's 24px padding. */
    padding: 16px 0 24px;
    /* stretch, not start — the groups hold different row counts, and ragged card
       heights read as broken rather than as content-driven. */
    align-items: stretch;
}

/* Padding lives on the title and the rows, not the card, so every divider runs
   the full width of the card the way the reference design does. Corners stay
   square — the whole redesign is sharp-cornered, so this is pinned explicitly
   rather than left to --radius.

   Transparent, so the hero's facet fill reads through; a hairline outline is what
   defines each card against it. The 3px brand-red top edge is the hinge: each card
   reads as pulled down from the Additional Details strip it hangs off.

   A two-column grid (label | value) that every row joins through subgrid, so the
   label column is sized once per card — to its longest label — and all values in
   the card start on the same line. That is what lets the card hug its content: a
   fixed 2fr/3fr split per row had no intrinsic width to size the card from. */
.co-det-card {
    display: grid;
    /* Value column is 1fr so the rows fill whatever width the card is given — a card
       stretched to the widest card's width spreads its rows across it instead of
       leaving an empty strip on the right. */
    /* Label column capped at half the card, so values never break letter by letter. */
    grid-template-columns: fit-content(50%) minmax(0, 1fr);
    align-content: start;
    height: 100%;
    background: transparent;
    border: 1px solid var(--co-det-line);
    border-top: 3px solid var(--main-color);
    border-radius: 0;
    padding: 0;
    overflow: hidden;
}

/* Heading band: title-case, bold, left-aligned on the same axis as the rows, on
   its own shade so the band separates from the rows without a heavier rule. */
.co-det-card-title {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    /* 10px — the nav pane's .nav-item icon/label gap. */
    gap: 10px;
    margin: 0;
    padding: 12px 16px;
    background: var(--co-det-head-bg);
    border-bottom: 1px solid var(--co-det-line);
    color: var(--co-det-text);
    font-size: 13px;
    /* !important: the title is an <h3>, and the global h3 rule in Style.css sets
       font-weight:300 !important, which beats any non-!important weight here. */
    font-weight: var(--font-extra-bold) !important;
}

/* Bare glyph, no chip behind it — an 18px box the heading hangs off, sized like
   the nav pane's .nav-icon. Neutral: the red top edge already carries the brand,
   and a red glyph under it doubled up. */
.co-det-card-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 18px;
    color: var(--co-det-sub);
}

    .co-det-card-icon .svg-icon svg {
        width: 16px;
        height: 16px;
    }

/* Label beside value, both left-aligned. The row spans the card and takes its
   columns from the card via subgrid (see .co-det-card), so values line up across
   rows. A value only wraps when the card has been squeezed below its content. */
.co-det-row {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    align-items: baseline;
    column-gap: 16px;
    padding: 9px 16px;
    font-size: 13px;
    line-height: 1.45;
}

.co-det-label {
    display: block;
    font-size: 14px;
    font-weight: var(--font-regular);
    color: var(--co-det-sub);
}

.co-det-value {
    display: block;
    min-width: 0;
    color: var(--co-det-text);
    font-weight: var(--font-semi-bold);
    word-break: break-word;
}

/* Sales rep's name / email / phone each take their own line — run inline they
   wrapped mid-field (a phone number split across two lines) and read as one blur. */
.co-det-part {
    display: block;
}

    .co-det-part + .co-det-part {
        margin-top: 2px;
    }

/* Same brand red as the cards' top edge and the Actions button, so a link reads
   as part of the page's one accent rather than a second pink tint. */
.co-det-value a {
    color: var(--main-color);
    font-weight: var(--font-regular);
    text-decoration: underline;
}

/* On/off and signed/unsigned values are states, not prose — they read faster as a
   tag than as the word "Enabled" in the same weight as a country name. The --tag-*
   fills, with the on-dark foregrounds: the hero stays dark in both themes, and the
   light-mode foregrounds are too dark for it. */
.co-det-state {
    border: var(--tag-border);
    display: inline-block;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: var(--font-regular);
}

.co-det-state.on {
    background: var(--tag-green-bg);
    color: var(--tag-green-fg-on-dark);
}

.co-det-state.off {
    background: var(--tag-red-bg);
    color: var(--tag-red-fg-on-dark);
}

/* External ids (Salesforce) are strings a reader compares character by character
   or copies out — a fixed pitch makes both reliable. */
.co-det-value.id {
    font-family: Consolas, 'Courier New', monospace;
    font-size: 12px;
    letter-spacing: .2px;
}

/* ── Hero on a narrow viewport ────────────────────────────────────────
   .co-hero is flex-wrap now (see the base rule above), so the mark/stats/Actions
   group gives way fluidly at whatever width or browser zoom level actually runs
   out of room — no fixed viewport threshold to keep in sync with the sidebar
   width or page padding, and nothing further is needed here for that.

   Placed at the END of the .co-* section on purpose: a media query carries no extra
   specificity, so these have to come after the base rules they override. */
@media (max-width: 760px) {
    /* Mark and name share row 1 with Actions (reordered ahead of the stats block
       below so it wraps with them rather than being pushed down too); stats
       become their own full-width row once they no longer fit beside the name. */
    .co-hero {
        row-gap: 10px;
        column-gap: 12px;
        align-items: flex-start;
        padding: 16px;
    }

    .co-hero > .co-logo {
        width: 36px;
        height: 36px;
        font-size: 13px;
    }

    /* Free to shrink past its usual 220px floor here — the stats have already
       been moved off this row entirely (below), so the name is the only thing
       left to give way once the mark + Actions button leave it little room. */
    .co-hero > .co-meta {
        min-width: 0;
    }

    .co-meta h2 {
        font-size: 15px;
    }

    /* Same facts resurface in Additional Details' Location group. */
    .co-meta p.co-subtitle {
        display: none;
    }

    /* Row order: mark + name, stats (.co-right, order 1), Actions (order 2), then
       Additional Details (order 3). */
    .co-hero > .co-actions-wrap {
        order: 2;
        flex: 1 1 100%;
    }

    /* Full-width Actions button under the name, so the name and chips keep the row. */
    .co-actions-wrap #actions-dropdown,
    .co-actions-wrap #actions-dropdown .dx-dropdownbutton,
    .co-actions-wrap #actions-dropdown .dx-buttongroup,
    .co-actions-wrap #actions-dropdown .dx-buttongroup-wrapper {
        display: flex;
        width: 100%;
    }

    .co-actions-wrap #actions-dropdown .dx-dropdownbutton-action {
        flex: 1 1 auto;
    }

    /* One line; a chip that cannot fit is clipped rather than wrapped. */
    .co-hero .co-chips {
        gap: 6px;
        flex-wrap: nowrap;
    }

    .co-hero .co-chip {
        font-size: 11px;
        padding: 2px 8px;
        min-width: 0;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .co-hero > .co-right {
        order: 1;
        flex: 1 1 0;
        min-width: 0;
        align-items: stretch;
    }

    /* Last row: without an order it sorted ahead of Actions and the stats. */
    .co-hero > .co-more {
        order: 3;
    }

    /* Collapsed by default; the Details toggle's JS adds/removes .open on click. */
    .co-hero-extras {
        display: none;
    }

    .co-hero-extras.open {
        display: block;
    }

    .co-hero-toggle {
        display: inline-flex;
        order: 1;
        align-self: center;
        margin-top: 0;
        flex-shrink: 0;
    }

    /* The name block dissolves into the hero's flex row so the Details toggle can
       sit on the stats row: logo + name, (expanded extras), then toggle + stats. */
    .co-hero > .co-meta {
        display: contents;
    }

    .co-meta .co-name-row {
        flex: 1 1 calc(100% - 48px);
        min-width: 0;
        margin-bottom: 0;
    }

    .co-hero-extras.open {
        flex: 0 0 100%;
    }

    /* NetSuite and Salesforce take 40% each, left aligned, with 20% left free. */
    .co-hero-extras .co-name-links {
        display: grid;
        grid-template-columns: 40% 40%;
        width: 100%;
    }

    .co-hero-extras .co-name-links > a {
        width: auto;
        height: 18px;
        background-position: left center;
    }

    /* The strip cancels the hero's padding with negative margins, so it has to
       follow the padding down. */
    .co-hero > .co-more {
        margin: 4px -16px -16px;
    }

    .co-more-toggle {
        padding-left: 16px;
        padding-right: 16px;
    }

    .co-more-body {
        padding-left: 16px;
        padding-right: 16px;
    }

    /* One row, equal-width columns; labels ellipsis instead of wrapping to 2 lines. */
    .co-stats {
        display: grid;
        grid-auto-flow: column;
        grid-auto-columns: 1fr;
        gap: 0;
        column-gap: 0;
    }

    .co-stat-divider {
        display: none;
    }

    /* Five or more stats (users total, maintenance): stats take their own full-width
       row under the Details toggle and lay out three across instead of one cramped row. */
    .co-hero > .co-right:has(.co-stat:nth-child(5 of .co-stat)) {
        flex-basis: 100%;
    }

    /* In that case the toggle sits at the end of the logo + name row instead. */
    .co-hero:has(.co-stat:nth-child(5 of .co-stat)) .co-hero-toggle {
        order: 0;
        align-self: flex-start;
    }

    .co-hero:has(.co-stat:nth-child(5 of .co-stat)) .co-name-row {
        flex: 1 1 0;
    }

    .co-hero:has(.co-stat:nth-child(5 of .co-stat)) .co-hero-extras {
        order: 1;
    }

    .co-stats:has(.co-stat:nth-child(5 of .co-stat)) {
        grid-auto-flow: row;
        grid-template-columns: repeat(3, 1fr);
        row-gap: 14px;
    }

    .co-stat {
        min-width: 0;
        padding: 0 4px;
    }

    .co-stat:first-child {
        padding-left: 0;
    }

    .co-stat:last-child {
        padding-right: 0;
    }

    .co-stat .cs-lbl {
        overflow-wrap: break-word;
        line-height: 1.3;
    }
}

/* Desktop hero: logo and name top-aligned on the left; Actions above the stats in
   a right-hand column; Additional Details spans the bottom. */
@media (min-width: 761px) {
    .co-hero {
        display: grid;
        grid-template-columns: auto minmax(260px, 1fr) minmax(0, max-content);
        align-items: start;
        gap: 14px 20px;
    }

    .co-hero > .co-logo {
        grid-column: 1;
        grid-row: 1 / span 2;
    }

    .co-hero > .co-meta {
        grid-column: 2;
        grid-row: 1 / span 2;
        min-width: 0;
    }

    .co-hero > .co-actions-wrap {
        grid-column: 3;
        grid-row: 1;
        justify-self: end;
    }

    .co-hero > .co-right {
        grid-column: 3;
        grid-row: 2;
        justify-self: end;
        min-width: 0;
    }

    .co-hero .co-stats {
        justify-content: flex-end;
    }

    /* Five or more stats: always one line (labels may wrap, the numbers never do). */
    .co-hero .co-stats:has(.co-stat:nth-child(5 of .co-stat)) {
        flex-wrap: nowrap;
        gap: 14px;
    }

    .co-hero .co-stats:has(.co-stat:nth-child(5 of .co-stat)) .co-stat {
        min-width: 0;
    }

    .co-hero:not(:has(> .co-actions-wrap)) > .co-right {
        grid-row: 1 / span 2;
    }

    .co-hero > .co-more {
        grid-column: 1 / -1;
        grid-row: 3;
    }
}

/* Too narrow for five or more stats beside the name: they take a full-width line under
   the header row, with Actions staying top right. */
@media (min-width: 761px) and (max-width: 1299px) {
    .co-hero > .co-right:has(.co-stat:nth-child(5 of .co-stat)) {
        grid-column: 1 / -1;
        grid-row: 3;
        justify-self: stretch;
    }

    .co-hero:has(.co-stat:nth-child(5 of .co-stat)) > .co-more {
        grid-row: 4;
    }

    .co-hero .co-stats:has(.co-stat:nth-child(5 of .co-stat)) {
        justify-content: space-between;
    }
}

/* Never collapsed at any width: grid-auto-flow:column kept every group on one
   row, giving ~90px columns that broke values character by character. */
@media (max-width: 900px) {
    .co-det-grid {
        grid-auto-flow: row;
        grid-template-columns: 1fr;
        /* Stacked, the cards take the full row rather than the widest card's width. */
        width: auto;
        min-width: 0;
    }
}

/* Three cards across but too narrow for label + value side by side: stack them. */
@media (width > 900px) {
    @container co-more (max-width: 1000px) {
        .co-det-card {
            grid-template-columns: minmax(0, 1fr);
        }

        .co-det-row {
            row-gap: 2px;
            padding: 8px 16px;
        }

        .co-det-label {
            font-size: 12px;
        }
    }
}

/* State tags never wrap. */
.co-det-state {
    white-space: nowrap;
}

#actions-dropdown .dx-dropdownbutton-action,
#actions-dropdown .dx-dropdownbutton-toggle {
    background: var(--main-color);
    border: none;
    border-radius: 0;
}

/* Wider target with smaller type. min-width sits on the action half only — the
   toggle chevron keeps its natural width, so the split stays proportionate. */
#actions-dropdown .dx-dropdownbutton-action {
    min-width: 116px;
}

#actions-dropdown .dx-button-content {
    padding: 6px 14px;
}

#actions-dropdown .dx-button-text {
    font-size: 11px;
    font-weight: var(--font-semi-bold);
    text-transform: uppercase;
    letter-spacing: .5px;
}

#actions-dropdown .dx-dropdownbutton-action:hover,
#actions-dropdown .dx-dropdownbutton-action.dx-state-hover,
#actions-dropdown .dx-dropdownbutton-toggle:hover,
#actions-dropdown .dx-dropdownbutton-toggle.dx-state-hover {
    background: var(--main-color-dark);
}

#actions-dropdown .dx-icon-spindown {
    color: var(--white-color);
}

/* Actions dropdown popup menu — this is the only dxDropDownButton in the app,
   so a global selector is safe (the DataGrid export menu has its own more
   specific override and is unaffected). */
.dx-dropdownbutton-popup-wrapper .dx-overlay-content {
    border-radius: 0;
    /* No min-width here: it out-ranked the 300px width on the .ca-menu rule below
       (min-width wins over width), holding the Actions menu at 400px. */
}

/* ── Registration Token dialog ────────────────────────────────────────
   One code and one note. The code leads at 22px in a boxed, tracked-out fixed
   pitch — it gets read aloud down a phone line or compared character by character,
   which is also why the note under it stays a 12px footnote instead of opening the
   dialog as a full-size sentence. */
.otp-dialog {
    padding: 4px 0 8px;
}

.otp-row {
    display: flex;
    align-items: stretch;
    gap: 10px;
}

.otp-code {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8px 12px;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    color: var(--main-text-color);
    font-family: Consolas, 'Courier New', monospace;
    font-size: 22px;
    font-weight: var(--font-bold);
    letter-spacing: .28em;
    /* letter-spacing adds its gap AFTER the last character too, which pushes the
       code visibly off-centre in the box. */
    text-indent: .28em;
    white-space: nowrap;
    overflow-x: auto;
}

.otp-copy {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.otp-note {
    margin: 10px 0 0;
    text-align: center;
    /* Over-qualified on purpose: dx.alterations.css carries a global
       `.dx-popup-content p { font-size: 16px }`, which is what made this line read as
       a headline. */
    font-size: 12px;
    color: var(--text-sub);
}

.dx-popup-content .otp-note {
    font-size: 12px;
}

/* Actions menu rows. The menu content is ours rather than a dxList (see
   dropDownContentTemplate in Details.cshtml, which is what lets a row hold a
   loader while its request runs), so the row styling lives here instead of on
   .dx-list-item. */
/* Capped here, not via dxDropDownButton's maxWidth/maxHeight — those ignore
   calc() strings. overflow-y turns the cap into an internal scroll. */
.ca-menu {
    display: flex;
    flex-direction: column;
    max-height: var(--ca-menu-max-h, calc(100vh - 140px));
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* Surface: the vendor paints every popup --main-background-color-subtle, so a menu
   whose rows are transparent reads as one grey block. The rows are the surface here:
   they take the page's own background, which leaves the .ca-group captions as the
   only grey and turns them back into the section labels they are meant to be.

   Width: this is the menu's real width, NOT dropDownOptions.width in Details.cshtml.
   dxDropDownButton writes its dimensions as inline styles on this element, and setting
   the option there had no effect on the rendered popup — consistent with the widget not
   parsing the calc() strings its sibling maxWidth/maxHeight carry. An author rule only
   out-ranks a style attribute with !important, so that is what it takes. Moving the
   state tag onto the label's line (.ca-item-head) is what made a narrow menu possible
   at all — while it was a column of its own the menu could not go below ~380px without
   wrapping the hints three lines deep. max-width still clamps this on a phone. */
.dx-dropdownbutton-popup-wrapper .dx-overlay-content:has(.ca-menu) {
    width: var(--ca-menu-width, 300px) !important;
    max-width: calc(100vw - 24px);
    background: var(--main-background-color);
}

/* Section caption. Unfilled: it sits on the menu's own surface and is set apart by
   the rule above it and by being small, uppercase and muted. It used to carry the
   --main-background-color-subtle band the app's table headers use, which turned four
   captions in a seven-row menu into four grey stripes — more banding than a menu this
   short can carry. Asymmetric padding groups the caption with the rows it heads. */
.ca-group {
    padding: 11px 14px 4px;
    background: transparent;
    border-top: 1px solid var(--main-border-color);
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    letter-spacing: .5px;
    text-transform: uppercase;
    color: var(--text-sub);
}

.ca-menu > .ca-group:first-child {
    border-top: none;
}

/* The caption is the separator for the first row of its section; the rule belongs
   above the caption, not between it and the row it heads. */
.ca-group + .ca-item {
    border-top: none;
}

.ca-item {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 10px 14px;
    background: transparent;
    border: 0;
    border-top: 1px solid var(--main-border-color);
    border-radius: 0;
    font-family: var(--font);
    font-size: 13px;
    color: var(--main-text-color);
    text-align: left;
    cursor: pointer;
    transition: background .15s;
    /* Anchor for the row's .cd-loading-overlay, which pins itself to inset: 0. */
    position: relative;
}

.ca-item:first-child {
    border-top: none;
}

.ca-item:hover:not(:disabled),
.ca-item:focus-visible {
    background: var(--main-background-color-subtle);
}

/* Rows are real buttons, so they are in the tab order — the focused row needs to
   be as obvious as the hovered one. Inset bar rather than an outline ring: the menu
   clips its content, and an outline on the first/last row would be cut off. */
.ca-item:focus-visible {
    outline: none;
    box-shadow: inset 2px 0 0 var(--main-color);
}

.ca-item:disabled {
    opacity: .45;
    cursor: not-allowed;
}

/* Bare glyph carrying the row's subject, in the same muted grey as the hint line
   beneath it. This was the detail cards' .co-det-card-icon recipe — a red glyph on a
   red-tinted square — which put seven accent marks down the left edge of a menu where
   nothing is an alert, and left the chips outranking the labels the reader is actually
   choosing between. The tint could not simply be swapped for the subtle grey: that is
   the row's own hover fill, so each square would have vanished under the cursor in
   turn. The 28px box stays, so the labels keep their column. */
.ca-item-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    background: transparent;
    color: var(--text-sub);
}

    .ca-item-chip .svg-icon svg {
        width: 16px;
        height: 16px;
    }

.ca-item-body {
    flex: 1;
    min-width: 0;
}

.ca-item-text {
    display: block;
    line-height: 1.3;
}

/* What the action actually does. These get used once a month at most, and a label
   like "Maintenance Override" does not say what it overrides. On a disabled row this
   line carries the reason instead. */
.ca-item-sub {
    display: block;
    margin-top: 1px;
    font-size: 11px;
    line-height: 1.35;
    color: var(--text-sub);
}

/* Label and current value share the row's first line, with the hint running the full
   width beneath them. The value used to be a third column beside the body
   (.ca-item-state, with a lower-case "now" qualifier): that column reserved the width
   of the longest tag — "Customer / Distributor" — on every row, squeezed the hint into
   what was left, and was what held the menu at 380px. Against the title it costs
   nothing the title was not already occupying, and the "now" is redundant once the
   tag sits beside the label it qualifies. */
.ca-item-head {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* min-width:0 so a long label ellipsises or wraps rather than pushing the tag out of
   the row — a flex item's default min-width:auto would let it overflow. */
.ca-item-head .ca-item-text {
    flex: 1;
    min-width: 0;
}

.ca-item-head .ca-item-tag {
    flex-shrink: 0;
}

.ca-item-tag {
    border: var(--tag-border);
    padding: 2px 7px;
    font-size: 11px;
    font-weight: var(--font-regular);
    white-space: nowrap;
}

.ca-item-tag.on {
    background: var(--tag-green-bg);
    color: var(--tag-green-fg);
}

.ca-item-tag.off {
    background: var(--tag-red-bg);
    color: var(--tag-red-fg);
}

/* For values that are one of two kinds rather than on or off (an internal account is
   not a worse account than a customer one), so it takes the neutral tag colours. */
.ca-item-tag.neutral {
    background: var(--main-background-color-subtle);
    color: var(--text-sub);
}

/* Loader on the row that was clicked, so the feedback belongs to the action the
   reader picked rather than to the button that opened the menu: a ring spins around
   the row's glyph and the glyph takes the brand colour. It replaced a dots overlay
   over the whole row, which was small and brief enough to be missed. */
.ca-item-chip {
    position: relative;
}

.ca-item-ring {
    display: none;
    position: absolute;
    inset: 1px;
    border: 2px solid color-mix(in srgb, var(--main-color) 20%, transparent);
    border-top-color: var(--main-color);
    border-radius: 50%;
}

.ca-item.is-loading .ca-item-ring {
    display: block;
    animation: ca-item-ring-spin .8s linear infinite;
}

.ca-item.is-loading .ca-item-chip {
    color: var(--main-color);
}

@keyframes ca-item-ring-spin {
    to {
        transform: rotate(360deg);
    }
}

/* A still ring still reads as "working" without the motion. */
@media (prefers-reduced-motion: reduce) {
    .ca-item.is-loading .ca-item-ring {
        animation: none;
    }
}

/* While one action runs the rest are inert and dimmed — pointer-events rather than
   :disabled so the running row keeps its own full-strength appearance. */
.ca-menu.is-busy .ca-item {
    pointer-events: none;
}

.ca-menu.is-busy .ca-item:not(.is-loading) {
    opacity: .45;
}

/* ── Inline filter checkbox (e.g. "Include inactive users"); box drawn by the
   global checkbox rule. ─────────────────── */
.cd-check {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    padding: 8px;
    /* Keep the label on one line — wrapping it also changed the height of the whole
       toolbar row, which stretched the search input beside it. */
    white-space: nowrap;
    font-size: 14px;
    font-weight: var(--font-regular);
    cursor: pointer;
}

/* Search box and "Include inactive users" stay side by side at every width: the
   input gives up width first (down from 200px), the nowrap label never shrinks. */
.cd-users-search-row {
    flex-wrap: nowrap;
    min-width: 0;
    max-width: 100%;
}

.cd-users-search-row input.cd-filter-input:not([type="checkbox"]):not([type="radio"]) {
    flex: 0 1 200px;
    min-width: 0;
}

.cd-users-search-row .cd-check {
    flex-shrink: 0;
}

.cd-check input[type="checkbox"] {
    margin: 0;
}

.cs-val.expiring,
.maint-item-val.expiring {
    color: var(--warning-color);
}

.cs-val.expired {
    color: var(--error-text);
}

/* ── Search hero ────────────────────────────────────────────────── */
.search-hero {
    background: var(--df-facet-tile);
    padding: 28px 32px;
    margin-bottom: 24px;
    position: relative;
    overflow: hidden;
}

/* Red hexagon watermark dropped here for the same reason as .co-hero — see the
   note there. */

.search-hero h1,
.search-hero h2 {
    color: var(--white-color);
    font-size: 20px;
    font-weight: var(--font-extra-bold);
    margin: 0 0 5px;
    position: relative;
}

.search-hero p {
    color: rgba(255, 255, 255, .5);
    font-size: 13px;
    margin: 0;
    position: relative;
}

/* Compact hero: subtitle dropped (title + stats already say enough), padding
   tightened. */
@media (max-width: 600px) {
    .search-hero {
        padding: 16px;
    }

    .search-hero p {
        display: none;
    }
}

/* flex-wrap alone was not enough: flex-shrink:0 + min-width:auto meant this
   could never be narrower than max-content, so it never had cause to wrap. */
.cs-hero-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 24px;
    min-width: 0;
    max-width: 100%;
}

.cs-hero-stats > div {
    text-align: center;
    min-width: 0;
}

.cs-hero-stat-v {
    color: var(--white-color);
    font-size: 22px;
    font-weight: var(--font-extra-bold);
    line-height: 1;
    text-align:center;
}

.cs-hero-stat-l {
    color: rgba(255, 255, 255, .4);
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .6px;
    margin-top: 3px;
}

.cs-mode-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    margin-bottom: 14px;
    position: relative;
}

.cs-mode-tab {
    padding: 5px 16px;
    border: none;
    font-size: 12px;
    font-weight: var(--font-bold);
    cursor: pointer;
    background: rgba(255, 255, 255, .07);
    color: rgba(255, 255, 255, .5);
    letter-spacing: .02em;
    transition: background .15s, color .15s;
}

/* Selected tab is brand red, same as .cs-filter-pill.active in the toolbar below
   it — the translucent white it used before read as "slightly lighter", which put
   the page's two selection controls in two different visual languages. */
.cs-mode-tab.active {
    background: var(--main-color);
    color: var(--white-color);
}

/* Unselected tabs still need a hover, or the red one looks like the only live
   control in the group. */
.cs-mode-tab:hover:not(.active) {
    background: rgba(255, 255, 255, .16);
    color: var(--white-color);
}

.cs-hero-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
    margin-bottom: 20px;
    position: relative;
}

/* Unconditional, not breakpoint-gated: the filter block never shrinks, so it
   must be free to drop to its own line at whatever width it stops fitting. */
.cs-hero-search {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .1);
    position: relative;
}

.cs-hero-search input[type=text],
.cs-hero-search input[type=search] {
    /* Real basis, not flex:1 (basis 0): a zero-basis input demands no width, so
       line-breaking kept the filters alongside it and they overflowed. */
    flex: 1 1 160px;
    min-width: 0;
    border: none;
    outline: none;
    font-size: 14px;
    background: transparent;
    color: var(--white-color);
}

.cs-hero-search input::placeholder {
    color: rgba(255, 255, 255, .35);
}

/* Autofilled / remembered search text stays white on the dark hero band in both
   themes, like typed text (task 370063). Explicit rather than relying only on the
   app-wide autofill transition in Style.css, because this band never changes
   colour and black-on-black is unreadable if that transition is ever skipped. */
.cs-hero-search input:-webkit-autofill,
.cs-hero-search input:-webkit-autofill:hover,
.cs-hero-search input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--white-color);
    caret-color: var(--white-color);
}

/* Flex, not the default inline span: the inner .svg-icon is inline-flex with
   line-height:0, so on a baseline it sits high by the line box's descender.
   Flex removes the baseline from the equation and centres it on the input. */
.cs-hero-search-icon {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    font-size: 18px;
    color: rgba(255, 255, 255, .3);
}

.cs-hero-divider {
    width: 1px;
    height: 18px;
    background: rgba(255, 255, 255, .18);
    display: inline-block;
    flex: 0 0 auto;
}

/* Filter dropdowns sit to the right of the hero input. flex:0 0 auto keeps the
   input (flex:1) from squeezing them — without it "All Statuses" clips.
   flex-wrap here too: .cs-hero-search wrapping this whole block onto its own
   line (below the search box) isn't enough on its own — at very narrow
   widths the two selects inside THIS container still sat side by side with
   nothing to stop them overflowing it, clipping "All Statuses" the same way
   at one level down. */
#heroSearchFilters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    /* Shrinkable + min-width:0 so its own flex-wrap can fire; the selects keep
       flex-shrink:0 on purpose, so the block wraps and they never squash. */
    flex: 0 1 auto;
    min-width: 0;
}

/* On phones the hero dropdowns only duplicate the .cs-toolbar filter pills
   directly below and wrap into a second row that crowds the search box, so the
   hero keeps just the input. Filtering is unaffected: the pills drive the same
   state (People pills write through to these hidden selects). */
@media (max-width: 767.98px) {
    #heroSearchFilters {
        display: none;
    }
}

.cs-hero-search select {
    -webkit-appearance: none;
    appearance: none;
    border: none;
    outline: none;
    /* Style.css has a global `input…, select { width: 100% }` plus a bare
       `select { width:100%; height:36px }`. Both must be undone here or the
       filters size to the whole filter bar instead of to their own text. */
    width: auto;
    height: auto;
    /* appearance:none drops the native arrow, so draw our own chevron —
       without it these read as static labels rather than dropdowns. */
    background-color: rgba(255, 255, 255, .1);
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23ffffff' stroke-opacity='.65' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 9px center;
    background-size: 10px 6px;
    font-size: 12px;
    color: rgba(255, 255, 255, .85);
    cursor: pointer;
    padding: 4px 26px 4px 10px;
    flex: 0 0 auto;
}

.cs-hero-search select:hover {
    background-color: rgba(255, 255, 255, .18);
}

.cs-hero-search select option {
    color: var(--main-text-color);
    background: var(--main-background-color);
}

/* The rules above style the raw <select> — dead weight once shEnhanceSelect (utils.js)
   replaces it with the .sh-select-toggle/.sh-select-menu widget and hides the original
   with `select.sh-enhanced { display:none }`. Left in place as a plain-select fallback
   (e.g. JS disabled), but the widget itself needs its own styling here.

   .sh-dark (opt-in per select via an "sh-dark" class, carried onto both the toggle's
   wrapper and the detached menu by enhanceSelect) locks a control to the dark-mode
   palette regardless of the site's active theme. The Customer Search hero filters no
   longer opt in: the lock only redeclared the text tokens, not --control-bg, so in
   light theme they rendered light-grey text on a white box. They follow the theme now.
   Redeclaring the theme tokens these controls already build on, rather than every
   property directly, keeps this in sync with .dark-mode (Style.css) instead of
   duplicating and drifting from it. Two selectors because the menu is detached to
   <body> (a modal-escape trick — see its own rule) and so isn't a descendant of
   .sh-select for these custom properties to inherit through. */
.sh-select.sh-dark,
.sh-select-menu.sh-dark {
    --main-background-color: #141414;
    --main-background-color-subtle: #202024;
    --main-text-color: #C5C8CC;
    --main-border-color: rgba(255, 255, 255, .15);
    --light-grey-color: rgba(220, 220, 230, .6);
}

/* ── Company-search toolbar / list / rows ───────────────────────── */
.cs-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
    gap: 10px;
    flex-wrap: wrap;
}

/* Customer Search toolbars collapse behind a "Filter" button on phones, reusing the
   shared .list-filter-toggle-btn / initListFilterToggle pieces. Scoped here instead of
   joining .list-filter-toolbar because that pattern collapses at 900px and restyles
   the sort select; this page keeps its tablet/desktop toolbar as-is.
   display:contents leaves the panel's two groups as direct flex items of .cs-toolbar
   above the breakpoint, so space-between still splits pills from sort/view. */
.cs-toolbar-panel {
    display: contents;
}

@media (max-width: 767.98px) {
    .cs-toolbar.has-filter-toggle .list-filter-toggle-wrap {
        display: inline-flex;
        align-items: center;
        border: 1.5px solid var(--main-border-color);
    }

    .cs-toolbar.has-filter-toggle .cs-toolbar-panel {
        display: none;
        flex-basis: 100%;
        flex-direction: column;
        align-items: flex-start;
        gap: 10px;
    }

    .cs-toolbar.has-filter-toggle.filters-open .cs-toolbar-panel {
        display: flex;
    }

    .cs-toolbar.filters-open .list-filter-toggle-btn {
        color: var(--main-color);
    }
}

.cs-filter-pill {
    padding: 5px 12px;
    font-size: 12px;
    font-weight: var(--font-semi-bold);
    color: var(--text-sub);
    border: 1px solid var(--main-border-color);
    cursor: pointer;
    background: var(--main-background-color);
    transition: background .15s, color .15s, border-color .15s;
}

.cs-filter-pill:hover {
    border-color: rgba(var(--main-color-rgb), .4);
    color: var(--main-color);
}

.cs-filter-pill.active {
    background: var(--main-color);
    color: var(--white-color);
    border-color: var(--main-color);
}

/* Status filter pills carry their state in the icon colour: green tick for
   Active, red alert for Inactive. */
.cs-filter-pill-ok > .svg-icon {
    color: var(--success-text);
}

.cs-filter-pill-off > .svg-icon {
    color: var(--error-text);
}

/* Selected pill is filled with --main-color, so the icon drops back to the
   pill's own (white) text colour to stay legible. */
.cs-filter-pill.active > .svg-icon {
    color: currentColor;
}

/* The sort select shares this row with .cs-filter-pill and .cs-view-toggle, which
   are 1px-bordered and 5px-padded. The global .sh-select-toggle is 1.5px/8px and so
   stood 6px taller than everything beside it. Scoped here rather than changed
   globally — other toolbars (Cases, backlogs modals) set their own heights off the
   global value. .cs-view-toggle is align-self:stretch, so it follows this height. */
.cs-toolbar .sh-select .sh-select-toggle {
    padding: 5px 12px;
    border-width: 1px;
}

.cs-toolbar-divider {
    width: 1px;
    height: 16px;
    background: var(--main-border-color);
    margin: 0 4px;
}

.cs-view-toggle {
    display: flex;
    border: 1px solid var(--main-border-color);
    overflow: hidden;
    flex-shrink: 0;
    /* Match the height of whatever sits beside it (the sort select on the
       Companies toolbar) instead of the old `height: stretch`. */
    align-self: stretch;
}

.cs-view-btn {
    padding: 5px 11px;
    font-size: 13px;
    background: var(--main-background-color);
    /* Theme variable, not a literal — this was hardcoded #333, which left the
       icons all but invisible against the dark-mode toolbar. */
    color: var(--text-sub);
    border: none;
    cursor: pointer;
    line-height: 1.4;
    flex-shrink: 0;
    display: flex;
    align-items: center;
}

.cs-view-btn > svg {
    display: block;
}

.cs-view-btn.active {
    background: var(--gray-bg);
    color: var(--main-text-color);
}

.cs-view-btn:hover {
    background: var(--gray-bg);
}

.cs-alpha-hdr {
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    color: var(--text-sub);
    letter-spacing: 1.5px;
    padding: 10px 2px 5px;
    border-bottom: 2px solid var(--main-border-color);
    margin-top: 10px;
}

.cs-alpha-hdr:first-child {
    margin-top: 0;
}

.cs-list {
    border: 1px solid var(--main-border-color);
    border-top: none;
    overflow: hidden;
    background: var(--list-surface-bg);
    margin-bottom: 4px;
}

.cs-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 11px 18px;
    border-bottom: 1px solid var(--main-border-color);
    cursor: pointer;
    transition: background .15s;
    color: var(--main-text-color);
    text-decoration: none;
    /* Anchor for the row's centred .cd-loading-overlay while the company details
       page loads (setRowLoading's centered mode, company-search.js). */
    position: relative;
}

.cs-row:last-child {
    border-bottom: none;
}

.cs-row:hover {
    background: var(--row-hover-bg);
    color: var(--main-text-color);
}

.cs-row.partner-row {
    background: rgba(var(--purple-color-rgb), .04);
}

.cs-row.partner-row:hover {
    background: rgba(var(--purple-color-rgb), .08);
}

.cs-row-av {
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--white-color);
    font-size: 12px;
    font-weight: var(--font-extra-bold);
    flex-shrink: 0;
    background: var(--avatar-company-bg);
}

.cs-row-main {
    flex: 1;
    min-width: 0;
}

/* flex:1 is basis 0, so with every sibling flex-shrink:0 this column measured
   0px and the company name vanished. A floor plus row wrap keeps it. */
@media (max-width: 600px) {
    .cs-row {
        flex-wrap: wrap;
        row-gap: 6px;
    }

    .cs-row-main {
        min-width: 120px;
    }
}

.cs-row-name {
    font-size: 13px;
    font-weight: var(--font-bold);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cs-row-sub {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 1px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cs-subsidiary-chip {
    display: inline;
    font-size: 11px;
    color: var(--primary-color);
    cursor: pointer;
}

.cs-subsidiary-chip:hover {
    text-decoration: underline;
}

.cs-row-stats {
    display: flex;
    gap: 5px;
    flex-shrink: 0;
}

.cs-stat-chip {
    border: var(--tag-border);
    font-size: 10px;
    padding: 2px 7px;
    background: var(--tag-neutral-bg);
    color: var(--tag-neutral-fg);
    white-space: nowrap;
    font-weight: var(--font-semi-bold);
}

.cs-stat-chip.warn {
    background: var(--tag-brand-bg);
    color: var(--tag-brand-fg);
}

.cs-row-ago {
    font-size: 11px;
    color: var(--text-sub);
    flex-shrink: 0;
    min-width: 72px;
    text-align: right;
}

/* Six non-shrinking pieces follow the name, together exceeding the row well
   before phone widths. Drop the supplementary counts; identity and status stay.
   820px, not 600px — this row is denser than the rest of the pass. */
@media (max-width: 820px) {
    .cs-row-stats,
    .cs-row-ago {
        display: none;
    }

    /* The row is itself an <a> to the same place, so View is the redundant one
       to drop. Scoped here — other lists share .u-action-btn without that. */
    #companySearchList .u-action-btn:not(.impersonate) {
        display: none;
    }
}

.cs-row-arrow {
    font-size: 18px;
    color: var(--main-color);
    flex-shrink: 0;
    margin-left: 2px;
    font-weight: 300;
    transition: color .15s;
}

.cs-row:hover .cs-row-arrow {
    color: var(--main-color);
}

.sr-badge {
    border: var(--tag-border);
    font-size: 11px;
    font-weight: var(--font-regular);
    padding: 3px 8px;
    flex-shrink: 0;
}

.sr-badge.active {
    background: var(--tag-green-bg);
    color: var(--tag-green-fg);
}

.sr-badge.inactive {
    background: var(--tag-neutral-bg);
    color: var(--tag-neutral-fg);
}

.sr-type-tag {
    border: var(--tag-border);
    font-size: 11px;
    font-weight: var(--font-regular);
    padding: 3px 8px;
    flex-shrink: 0;
}

.sr-type-tag.customer {
    background: var(--tag-blue-bg);
    color: var(--tag-blue-fg);
}

.sr-type-tag.partner {
    background: var(--tag-purple-bg);
    color: var(--tag-purple-fg);
}

/* ── People search rows (Customer Search → People tab) ──────────── */
.ppl-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 11px 16px;
    border-bottom: 1px solid var(--main-border-color);
    cursor: pointer;
    transition: background .15s;
    color: var(--main-text-color);
    text-decoration: none;
    /* Anchor for the row's centred .cd-loading-overlay while the profile is fetched
       (setRowLoading's centered mode), the same way .user-row and .cs-row are. Without
       it the overlay pins to whatever ancestor happens to be positioned and the dots
       land somewhere other than the row that was clicked. */
    position: relative;
}

/* Superseded by the card treatment in the consolidated 600px block below
   (identity/actions/status reorder, plus the bordered-card sizing) — this
   used to be the only reorder rule, scoped to 480px on its own. */

.ppl-row:last-child { border-bottom: none; }

.ppl-row:hover {
    background: var(--row-hover-bg);
    color: var(--main-text-color);
}

.ppl-av {
    width: 36px;
    height: 36px;
    background: var(--avatar-user-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: var(--font-extra-bold);
    color: var(--white-color);
    flex-shrink: 0;
}

.ppl-info {
    flex: 1;
    min-width: 0;
}

/* Was missing the ellipsis treatment .cs-row-name has, so long names wrapped
   onto a second line and overlapped the role tag beside them. */
.ppl-name {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ppl-meta {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ppl-role-tag {
    border: var(--tag-border);
    font-size: 11px;
    font-weight: var(--font-regular);
    padding: 2px 8px;
    flex-shrink: 0;
}

.ppl-role-tag.admin,
.ppl-role-tag.connect-system-admin { background: var(--tag-red-bg);    color: var(--tag-red-fg); }
.ppl-role-tag.user                 { background: var(--tag-blue-bg);   color: var(--tag-blue-fg); }
.ppl-role-tag.prospect             { background: var(--tag-purple-bg); color: var(--tag-purple-fg); }
.ppl-role-tag.employee             { background: var(--tag-green-bg);  color: var(--tag-green-fg); }

/* Company identity is never hue-coded. This tag sits directly beside .ppl-role-tag,
   and tinting both meant a User at a customer read as two blue tags and a Prospect
   at a partner as two purple — the same hue standing for two unrelated dimensions.
   Colour on a people row now means role and nothing else; partner status rides on
   the star, the same marker the Partners filter pill uses. */
.ppl-company-tag {
    border: var(--tag-border);
    font-size: 11px;
    color: var(--tag-neutral-fg);
    flex-shrink: 0;
    background: var(--tag-neutral-bg);
    padding: 2px 8px;
    max-width: 200px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ppl-company-tag > .svg-icon {
    color: var(--tag-purple-fg);
    margin-right: 4px;
    vertical-align: -1px;
}

.ppl-status-dot {
    width: 7px;
    height: 7px;
    flex-shrink: 0;
}

.ppl-status-dot.active   { background: var(--success-color); }
.ppl-status-dot.inactive { background: var(--text-sub); }

/* Same crowding as .cs-row; the status text already conveys state. Scoped to
   this list — these classes are shared with Details and the invite form. View
   stays (as an icon button below 600px, see .u-btn-txt) rather than being
   dropped — both actions read the same way the prototype has them. */
@media (max-width: 820px) {
    #userSearchList .ppl-role-tag,
    #userSearchList .ppl-company-tag {
        display: none;
    }
}

/* ── Customer Details — user / invoice / unregistered rows ──────── */
.user-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--main-border-color);
    transition: background .15s;
    /* Anchor for the row's own centred .cd-loading-overlay while the profile dialog
       or an impersonation is loading (cdSetUserRowLoading in Details.cshtml). */
    position: relative;
}

.user-row:last-child { border-bottom: none; }

/* Company details Users tab: the whole row opens the profile (delegated handler in Details.cshtml). */
#UsersList .user-row { cursor: pointer; }

/* Wraps role tag + status pill so they sit side by side, not stacked. Base-level
   so the gap survives above 600px too — .user-row's own gap only spaces .u-tags
   from its siblings, not the tags inside it. */
.user-row .u-tags {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Collapsed overflow (Key Contacts in the partner Customers panel): the hidden
   .pcs-collapse-rows container is still the last child, so no .user-row is
   :last-child and the second visible row keeps a rule with nothing beneath it.
   Drop it while collapsed — expanding removes [hidden] and the border comes
   back as a genuine divider between rows two and three. */
.user-row:has(+ .pcs-collapse-rows[hidden]) { border-bottom: none; }

.user-row:hover {
    background: var(--row-hover-bg);
}

.u-avatar {
    width: 34px;
    height: 34px;
    background: var(--avatar-user-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--white-color);
    font-size: 12px;
    font-weight: var(--font-extra-bold);
    flex-shrink: 0;
}

.u-info {
    flex: 1;
    min-width: 0;
}

/* Same zero-basis collapse as .cs-row-main: .u-info measured 0px, so the name
   wrapped under the role tag and overlapped it, and Impersonate was clipped. */
@media (max-width: 600px) {
    .user-row {
        flex-wrap: wrap;
        row-gap: 8px;
    }

    .u-info {
        min-width: 120px;
    }

    /* Keeps the row reading identity → state → actions once wrapped. */
    .user-row .u-actions {
        margin-left: auto;
    }
}

.u-name {
    font-size: 13px;
    font-weight: var(--font-semi-bold);
    color: var(--main-text-color);
}

/* The name opens the profile dialog, the same action as the View button at the end of
   the row — inline-block so the hit area is the text, not the full row width. */
.u-name-link {
    display: inline-block;
    cursor: pointer;
}

.u-name-link:hover {
    color: var(--main-color);
    text-decoration: underline;
}

.u-email {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 1px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.u-role-tag {
    border: var(--tag-border);
    font-size: 11px;
    font-weight: var(--font-bold);
    padding: 2px 8px;
    background: rgba(45, 143, 210, .15);
    color: var(--blue-link-hover-color);
    flex-shrink: 0;
}

.u-role-tag.admin    { background: rgba(var(--purple-color-rgb), .15); color: var(--purple-color-dark); }
.u-role-tag.viewer   { background: var(--gray-bg); color: var(--text-sub); }
.u-role-tag.employee { background: rgba(var(--success-color-rgb), .15); color: var(--success-color-dark); }

.u-actions {
    display: flex;
    gap: 6px;
    margin-left: auto;
    flex-shrink: 0;
}

.u-action-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: var(--font-bold);
    padding: 4px 10px;
    border: 1px solid var(--main-border-color);
    color: var(--text-sub);
    background: var(--main-background-color);
    cursor: pointer;
    text-decoration: none;
}

.u-action-btn .svg-icon {
    flex-shrink: 0;
}

.u-action-btn .svg-icon > svg {
    width: 13px;
    height: 13px;
}

/* Mobile card treatment for People Search rows and the Users tab on Company
   Details — same list shape, two pages. Each row becomes its own bordered
   card (square corners — matches the app's sharp-corner shell everywhere
   else, not the rounded corners the reference mockup happened to show):
   identity (bigger avatar, name, email) leads, View/Impersonate go icon-only
   and sit at the top of the card beside a trailing chevron (§4.3 "text
   buttons become icon buttons"), and the role/status line drops to a second
   line indented under the name rather than crowding it. .u-btn-txt wraps
   only the action labels so the icon itself survives the hide. */
@media (max-width: 600px) {
    .u-btn-txt {
        display: none;
    }

    #userSearchList .ppl-row,
    .user-row {
        flex-wrap: wrap;
        align-items: flex-start;
        gap: 12px;
        padding: 16px;
        margin: 0 0 10px;
        border: 1px solid var(--main-border-color);
        border-radius: 0;
    }

    /* Each row is its own bordered card now, so the desktop "no border on the
       last row of a continuous list" rule (:last-child) would otherwise leave
       only the last card missing its bottom edge. */
    #userSearchList .ppl-row:last-child,
    .user-row:last-child {
        margin-bottom: 0;
        border-bottom: 1px solid var(--main-border-color);
    }

    #userSearchList .ppl-av,
    .user-row .u-avatar {
        width: 52px;
        height: 52px;
        border-radius: 0;
        font-size: 18px;
    }

    #userSearchList .ppl-name,
    .user-row .u-name {
        font-size: 16px;
    }

    #userSearchList .ppl-meta,
    .user-row .u-email {
        font-size: 13px;
        margin-top: 2px;
    }

    /* Identity first, then actions + chevron trailing the top line. */
    #userSearchList .ppl-info,
    .user-row .u-info {
        order: 1;
    }

    #userSearchList .u-actions,
    .user-row .u-actions {
        order: 2;
        margin-left: auto;
        gap: 6px;
    }

    /* Dropped rather than reflowed onto its own line — redundant once
       View/Impersonate are explicit buttons. */
    #userSearchList .cs-row-arrow,
    .user-row .cs-row-arrow {
        display: none;
    }

    /* Role tag and status (the dot+text on People rows, the Active/Inactive
       pill on Users rows) move to a full-width second line, indented to align
       under the name past the now-larger avatar. */
    #userSearchList .ppl-company-tag,
    #userSearchList .ppl-role-tag,
    #userSearchList .ppl-status {
        order: 4;
    }

    #userSearchList .ppl-status {
        flex-basis: 100%;
        margin-top: 2px;
        margin-left: calc(52px + 12px);
    }

    .user-row .u-tags {
        order: 4;
        flex-basis: 100%;
        margin-top: 2px;
        margin-left: calc(40px + 12px);
    }

    /* Compact Users row: smaller avatar and buttons; the name truncates on one line. */
    .user-row {
        align-items: center;
        padding: 12px;
    }

    .user-row .u-avatar {
        width: 40px;
        height: 40px;
        font-size: 14px;
    }

    .user-row .u-name {
        font-size: 15px;
        max-width: 100%;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        vertical-align: top;
    }

    .user-row .u-action-btn {
        width: 36px;
        height: 36px;
    }

    #userSearchList .u-action-btn,
    .user-row .u-action-btn {
        width: 40px;
        height: 40px;
        padding: 0;
        border-radius: 0;
        justify-content: center;
        gap: 0;
    }

    #userSearchList .u-action-btn .svg-icon > svg,
    .user-row .u-action-btn .svg-icon > svg {
        width: 17px;
        height: 17px;
    }
}

.u-action-btn:hover {
    border-color: var(--main-color);
    color: var(--main-color);
}

.u-action-btn:disabled {
    opacity: .5;
    cursor: not-allowed;
}

.u-action-btn.invite {
    background: var(--main-color);
    color: var(--white-color);
    border-color: var(--main-color);
}

.u-action-btn.invite:hover { background: var(--main-color-dark); color: var(--white-color); }

.unreg-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--main-border-color);
}

.unreg-row:last-child { border-bottom: none; }

.unreg-dot {
    width: 8px;
    height: 8px;
    background: var(--warning-color);
    flex-shrink: 0;
}

/* ── Invoices tab table ──────────────────────────────────────────────
   Built on the shared .role-table so it matches every other list on the page;
   these rules only add what the invoice columns need on top. */
.inv-table tr.inv-tr {
    cursor: pointer;
}

.inv-col-select {
    width: 1%;
    white-space: nowrap;
}

    .inv-col-select input[type="checkbox"] {
        display: block;
        margin: 0;
        cursor: pointer;
        accent-color: var(--main-color);
    }

/* Amounts read as a column of figures, so they align on the right edge. */
.inv-table th.inv-col-amount,
.inv-table td.inv-col-amount {
    text-align: right;
}

.inv-num {
    color: var(--main-color);
    font-weight: var(--font-bold);
    text-decoration: none;
    white-space: nowrap;
}

.inv-num:hover {
    color: var(--main-color-dark);
    text-decoration: underline;
}

.inv-amt {
    font-weight: var(--font-extra-bold);
    white-space: nowrap;
}

/* Pay button and status pill share one cell; the pill keeps its place when the
   invoice is already paid and no button renders. */
.inv-pay-cell {
    display: flex;
    align-items: center;
    gap: 8px;
}

.inv-dl {
    font-size: 11px;
    font-weight: var(--font-bold);
    padding: 3px 10px;
    border: 1px solid var(--main-border-color);
    color: var(--text-sub);
    background: var(--main-background-color);
    cursor: pointer;
    white-space: nowrap;
    text-decoration: none;
}

.inv-dl:hover {
    border-color: var(--main-color);
    color: var(--main-color);
}

.inv-pay {
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    padding: 4px 12px;
    border: none;
    color: var(--white-color);
    background: var(--main-color);
    cursor: pointer;
    white-space: nowrap;
    transition: background .15s;
    text-decoration: none;
}

.inv-pay:hover { background: var(--main-color-dark); color: var(--white-color); }

/* Both row buttons carry a leading inline SVG (see #invIconTemplates). */
.inv-pay,
.inv-dl {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

    .inv-pay .svg-icon svg,
    .inv-dl .svg-icon svg {
        width: 13px;
        height: 13px;
    }

    /* Brand-red download arrow so the PDF button reads as a download, not a label. */
    .inv-dl .svg-icon {
        color: var(--main-color);
    }

/* ── Invoices tab — YTD total and bulk-pay bar ───────────────────── */
.inv-ytd {
    font-size: 13px;
    color: var(--text-sub);
    white-space: nowrap;
}

    .inv-ytd strong {
        color: var(--main-text-color);
        font-weight: var(--font-extra-bold);
    }

/* Revealed only while invoices are ticked. Tinted in the theme red so it reads
   as an active selection rather than another row of chrome. */
.inv-bulk-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    padding: 12px 18px;
    margin-bottom: 12px;
    background: rgba(var(--main-color-rgb), .07);
    border: 1px solid rgba(var(--main-color-rgb), .25);
    font-size: 13px;
}

.inv-bulk-bar[hidden] {
    display: none;
}

.inv-bulk-count {
    font-weight: var(--font-bold);
    color: var(--main-color-dark);
}

.inv-bulk-right {
    display: flex;
    align-items: center;
    gap: 16px;
}

.inv-bulk-total {
    color: var(--text-sub);
    white-space: nowrap;
}

    .inv-bulk-total strong {
        color: var(--main-text-color);
        font-weight: var(--font-extra-bold);
    }

    /* Icon + label button, sized to sit inside the bar rather than as a page action. */
    .inv-bulk-right .btn-primary-sh {
        display: inline-flex;
        align-items: center;
        gap: 6px;
    }

        .inv-bulk-right .btn-primary-sh .svg-icon svg {
            width: 14px;
            height: 14px;
        }

/* ── Customer Details — small filter dropdown ───────────────────── */
.cd-filter-select {
    width: auto;
    min-width: 140px;
    flex: 0 0 auto;
    padding: 8px 12px;
    border: 1px solid var(--main-border-color);
    font-size: 13px;
    background: var(--main-background-color);
    color: var(--main-text-color);
    outline: none;
    cursor: pointer;
}

.cd-filter-select:focus {
    border-color: var(--main-color);
}

/* Text filter in a details-panel toolbar (the Users tab search). Deliberately a
   native input styled to match .cd-filter-select beside it: the DevExpress
   dxTextBox it replaced kept its own placeholder layer drawn over the typed
   value, and carried DX's chrome into an otherwise restyled toolbar. */
.cd-filter-input {
    width: auto;
    min-width: 200px;
    flex: 0 0 auto;
    padding: 0 12px;
    border: 1px solid var(--main-border-color);
    font-size: 13px;
    background: var(--main-background-color);
    color: var(--main-text-color);
    outline: none;
}

/* `width: auto` above never took effect: Style.css has a global
   `input:not([type="checkbox"]):not([type="radio"]) { width: 100% }` reset whose
   specificity (0,2,1) outranks a lone `.cd-filter-input` (0,1,0). So the search box
   stretched across the whole toolbar, which squeezed the "Include inactive users"
   label into three wrapped lines and — because a flex row stretches its children —
   dragged the input up to that wrapped label's height. Mirroring the reset's shape
   here beats it on specificity without resorting to !important. */
input.cd-filter-input:not([type="checkbox"]):not([type="radio"]) {
    width: 200px;
    max-width: 100%;
    height: 36px;
    box-sizing: border-box;
    flex: 0 0 auto;
    /* Same reset also sets `background-color: var(--main-background-color-subtle)`,
       which greyed the box out against the toolbar; restated here so the field
       reads as an input surface rather than a tinted panel. */
    background-color: var(--main-background-color);
}

.cd-filter-input:focus {
    border-color: var(--main-color);
}

.cd-filter-input::placeholder {
    color: var(--text-sub);
}

/* Every filter dropdown on this page is enhanced into the shared .sh-select
   (utils.js inserts the wrapper directly after the native select and hides it),
   so the menu scrolls inside a capped box instead of opening the OS popup —
   the same dropdown the Configuration pages use. These rules just carry the
   native filter's sizing over to the toggle that replaces it. */
select.cd-filter-select + .sh-select {
    flex: 0 0 auto;
    min-width: 0;
}

    select.cd-filter-select + .sh-select .sh-select-toggle {
        min-width: 140px;
        border-width: 1px;
    }

/* ── Customer Details — module lists on the Configuration table theme ──
   Each tab on the company page (Users, Support Cases, Invoices, Projects,
   Files, Unregistered, OTP Tokens, Related Dev Items) presents its records in
   the same shell the Configuration pages use: a bordered, square-cornered card
   whose body scrolls internally under a pinned header, so the card always ends
   inside the viewport. The cap is viewport-relative like .config-list-scroll
   but subtracts more chrome, because this page stacks a company header and the
   summary cards above the tab bar. */
.cd-list-card {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    overflow: hidden;
}

/* A failed load clears the list and shows only the notice above it, so drop the empty card. */
.cd-list-card:has(> .cd-list-scroll:empty),
.cd-list-card:has(> .proj-grid:empty) {
    display: none;
}

.cd-list-card.padded {
    padding: 16px;
}

.cd-list-scroll {
    max-height: max(260px, calc(100vh - 420px));
    overflow-y: auto;
    overflow-x: auto;
}

/* Phone: keep the scrollbars of hand-built tables on screen. The inherited :root
   scrollbar-color makes Chrome ignore ::-webkit-scrollbar styling and use overlay bars that
   fade out; resetting it lets the site's own bars apply. Covers Company Details lists
   (.cd-list-scroll), Configuration lists (.config-list-scroll, .notification-table-scroll)
   and the dashboard cards. */
@media (max-width: 600px) {
    .cd-list-scroll,
    .config-list-scroll,
    .notification-table-scroll,
    .card-body:has(> .data-table),
    .grid-main > .card > .card-body {
        scrollbar-width: auto;
        scrollbar-color: auto;
    }

    /* Same square bar as the global one; the corner is made transparent to
       remove the white square where the two bars meet. */
    .cd-list-scroll::-webkit-scrollbar-corner,
    .config-list-scroll::-webkit-scrollbar-corner,
    .notification-table-scroll::-webkit-scrollbar-corner,
    .card-body:has(> .data-table)::-webkit-scrollbar-corner,
    .grid-main > .card > .card-body::-webkit-scrollbar-corner {
        background: transparent;
    }
}

    /* th already carries an opaque background, so the header can pin as-is. */
    .cd-list-scroll table.role-table thead th {
        position: sticky;
        top: 0;
        z-index: 1;
    }

    /* Company-page rows are single-line (pills, stars, dates), unlike the
       Configuration lists whose audience cells stack over several lines. */
    .cd-list-scroll table.role-table td {
        vertical-align: middle;
    }

    /* .role-table paints its rows in the page tone (Snow), which is right on the
       Configuration pages but leaves the Cases table a shade darker than the
       Invoices/Users/Unregistered rows sitting directly on the card. Inside a
       .cd-list-card every tab's rows share the card's own background. */
    .cd-list-scroll table.role-table tbody tr {
        background: var(--main-background-color);
    }

/* ── Customer Details — DevExtreme grids on the Configuration table theme ──
   The OTP Tokens, Related Dev Items and Partner Customers tabs render through
   dxDataGrid, which ships the app-wide solid-red header from dx.alterations.css.
   Inside the company page those three take the muted uppercase .role-table
   header instead, so every tab reads as one table. Scoped to the opt-in
   .cd-grid-card wrapper rather than .cd-panel, so the Files tab's file-manager
   grid — which sizes its own rows around file icons — is left alone, as is
   every other grid in the app.

   !important on the tone and size: dx.alterations.css carries an app-wide
   `.dx-grid-section .dx-header-row > td { background: …white… !important;
   color: #6f6f6f !important; font-size: 15px }`, and these tables are
   .dx-grid-section too. Without it only the inner .dx-datagrid-text-content
   took the muted treatment while the cell around it stayed white at 15px, so
   the header read as a mismatched band instead of the Cases table's header. */
.cd-grid-card .dx-datagrid .dx-datagrid-headers .dx-header-row > td,
.cd-grid-card .dx-datagrid .dx-datagrid-headers .dx-header-row > td .dx-datagrid-text-content {
    background-color: var(--contrast-bg) !important;
    color: var(--table-head-text) !important;
    font-size: 11px !important;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .4px;
}

/* Hovering a sortable column repaints the cell (dx.alterations.css blanks it to
   transparent / `color: inherit`, which was invisible while the header was white
   but flashes it pale now that the header carries the Fog tone). These selectors
   deliberately mirror the vendor-alteration ones one class deeper — theirs carry
   five :not()s, so a shorter selector loses on specificity even with !important. */
.cd-grid-card .dx-datagrid .dx-datagrid-headers .dx-datagrid-table .dx-header-row > td:hover:not(.dx-command-select):not(.dx-command-expand):not(.dx-editor-cell):not(.dx-command-edit):not(.dx-datagrid-group-space),
.cd-grid-card .dx-datagrid .dx-datagrid-headers .dx-datagrid-table .dx-header-row > td:hover .dx-datagrid-text-content,
.cd-grid-card .dx-datagrid .dx-datagrid-headers .dx-datagrid-table .dx-header-row .dx-datagrid-action:hover {
    background-color: var(--contrast-bg) !important;
    color: var(--table-head-text) !important;
}

.cd-grid-card .dx-datagrid-headers .dx-datagrid-table .dx-header-row > td {
    border-left: none;
    border-bottom: 1px solid var(--main-border-color);
}

    /* Sort arrows and header-filter funnels are drawn white for the red header;
       on the light header they have to follow the muted header text instead. */
    .cd-grid-card .dx-datagrid-headers .dx-datagrid-table .dx-row > td .dx-sort-indicator,
    .cd-grid-card .dx-datagrid-headers .dx-datagrid-table .dx-row > td .dx-sort,
    .cd-grid-card .dx-datagrid .dx-datagrid-headers .dx-header-filter,
    .cd-grid-card .dx-datagrid .dx-datagrid-headers .dx-header-filter-empty {
        color: var(--table-head-text);
    }

/* The grid already sits inside a bordered card, so the container's own rounded
   border would double up on it. */
.cd-grid-card .dx-datagrid.dx-gridbase-container {
    border: none;
    border-radius: 0;
}

/* Style.css has a bare `table { box-shadow: var(--shadow-sm) }`, and dxDataGrid
   renders its header row as a SEPARATE <table> from the rows — so the header table
   cast a 12px drop shadow down over the first data rows, reading as a grey smudge
   under the column titles. The card supplies the only frame these grids need. */
.cd-grid-card .dx-datagrid table {
    box-shadow: none;
}

.cd-grid-card .dx-datagrid .dx-row > td {
    padding: 12px 16px;
    font-size: 13px;
    font-weight: var(--font-regular);
    border-bottom: 1px solid var(--main-border-color);
}

/* The grid widget paints its own surface tones from the DevExpress theme, which
   leaves the rows a shade off the Invoices row tone. Pin the whole stack — widget,
   rowsview and data cells — to the card background so an OTP Tokens or Related
   Dev Items row is exactly the colour of an Invoices row. Hovered, selected and
   focused rows are excluded outright rather than left to specificity, so those
   states keep the tints DevExtreme (and the hover rule below) give them. */
.cd-grid-card .dx-datagrid,
.cd-grid-card .dx-datagrid .dx-datagrid-rowsview,
.cd-grid-card .dx-datagrid .dx-datagrid-rowsview .dx-row:not(.dx-selection):not(.dx-row-focused):not(.dx-state-hover) > td {
    background-color: var(--main-background-color);
}

/* DevExtreme drives row hover off a .dx-state-hover class, not :hover — match
   that (and skip command cells) so the tint lands on the whole row. */
.cd-grid-card .dx-datagrid-rowsview .dx-data-row.dx-state-hover > td:not(.dx-command-edit):not(.dx-command-select) {
    background-color: rgba(var(--main-color-rgb), .03);
}

/* ── Customer Details — Cases tab avg CSAT summary bar ──────────── */
.avg-csat-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 14px;
    padding: 12px 18px;
    margin-bottom: 12px;
    background: var(--main-background-color-subtle);
    border: 1px solid var(--main-border-color);
    border-radius: unset;
    font-size: 13px;
    color: var(--text-sub);
}

.avg-csat-item {
    display: flex;
    align-items: baseline;
    gap: 6px;
    white-space: nowrap;
}

.avg-csat-title {
    font-weight: var(--font-bold);
    text-transform: uppercase;
    font-size: 11px;
    letter-spacing: .5px;
    color: var(--main-text-color);
}

.avg-csat-stars { color: var(--warning-color); }

.avg-csat-value {
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
    font-size: 16px;
}

.avg-csat-outof { color: var(--text-sub); }

.avg-csat-divider {
    width: 1px;
    height: 16px;
    background: var(--main-border-color);
}

.avg-csat-pending { color: var(--success-color-dark); }

.csat-grid-stars {
    color: var(--warning-color);
    font-size: 12px;
    letter-spacing: -1px;
    white-space: nowrap;
}

/* ── Project cards ──────────────────────────────────────────────── */
.proj-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr));
    gap: 18px;
}

.proj-card {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    overflow: hidden;
    transition: box-shadow .2s;
    cursor: pointer;
    text-decoration: none;
    color: inherit;
    display: block;
    /* Anchor for the card's centred .cd-loading-overlay while the project page loads
       (startRowNavigation in utils.js). */
    position: relative;
}

.proj-card:hover {
    box-shadow: var(--shadow-md);
    color: inherit;
}

.proj-card-top {
    padding: 18px 20px 14px;
    border-bottom: 1px solid var(--main-border-color);
}

.proj-card-title-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
}

.proj-name {
    font-size: 14px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
}

.proj-phase {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 3px;
    display: flex;
    align-items: center;
    gap: 6px;
}

.proj-progress-label {
    display: flex;
    justify-content: space-between;
    font-size: 11px;
    color: var(--text-sub);
    margin: 12px 0 5px;
}

.proj-track {
    height: 5px;
    background: var(--gray-bg);
    overflow: hidden;
}

.proj-fill {
    height: 100%;
    background: var(--main-color);
}

.proj-fill.blue  { background: var(--blue-color); }
.proj-fill.green { background: var(--success-color); }

.proj-card-body {
    padding: 14px 20px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    color: var(--text-sub);
    font-size: 12px;
}

/* Milestone list between a card's progress bar and its footer. Each row states its
   own state in text (struck through / "in progress" via weight and the date), so the
   marker colour reinforces rather than carries it. */
.proj-ms-list {
    padding: 12px 20px;
    border-bottom: 1px solid var(--main-border-color);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.proj-ms {
    display: flex;
    align-items: center;
    gap: 9px;
    font-size: 12px;
}

.proj-ms-marker {
    width: 9px;
    height: 9px;
    flex-shrink: 0;
    background: var(--main-border-color);
}

.proj-ms-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--main-text-color);
}

.proj-ms-date {
    flex-shrink: 0;
    font-size: 11px;
    color: var(--text-sub);
}

/* Done: the strike-through is the state, so the row recedes. */
.proj-ms.done .proj-ms-marker { background: var(--success-color); }

.proj-ms.done .proj-ms-name,
.proj-ms.done .proj-ms-date {
    color: var(--text-sub);
    text-decoration: line-through;
}

/* In progress: the one row a reader should land on. */
.proj-ms.current .proj-ms-marker { background: var(--main-color); }

.proj-ms.current .proj-ms-name {
    font-weight: var(--font-bold);
}

.proj-ms.current .proj-ms-date {
    color: var(--main-color);
    font-weight: var(--font-bold);
}

.proj-ms.upcoming .proj-ms-name {
    color: var(--text-sub);
}

.proj-ms-more {
    font-size: 11px;
    color: var(--text-sub);
    padding-left: 18px;
}

.proj-card-meta {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
}

.proj-card-meta-item {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.proj-card-meta-item .svg-icon svg,
.proj-card-meta-item svg {
    width: 13px;
    height: 13px;
}

/* ── Case Detail (cd-*) — embedded inside .page, NOT an overlay ─── */
.cd-case-num {
    font-size: 12px;
    font-weight: var(--font-extra-bold);
    color: var(--main-color);
    letter-spacing: .04em;
    margin-bottom: 4px;
}

.cd-title {
    font-size: 20px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
    line-height: 1.3;
    margin: 0 0 10px;
}

.cd-badges {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.cd-section-label {
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--text-sub);
    margin: 0 0 10px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--main-border-color);
}

/* Section header row: label spans full width, action button sits inline above the border */
.cd-section-row {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    margin-bottom: 10px;
}

.cd-section-row .cd-section-label {
    flex: 1;
    margin-bottom: 0;
}

.cd-section-row .cd-section-action {
    margin: 0 0 6px;
    flex-shrink: 0;
}

.cd-desc-box {
    background: var(--main-background-color-subtle);
    border: 1px solid var(--main-border-color);
    padding: 14px 16px;
    font-size: 14px;
    color: var(--main-text-color);
    line-height: 1.65;
    margin-bottom: 24px;
    min-width: 0;
    overflow-wrap: break-word;
}

.cd-activity {
    display: flex;
    flex-direction: column;
    gap: 0;
    margin-bottom: 24px;
}

.cd-msg {
    display: flex;
    gap: 12px;
    padding: 14px 0;
    border-bottom: 1px solid var(--main-border-color);
}

.cd-msg:last-child { border-bottom: none; }

.cd-msg-av {
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: var(--font-extra-bold);
    color: var(--white-color);
    flex-shrink: 0;
    margin-top: 2px;
    background: var(--main-color);
}

.cd-msg-right {
    flex: 1;
    min-width: 0;
}

.cd-msg-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 5px;
    flex-wrap: wrap;
}

.cd-msg-name {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.cd-msg-role-badge {
    border: var(--tag-border);
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    padding: 2px 7px;
}

.cd-msg-role-badge.support  { background: rgba(45, 143, 210, .15); color: var(--blue-link-hover-color); }
.cd-msg-role-badge.customer { background: rgba(var(--success-color-rgb), .15); color: var(--success-color-dark); }
.cd-msg-role-badge.internal { background: rgba(var(--error-color-rgb), .15); color: var(--error-text); }

.cd-msg-date {
    font-size: 11px;
    color: var(--text-sub);
    margin-left: auto;
}

.cd-msg-body {
    font-size: 13px;
    color: var(--main-text-color);
    line-height: 1.6;
    min-width: 0;
    overflow-wrap: break-word;
    -webkit-hyphens: manual;
    hyphens: manual;
    -webkit-hyphenate-character: "-";
    hyphenate-character: "-";
}

/* The other things a pasted message can be wider than its column with. */
.cd-msg-body img {
    max-width: 100%;
    height: auto;
}

.cd-msg-body pre,
.cd-msg-body table,
.cd-desc-box pre,
.cd-desc-box table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
}

/* ── Rich-text lists inside a rendered message / description ──────────────────────
   Messages are authored in the dxHtmlEditor, and devextreme-quill marks EVERY list as
   <ol> with the real kind in li[data-list] — the markers themselves come from
   .dx-htmleditor-content rules (dx.material.sigmanest.css) that only apply inside the
   editor. Rendered here, that left bullet items numbered and hanging off the text
   column, so the markers are re-declared for this context.
   The marker is positioned rather than inline (the editor's own rule uses a negative
   inline margin) so it stays level with the TOP of the item — an item whose content is
   an image is a block that would otherwise push an inline marker onto its own line. */
.cd-msg-body ol,
.cd-msg-body ul,
.cd-desc-box ol,
.cd-desc-box ul {
    padding-inline-start: 1.5em;
    margin: 6px 0;
}

.cd-msg-body li,
.cd-desc-box li {
    list-style-type: none;
    padding-inline-start: 1.5em;
    position: relative;
}

.cd-msg-body li::before,
.cd-desc-box li::before {
    position: absolute;
    inset-inline-start: 0;
    top: 0;
    width: 1.2em;
    text-align: end;
    white-space: nowrap;
}

.cd-msg-body li[data-list="bullet"]::before,
.cd-desc-box li[data-list="bullet"]::before {
    content: "\2022";
}

.cd-msg-body li[data-list="checked"]::before,
.cd-desc-box li[data-list="checked"]::before {
    content: "\2611";
}

.cd-msg-body li[data-list="unchecked"]::before,
.cd-desc-box li[data-list="unchecked"]::before {
    content: "\2610";
}

.cd-msg-body ol,
.cd-desc-box ol {
    counter-reset: cd-list-num;
}

.cd-msg-body li[data-list="ordered"],
.cd-desc-box li[data-list="ordered"] {
    counter-increment: cd-list-num;
}

.cd-msg-body li[data-list="ordered"]::before,
.cd-desc-box li[data-list="ordered"]::before {
    content: counter(cd-list-num, decimal) ".";
}

/* Quill's editor-only marker handle — nothing to show once the content is rendered. */
.cd-msg-body li > .ql-ui,
.cd-desc-box li > .ql-ui {
    display: none;
}

/* Images stay inside the item's content column (the li's padding), never wider than it,
   and render as blocks so no baseline gap opens underneath them. */
.cd-msg-body img,
.cd-desc-box img {
    display: block;
    max-width: 100%;
    height: auto;
    margin: 6px 0;
}

.cd-msg.internal-note {
    background: rgba(var(--error-color-rgb), .04);
    padding: 12px;
    margin: 4px 0;
    border: 1px dashed rgba(var(--error-color-rgb), .2);
}

.cd-msg.system {
    background: var(--main-background-color-subtle);
    padding: 10px 12px;
    margin: 4px 0;
    border: 1px dashed var(--main-border-color);
}

/* Compact activity feed for sidebar — smaller avatar, tighter spacing */
.cd-activity-compact .cd-msg {
    gap: 8px;
    padding: 8px 0;
}

.cd-activity-compact .cd-msg.system {
    padding: 8px 10px;
    margin: 2px 0;
}

.cd-activity-compact .cd-msg-av {
    width: 24px;
    height: 24px;
    font-size: 11px;
    margin-top: 0;
}

.cd-activity-compact .cd-msg-name {
    font-size: 12px;
}

.cd-activity-compact .cd-msg-role-badge {
    font-size: 10px;
    padding: 1px 5px;
}

.cd-activity-compact .cd-msg-date {
    font-size: 10px;
}

.cd-activity-compact .cd-msg-body {
    font-size: 12px;
    line-height: 1.5;
}

.cd-reply-wrap {
    margin-top: 4px;
    margin-bottom: 24px;
}

.cd-reply-tabs {
    display: flex;
    gap: 0;
    border-bottom: 1px solid var(--main-border-color);
    margin-bottom: 0;
}

.cd-reply-tab {
    padding: 7px 14px;
    font-size: 12px;
    font-weight: var(--font-bold);
    cursor: pointer;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--text-sub);
    transition: all .12s;
    margin-bottom: -1px;
    background: none;
}

/* Stays on the nav pane's active shade. These are sub-tabs inside a control, not a
   page-level tab bar, so they did NOT follow .tab-item.active to brand red — the red
   there is wayfinding for the page's main sections. */
.cd-reply-tab.active {
    color: var(--sidebar-active-text);
    border-bottom-color: var(--sidebar-active-text);
}

.cd-reply-tab:hover:not(.active) {
    color: var(--main-text-color);
}

.cd-reply-box {
    position: relative;
    border: 1px solid var(--main-border-color);
    border-top: none;
    padding: 0 0 12px 0;
    background: var(--main-background-color-subtle);
    width: 100%;
    box-sizing: border-box;
}

.cd-reply-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
    border-top: 1px solid var(--main-border-color);
    padding: 10px 12px 0 12px;
}

.cd-meta-group {
    margin-bottom: 18px;
}

.cd-meta-label {
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--text-sub);
    margin-bottom: 8px;
}

.cd-meta-row {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-bottom: 10px;
}

.cd-meta-key {
    font-size: 11px;
    color: var(--text-sub);
}

.cd-meta-val {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.cd-meta-val.muted {
    font-weight: var(--font-regular);
    color: var(--text-sub);
}

.cd-layout-sidebar .cd-meta-val .dx-selectbox,
.cd-layout-sidebar .cd-meta-group > .dx-selectbox {
    width: 100% !important;
}

.cd-layout-sidebar .cd-meta-group .btn-primary-sh,
.cd-layout-sidebar .cd-meta-group .btn-primary {
    margin-top: 4px;
}

/* ── Case Details sidebar — Customer Satisfaction card ──────────── */
.cd-csat-card {
    padding: 12px 14px;
    background: var(--main-background-color-subtle);
    border: 1px solid var(--main-border-color);
    border-radius: unset;
    margin-bottom: 10px;
}

.cd-csat-stars {
    display: inline-block;
    color: var(--warning-color);
    font-size: 15px;
    letter-spacing: 1px;
}

.cd-csat-word {
    display: inline-block;
    margin-left: 6px;
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.cd-csat-quote {
    margin-top: 8px;
    font-size: 12px;
    font-style: italic;
    color: var(--text-sub);
    line-height: 1.4;
}

.cd-csat-date {
    margin-top: 6px;
    font-size: 11px;
    color: var(--text-sub);
}

/* Action bars (employee/customer/partner) */
.cd-emp-actions,
.cd-user-actions,
.cd-partner-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 20px;
    margin-bottom: 20px;
    flex-wrap: wrap;
}

/* ── Case Details — "User Actions" CTA (Provide Feedback), styled like cd-emp-actions;
   only the button needs pushing to the far right. ──────────────────────────────── */
.cd-user-actions-btn-wrap {
    margin-left: auto;
    display: flex;
    gap: 8px;
    flex-shrink: 0;
}

.cd-emp-actions {
    background: rgba(var(--main-color-rgb), .07);
    border: 1px solid rgba(var(--main-color-rgb), .15);
}


.cd-emp-field {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    flex-wrap: nowrap;
}

.cd-emp-field > .cd-emp-actions-field-label {
    white-space: nowrap;
}

.cd-emp-actions-cta {
    margin-left: auto;
    flex-shrink: 0;
    font-size: 12px;
    padding: 7px 14px;
}

/* ————— Staff Actions below the mobile boundary —————*/
@media (max-width: 600px) {
    .cd-emp-actions {
        flex-direction: column;
        align-items: stretch;
        gap: 10px;
    }

    .cd-emp-field {
        width: 100%;
    }

    .cd-emp-field > .dx-widget {
        flex: 1 1 auto;
        min-width: 0;
        width: 100% !important;
    }

    .cd-emp-actions > button {
        align-self: flex-start;
    }

    .cd-emp-actions > .cd-emp-actions-cta {
        margin-left: 0;
        align-self: flex-end;
    }
}

.cd-partner-actions {
    background: rgba(var(--warning-color-rgb), .10);
    border: 1px solid rgba(var(--warning-color-rgb), .25);
    color: var(--warning-text);
}

.dark-mode .cd-partner-actions {
    background: rgba(var(--warning-color-rgb), .06);
    border-color: rgba(var(--warning-color-rgb), .20);
}

.cd-partner-actions .cd-emp-actions-label {
    color: var(--warning-text);
}
.cd-user-actions {
    background: rgba(45, 143, 210, .15);
    color: var(--blue-link-hover-color);
    border: 1px solid rgba(var(--blue-link-hover-color), .15);
}

.cd-emp-actions-label {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-right: 16px;
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    color: var(--main-color);
    text-transform: uppercase;
    letter-spacing: .04em;
    flex-shrink: 0;
}

.cd-emp-actions-label .svg-icon {
    flex-shrink: 0;
}

/* Scoped override of Style.css's global bare-icon fallback (.svg-icon:not(.nav-icon):not(.ui-icon) > svg
   { width/height: 1.1em }) — bumps just the Staff Actions user-circle icon to 1.5rem instead of
   changing that fallback's size for every other bare <icon> on the site. */
.cd-emp-actions-label .svg-icon:not(.nav-icon):not(.ui-icon) {
    width: 1.5rem;
    height: 1.5rem;
}

.cd-emp-actions-label .svg-icon:not(.nav-icon):not(.ui-icon) > svg {
    width: 1.5rem;
    height: 1.5rem;
}

/* Case Actions (the customer-facing bar, as opposed to Staff/Partner Actions) uses the same
   blue as .pill-blue instead of the shared bar label's --main-color, so it doesn't read as an
   employee-only control. The icon inherits this via currentColor, no separate override needed. */
#case-user-actions-banner .cd-emp-actions-label {
    color: var(--blue-link-hover-color);
}

/* ── Confirm / discard pair for a pending Status or Assign change ─────────────────
   Both of those saves are one-way and visible to the customer, so the pick is staged
   and only committed on the tick — the cross puts the dropdown back. Appears inline
   beside the dropdown it belongs to (created by createCaseConfirmControls in
   Cases/Details.cshtml), and only while a change is actually pending. */
.cd-confirm-actions {
    display: none;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
}

.cd-confirm-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--main-border-color);
    background: var(--main-background-color);
    cursor: pointer;
    transition: background .12s, border-color .12s, color .12s;
}

.cd-confirm-btn .svg-icon,
.cd-confirm-btn .svg-icon > svg {
    width: 15px;
    height: 15px;
}

.cd-confirm-yes {
    color: var(--success-color-dark);
}

.cd-confirm-yes:hover {
    background: rgba(var(--success-color-rgb), .15);
    border-color: var(--success-color);
}

.cd-confirm-no {
    color: var(--error-text);
}

.cd-confirm-no:hover {
    background: rgba(var(--error-color-rgb), .12);
    border-color: var(--error-color);
}

.cd-emp-select {
    padding: 5px 10px;
    border: 1px solid var(--main-border-color);
    font-size: 12px;
    background: var(--main-background-color);
    color: var(--main-text-color);
    outline: none;
    cursor: pointer;
}

.cd-emp-select:focus { border-color: var(--main-color); }

/* 2-column case-detail layout — matches prototype cd-body / cd-main / cd-sidebar */
.cd-layout {
    display: grid;
    grid-template-columns: 1fr 268px;
    align-items: start;
}

.cd-layout-main {
    background: var(--main-background-color);
    padding: 28px 28px 40px;
    min-width: 0;
}

.cd-layout-sidebar {
    align-self: stretch;
    background: var(--main-background-color-subtle);
    border-left: 1px solid var(--main-border-color);
    padding: 28px 20px 40px;
}

/* Opens the meta drawer below 600px. Hidden above it, where the sidebar is a
   docked column and there is nothing to open. */
#cdSidebarToggle {
    display: none;
    align-items: center;
    gap: 5px;
    flex-shrink: 0;
    padding: 6px 10px;
    border: 1px solid var(--main-border-color);
    border-radius: unset;
    background: var(--main-background-color-subtle);
    color: var(--text-sub);
    font-family: inherit;
    font-size: 12px;
    font-weight: var(--font-semi-bold);
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
}

#cdSidebarToggle.open {
    border-color: var(--main-color);
    background: var(--main-color-highlight);
    color: var(--brand-text);
}

#cdSidebarBackdrop {
    display: none;
}

/* ───── Case detail: the meta sidebar becomes a fly-out─────
   Below 600px the docked column would take the whole width, and moving it under
   the conversation buries the discussion behind a long scroll. It slides in over
   the detail pane instead, opened from the Details button in .cd-topbar. The
   backdrop is a sibling inside .cd-layout rather than fixed to the viewport, so
   it dims only the detail pane. transform (not right/left) keeps the slide
   composited. */
@media (max-width: 600px) {
    /* Stretches .cd-layout to the full remaining viewport height, so a short main
       column (e.g. Projects' task list) still gives the drawer a full-height sidebar. */
    .cd-page {
        display: flex;
        flex-direction: column;
        min-height: calc(100vh - var(--topbar-h));
    }

    .cd-layout {
        grid-template-columns: 1fr;
        position: relative;
        margin: 0;
        flex: 1;
        /* The sidebar's closed-state translateX(100%) sticks out past this container's edge;
           without clipping it, that reach becomes real scrollable page width. */
        overflow-x: hidden;
    }

    .cd-layout-main {
        padding: 16px 16px 32px;
    }

    .cd-layout-sidebar {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        z-index: 10;
        width: 85%;
        max-width: 300px;
        border-left: 1px solid var(--main-border-color);
        box-shadow: -4px 0 24px rgba(0, 0, 0, .18);
        overflow-y: auto;
        padding: 16px 16px 32px;
        transform: translateX(100%);
        transition: transform .25s cubic-bezier(.4, 0, .2, 1);
    }

    .cd-layout-sidebar.cd-sidebar-open {
        transform: translateX(0);
    }

    #cdSidebarBackdrop {
        position: absolute;
        inset: 0;
        z-index: 9;
        display: none;
        background: rgba(0, 0, 0, .25);
        cursor: pointer;
    }

    #cdSidebarBackdrop.open {
        display: block;
    }

    #cdSidebarToggle {
        display: inline-flex;
    }

    .cd-topbar {
        padding: 10px 16px;
        gap: 8px;
    }

    .cd-topbar-title {
        font-size: 13px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .cd-layout-sidebar {
        transition: none;
    }
}

/* ── Inline loading spinner ─────────────────────────────────────── */
.cs-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 40px 20px;
    color: var(--text-sub);
    font-size: 13px;
}

/* The ring spinner (.cs-spinner / .cs-spinner-sm / @keyframes cs-spin) was
   removed: the app now has one loader, the 5-dot .cd-spinner below. Every
   .cs-loading block still works — it just holds dots instead of a ring. */

/* Five-dot "Lumia style" loader — a row of brand-red dots pulsing left-to-right,
   evoking the classic Windows Phone progress indicator. Configuration-only
   (Feature Flags / Roles / Permissions / Notifications): markup is 5 child
   `<span class="dot">` elements (see csSpinnerDotsHtml() in utils.js for the
   JS-built button/list variants, and the static .cs-loading blocks in the
   Configuration views for the ones authored directly in Razor). */
.cd-spinner {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    flex-shrink: 0;
}

.cd-spinner .dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--main-color);
    animation: cs-dot-pulse 1.2s ease-in-out infinite;
}

.cd-spinner .dot:nth-child(1) { animation-delay: 0s; }
.cd-spinner .dot:nth-child(2) { animation-delay: .12s; }
.cd-spinner .dot:nth-child(3) { animation-delay: .24s; }
.cd-spinner .dot:nth-child(4) { animation-delay: .36s; }
.cd-spinner .dot:nth-child(5) { animation-delay: .48s; }

@keyframes cs-dot-pulse {
    0%, 80%, 100% { opacity: .25; transform: scale(.6); }
    40% { opacity: 1; transform: scale(1); }
}

/* Small spinner variant for inline use inside buttons (Save/Apply while a
   request is in flight) */
.cd-spinner-sm {
    gap: 3px;
    vertical-align: 1px;
    margin-right: 8px;
}

.cd-spinner-sm .dot {
    width: 4px;
    height: 4px;
}

/* ── Skeleton placeholders ──────────────────────────────────────────
   Opt-in, and deliberately NOT a replacement for .cd-spinner: the one-loader rule
   above still stands for anything that blocks a control or a whole panel. A
   skeleton earns its place only where the shape of what is coming is already
   known — a card whose rows/labels are fixed and whose data arrives by fetch —
   because then it says *what* is loading instead of only *that* something is.

   Markup: <div class="skeleton-line"></div> repeated, optionally with .w60/.w40 to
   vary line length so it reads as text rather than as bars. Remove the nodes when
   the data lands; do not leave them shimmering under real content.

   This is the only looping animation in the app outside .cd-spinner, and it is
   allowed to loop because it is guaranteed to be removed. */
.skeleton-line {
    height: 12px;
    background: var(--gray-bg);
    margin-bottom: 8px;
    position: relative;
    overflow: hidden;
}

.skeleton-line.w60 { width: 60%; }
.skeleton-line.w40 { width: 40%; }
.skeleton-line:last-child { margin-bottom: 0; }

.skeleton-line::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba(var(--main-color-rgb), .18), transparent);
    animation: skeleton-shimmer 1.1s linear infinite;
}

@keyframes skeleton-shimmer {
    from { transform: translateX(-100%); }
    to { transform: translateX(100%); }
}

/* Sidebar Status/Assigned Engineer loaders replace a pill/avatar row, so they
   need to be centered in that row rather than left-aligned like the default
   inline spinner. */
#case-status-sidebar-loading,
#case-assigned-to-sidebar-loading {
    width: 100%;
    justify-content: center;
    margin-right: 0;
}

/* In-button spinner (set by setButtonLoading): dots take the button's own text
   colour so they stay visible on filled brand-red buttons, where the default
   --main-color dots would be red-on-red and effectively invisible. */
.cd-spinner-btn .dot {
    background: currentColor;
}

/* Full-page centred dot loader (togglePageLoader in utils.js). */
#appPageLoader {
    position: fixed;
    inset: 0;
    z-index: 100000;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, .35);
}

    #appPageLoader.active {
        display: flex;
    }

    #appPageLoader .cd-spinner .dot {
        width: 10px;
        height: 10px;
    }

/* List row while what it opens is loading (setRowLoading in utils.js). Only the
   clicked row changes — dimming the whole list would flash on every click — and
   pointer-events:none keeps an impatient second click from firing again. */
.row-loading {
    pointer-events: none;
    opacity: .6;
}

/* Centred variant (setRowLoading's `centered` mode): the overlay brings its own 40%
   scrim, so the dim above would double up and grey the row out twice. */
.row-loading-centered {
    opacity: 1;
}

/* Table rows use the centred variant too (support cases and invoices on the customer
   page). The overlay is appended into the row's first cell — see rowOverlayHost in
   utils.js — so the row has to be the positioned ancestor, and the cell must not be,
   or inset:0 would collapse the dots into that one column. */
tr.row-loading-centered {
    position: relative;
}

/* A positioned row paints above the table's collapsed borders, so its opaque row
   background would erase the separator lines above and below it. The selector must
   out-rank the `.role-table tbody tr` background rules. */
table.role-table tbody tr.row-loading-centered {
    background: transparent;
}

    tr.row-loading-centered > td,
    tr.row-loading-centered > th {
        position: static;
        overflow: visible;
    }

    /* Collapsed borders sit on the row's edges, so a full-inset scrim hides the separator lines. */
    tr.row-loading-centered > td > .cd-loading-overlay,
    tr.row-loading-centered > th > .cd-loading-overlay {
        top: 1px;
        bottom: 1px;
    }

/* The chevron slot becomes the spinner, so it needs flex to centre the dots and a
   floor width to stop the row's right edge jumping as they swap in.

   Scoped to the NON-centred mode, which is the only one that swaps the chevron out.
   Unscoped it also hit centred-mode rows (company search), where the dots are an
   overlay and the chevron stays put — so the 34px floor reserved space for a spinner
   that never arrived, widening a ~9px slot the moment the row was clicked and shoving
   the rest of the flex row left. That was the row visibly shifting on click. */
.row-loading:not(.row-loading-centered) .cs-row-arrow {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    min-width: 34px;
}

/* Grid tiles have no chevron — the dots are appended and pinned to the corner
   so the tile keeps its height while it loads. */
.row-loading-dots {
    display: inline-flex;
    align-items: center;
    margin-left: auto;
}

.alpha-card.row-loading {
    position: relative;
}

    .alpha-card.row-loading .row-loading-dots {
        position: absolute;
        top: 10px;
        right: 12px;
        margin-left: 0;
    }

/* Button while its request is in flight (set/cleared by setBtnLoading in views) */
.btn-loading {
    pointer-events: none;
    opacity: .7;
}

.search-hero-bar,
.search-bar-row {
    display: flex;
    align-items: center;
    gap: 10px;
    max-width: 620px;
    margin: 0 auto;
    padding: 8px 14px;
    border: 1.5px solid var(--main-border-color);
    background: var(--main-background-color);
    transition: border-color .15s, box-shadow .15s;
}

.search-hero-bar:focus-within,
.search-bar-row:focus-within {
    border-color: var(--main-color);
    box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .1);
}

.search-hero-input,
.search-bar-row input[type=text] {
    flex: 1;
    border: none;
    outline: none;
    font-size: 14px;
    background: transparent;
    color: var(--main-text-color);
}

.search-hero-btn {
    padding: 8px 18px;
    background: var(--main-color);
    color: var(--white-color);
    border: none;
    font-size: 13px;
    font-weight: var(--font-bold);
    cursor: pointer;
}

.search-hero-btn:hover {
    background: var(--main-color-dark);
}

/* ── Service card (system health) ───────────────────────────────── */
.service-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

.service-card {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    min-width: 0;
    padding: 16px 18px;
}

.service-card-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 12px;
}

.service-name {
    font-size: 14px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
}

.service-name span {
    font-size: 11px;
    color: var(--text-sub);
    font-weight: var(--font-regular);
    margin-left: 4px;
}

/* Drawn as SVG, not the 🇺🇸/🇨🇦 emoji: Windows' emoji font renders those regional-indicator
   pairs as plain "US"/"CA" letters instead of a flag. */
.flag {
    display: inline-block;
    width: 14px;
    height: 10px;
    margin-left: 6px;
    vertical-align: middle;
    background-size: cover;
    background-position: center;
}

.flag-us {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 10'%3E%3Crect width='20' height='10' fill='%23fff'/%3E%3Cg fill='%23B22234'%3E%3Crect y='0' width='20' height='.77'/%3E%3Crect y='1.54' width='20' height='.77'/%3E%3Crect y='3.08' width='20' height='.77'/%3E%3Crect y='4.62' width='20' height='.77'/%3E%3Crect y='6.15' width='20' height='.77'/%3E%3Crect y='7.69' width='20' height='.77'/%3E%3Crect y='9.23' width='20' height='.77'/%3E%3C/g%3E%3Crect width='8' height='5.38' fill='%233C3B6E'/%3E%3C/svg%3E");
}

.flag-ca {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 10'%3E%3Crect width='20' height='10' fill='%23fff'/%3E%3Crect width='5' height='10' fill='%23d52b1e'/%3E%3Crect x='15' width='5' height='10' fill='%23d52b1e'/%3E%3Cpolygon points='10,2 10.6,3.5 12.2,2.8 11.6,4.4 13.4,4.7 12,5.8 12.6,7.4 11,6.9 11,8.5 10,7.3 9,8.5 9,6.9 7.4,7.4 8,5.8 6.6,4.7 8.4,4.4 7.8,2.8 9.4,3.5' fill='%23d52b1e'/%3E%3C/svg%3E");
}

.service-meta {
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 2px;
}

.status-dot {
    width: 10px;
    height: 10px;
    display: inline-block;
    flex-shrink: 0;
    margin-top: 4px;
}

.status-dot.operational { background: var(--success-color); }
.status-dot.degraded    { background: var(--warning-color); }
.status-dot.down        { background: var(--error-color); }

.service-metrics {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
    margin-bottom: 10px;
}

.svc-metric {
    background: var(--gray-bg);
    padding: 8px;
    text-align: center;
}

.svc-metric-v {
    font-size: 14px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
}

.svc-metric-l {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .6px;
    color: var(--text-sub);
    margin-top: 2px;
}

.service-card-body {
    border-top: 1px solid var(--main-border-color);
    padding-top: 10px;
}

/* Health overview row */
.health-overview {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px;
    margin-bottom: 20px;
}

.health-ov-card {
    padding: 16px;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-top: 3px solid var(--main-color);
    text-align: center;
}

.health-ov-card.green  { border-top-color: var(--success-color); }
.health-ov-card.orange { border-top-color: var(--warning-color); }
.health-ov-card.red    { border-top-color: var(--error-color); }
.health-ov-card.blue   { border-top-color: var(--blue-color); }

/* System Health cards, dark side: the side navigation's grey (--sidebar-bg, #2A2E33)
   instead of the #141414 card colour, which sat only a shade off the page and let the
   cards blend into it. Light mode keeps its white cards. Covers every card on the
   page: overview stats, per-service health, Azure storage, and service overrides. */
.dark-mode .health-ov-card,
.dark-mode .service-card,
.dark-mode .az-stor-card,
.dark-mode .sho-card {
    background: var(--sidebar-bg);
}

.hov-num {
    font-size: 24px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
    line-height: 1.1;
}

.hov-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .6px;
    color: var(--text-sub);
    font-weight: var(--font-bold);
    margin-top: 4px;
}

.uptime-bar {
    height: 30px;
    margin-top: 4px;
}

.svc-uptime-label {
    margin-bottom: 6px;
    font-size: 11px;
    color: var(--text-sub);
}

/* ── Inline notice (_ServiceUnavailableInline) ──────────────────────
   Panel-level "this data couldn't load" strip. Shares the .svc-banner
   treatment below so a failed panel and a failed service read the same. */
.inline-notice {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 12px 14px;
    border: 1px solid;
    border-left-width: 3px;
    border-radius: 0;
    font-size: 13px;
}

.inline-notice-dot {
    width: 6px;
    height: 6px;
    flex-shrink: 0;
    transform: translateY(-2px);
}

.inline-notice-body {
    min-width: 0;
}

.inline-notice-title {
    font-weight: var(--font-bold);
    line-height: 1.5;
    overflow-wrap: break-word;
}

.inline-notice-link {
    display: inline-block;
    margin-top: 4px;
    font-size: 12px;
    font-weight: var(--font-bold);
    color: inherit;
    text-decoration: underline;
}

/* Body text in the normal text colour: the warning fill (#F78B37) measures 2.4:1
   as text. The rail, dot and wash carry the state, as on .notice. */
.inline-notice-warn {
    color: var(--main-text-color);
    background: rgba(var(--warning-color-rgb), .08);
    border-color: rgba(var(--warning-color-rgb), .35);
}

.inline-notice-warn .inline-notice-dot {
    background: var(--warning-color);
}

/* Incident note on a degraded/down service card. Square corners, a solid status
   rail on the leading edge and a square dot marker — same vocabulary as the
   status pills, rather than the emoji this used to lead with. */
.svc-banner {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 12px;
    line-height: 1.5;
    padding: 8px 10px;
    margin-top: 10px;
    border: 1px solid;
    border-left-width: 3px;
    border-radius: 0;
}

.svc-banner-dot {
    width: 6px;
    height: 6px;
    flex-shrink: 0;
    /* Nudge onto the first line's optical centre, since the row is baseline-aligned
       so long messages wrap tidily under themselves. */
    transform: translateY(-2px);
}

.svc-banner-text {
    min-width: 0;
    overflow-wrap: break-word;
}

.svc-banner-degraded {
    color: var(--warning-color);
    background: rgba(var(--warning-color-rgb), .08);
    border-color: rgba(var(--warning-color-rgb), .35);
}

.svc-banner-degraded .svc-banner-dot {
    background: var(--warning-color);
}

.svc-banner-down {
    color: var(--error-text);
    background: rgba(var(--error-color-rgb), .08);
    border-color: rgba(var(--error-color-rgb), .35);
}

.svc-banner-down .svc-banner-dot {
    background: var(--error-color);
}

.az-section,
.sho-section {
    margin-top: 20px;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    overflow: hidden;
}

/* ── Service health override cards ──────────────────────────────────
   Status-accented card: coloured rail on the left, name + status pill on the
   first row, then the override value, optional note, enabled toggle and the
   edit/delete pair. Square corners throughout, matching the rest of the app. */
.sho-card {
    position: relative;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-left: 4px solid var(--main-border-color);
    padding: 16px 18px;
}

.sho-card-available   { border-left-color: var(--success-color); }
.sho-card-degraded    { border-left-color: var(--warning-color); }
.sho-card-unavailable { border-left-color: var(--error-color); }

.sho-card-hdr {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

@media (max-width: 600px) {
    .sho-card-hdr {
        flex-direction: column;
        align-items: flex-start;
        gap: 6px;
    }
}

.sho-card-name {
    font-size: 16px;
    font-weight: var(--font-extra-bold);
    min-width: 0;
    color: var(--main-text-color);
    word-break: break-word;
}

.sho-pill {
    border: var(--tag-border);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    padding: 4px 10px;
    font-size: 12px;
    font-weight: var(--font-regular);
    border-radius: 0;
}

.sho-pill-dot {
    width: 6px;
    height: 6px;
}

.sho-pill-available {
    background: var(--tag-green-bg);
    color: var(--tag-green-fg);
}

.sho-pill-available .sho-pill-dot { background: var(--success-color); }

.sho-pill-degraded {
    background: var(--tag-orange-bg);
    color: var(--tag-orange-fg);
}

.sho-pill-degraded .sho-pill-dot { background: var(--warning-color); }

.sho-pill-unavailable {
    background: var(--tag-red-bg);
    color: var(--tag-red-fg);
}

.sho-pill-unavailable .sho-pill-dot { background: var(--error-color); }

.sho-card-id {
    margin-top: 4px;
    font-size: 12px;
    color: var(--text-sub);
}

.sho-card-override {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--main-border-color);
    font-size: 13px;
}

.sho-override-label {
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.sho-override-value {
    font-weight: var(--font-bold);
}

.sho-override-value-available   { color: var(--success-color-dark); }
.sho-override-value-degraded    { color: var(--warning-color); }
.sho-override-value-unavailable { color: var(--error-text); }

.sho-card-desc {
    margin-top: 10px;
    font-size: 12px;
    color: var(--text-sub);
}

.sho-card-created {
    margin-top: 10px;
    font-size: 11px;
    font-style: italic;
    color: var(--text-sub);
}

/* Sits at the right end of the override row, using the same .toggle slider the
   Create/Edit dialog uses so the control reads identically in both places. */
.sho-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 0 auto;
    flex-shrink: 0;
    font-weight: var(--font-bold);
}

.sho-toggle-text {
    font-size: 12px;
    line-height: 1;
    color: var(--text-sub);
    white-space: nowrap;
}

.sho-toggle.on .sho-toggle-text {
    color: var(--main-text-color);
}

/* Both actions sit at the right of the card and share a min-width so they read as
   a matched pair rather than one stretching to fill the row. */
.sho-card-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 12px;
}

.sho-btn {
    flex: 0 0 auto;
    min-width: 78px;
    padding: 6px 14px;
    font-size: 12px;
    font-family: inherit;
    font-weight: var(--font-bold);
    text-align: center;
    white-space: nowrap;
    border-radius: 0;
    cursor: pointer;
}

.sho-btn-edit {
    background: var(--gray-bg);
    border: 1px solid var(--main-border-color);
    color: var(--main-text-color);
}

.sho-btn-edit:hover {
    background: var(--main-border-color);
}

.sho-btn-delete {
    background: rgba(var(--error-color-rgb), .06);
    border: 1px solid rgba(var(--error-color-rgb), .35);
    color: var(--error-text);
}

.sho-btn-delete:hover {
    background: rgba(var(--error-color-rgb), .14);
}

.sho-empty {
    grid-column: 1 / -1;
    font-size: 13px;
    font-style: italic;
    color: var(--text-sub);
}

.az-section-hdr {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--main-border-color);
}

.az-hdr-left {
    flex: 1 1 260px;
    min-width: 0;
}

.az-section-title {
    font-size: 16px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
}

.az-section-meta {
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 2px;
}

.az-section-body {
    padding: 18px 20px;
}

.az-stor-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 12px;
}

.az-stor-card {
    display: flex;
    flex-direction: column;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    padding: 16px 18px;
}

/* Name left, status pill right — same header as .sho-card-hdr on the override
   cards. flex-start rather than center because container names wrap to two lines. */
.az-stor-hdr {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}

@media (max-width: 600px) {
    .az-stor-hdr {
        flex-direction: column;
        align-items: flex-start;
        gap: 6px;
    }
}

.az-stor-pill {
    border: var(--tag-border);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    padding: 3px 9px;
    font-size: 11px;
    font-weight: var(--font-regular);
    white-space: nowrap;
    border-radius: 0;
}

.az-stor-pill-dot {
    width: 6px;
    height: 6px;
}

.az-stor-pill-available {
    background: var(--tag-green-bg);
    color: var(--tag-green-fg);
}

.az-stor-pill-available .az-stor-pill-dot { background: var(--success-color); }

.az-stor-pill-unavailable {
    background: var(--tag-red-bg);
    color: var(--tag-red-fg);
}

.az-stor-pill-unavailable .az-stor-pill-dot { background: var(--error-color); }

.az-stor-pill-deleted {
    background: rgba(var(--warning-color-rgb), .18);
    color: var(--warning-color);
}

.az-stor-pill-deleted .az-stor-pill-dot { background: var(--warning-color); }

.az-stor-name {
    font-size: 14px;
    font-weight: var(--font-semi-bold);
    line-height: 1.4;
    min-width: 0;
    color: var(--main-text-color);
    /* break-word, not break-all: keeps "connect-files (East US)" readable and only
       wraps the long account name when it genuinely has to. */
    overflow-wrap: break-word;
}

.az-stor-sub {
    font-size: 11px;
    color: var(--text-sub);
    margin-bottom: 8px;
}

/* Hairline between the name and the detail rows, as in the reference layout. */
.az-stor-detail {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--main-border-color);
    font-size: 12px;
    color: var(--text-sub);
}

.az-stor-row {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 8px;
}

.az-stor-row:last-child {
    margin-bottom: 0;
}

/* Timestamps are long enough that label and value read better stacked. */
.az-stor-row-stacked {
    flex-direction: column;
    gap: 2px;
}

.az-stor-row-l {
    color: var(--text-sub);
}

.az-stor-row-v {
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.az-stor-row-warn {
    color: var(--warning-color);
    font-weight: var(--font-bold);
}

.az-stor-row-error {
    color: var(--error-text);
}

.az-stor-pub-blob {
    color: var(--warning-color);
    font-weight: var(--font-bold);
}

/* Chevron that shows/hides a section body. Points down while expanded and flips
   to point up when collapsed. */
.az-collapse-btn {
    background: none;
    border: none;
    padding: 4px 6px;
    font-size: 11px;
    line-height: 1;
    color: var(--text-sub);
    cursor: pointer;
    flex-shrink: 0;
    transition: transform .22s;
}

.az-collapse-btn:hover {
    color: var(--main-text-color);
}

.az-collapse-btn.collapsed {
    transform: rotate(180deg);
}

.az-section-body.collapsed {
    display: none;
}

.az-hdr-right {
    display: flex;
    align-items: center;
    gap: 10px;
}

@media (max-width: 900px) {
    .az-hdr-left {
        flex: 1 1 auto;
    }

    .az-section-hdr > .az-collapse-btn {
        align-self: flex-start;
    }

    .az-hdr-right {
        order: 1;
        width: 100%;
    }

}

@media (max-width: 600px) {
    .az-hdr-right > .btn-primary-sh {
        flex: 1 1 auto;
    }
}

@media (max-width: 600px) {
    .az-collapse-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 40px;
        height: 40px;
        padding: 0;
    }

    .sho-btn {
        min-height: 40px;
    }
}

.az-fn-summary {
    background: var(--main-background-color-subtle);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    padding: 12px 16px;
    font-size: 13px;
    margin-bottom: 14px;
}

.az-fn-summary strong {
    display: block;
    margin-bottom: 6px;
}

.az-fn-ok-line {
    color: var(--main-text-color);
    line-height: 1.6;
    display: flex;
    align-items: center;
    gap: 6px;
}

.az-fn-fail-line {
    color: var(--error-text);
    font-weight: var(--font-bold);
    line-height: 1.6;
    display: flex;
    align-items: center;
    gap: 6px;
}

.az-fn-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 12px;
}

.az-fn-card {
    background: var(--main-background-color-subtle);
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    padding: 12px 14px;
    text-decoration: none;
    color: var(--main-text-color);
    display: block;
    transition: box-shadow .15s, border-color .15s;
}

.az-fn-card:hover {
    box-shadow: var(--shadow-sm);
    border-color: rgba(var(--main-color-rgb), .35);
    color: var(--main-text-color);
}

.az-fn-card-name {
    font-size: 13px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
    margin-bottom: 6px;
    word-break: break-all;
}

.az-fn-row {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-sub);
    line-height: 1.5;
}

.az-fn-row .ico {
    font-size: 13px;
    flex-shrink: 0;
}

.service-status-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
    gap: 20px;
    padding: 12px;
    background-color: var(--main-background-color);
}

.az-section-body > .service-status-grid {
    padding: 0;
}

.az-collapse-btn:focus-visible,
.sho-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .35);
}

.az-fn-card:focus-visible {
    outline: none;
    border-color: var(--main-color);
    box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .35);
}

@media (max-width: 600px) {
    .service-status-grid,
    .az-fn-grid,
    .az-stor-grid {
        grid-template-columns: 1fr;
    }
}

/* ── Modal ──────────────────────────────────────────────────────── */
/* Overlay covers only the main panel (right of sidebar, below topbar,
   above the impersonation banner) so the dialog centers inside the
   workspace and the global nav stays visible / interactive. */
.modal-overlay {
    position: fixed;
    top: var(--topbar-h);
    left: var(--sidebar-w);
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 5000;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

body.impersonating .modal-overlay {
    bottom: var(--imp-banner-h);
}

/* Fixed anchor every dxPopup centers/shades against — matches .modal-overlay's box. */
#dx-popup-anchor {
    position: fixed;
    top: var(--topbar-h);
    left: var(--sidebar-w);
    right: 0;
    bottom: 0;
    pointer-events: none;
    z-index: 5000; /* position:fixed + z-index:auto would stack by DOM order and lose to later content */
}

body.impersonating #dx-popup-anchor {
    bottom: var(--imp-banner-h);
}

@media (max-width: 900px) {
    #dx-popup-anchor {
        left: 0;
        top: 0;
    }
}

#dx-popup-anchor .dx-overlay-content {
    max-width: 96vw;
    max-height: min(90vh, 100%);
}

@media (max-width: 900px) {
    /* Sidebar collapses on narrow screens; let the overlay fill the viewport. */
    .modal-overlay {
        left: 0;
        top: 0;
    }
}

/* Full-screen pages have no sidebar/topbar, so shade the whole viewport. */
body.fullscreen-page .modal-overlay,
body.fullscreen-page #dx-popup-anchor {
    top: 0;
    left: 0;
}

.modal-overlay.open {
    display: flex;
}

/* ── Dialog entrance ─────────────────────────────────────────────────
   A dialog used to appear between two frames, which reads as the page having
   jumped rather than as something opening. The backdrop fades in and the dialog
   settles the last few pixels into place behind it, so the eye follows it in.

   Animation, not transition, and with no fill: the dialog holds no transform at
   rest, so it never becomes the containing block for the position:fixed layers
   inside it (DevExtreme dropdowns and date pickers opened from dialog fields).
   Both replay whenever .open is set again, which is what re-opening should do.

   Three dialog shells, not one. Most dialogs are .modal-overlay > .modal, but the
   Settings dialog wraps its shell in the profile <form> (.modal-overlay > form >
   .pm-modal) and the product Patches dialog uses .patches-modal — neither carries
   .modal, so both used to open with no entrance at all while every dialog around
   them faded in. They are listed explicitly rather than matched with a descendant
   selector so a future dialog nested inside one of these cannot animate twice. */
@media (prefers-reduced-motion: no-preference) {
    .modal-overlay.open {
        animation: dlg-backdrop-in var(--dur-base) var(--ease-out);
    }

    .modal-overlay.open > .modal,
    .modal-overlay.open > form > .pm-modal,
    .modal-overlay.open > .patches-modal {
        animation: dlg-in 220ms var(--ease-out);
    }

    @keyframes dlg-backdrop-in {
        from {
            opacity: 0;
        }

        to {
            opacity: 1;
        }
    }

    @keyframes dlg-in {
        from {
            opacity: 0;
            /* Scale stays within a couple of percent — enough to give the dialog a
               sense of arriving, small enough that text never visibly resamples. */
            transform: translateY(10px) scale(.98);
        }

        to {
            opacity: 1;
            transform: translateY(0) scale(1);
        }
    }

    /* ── Dialog exit ─────────────────────────────────────────────────
       Closing is a `.open` flip spread over ~60 call sites (17 classList, 43
       jQuery removeClass), so an exit could not be written at the call sites
       without touching every one of them and risking a dialog that never shuts.
       Instead motion.js watches .modal-overlay for the class leaving and stamps
       .is-closing for the length of this animation, which is the one place the
       behaviour lives. The rules below are what that class buys.

       Faster than the entrance and accelerating out: a dismissal the user has
       already decided on should get out of the way, not be watched. pointer-events
       is off throughout, so the 150ms the overlay lingers can never eat a click
       meant for the page underneath. */
    .modal-overlay.is-closing {
        display: flex;
        pointer-events: none;
        animation: dlg-backdrop-out 150ms var(--ease-out) forwards;
    }

    .modal-overlay.is-closing > .modal,
    .modal-overlay.is-closing > form > .pm-modal,
    .modal-overlay.is-closing > .patches-modal {
        animation: dlg-out 150ms var(--ease-out) forwards;
    }

    /* `forwards` is safe here where it is not on the entrance: .is-closing is
       removed the moment the animation ends, taking the held transform with it,
       so nothing is left behind to act as a containing block. */
    @keyframes dlg-backdrop-out {
        to {
            opacity: 0;
        }
    }

    @keyframes dlg-out {
        to {
            opacity: 0;
            transform: translateY(4px) scale(.98);
        }
    }
}

/* ── Dialog scroll lock ──────────────────────────────────────────────
   While a dialog is open the page behind it must not scroll*/
body.modal-scroll-lock,
body:has(.dx-overlay-shader:not(.dx-state-invisible)) {
    overflow: hidden;
}

/* Keeping the scrollbar's gutter stops the fixed topbar and footer shifting when the lock
   hides it; browsers without scrollbar-gutter fall back to padding the body only. */
@supports (scrollbar-gutter: stable) {
    html:has(body.modal-scroll-lock, .dx-overlay-shader:not(.dx-state-invisible)) {
        scrollbar-gutter: stable;
    }
}

@supports not (scrollbar-gutter: stable) {
    body.modal-scroll-lock {
        padding-right: var(--modal-scrollbar-gap, 0px);
    }
}

/* Scope to .modal-overlay > .modal so this rule beats Bootstrap 5's
   global .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100% }
   reset — without these overrides our modal would render as a left-anchored
   full-height strip instead of centering inside the overlay. */
.modal-overlay > .modal {
    position: relative;
    top: auto;
    left: auto;
    right: auto;
    bottom: auto;
    margin: auto;
    background: var(--dialog-bg);
    border-radius: var(--radius-lg);
    width: 560px;
    max-width: 96vw;
    height: auto;
    /* 90vh is measured against the VIEWPORT, but this overlay is inset from it — below the
       topbar, and above the impersonation banner while one is up. On a short screen (or any
       screen while impersonating) a 90vh-tall dialog therefore overflowed its own overlay and
       slid under the banner, taking the footer buttons with it. 100% is the overlay's real
       height, so the dialog can never be taller than the space it is allowed to occupy. */
    max-height: min(90vh, 100%);
    display: flex;
    flex-direction: column;
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    z-index: 1;
}

/* Shown centered over a modal, or a Feature Flags row (not inside a button or
   toggle switch) while its Save/Delete/Apply/Enable request is in flight — see
   toggleLoadingOverlay()/toggleModalLoadingOverlay() in utils.js. The backdrop
   (::before) carries the 40% opacity so the dialog/row shows through behind
   it; the dots are a normal child, painted on top of the backdrop at
   full opacity so the animation itself stays crisp. Requires the container
   this is appended into (.modal, .feature-row) to be position:relative. */
.cd-loading-overlay {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: none;
    align-items: center;
    justify-content: center;
}

.cd-loading-overlay::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--main-background-color);
    opacity: .4;
}

.cd-loading-overlay.active {
    display: flex;
}

/* Host wrapper for a list/table loading overlay: makes the overlay's inset:0
   resolve to the DATA region (table + rows) instead of the whole card, so the
   loader centres over where the rows appear rather than over the filters/header. */
.cd-loading-host {
    position: relative;
}

/* Hold open a band in the data region while loading so the centred loader has
   somewhere to sit even before any rows exist (the tbody is empty on first load). */
.cd-loading-host:has(> .cd-loading-overlay.active) {
    min-height: 240px;
}

/* Fallback for browsers without :has() — release the bottom pin from inset:0 so
   min-height defines a top-anchored band the dots centre within. */
    .cd-loading-host > .cd-loading-overlay.active {
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        height: auto; /* let top/bottom stretch it instead of a fixed height */
    }

.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    /* Brand facet texture — the same fill the page banners carry, so a dialog
       header and a hero band read as the same material. Shared by every dialog
       header. */
    background: var(--df-facet-tile);
    color: var(--white-color);
    border-bottom: none;
    /* Bootstrap's .modal-header rounds the top corners (border-top-*-radius),
       which let the white modal background show through at the tips once the
       header got a solid gradient. Force it square. */
    border-radius: 0;
}

.modal-header h3 {
    font-size: 16px;
    /* !important: the global h3 rule sets font-weight:300 !important, which
       otherwise wins here regardless of this selector's higher specificity —
       an !important rule always beats a non-!important one. */
    font-weight: var(--font-extra-bold) !important;
    color: var(--white-color);
    margin: 0;
    /* Flex so a leading <icon> centres against the title text. As a plain block
       the icon was an inline-flex box aligned on the text baseline, and since
       .svg-icon carries line-height:0 with no text of its own, that baseline is
       synthesised from its bottom edge — which left it sitting high. Flex takes
       baselines out of it entirely. gap (not the markup's literal space, which a
       flex container trims at the start of the anonymous text item) does the
       spacing. */
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

/* ── Outside-click nudge ─────────────────────────────────────────────
   Clicking the backdrop never dismisses a dialog (see the guard in _Layout).
   The shell gives a brief nudge instead, so the click is acknowledged and the
   user is pointed at Cancel/Save. Applied by adding .dialog-nudge to the shell;
   the selector covers every dialog family so the feel is identical app-wide.
   Was Bootstrap's backdrop:'static' scale effect on the one Bootstrap dialog. */
.dialog-nudge {
    animation: dialogNudge .3s ease-out;
}

@keyframes dialogNudge {
    0%   { transform: scale(1); }
    30%  { transform: scale(1.015); }
    100% { transform: scale(1); }
}

/* The custom shells are centred by `margin: auto` inside a flex overlay, so a
   transform is safe. DevExpress positions .dx-overlay-content with an inline
   transform, which animating transform would fight — so those flash an outline
   instead: same timing, no movement. Outline (not box-shadow) because the shared
   shell rule sets `box-shadow: ... !important`, and !important beats an
   animation, which would have made a box-shadow keyframe silently do nothing. */
.dx-overlay-content.dialog-nudge {
    animation: dialogNudgeRing .3s ease-out;
}

@keyframes dialogNudgeRing {
    0%   { outline: 0 solid rgba(var(--main-color-rgb), 0); outline-offset: 0; }
    30%  { outline: 3px solid rgba(var(--main-color-rgb), .45); outline-offset: 0; }
    100% { outline: 3px solid rgba(var(--main-color-rgb), 0); outline-offset: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .dialog-nudge,
    .dx-overlay-content.dialog-nudge {
        animation: none;
    }
}

/* Optional second line under a dialog title, for dialogs whose header carries a
   short explanatory subtitle as well as the title. Part of the shared header so
   those dialogs don't have to re-declare the gradient/padding/close button just
   to get one extra line of text. */
.modal-header-sub {
    font-size: 12px;
    color: rgba(255, 255, 255, .75);
    margin-top: 3px;
}

/* Titles sit optically better top-aligned once there are two lines of text. */
.modal-header:has(.modal-header-sub) {
    align-items: flex-start;
}

/* ── Dialog drag handle ──────────────────────────────────────────────
   The header doubles as the grab handle for modal-drag.js. Gated to the width
   where dragging is actually offered: below 900px the overlay loses its inset
   and dialogs run near-full-screen, so there is nowhere to drag to — and
   touch-action:none down there would eat the touch scroll for no gain. Keep
   this breakpoint in step with MIN_DRAG_WIDTH in modal-drag.js. */
@media (width > 900px) {
    .modal-overlay .modal-header {
        cursor: move;
        /* Stops the browser claiming a touch/pen drag as a pan before the
           pointermove handler ever sees it. */
        touch-action: none;
    }

    /* The controls in the header keep their own affordance — the drag handler
       already declines to start a drag on them. */
    .modal-overlay .modal-header button,
    .modal-overlay .modal-header a {
        cursor: pointer;
    }
}

/* Text across the page would otherwise select as the pointer sweeps over it
   mid-drag. Set on <body> for the duration of the drag only. */
body.dlg-dragging {
    user-select: none;
}

/* modal-drag.js positions a dialog with left/top, which only a positioned element
   honours. .modal-overlay > .modal and .ump-modal already declare position:relative;
   these two were the only shells left static. Relative positioning with no offset
   changes nothing on its own — and it is what .cd-loading-overlay's inset:0 already
   expects of the shell it is appended into (see toggleLoadingOverlay in utils.js),
   which both of these dialogs use. */
.modal-overlay .pm-modal,
.modal-overlay .patches-modal {
    position: relative;
}

.modal-close {
    width: 30px;
    height: 30px;
    border-radius: var(--radius);
    border: none;
    background: rgba(255, 255, 255, 0.12);
    cursor: pointer;
    font-size: 18px;
    color: var(--white-color);
    display: flex;
    align-items: center;
    justify-content: center;
}

.modal-close:hover {
    background: rgba(255, 255, 255, 0.24);
    color: var(--white-color);
}

/* Square every dialog shell so the gradient header fills edge-to-edge (rounded
   shells left white tips at the header corners) and to match the app's
   sharp-corner aesthetic. Covers DevExpress dxPopups AND Bootstrap modals —
   current and future. */
.dx-popup-wrapper > .dx-overlay-content:not(.dx-popup-fullscreen),
.modal-content {
    border-radius: 0 !important;
    overflow: hidden;
}

/* Dialog surface — white (light) / nav-pane #2A2E33 (dark). See the
   --dialog-bg note in Style.css for why this is not --page-bg.
   Excludes dropdown/list overlays (select boxes, autocompletes) — those keep
   their own surface so they don't look tinted. In dark mode that leaves an open
   dropdown at #141414 inside a #2A2E33 dialog, i.e. the same tone as the field
   it drops from: deliberate, so the menu reads as attached to its field. */
/* :not(.dx-tooltip-wrapper) matters more than it looks — a dxTooltip's wrapper
   also carries .dx-popup-wrapper, so without it this rule painted every tooltip
   with the dialog surface. It beat the Tooltips block at the end of this file on
   specificity (five classes to four, both !important), which is why tooltips
   rendered as plain page-coloured boxes instead of the red-tint chip. Excluding
   them here fixes the cause rather than escalating selectors down there. */
.dx-popup-wrapper:not(.dx-dropdowneditor-overlay):not(.dx-dropdownlist-popup-wrapper):not(.dx-tooltip-wrapper) > .dx-overlay-content:not(.dx-popup-fullscreen),
.dx-popup-wrapper:not(.dx-dropdowneditor-overlay):not(.dx-dropdownlist-popup-wrapper):not(.dx-tooltip-wrapper) > .dx-overlay-content .dx-popup-content,
.modal-content {
    background: var(--dialog-bg) !important;
}

/* Auto-injected dialog-title icon (chosen from the dialog name in JS). Inherits
   the header's white text colour via currentColor. */
/* Both title bars are flex containers now, so margin-right/vertical-align (which
   existed for the old inline layout) would double up with the container's gap and
   push the icon off centre. Spacing and centring are the container's job. */
.modal-header .dialog-title-icon,
.dx-popup-title .dialog-title-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

    .modal-header .dialog-title-icon svg,
    .dx-popup-title .dialog-title-icon svg {
        width: 100%;
        height: 100%;
    }

/* Native DevExpress dialog header (dxPopup title bar) — same brand facet texture
   as .modal-header, applied globally so every current AND future dxPopup matches.
   !important guards against the vendor theme's own rules. */
.dx-popup-title {
    background: var(--df-facet-tile) !important;
    color: var(--white-color) !important;
    border-bottom: none !important;
}

.dx-popup-title .dx-toolbar-label,
.dx-popup-title .dx-toolbar-label > div,
.dx-popup-title .dx-toolbar-label .dx-toolbar-item-content > div {
    color: var(--white-color) !important;
}

/* Keep the injected title icon on the same line as the title. DevExpress wraps
   the label text in a block-level <div>, which otherwise pushed the icon onto
   its own row above the text. */
.dx-popup-title .dx-toolbar-label {
    display: flex;
    align-items: center;
    gap: 8px;
}

    .dx-popup-title .dx-toolbar-label > div,
    .dx-popup-title .dx-toolbar-label .dx-toolbar-item-content,
    .dx-popup-title .dx-toolbar-label .dx-toolbar-item-content > div {
        display: inline-flex;
        align-items: center;
    }

/* Vertically centre the title in the bar. align-items:center above was a no-op
   because the label box is only as tall as its text — its position came from the
   vendor's asymmetric toolbar padding, which left the text sitting high. Giving
   the label chain the full bar height is what makes the centring take effect. */
.dx-popup-title .dx-toolbar-items-container,
.dx-popup-title .dx-toolbar-before,
.dx-popup-title .dx-toolbar-label,
.dx-popup-title .dx-toolbar-label .dx-toolbar-item-content {
    height: 100%;
}

.dx-popup-title .dx-toolbar-before {
    display: flex;
    align-items: center;
}

/* vertical-align is meaningless once the label is a flex container (it only
   applied to the inline .modal-header case) and nudged the icon off-centre. */
.dx-popup-title .dialog-title-icon {
    vertical-align: 0;
}

.dx-popup-title .dx-closebutton .dx-icon {
    color: var(--white-color) !important;
}

/* Match .modal-header's title/close-button sizing exactly — the toolbar widget behind
   dx-popup-title otherwise keeps the vendor theme's own (smaller/differently-padded) text
   size and a bare, boxless close icon, which read as visually "off" next to every other
   dialog's header once the background gradient above made the two look related. */
.dx-popup-title {
    padding: 16px 8px 16px 20px !important;
    min-height: 0 !important;
}

.dx-popup-title .dx-toolbar-label,
.dx-popup-title .dx-toolbar-label > div,
.dx-popup-title .dx-toolbar-label .dx-toolbar-item-content > div {
    font-size: 16px !important;
    font-weight: var(--font-extra-bold) !important;
}

.dx-popup-title .dx-closebutton {
    width: 30px;
    height: 30px;
    border-radius: 0 !important;
    background: rgba(255, 255, 255, .12);
}

.dx-popup-title .dx-closebutton.dx-state-hover {
    background: rgba(255, 255, 255, .24);
}

/* ── DevExpress dialog footer buttons ───────────────────────────────
   Match the app's button styling (sharp, app font, sentence case) instead of
   the vendor theme's uppercase flat buttons. Default is the outlined
   secondary; add .dlg-btn-primary via elementAttr for the primary action. */
/* Rest/hover mirror .btn-ghost-sh in a .modal-footer exactly — transparent on the
   dialog surface, lifting to --gray-bg on hover — which is what this block set out
   to do. The old rest fill was --main-background-color, i.e. #141414 in dark, a
   filled dark slab where the custom dialogs' Cancel is an outline. */
.dx-popup-bottom .dx-button {
    border-radius: 0;
    font-family: var(--font);
    background: transparent;
    border: 1px solid var(--dialog-field-border);
}

    .dx-popup-bottom .dx-button .dx-button-text {
        text-transform: none;
        font-weight: var(--font-bold);
        font-size: 13px;
        color: var(--main-text-color);
    }

    .dx-popup-bottom .dx-button .dx-icon {
        color: var(--text-sub);
    }

    .dx-popup-bottom .dx-button:hover {
        background: var(--gray-bg);
        border-color: var(--text-sub);
    }

    .dx-popup-bottom .dx-button.dlg-btn-primary {
        background: var(--main-color);
        border-color: var(--main-color);
    }

        .dx-popup-bottom .dx-button.dlg-btn-primary .dx-button-text,
        .dx-popup-bottom .dx-button.dlg-btn-primary .dx-icon {
            color: var(--white-color);
        }

        .dx-popup-bottom .dx-button.dlg-btn-primary:hover {
            background: var(--main-color-bright);
            border-color: var(--main-color-bright);
        }

/* ── Notification popup footer buttons ──────────────────────────────
   Targeted by id (the dxButton configs in _Layout set elementAttr ids) because
   the vendor theme's own two-class rules for `type: 'normal'` / `type: 'danger'`
   contained buttons outrank the .dx-popup-bottom .dx-button defaults above.
   Acknowledge (type: 'normal') was picking up a dark vendor background while
   still taking the app's dark --main-text-color for its label, so it rendered as
   dark-on-dark and was effectively invisible. Ids win outright, no !important.

   Acknowledge is the primary action, so it gets the brand fill; Remind Me Later
   becomes the outlined secondary rather than a second red button competing with it. */
#notification-acknowledge-button {
    background: var(--main-color);
    border: 1px solid var(--main-color);
}

    #notification-acknowledge-button .dx-button-text {
        color: var(--white-color);
    }

    #notification-acknowledge-button:hover {
        background: var(--main-color-bright);
        border-color: var(--main-color-bright);
    }

#notification-remind-me-later-button {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
}

    #notification-remind-me-later-button .dx-button-text {
        color: var(--main-text-color);
    }

    #notification-remind-me-later-button:hover {
        background: var(--gray-bg);
        border-color: var(--text-sub);
    }

.modal-body {
    padding: 20px;
    overflow-y: auto;
    flex: 1;
}

/* Confirmation message inside a delete/confirm dialog — kept small so it sits
   comfortably within the compact dialog rather than dominating it. */
.modal-confirm-text {
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--main-text-color);
}

/* Action bar. Deliberately unseparated from the body — no rule above it and no
   band behind it: the footer's own padding is the only gap, so the dialog reads
   as one continuous surface from the facet header down to the buttons. The
   buttons are strong enough shapes to close the dialog without a line drawn
   across it. Keep it that way; a border-top here was tried and removed. */
.modal-footer {
    padding: 12px 20px;
    display: flex;
    justify-content: flex-end;
    gap: 8px;
}

/* Cancel takes the same edge as the fields above it. Its default outline is
   --main-border-color, which at .08 alpha on the #2A2E33 dark dialog composites to
   roughly #323539 and all but disappears; --dialog-field-border is the weight the
   dark dialog is tuned for, and it keeps the button in step with the form. Hover is
   untouched: --gray-bg still contrasts in both themes (#F4F5F6 on the white dialog,
   #202024 on the dark one). */
.modal-footer .btn-ghost-sh,
.modal-footer .btn-ghost,
.patches-modal-footer .btn-ghost-sh,
.patches-modal-footer .btn-ghost,
.pm-modal-footer .btn-ghost-sh,
.pm-modal-footer .btn-ghost {
    border-color: var(--dialog-field-border);
}

/* Footer variant for a dialog that carries a one-line note about what submitting
   actually does: the note takes its own row ABOVE the buttons, both right-aligned,
   so the explanation reads as part of the commit action instead of as fine print
   trailing the last field. Opt-in — the plain .modal-footer is untouched. */
.modal-footer.modal-footer-stacked {
    flex-direction: column;
    align-items: flex-end;
    gap: 10px;
}


.modal-footer.modal-footer-inline {
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 14px;
}


.modal-footer-inline > .dlg-note-info {
    flex: 1 1 auto;
    min-width: 0;
    align-items: center;
    width: 340px;
}

.modal-footer-inline > .modal-footer-actions {
    flex: 0 0 auto;
    margin-left: auto;
}


.modal-footer-actions {
    display: flex;
    gap: 8px;
}

/* Informational, not a warning: the app's info hue (the same one .maint-alert.info uses), and
   a text colour of --blue-link-hover-color so it inverts with the theme instead of staying dark
   on the dark dialog surface.

   Upright here, where it is the lone note in a dialog and carries weight. The New Item dialogs
   below opt into italic instead: they show this treatment twice, once under the header and once
   in the footer, and italic keeps the pair reading as asides rather than as two banners. */
.dlg-note-info {

    --note-bg: rgba(var(--blue-color-rgb), .08);
    --note-edge: var(--blue-color);
    --note-fg: var(--blue-link-hover-color);
    display: flex;
    align-items: flex-start;
    gap: 7px;
    padding: 7px 11px;
    background: var(--note-bg);
    border-left: 3px solid var(--note-edge);
    font-size: 10.5px;
    line-height: 1.45;
    color: var(--note-fg);
    text-align: left;
}


#niModalProduct .dlg-note-info {
    font-style: italic;
    --note-bg: var(--tag-blue-bg);
    --note-edge: var(--blue-link-color);
    --note-fg: var(--tag-blue-fg);
}

#niModalDevops .dlg-note-info {
    font-style: italic;
    --note-bg: var(--tag-purple-bg);
    --note-edge: var(--purple-color-dark);
    --note-fg: var(--tag-purple-fg);
}

#niModalConnect .dlg-note-info {
    font-style: italic;
    --note-bg: var(--tag-green-bg);
    --note-edge: var(--success-color-dark);
    --note-fg: var(--tag-green-fg);
}

    .dlg-note-info .svg-icon {
        flex-shrink: 0;
        margin-top: 1px;
    }

    .dlg-note-info .svg-icon svg {
        width: 13px;
        height: 13px;
    }

/* "Log this case on behalf of a customer" section (New Case popup, spec §6.5) —
   partner-only. Structural only; the note re-uses .dlg-note-info's colouring. */
.case-behalf-section {
    border-top: 1px solid var(--main-border-color);
    margin-top: 4px;
    padding: 14px 0 2px;
}
.case-behalf-toggle {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    user-select: none;
    color: var(--main-text-color);
}
.case-behalf-toggle input[type="checkbox"] {
    width: 15px;
    height: 15px;
    accent-color: var(--main-color);
    cursor: pointer;
    flex-shrink: 0;
}
.case-behalf-picker { margin-top: 12px; }
.case-behalf-note { margin-top: 10px; }


#niModalProduct .modal-body,
#niModalProduct .modal-body > form,
#niModalDevops .modal-body,
#niModalDevops .modal-body > form,
#niModalConnect .modal-body,
#niModalConnect .modal-body > form {
    display: flex;
    flex-direction: column;
    gap: 16px;
}


#niModalProduct #niProdBugFields {
    display: flex;
    flex-direction: column;
    gap: 16px;
}



#niModalProduct .sh-select-toggle,
#niModalDevops .sh-select-toggle,
#niModalConnect .sh-select-toggle,
#page-backlogs .bl-filters .sh-select-toggle {
    height: 36px;
}

#niModalProduct .modal-footer-actions .dx-button,
#niModalDevops .modal-footer-actions .dx-button,
#niModalConnect .modal-footer-actions .dx-button {
    height: auto;
    padding: 9px 18px;
    border-radius: unset;
    border: 1.5px solid transparent;
    box-shadow: none;
    min-width: 0;
}

/* Padding moved to the button above so all three share one box; this is only the label. */
#niModalProduct .modal-footer-actions .dx-button .dx-button-content,
#niModalDevops .modal-footer-actions .dx-button .dx-button-content,
#niModalConnect .modal-footer-actions .dx-button .dx-button-content {
    padding: 0;
    line-height: 1.2;
}


#niModalProduct .modal-footer-actions .dx-button .dx-button-text,
#niModalDevops .modal-footer-actions .dx-button .dx-button-text,
#niModalConnect .modal-footer-actions .dx-button .dx-button-text {
    font-size: 13px;
    font-weight: var(--font-bold);
    font-family: inherit;
    text-transform: none;
    letter-spacing: normal;
    /* Stated here as well as on the content: the span's inherited "normal" resolves to 17px,
       which would leave these 1.4px taller than Cancel. */
    line-height: 1.2;
}

/* Submit is the commit action -> .btn-primary. */
#niModalProduct .modal-footer-actions .dx-button.dx-button-success,
#niModalDevops .modal-footer-actions .dx-button.dx-button-success,
#niModalConnect .modal-footer-actions .dx-button.dx-button-success {
    background: var(--main-color);
}

#niModalProduct .modal-footer-actions .dx-button.dx-button-success .dx-button-text,
#niModalDevops .modal-footer-actions .dx-button.dx-button-success .dx-button-text,
#niModalConnect .modal-footer-actions .dx-button.dx-button-success .dx-button-text {
    color: var(--white-color);
}

#niModalProduct .modal-footer-actions .dx-button.dx-button-success.dx-state-hover,
#niModalDevops .modal-footer-actions .dx-button.dx-button-success.dx-state-hover,
#niModalConnect .modal-footer-actions .dx-button.dx-button-success.dx-state-hover {
    background: var(--main-color-dark);
}

/* Clear is secondary -> .btn-ghost. */
#niModalProduct .modal-footer-actions .dx-button.dx-button-danger,
#niModalDevops .modal-footer-actions .dx-button.dx-button-danger,
#niModalConnect .modal-footer-actions .dx-button.dx-button-danger {
    background: transparent;
    border-color: var(--main-border-color);
}

#niModalProduct .modal-footer-actions .dx-button.dx-button-danger .dx-button-text,
#niModalDevops .modal-footer-actions .dx-button.dx-button-danger .dx-button-text,
#niModalConnect .modal-footer-actions .dx-button.dx-button-danger .dx-button-text {
    color: var(--main-text-color);
}

#niModalProduct .modal-footer-actions .dx-button.dx-button-danger.dx-state-hover,
#niModalDevops .modal-footer-actions .dx-button.dx-button-danger.dx-state-hover,
#niModalConnect .modal-footer-actions .dx-button.dx-button-danger.dx-state-hover {
    background: var(--gray-bg);
    border-color: rgba(var(--main-color-rgb), .4);
}

/* ── Form grid ──────────────────────────────────────────────────── */
.form-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
}

/* ————— New Case / Customer Case popup: single column below 600px —————
   Every field (native input, .sh-select combo, dxSelectBox) is already width:100%
   of its own grid cell — collapsing the grid to one column is the only change
   needed for the dropdowns to fill the popup's width too. .form-group-full's
   grid-column:1/-1 still spans correctly with one column, so Title/Description
   need no extra rule. Scoped to the two case dialogs, not every .form-grid user
   (Invite User, the bug modals). */
@media (max-width: 600px) {
    #newCaseModal .form-grid,
    #newCustomerCaseModal .form-grid {
        grid-template-columns: 1fr;
    }
}

/* Single-column layout for narrow modals where every field should be
   full width (e.g. the New Case dialog). */
.form-stack {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.form-stack > * { width: 100%; }

.form-group-sh {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.form-group-sh label {
    font-size: 12px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.form-group-sh input:not([type="checkbox"]):not([type="radio"]):not(.dx-texteditor-input),
.form-group-sh select,
.form-group-sh textarea {
    width: 100%;
    padding: 8px 12px;
    border: 1.5px solid var(--main-border-color);
    border-radius: var(--radius);
    font-size: 13px;
    font-family: inherit;
    color: var(--main-text-color);
    outline: none;
    transition: border-color .15s, box-shadow .15s;
}

/* ══ Tabbed rich text editor — the New Item dialogs ════════════════════════
   Multiple rich text fields share one bordered block, field labels as tabs.
   Each field keeps its own editor instance, name=, and dxValidator. */
/* This block IS a field (several, behind tabs), so it takes the dialog field fill
   and edge rather than --gray-bg / --main-border-color. It keeps a resting edge
   where the single-line fields drop theirs: it is a tall multi-part control, and
   without an outline its tab strip reads as dialog chrome instead of as the top
   of one field. */
.ni-rt-tabs-wrap {
    border: 1.5px solid var(--dialog-field-border);
    /* Buttons alternate with their sections in the DOM (tab order needs it); `order` puts them
       back on one row above the panes. */
    display: flex;
    flex-wrap: wrap;
    background: var(--dialog-field-bg);
}

/* Overrides .notReady's visibility:hidden - greys out via option() instead of leaving a gap. */
.ni-rt-tabs-wrap .ni-rt-field.notReady {
    visibility: visible;
}

/* The message bindInlineFieldErrors() writes for this field (see utils.js). */
.ni-rt-field > .dlg-field-error {
    display: block;
    padding: 6px 12px 8px;
}

.ni-rt-tab {
    order: 0;
    padding: 9px 14px;
    border: none;
    border-bottom: 2px solid transparent;
    background: none;
    font-family: inherit;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--text-sub);
    white-space: nowrap;
    cursor: pointer;
}

    .ni-rt-tab:hover {
        color: var(--main-text-color);
    }

    /* Stays on the nav pane's active shade — a sub-tab inside a control, not a
       page-level tab bar, so it did not follow .tab-item.active to brand red. */
    .ni-rt-tab.active {
        color: var(--sidebar-active-text);
        border-bottom-color: var(--sidebar-active-text);
        background: var(--main-background-color);
    }

    /* Flags an invalid field's validation message, hidden while another tab is open. */
    .ni-rt-tab.has-error {
        color: var(--error-text);
    }

        .ni-rt-tab.has-error.active {
            border-bottom-color: var(--error-color);
        }

    /* Decorative, same as the asterisk in a .form-group-sh label — the
       dxValidator is what actually enforces the field. */
    .ni-rt-tab .required-mark {
        margin-left: 3px;
    }

.ni-rt-pane {
    display: none;
    order: 1;
    flex-basis: 100%;
    min-width: 0;
}

    .ni-rt-pane.active {
        display: block;
        /* Opaque, so the strip fill above stops at the top of the editor. */
        background: var(--main-background-color);
        border-top: 1px solid var(--main-border-color);
    }

/* The block's own border replaces the editor's (dx.alterations.css sets it !important),
   so the editor doesn't add a second one a pixel apart. */
.ni-rt-tabs-wrap .dx-htmleditor {
    border: none !important;
}

/* Set in the same pass as the per-tab error flag below. */
.ni-rt-tabs-wrap.ni-rt-invalid {
    border-color: var(--error-color);
}

/* ── Footer note as a tooltip (mobile) ────────────────────────────────────
   Below 600px, the footer note folds into an info button beside Cancel (tap-tooltip). */
.dlg-note-tip {
    display: none;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: none;
    background: none;
    color: var(--blue-color);
    cursor: pointer;
    /* Halves .modal-footer-actions' 8px gap on this one item so the button
       reads as attached to Cancel rather than as a third action. */
    margin-right: -4px;
}

    .dlg-note-tip > .svg-icon > svg {
        width: 18px;
        height: 18px;
    }

@media (max-width: 600px) {
    /* Only the two-up grids need collapsing; .form-group-sh is already a column. */
    .form-grid {
        grid-template-columns: 1fr;
    }

    /* Dialog is capped at 96vw; the overlay's 24px padding was costing it width here. */
    .modal-overlay {
        padding: 12px;
    }

    .modal-overlay > .modal {
        max-width: 100%;
    }

    /* Note collapses into .dlg-note-tip below - keeps the footer row on one line. */
    .modal-footer-inline > .dlg-note-info {
        display: none;
    }

    .dlg-note-tip {
        display: inline-flex;
    }

    /* Tab labels can't shrink further; free up space before they wrap to a second row. */
    .ni-rt-tab {
        padding: 8px 10px;
        letter-spacing: .03em;
    }
}

/* Hide MVC's empty validation-summary div so it doesn't add a blank
   row at the top of the modal body. */
.modal-body .validation-summary-valid { display: none; }
.modal-body .validation-summary-valid + * { margin-top: 0; }



/* ══ Dialog fields — the topbar-search recipe, app-wide ═══════════════════
   Every native control inside any dialog shell, not just the .form-group-sh
   ones: filled with --dialog-field-bg, no resting border, brand-red edge plus a
   red halo on focus. That is exactly what .topbar-search / .topbar-search:focus-
   within do (layout.css), so the global search box and a dialog field are one
   control family rather than two.

   Two things this fixes outright:

   1. Fill. Nothing styled dialog fields; they fell through to Style.css's global
      `input:not([type=checkbox]):not([type=radio]) { background-color:
      --main-background-color-subtle }`. That painted them #F4F5F6 in light mode —
      the page canvas colour, and a shade DARKER than the #FAFAFA dialog around
      them — and #202024 in dark, a shade LIGHTER than the #141414 dialog. Either
      way the field read as raised above the dialog holding it.
   2. Focus. `.form-group-sh input` has declared `transition: border-color,
      box-shadow` since it was written, but no `:focus` rule ever existed to fire
      it. Clicking a dialog field produced no visible change at all in either
      theme — `outline: none` is set and nothing replaced it. This is also an
      accessibility fix, not only a cosmetic one (WCAG 2.4.7).

   Scoped to the four dialog shells rather than to `input` globally, so page
   forms, filter bars and the topbar are untouched. Excludes checkbox/radio
   (which draw their own control), range/colour/file (whose fill IS the control),
   the button-like types, and .dx-texteditor-input — a DevExtreme editor is styled
   through its container in the block below, and painting its inner <input>
   produces a box inside a box. */
.modal-overlay input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not(.dx-texteditor-input),
.modal-overlay select,
.modal-overlay textarea,
.dx-popup-content input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not(.dx-texteditor-input),
.dx-popup-content select,
.dx-popup-content textarea,
.modal-content input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not(.dx-texteditor-input),
.modal-content select,
.modal-content textarea {
    background-color: var(--dialog-field-bg);
    color: var(--main-text-color);
    /* Stated as three longhands, not `border: 1.5px solid transparent`: the
       shorthand would also reset border-image, and more importantly the fields
       that arrive here already carry a border WIDTH from their own rule (1px on
       the profile-modal filters, 1.5px on .form-group-sh). Normalising the width
       here is what makes the red focus edge identical in every dialog. Nothing
       moves — box-sizing: border-box is global (top of this file), so the 0.5px
       goes out of the padding box, not the layout box. */
    border-width: 1.5px;
    border-style: solid;
    border-color: var(--dialog-field-border);
    outline: none;
    transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}

/* THE RED FOCUS EDGE — identical in light and dark. :focus rather than
   :focus-visible on purpose: unlike a button, a text field that has taken the
   caret must show it whether the user arrived by click or by Tab. */
.modal-overlay input:not([type="checkbox"]):not([type="radio"]):not(.dx-texteditor-input):focus,
.modal-overlay select:focus,
.modal-overlay textarea:focus,
.dx-popup-content input:not([type="checkbox"]):not([type="radio"]):not(.dx-texteditor-input):focus,
.dx-popup-content select:focus,
.dx-popup-content textarea:focus,
.modal-content input:not([type="checkbox"]):not([type="radio"]):not(.dx-texteditor-input):focus,
.modal-content select:focus,
.modal-content textarea:focus {
    border-color: var(--main-color);
    box-shadow: var(--dialog-focus-ring);
}

/* Browser autofill: repaint Chrome/Edge's autofill colours with the dialog field colours
   (same trick as the topbar search). The full :not() chain outranks the field/focus rules above. */
.modal-overlay input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not(.dx-texteditor-input):-webkit-autofill,
.dx-popup-content input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not(.dx-texteditor-input):-webkit-autofill,
.modal-content input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not(.dx-texteditor-input):-webkit-autofill {
    -webkit-text-fill-color: var(--main-text-color);
    caret-color: var(--main-text-color);
    box-shadow: inset 0 0 0 100px var(--dialog-field-bg);
    transition: background-color 9999s ease-in-out 0s, border-color var(--dur-fast);
}

/* Dark mode: makes browser-drawn autofill text and previews use dark colours.
   Checkboxes/radios excluded so native ones keep their current look. */
.dark-mode .modal-overlay input:not([type="checkbox"]):not([type="radio"]),
.dark-mode .dx-popup-content input:not([type="checkbox"]):not([type="radio"]),
.dark-mode .modal-content input:not([type="checkbox"]):not([type="radio"]) {
    color-scheme: dark;
}

.modal-overlay input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not(.dx-texteditor-input):-webkit-autofill:focus,
.dx-popup-content input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not(.dx-texteditor-input):-webkit-autofill:focus,
.modal-content input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not(.dx-texteditor-input):-webkit-autofill:focus {
    box-shadow: inset 0 0 0 100px var(--dialog-field-bg), var(--dialog-focus-ring);
}

/* The enhanced <select>. enhanceSelect() (utils.js) hides the native control and
   renders this button + menu in its place, so the `select` selectors above reach
   only the element nobody can see — without this rule every enhanced dropdown in
   a dialog would keep the page's --control-bg while the fields around it moved.
   The open state holds the focus edge so the toggle stays visibly tied to the
   menu hanging off it (the menu detaches to <body>, so proximity is all there is).
   :focus-visible rather than :focus: it is a button, and a mouse click that opens
   the menu is already covered by .open. */
.modal-overlay .sh-select-toggle,
.dx-popup-content .sh-select-toggle,
.modal-content .sh-select-toggle {
    background: var(--dialog-field-bg);
    border-color: var(--dialog-field-border);
    transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}

.modal-overlay .sh-select-toggle:focus-visible,
.modal-overlay .sh-select.open .sh-select-toggle,
.dx-popup-content .sh-select-toggle:focus-visible,
.dx-popup-content .sh-select.open .sh-select-toggle,
.modal-content .sh-select-toggle:focus-visible,
.modal-content .sh-select.open .sh-select-toggle {
    outline: none;
    border-color: var(--main-color);
    box-shadow: var(--dialog-focus-ring);
}

/* An invalid field keeps its own colour through focus — the red focus edge and
   the error edge are the same hue, so without this a failed field looks
   identical to a focused healthy one. The halo still fires, so focus is not lost. */
.modal-overlay input.input-validation-error,
.modal-overlay select.input-validation-error,
.modal-overlay textarea.input-validation-error,
.dx-popup-content input.input-validation-error,
.dx-popup-content select.input-validation-error,
.dx-popup-content textarea.input-validation-error {
    border-color: var(--error-color);
}

/* ── DevExtreme dropdown editors inside a dialog ──────────────────────
   Makes dxSelectBox / dxAutocomplete / dxTagBox match the plain
   .form-group-sh input above, so a dialog mixing native inputs and
   DevExtreme dropdowns reads as one form.

   These rules were written for the New Case dialog but scoped to
   #newCaseModal, so every other dialog's dropdown (Invite User's role
   picker, for one) kept the raw Material look — rounded, with the vendor
   theme's own inset ring. Scoped to dialog bodies instead of one page.

   The border goes on .dx-texteditor-container — the wrapper around BOTH the
   input and the dropdown button — not the outer .dx-editor-outlined. On the
   outer element the theme's `box-shadow: inset 0 0 0 1px` stayed visible
   underneath, which is what made the dropdown button look separately boxed. */
/* Widened from .dx-dropdowneditor to .dx-texteditor so a plain dxTextBox /
   dxTextArea / dxNumberBox in a dialog gets the same treatment as the pickers —
   they are the same field to the user, and the narrower selector left them on the
   vendor's own fill. */
.modal-body .dx-texteditor.dx-editor-outlined,
.dx-popup-content .dx-texteditor.dx-editor-outlined {
    border-radius: unset;
    background: var(--dialog-field-bg);
    box-shadow: none;
}

.modal-body .dx-texteditor.dx-editor-outlined .dx-texteditor-input-container,
.modal-body .dx-texteditor.dx-editor-outlined .dx-texteditor-buttons-container,
.dx-popup-content .dx-texteditor.dx-editor-outlined .dx-texteditor-input-container,
.dx-popup-content .dx-texteditor.dx-editor-outlined .dx-texteditor-buttons-container {
    border: none;
    box-shadow: none;
}

/* Same resting hairline and the same 1.5px weight the native dialog fields
   carry, so a dropdown and a text input in one dialog are the same box. */
.modal-body .dx-texteditor.dx-editor-outlined .dx-texteditor-container,
.dx-popup-content .dx-texteditor.dx-editor-outlined .dx-texteditor-container {
    border: 1.5px solid var(--dialog-field-border);
    border-radius: var(--radius);
    transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}

/* Hover darkens the existing edge instead of the old --main-text-color, which
   jumped straight to a near-black outline. */
.modal-body .dx-texteditor.dx-editor-outlined.dx-state-hover .dx-texteditor-container,
.dx-popup-content .dx-texteditor.dx-editor-outlined.dx-state-hover .dx-texteditor-container {
    border-color: var(--text-sub);
}

/* Brand red + halo, identical to the native fields' :focus. Was a neutral
   near-black edge with a colour-mix halo, so a focused dropdown and a focused
   text input in the same dialog highlighted in two different colours. */
.modal-body .dx-texteditor.dx-editor-outlined.dx-state-focused .dx-texteditor-container,
.dx-popup-content .dx-texteditor.dx-editor-outlined.dx-state-focused .dx-texteditor-container {
    border-color: var(--main-color);
    box-shadow: var(--dialog-focus-ring);
}

/* DevExtreme marks a failed rule on the widget root; hold the error hue through
   focus for the same reason .input-validation-error does on native fields. */
.modal-body .dx-texteditor.dx-editor-outlined.dx-invalid .dx-texteditor-container,
.dx-popup-content .dx-texteditor.dx-editor-outlined.dx-invalid .dx-texteditor-container {
    border-color: var(--error-color);
    /* Same 2px bottom rail as a native invalid input (Style.css). */
    box-shadow: inset 0 -2px 0 var(--error-color);
}

.modal-body .dx-list-item.dx-list-item-selected,
.dx-selectbox-popup-wrapper .dx-list-item.dx-list-item-selected,
.dx-selectbox-popup-wrapper .dx-list-item.dx-state-focused.dx-list-item-selected {
    background-color: var(--select-selected-bg);
    color: var(--main-text-color);
}

/* Hover (DevExtreme drives it off .dx-state-hover, not :hover) paints the same band
   as the selected row — vendor default is a 4% wash, invisible beside it. Covers the
   unselected rows too, so the highlight simply follows the pointer. */
.modal-body .dx-list-item.dx-state-hover,
.dx-selectbox-popup-wrapper .dx-list-item.dx-state-hover,
.dx-selectbox-popup-wrapper .dx-list-item.dx-list-item-selected.dx-state-hover:not(.dx-state-focused) {
    background-color: var(--select-selected-bg);
    color: var(--main-text-color);
}

/* Field text matches the native inputs' 13px. */
.modal-body .dx-texteditor-input,
.dx-popup-content .dx-texteditor-input {
    font-size: 13px;
    font-family: inherit;
    color: var(--main-text-color);
}

/* One look for every validation message in a dialog, whichever mechanism produced it:
   MVC's ValidationMessageFor span (.field-validation-error, +.text-danger), the plain
   spans the case dialogs write into for their DevExtreme-backed fields, and DevExtreme's
   own .dx-invalid-message. They previously drew from three different rules, so the same
   dialog reported errors in two different sizes/colours. */
.modal-body .field-validation-error,
.modal-body .text-danger,
.modal-body .dx-invalid-message > .dx-overlay-content,
.dx-popup-content .field-validation-error,
.dx-popup-content .text-danger {
    font-family: inherit;
    font-size: 13px;
    font-weight: var(--font-regular, 400);
    line-height: 1.5;
    color: var(--error-text);
}

/* Placeholders inside any dialog read as italic, so hint text is never mistaken for a
   value the field already holds. Covers both control families a dialog can contain:
   native inputs/textareas (::placeholder) and DevExtreme editors, whose placeholder is a
   real .dx-placeholder element rather than a pseudo-element — including the Quill-based
   rich-text editors, whose placeholder comes from .ql-editor's ::before. */
.modal-overlay input::placeholder,
.modal-overlay textarea::placeholder,
.modal-overlay .dx-placeholder,
.modal-overlay .ql-editor.ql-blank::before,
.dx-popup-content input::placeholder,
.dx-popup-content textarea::placeholder,
.dx-popup-content .dx-placeholder,
.dx-popup-content .ql-editor.ql-blank::before {
    font-style: italic;
}

/* ── Validation messages inside a dialog ─────────────────────────────
   DevExtreme renders a broken rule as a filled red tooltip bubble (white text on
   --error-color, rounded, padded). New Case instead shows the shared field message
   (badge + --error-text line) under the field via .field-validation-error (Style.css). Restyled to match so every
   dialog reports errors the same way — the Backlogs dialogs previously stacked all
   messages into a summary list at the top, which is gone.
   Paired with validationMessageMode:'always' + validationMessagePosition:'bottom' on the
   editors, so the message sits under its own field and stays put instead of behaving like
   a hover tooltip. Editors start valid, so nothing shows until a submit actually fails. */
.modal-body .dx-invalid-message > .dx-overlay-content {
    color: var(--error-text);
    background-color: transparent;
    border-radius: unset;
    border-left: 0;
    padding: 4px 0 0;
    font-size: 13px;
    line-height: 1.5;
}

/* Same square badge as the shared field message. */
.modal-body .dx-invalid-message > .dx-overlay-content::before {
    content: "!";
    display: inline-block;
    width: 15px;
    height: 15px;
    margin-right: 6px;
    vertical-align: -2px;
    background: var(--error-color);
    color: #fff;
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    line-height: 15px;
    text-align: center;
}

/* Placeholder tone, for native inputs and DevExtreme's .dx-placeholder overlay alike, so
   an unfilled field reads the same whichever widget renders it. Same value the New Case
   dialog set locally (#newCaseModal) — promoted to dialog scope so the New Product
   Backlog / DevOps / Connect dialogs match instead of each re-declaring it.
   --black-color-rgb flips with the theme, so this stays legible in dark mode. */
.modal-body input:not(.dx-texteditor-input)::placeholder,
.modal-body textarea::placeholder,
.modal-body .dx-placeholder,
.dx-popup-content .dx-placeholder {
    color: rgba(var(--black-color-rgb), .45);
}

.form-group-full {
    grid-column: 1 / -1;
}

/* Informational note below a dialog's fields (e.g. "you'll be redirected after
   submitting" in the New Case / New Customer Case popups) — a tinted, icon-led
   callout so it reads as an aside rather than another form field. */
.cd-form-note {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    margin-top: 12px;
    padding: 3px 10px;
    background: var(--gray-bg);
    border-left: 3px solid var(--main-border-color);
    font-size: 10.5px;
    font-style: italic;
    color: var(--text-sub);
    line-height: 1.4;
}

.cd-form-note svg {
    width: 11px;
    height: 11px;
    flex-shrink: 0;
    margin-top: 2px;
}


.form-group-sh label:not(.toggle) {
    text-transform: uppercase;
    letter-spacing: .4px;
    font-size: 11px;
    color: var(--text-sub);
}

.form-group-sh-inline {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
}

/* Disabled primary: a neutral grey fill instead of the red at half opacity. Faded red
   came out maroon on dark surfaces and pink on light ones, so people read it as a
   second button style rather than an inactive one (Login's Sign In above Sign Up).
   !important is needed only because Style.css forces .btn-primary's red fill with it. */
.btn-primary-sh:disabled,
.btn-primary:disabled,
.btn-primary-sh:disabled:hover,
.btn-primary:disabled:hover {
    background: var(--contrast-bg) !important;
    border-color: var(--contrast-bg) !important;
    color: var(--text-sub);
    opacity: 1;
    cursor: not-allowed;
}

.btn-danger-sh {
    color: var(--error-text);
    border-color: var(--error-color);
}

.btn-danger-sh:hover {
    background: rgba(var(--error-color-rgb), .08);
}

/* ── Shared admin data table (Feature Flags-adjacent config pages) ──── */
.role-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

/* ── Config list internal scroll ─────────────────────────────────────
   Long admin lists scroll inside their card instead of running off the bottom of
   the page: Feature Flags, News Feed, Patch Manager, Manage Roles and Manage
   Permissions. The cap is viewport-relative rather than a fixed pixel height so
   the card ends inside the viewport on any screen: the chrome above it (topbar,
   page header, tab bar, card header) is roughly 330px, and the floor keeps the
   list usable if the window is very short.

   Notifications is the one Configuration tab that keeps its own tighter
   .notification-table-scroll cap, because that page stacks two lists and a
   viewport-height cap on each would push Banners well below the fold. */
.config-list-scroll {
    max-height: max(240px, calc(100vh - 330px));
    overflow-y: auto;
    overflow-x: auto;
}

    /* .role-table's shared border-collapse:collapse fights position:sticky below --
       collapsed borders render outside the normal box flow, leaving a visible gap
       above the header once it sticks, through which the first row peeks. separate +
       border-spacing:0 keeps the same tight layout without that conflict -- same fix
       Notifications' own .notification-table-scroll already applies. */
    .config-list-scroll table.role-table {
        border-collapse: separate;
        border-spacing: 0;
    }

    /* th already carries an opaque background, so the header can pin as-is. The
       box-shadow duplicates that background 1px below the cell's own box: sticky
       positioning can leave a sub-pixel seam at the header's bottom edge once actually
       scrolled (rounding in the browser's sticky-offset math, not something border-
       collapse fixes), through which the row underneath peeks. The shadow masks that
       seam with more of the same solid colour instead of leaving it transparent. */
    .config-list-scroll table.role-table thead th {
        position: sticky;
        top: 0;
        z-index: 1;
        box-shadow: 0 1px 0 0 var(--gray-bg);
    }

    .role-table th {
        text-align: left;
        padding: 12px 16px;
        background: var(--gray-bg);
        font-size: 11px;
        font-weight: var(--font-extra-bold);
        text-transform: uppercase;
        letter-spacing: .4px;
        color: var(--text-sub);
        border-bottom: 1px solid var(--main-border-color);
        white-space: nowrap;
    }

    .role-table td {
        padding: 12px 16px;
        border-bottom: 1px solid var(--main-border-color);
        vertical-align: top;
    }

    .role-table tr:last-child td {
        border-bottom: none;
    }

    .role-table tr:hover td {
        background: var(--row-hover-bg);
    }

.role-actions-cell-inner {
    display: flex;
    gap: 6px;
}

.form-grid-role {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    gap: 16px;
}

/* Equal-width variant. The 1.4fr/1fr default suits a wide field beside a narrow one
   (name + priority), but a start/end date pair should match — two identical controls
   at different widths read as a mistake. Used by the schedule rows in the
   notification, banner and news modals. */
.form-grid-role.form-grid-even {
    grid-template-columns: 1fr 1fr;
}

/* Hint text on the left, a live character/word counter on the right, under a field. */
.field-hint-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.field-count {
    flex-shrink: 0;
    font-size: 11px;
    color: var(--text-sub);
}

.field-count.at-limit {
    color: var(--error-text);
    font-weight: var(--font-bold);
}

/* Asterisk marking a mandatory form field, e.g. NAME <span class="required-mark">*</span> */
.required-mark {
    color: var(--main-color);
    font-weight: var(--font-bold);
}

/* ── sh-select: themed single-select dropdown ───────────────────────
   Shared dropdown used across the Configuration pages: a toggle button plus
   an in-page menu capped in height with its own scrollbar (the native <select>
   popup is OS-rendered and can escape the window at high zoom).
   Native selects get this look via the sh-enhance class (see utils.js). */
.sh-select {
    position: relative;
}

.sh-select-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    min-width: 0;
    cursor: pointer;
    text-align: left;
    padding: 8px 12px;
    border: 1.5px solid var(--main-border-color);
    border-radius: 0;
    font-size: 13px;
    font-family: inherit;
    background: var(--control-bg);
    color: var(--main-text-color);
}

.sh-select-label {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.sh-select-caret {
    font-size: 10px;
    color: var(--text-sub);
    flex-shrink: 0;
}

.sh-select-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    min-width: 100%;
    max-height: min(260px, 50vh);
    overflow-y: auto;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    box-shadow: var(--shadow-lg);
    z-index: 100;
    display: none;
}

.sh-select.open .sh-select-menu {
    display: block;
}

/* Enhanced native selects position the menu fixed (coordinates set by JS) so
   it can escape overflow-hidden/scrolling modal bodies without clipping. */
.sh-select--fixed .sh-select-menu {
    position: fixed;
    z-index: 6000;
    min-width: 0;
}

/* enhanceSelect (utils.js) detaches its menu to <body> instead of leaving it nested
   in .sh-select--fixed: a transform on some ancestor of the toggle would otherwise
   become the fixed menu's containing block and throw its screen coordinates off. Living at the body root means the ancestor-scoped rules above
   never apply to it, so it carries the same position/sizing and its own open-state
   toggle directly. */
.sh-select-menu--detached {
    position: fixed;
    z-index: 6000;
    min-width: 0;
    display: none;
}

.sh-select-menu--detached.open {
    display: block;
}

.sh-select-option {
    display: block;
    width: 100%;
    text-align: left;
    padding: 8px 12px;
    background: none;
    border: none;
    font-size: 13px;
    font-family: inherit;
    color: var(--main-text-color);
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

    /* Same fill as .selected below — the highlight follows the pointer and the
       chosen row wears it at rest (see --select-selected-bg in Style.css). */
    .sh-select-option:hover {
        background: var(--select-selected-bg);
    }

    .sh-select-option.selected {
        /* Shared neutral selection fill (--select-selected-bg, Style.css) — the same row
           treatment every dropdown in the app uses, brand-red free by design. */
        background: var(--select-selected-bg);
        font-weight: var(--font-bold);
    }

.sh-select-option:disabled {
    color: var(--text-sub);
    cursor: not-allowed;
}

/* Loader row appended inside an sh-select-menu (e.g. the Cases page Product filter) while its
   options are still being fetched — sits alongside whatever options already exist rather than
   covering the closed field, since it's only ever visible once the menu itself is open. */
.sh-select-loading {
    display: flex;
    justify-content: center;
    padding: 8px 12px;
}

/* The hidden native select behind an enhanced dropdown (source of truth) */
select.sh-enhanced {
    display: none !important;
}

/* Locked sh-select (e.g. the mandatory English row in the message editor) */
.sh-select-disabled .sh-select-toggle {
    opacity: .65;
    cursor: not-allowed;
}

/* ── sh-select: searchable variant ───────────────────────────────────
   For a list too long to scroll — every customer in the table — the menu carries
   its own search box above the results rather than expecting the caller to have
   filtered first. Same shell and .open convention as every other sh-select, so
   utils.js's single document-level handler closes it on an outside click for free.

   The Change Company dialog in _UserProfileModal has a local copy of this as
   .ump-cc-*; it came first and is left alone rather than retrofitted. */
.sh-select--searchable .sh-select-menu {
    /* A menu that owns a search box must not clip or scroll it away. */
    max-height: none;
    overflow: visible;
}

/* An unchosen value has to read as a placeholder rather than as a selection. Shared
   here because it was only defined locally inside _UserProfileModal (.ump-cc-combo),
   so any other sh-select using the same convention rendered its placeholder in full
   text colour — indistinguishable from a real pick. */
.sh-select-label.is-placeholder {
    color: var(--text-sub);
    font-style: italic;
    color: rgba(var(--black-color-rgb), .45) !important;
    font-size: 12px;
    font-family: inherit;
}

.sh-select-search-row {
    padding: 8px;
    border-bottom: 1px solid var(--main-border-color);
}

    .sh-select-search-row input {
        width: 100%;
        padding: 6px 9px;
        border: 1.5px solid var(--main-border-color);
        border-radius: 0;
        background: var(--main-background-color);
        color: var(--main-text-color);
        font-family: var(--font);
        font-size: 12px;
        box-sizing: border-box;
    }

.sh-select-results {
    max-height: 190px;
    overflow-y: auto;
}

.sh-select-result {
    padding: 7px 10px;
    font-size: 12px;
    cursor: pointer;
}

    .sh-select-result:hover {
        background: var(--select-selected-bg);
    }

    /* Real tab stop now (utils.js), reachable one at a time from the search box. */
    .sh-select-result:focus-visible {
        outline: 2px solid var(--main-color);
        outline-offset: -2px;
    }

    /* Was a red tint — the only dropdown selection in the app that was branded.
       Now the shared neutral fill, so the searchable variant matches the plain one. */
    .sh-select-result.selected {
        background: var(--select-selected-bg);
        font-weight: var(--font-bold);
    }

    .sh-select-result-detail {
        font-size: 11px;
        color: var(--text-sub);
        margin-top: 1px;
    }

.sh-select-empty {
    padding: 10px;
    font-size: 12px;
    color: var(--text-sub);
    text-align: center;
}

.sh-select-searching {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 18px 10px;
}

/* Read-only counterpart, for a field whose value is fixed by where you opened the
   dialog from (the company page already knows the company). Same box as the toggle
   so the row lines up with the pickers beside it, minus the caret and the menu. */
.sh-select-static {
    display: flex;
    align-items: center;
    min-height: 36px;
    padding: 0 10px;
    border: 1.5px solid var(--main-border-color);
    background: var(--gray-bg);
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    box-sizing: border-box;
    opacity: 35%;
}

/* ── List filter toolbar ─────────────────────────────────────────────
   Shared search box + dropdown-filter row used above data tables (Manage
   Permissions, Notifications/Banners, News Feed) so every list page's
   filter bar looks and behaves identically. */
.list-filter-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
}

/* Nothing separated the toolbar from the header above: .section-subtitle has no bottom
   margin and neither does the button beside it. Worst once the header wraps and the
   button lands on the search field. Scoped so the Projects toolbars keep their own. */
.flex-between-center + .list-filter-toolbar {
    margin-top: 16px;
}

.list-filter-input {
    padding: 8px 12px;
    border: 1.5px solid var(--main-border-color);
    border-radius: 0;
    font-size: 13px;
    font-family: inherit;
    background: var(--main-background-color);
    color: var(--main-text-color);
}

/* Replaces the browser's rounded black focus outline with the red edge and halo
   the dialog fields use. input.perm-filter-input is the Manage Permissions search
   (input only: the module dropdown button shares the class). */
.list-filter-input:focus,
input.perm-filter-input:focus {
    outline: none;
    border-color: var(--main-color);
    box-shadow: var(--dialog-focus-ring);
}

input.list-filter-input {
    flex: 1 1 200px;
    min-width: 0;
    max-width: 280px;
}

/* Toolbar search/filter fields — Configuration module (Feature Flags, Role Management,
   Manage Permissions and the role modal's permission filter, Notifications, News Feed,
   Patch Manager), the Registrations page's attempt search and Period date pickers, and
   the Downloads pages' product search (Current / Previous / Archived all reuse the id).

   Every one of them sets its own fill via its own class (.list-filter-input above,
   .perm-filter-input, .pm-search-input, the shared .reg-filter-group /
   .reg-attempts-controls rule, and .filter-bar input), but Style.css's global
   `input:not([type="checkbox"]):not([type="radio"])` reset sets
   background-color: var(--main-background-color-subtle) at specificity (0,2,1) and wins over
   every one of them. The result in light mode was a grey field sitting in a toolbar of white
   .sh-select toggles, reading as a filled block rather than an input. IDs (1,0,0) beat the
   reset without !important.

   --control-bg is the form-control fill the toggles beside them already use: white in light
   mode, #202024 in dark — the latter being exactly what the reset was giving them, so dark
   mode is unchanged. Same fix as .cases-toolbar input.list-filter-input and #blSearch. */
#featureFlagSearchInput,
#roleSearchInput,
#permissionFilterInput,
#permissionSearchInput,
#notificationSearchInput,
#bannerSearchInput,
#newsItemSearchInput,
#patchSearchInput,
#regSearch,
#regStart,
#regEnd,
#dlSearchInput {
    background: var(--control-bg);
}

.list-filter-toolbar .sh-select {
    flex: 0 1 auto;
    min-width: 0;
    max-width: min(200px, 100%);
}

    .list-filter-toolbar .sh-select .sh-select-toggle {
        min-width: 130px;
    }

/* Customer Details' Cases tab filters (#casesStatusFilter/#casesProductFilter) — same width
   as the Cases page's own Product/Priority filters (.list-filter-toolbar .sh-select above),
   without needing that class on this page's own filter row wrapper. */
#casesStatusFilter + .sh-select,
#casesProductFilter + .sh-select {
    max-width: min(200px, 100%);
}

/* ── List filter toggle (collapsible filter panel) ───────────────────
   A "Filter" button carrying an active-count badge that, below 900px, hides a
   .list-filter-toolbar's non-search controls behind it instead of stacking every
   select/pill full-width — same breakpoint and behavior as the Backlogs/Patch
   Manager toolbars, factored out here so a third (fourth, fifth...) page doesn't
   hand-roll its own copy. The search input stays outside the panel and always
   visible, matching those toolbars.
   display:contents keeps .list-filter-panel invisible as a layout box above
   900px, so its children (selects, pills) are still direct flex items of
   .list-filter-toolbar and today's inline layout is unchanged; the toggle/badge/
   clear-X are display:none there and only appear inside the media query below. */
.list-filter-panel {
    display: contents;
}

.list-filter-toggle-wrap,
.list-filter-clear-x {
    display: none;
}

.list-filter-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--main-color);
    color: var(--white-color);
    font-size: 10px;
    font-weight: var(--font-bold);
}

@media (max-width: 900px) {
    .list-filter-toolbar.has-filter-toggle .list-filter-panel {
        display: none;
        flex-basis: 100%;
        flex-direction: column;
        gap: 8px;
    }

    .list-filter-toolbar.has-filter-toggle.filters-open .list-filter-panel {
        display: flex;
    }

    .list-filter-toolbar.has-filter-toggle .list-filter-panel .sh-select,
    .list-filter-toolbar.has-filter-toggle .list-filter-panel .dl-filter-pills {
        max-width: none;
        width: 100%;
    }

    .list-filter-toolbar.has-filter-toggle .list-filter-toggle-wrap {
        display: inline-flex;
        align-items: center;
        border: 1.5px solid var(--main-border-color);
    }

    .list-filter-toggle-btn {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        padding: 6px 10px;
        height: 34px;
        font-size: 12px;
        font-weight: var(--font-bold);
        border: none;
        background: var(--main-background-color);
        color: var(--text-sub);
        cursor: pointer;
    }

    .list-filter-toggle-btn:hover,
    .list-filter-toolbar.filters-open .list-filter-toggle-btn {
        color: var(--main-color);
    }

    .list-filter-toolbar.has-filter-toggle .list-filter-clear-x {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 26px;
        height: 34px;
        border: none;
        border-left: 1.5px solid var(--main-border-color);
        background: var(--main-background-color);
        color: var(--text-sub);
        cursor: pointer;
    }

    .list-filter-toolbar.has-filter-toggle .list-filter-clear-x:hover {
        color: var(--main-color);
    }

    .list-filter-toolbar.has-filter-toggle .list-filter-clear-x > .svg-icon > svg {
        width: 12px;
        height: 12px;
    }
}

#casesStatusFilter + .sh-select .sh-select-toggle,
#casesProductFilter + .sh-select .sh-select-toggle {
    min-width: 130px;
}

/* Audience role chips (Notifications / Banners / News Feed modals) are
   color-coded by role category, matching the audience pill colors:
   red = employee roles, blue = customer roles. */
.audience-chip.role-chip-employee {
    background: rgba(var(--error-color-rgb), .12);
    border-color: rgba(var(--error-color-rgb), .45);
    color: var(--error-text);
}

    .audience-chip.role-chip-employee.selected {
        background: var(--error-color-dark);
        border-color: var(--error-color-dark);
        color: var(--white-color);
    }

.audience-chip.role-chip-customer {
    background: rgba(45, 143, 210, .12);
    border-color: rgba(45, 143, 210, .5);
    color: var(--blue-link-hover-color);
}

    .audience-chip.role-chip-customer.selected {
        /* The darker link blue, not --blue-color: white on #2D8FD2 is only 3.52:1,
           whereas white on #2372A4 clears the text floor at 5.23:1. */
        background: var(--blue-link-color);
        border-color: var(--blue-link-color);
        color: var(--white-color);
    }

/* Role filter dropdown, grouped by audience category (Employee/Customer/Partner)
   instead of one flat mixed list — shared by Notifications, Banners, News Feed.
   Built on the same .sh-select toggle+menu shell as the single-select dropdowns
   (e.g. the Module filter) via initRoleDropdown() in utils.js, so it gets the
   same scroll-capped, click-outside-to-close menu instead of an always-expanded,
   unbounded list. .role-category-menu only adds the internal group spacing. */
.role-category-menu {
    padding: 6px 0;
}

.role-category-group {
    padding: 8px 12px;
}

    .role-category-group + .role-category-group {
        border-top: 1px solid var(--main-border-color);
    }

.role-category-label {
    font-size: 11px;
    font-weight: var(--font-bold);
    text-transform: uppercase;
    letter-spacing: .03em;
    margin-bottom: 6px;
}

    .role-category-label.role-category-employee { color: var(--error-text); }
    .role-category-label.role-category-customer { color: var(--blue-link-hover-color); }
    .role-category-label.role-category-partner { color: var(--text-sub); }

.role-checkbox-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.role-checkbox-item {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--main-text-color);
    cursor: pointer;
    padding: 3px 0;
}

    .role-checkbox-item input[type="checkbox"] {
        margin: 0;
        cursor: pointer;
    }

@media (max-width: 768px) {
    .form-grid-role,
    .form-grid-role.form-grid-even {
        grid-template-columns: 1fr;
    }
}

/* ── Utility classes ────────────────────────────────────────────── */
.hidden                 { display: none !important; }
.td-center              { text-align: center; }
.text-extra-bold        { font-weight: var(--font-extra-bold); }
.text-themed            { color: var(--main-color); }
.text-preline           { white-space: pre-line; }
.icon-xl                { font-size: 4rem; }
.cursor-pointer         { cursor: pointer; }
.cursor-not-allowed     { opacity: .6; cursor: not-allowed; }
.bg-unset               { background: unset; }
.mw-500                 { max-width: 500px; }
.ud-full-row            { grid-column: 1 / -1; }
.link-inherit           { color: inherit; text-decoration: underline; }

.flex-center-spaced     { display: flex; justify-content: center; margin: 10px 0; }
.flex-between           { display: flex; justify-content: space-between; }
.flex-between-center    { display: flex; justify-content: space-between; align-items: center; }
.flex-col-center        { display: flex; flex-direction: column; align-items: center; text-align: center; }
.flex-col-between       { display: flex; flex-direction: column; justify-content: space-between; }
.flex-end-gap           { display: flex; justify-content: flex-end; gap: 10px; }

.btn-modal-action       { width: 200px; max-width: 200px; min-width: 100px; margin-top: 1em; }
.grid-container-lg      { height: 500px; }
.grid-container-md      { height: 400px; }
.grid-container-xl      { height: 600px; }
.grid-tab-table         max-height: 395px; overflow-y: auto;
.rich-editor-box        { width: 100%; white-space: pre-wrap; word-break: break-word; box-sizing: border-box; }

/* ── Tables (light theme-friendly) ──────────────────────────────── */
.sh-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 13px;
}

.sh-table th,
.sh-table td {
    text-align: left;
    padding: 10px 14px;
    border-bottom: 1px solid var(--main-border-color);
}

.sh-table thead th {
    background: var(--gray-bg);
    color: var(--text-sub);
    font-weight: var(--font-bold);
    text-transform: uppercase;
    font-size: 11px;
    letter-spacing: .6px;
}

.sh-table tbody tr:hover {
    background: var(--row-hover-bg);
}

/* ── Empty state / placeholder ──────────────────────────────────── */
/* ── Empty state (single source) ─────────────────────────────────────
   One "there's nothing here" treatment in three sizes. Replaced .role-empty,
   .role-category-empty-note, .k-empty, .bl-rank-empty, .bell-drop-empty,
   .search-drop-empty and the unused .dash-empty-padded, which between them
   used seven different paddings and disagreed on whether the text was italic.

     .empty-state              default — inside a card or panel
     .empty-state-sm           dropdowns, kanban columns, inline notes
     .empty-state-lg           full-panel, e.g. a whole tab with no results */
.empty-state {
    padding: 40px 20px;
    text-align: center;
    font-size: 13px;
    color: var(--text-sub);
}

.empty-state-sm {
    padding: 16px 12px;
    font-size: 12px;
}

.empty-state-lg {
    padding: 64px 24px;
}

/* Icon slot. Stacks above the message; the only variant that had one was
   .empty-state itself, so this is now available to every empty state.
   .icon-xl / .svg-icon keep working for the states already using them;
   .empty-state-icon is the sized slot that follows the size modifier. */
.empty-state .icon-xl,
.empty-state .svg-icon,
.empty-state-icon {
    display: block;
    opacity: .5;
}

.empty-state-icon {
    font-size: 30px;
    line-height: 1;
}

.empty-state-sm .empty-state-icon {
    font-size: 22px;
    margin-bottom: 6px;
}

/* ── Status notice ───────────────────────────────────────────────────
   The one "this panel has something to tell you" block, replacing the
   Bootstrap `.alert alert-warning` that every tab used to inject on a failed
   or pending fetch. That alert was wrong three ways: it carried Bootstrap's
   own border-radius (the app's --radius is 0, so it was the only rounded
   thing on the page), it painted loading, pending and failure states the same
   yellow, and it sat as a margin-boxed card INSIDE the panel, leaving the
   panel's own bottom border showing as a stray strip beneath it.

   The tone classes below carry everything that varies — rail colour, wash and
   icon — so a caller only picks a tone. The icon is masked from the same
   /icons/ui library the <icon> tag helper reads, which keeps one source of
   truth and lets JS render a notice without embedding SVG markup in a string.

     .notice-loading   in flight
     .notice-warn      pending on something external (ERP sync not back yet)
     .notice-error     the request failed; pairs with .notice-action
     .notice-info      advisory, e.g. the employee-only banner */
.notice {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 14px 18px;
    border-radius: var(--radius);
    border-left: 3px solid var(--notice-color, var(--light-grey-color));
    background: var(--notice-bg, transparent);
    font-size: 13px;
    line-height: 1.45;
    color: var(--main-text-color);
}

/* Masked icon slot. --notice-size stays 0 unless a tone supplies an icon, which
   is how a tone opts out of one without needing its own ::before override. */
.notice::before {
    content: "";
    flex: 0 0 auto;
    width: var(--notice-size, 0);
    height: var(--notice-size, 0);
    margin-top: 1px;
    background: var(--notice-color, var(--light-grey-color));
    -webkit-mask: var(--notice-icon) center / contain no-repeat;
    mask: var(--notice-icon) center / contain no-repeat;
}

.notice-title {
    font-weight: var(--font-bold);
}

.notice-detail {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: var(--text-sub);
}

/* Tone washes are alpha, so they composite over whichever surface the notice
   lands on and need no .dark-mode counterpart; --text-sub and --main-text-color
   flip themselves. */
.notice-loading {
    --notice-color: var(--light-grey-color);
}

.notice-warn {
    --notice-color: var(--warning-color);
    --notice-bg: rgba(var(--warning-color-rgb), .09);
    --notice-icon: url('/icons/ui/alert-triangle.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo');
    --notice-size: 16px;
}

/* Error tone matches the shared form banner (.error-banner, Style.css): wash, a
   hairline on the other three edges, and the 3px rail from .notice. */
.notice-error {
    --notice-color: var(--error-color);
    --notice-bg: rgba(var(--error-color-rgb), .08);
    --notice-icon: url('/icons/ui/alert-circle.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo');
    --notice-size: 16px;
    border: 1px solid rgba(var(--error-color-rgb), .32);
    border-left: 3px solid var(--error-color);
}

.notice-info {
    --notice-color: var(--blue-color);
    --notice-bg: rgba(var(--blue-color-rgb), .07);
    --notice-icon: url('/icons/ui/info-circle.svg?v=rb_XSXktEbqqrBb_h7ea8PRIrIo&v=rb_XSXktEbqqrBb_h7ea8PRIrIo');
    --notice-size: 16px;
}

/* The loading tone swaps the masked icon for a spinner. Same 16px slot, so the
   text never shifts when a pending notice resolves into a failed one. */
.notice-loading::before {
    width: 14px;
    height: 14px;
    margin-top: 2px;
    background: none;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    opacity: .7;
    animation: notice-spin .7s linear infinite;
}

@keyframes notice-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .notice-loading::before { animation: none; }
}

.notice-action {
    margin-left: auto;
    flex: 0 0 auto;
    padding: 4px 12px;
    border: 1px solid currentColor;
    border-radius: var(--radius);
    background: none;
    color: var(--notice-color);
    font: inherit;
    font-size: 12px;
    font-weight: var(--font-bold);
    cursor: pointer;
}

.notice-action:hover {
    background: var(--notice-color);
    color: var(--white-color);
}

/* Slot a notice renders into: one per module, sitting directly above that
   module's card — the position the OTP tab's employee banner already occupies.
   The notice reports what went wrong; the card below still shows the module's
   own empty state, so the page keeps its shape instead of collapsing into a
   single message. Empty slots collapse, leaving no gap when all is well. */
.cd-notice-slot {
    margin-bottom: 12px;
}

.cd-notice-slot:empty {
    display: none;
}

/* "No data" inside a module's card. Was the same three inline properties copied
   across five renderers in Customers/Details.cshtml; the failure paths now show
   the same thing, which made a sixth copy the moment to name it. */
.panel-no-data {
    padding: 18px 20px;
    font-size: 13px;
    color: var(--text-sub);
}

/* The Projects tab's container is a grid, so its empty state would otherwise sit
   in the first column at one card's width. */
.proj-grid > .panel-no-data {
    grid-column: 1 / -1;
}

/* The nine .otp-*/.di-* badge declarations that used to live here (each
   repeating the same five properties, differing only in colour) now use the
   .pill base and .pill-green/-red/-orange/-blue/-gray tones defined once in
   the "Pills / badges" section above. Nothing new was needed. */
.dark-mode .pill-gray {
    background: rgba(255, 255, 255, .06);
    color: rgba(220, 220, 230, .5);
}

/* ── Toast ──────────────────────────────────────────────────────── */
#toastRegion {
    position: fixed;
    bottom: calc(24px + var(--imp-banner-h, 0px));
    left: 50%;
    transform: translateX(-50%);
    z-index: 9999;
    display: flex;
    flex-direction: column-reverse;
    gap: 8px;
    pointer-events: none;
}

.toast {
    background: var(--dark-grey-color);
    color: var(--white-color);
    padding: 10px 20px;
    border-radius: 0;
    font-size: 13px;
    box-shadow: var(--shadow-md);
    pointer-events: auto;
    opacity: 0;
    /* Rises rather than slides in from the side, unlike the shared dxToast
       (_ToastNotification.cshtml): this fallback region is bottom-CENTRE, and a
       sideways entrance on a centred element reads as a glitch rather than as
       something arriving from the edge. Same long decelerate and the same
       --dur-slow tempo as that toast, so the two paths feel like one message
       system whichever one a page happens to use. */
    transform: translateY(14px);
    transition: opacity var(--dur-slow) var(--ease-decel), transform var(--dur-slow) var(--ease-decel);
}

.toast.show {
    opacity: 1;
    transform: translateY(0);
}

.toast-success { background: var(--success-color-dark); }
/* Error toast: same as the DevExtreme one (dx.alterations.css) — dark surface,
   4px state rail — so the fallback region and the shared toast look identical. */
.toast-error   { background: var(--toast-bg); border-left: 4px solid var(--error-color); }
.toast-warning { background: var(--warning-color); }

/* ── License Keys page (server keys + product keys tables) ─────── */

.lic-sec-hdr {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px 12px;
    margin-bottom: 10px;
}

.lic-sec-hdr-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 15px;
    font-weight: var(--font-extra-bold);
}

.lic-sec-hdr-count {
    font-size: 12px;
    font-weight: var(--font-regular);
    color: var(--text-sub);
    margin-left: 4px;
}

.lic-sec-hdr-actions {
    display: flex;
    align-items: center;
    gap: 10px;
}

.lic-sec-hdr-btn {
    font-size: 11px;
    padding: 5px 12px;
    white-space: nowrap;
}

@media (max-width: 900px) {
    .lic-sec-hdr-btn {
        font-size: 13px;
    }
}

.lic-sec-hint {
    font-size: 11px;
    color: var(--text-sub);
    font-style: italic;
}

@media (max-width: 600px) {
    .lic-sec-hdr-actions {
        width: 100%;
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
    }
}

.lic-srv-tbl {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    overflow-x: auto;
    margin-bottom: 24px;
}

.lic-srv-inner {
    min-width: 720px;
}

.lic-srv-thead {
    display: grid;
    grid-template-columns: 2fr 1.8fr 1fr 1.4fr 64px;
    align-items: center;
    padding: 10px 16px;
    background: var(--main-background-color-subtle);
    border-bottom: 1px solid var(--main-border-color);
    gap: 12px;
}

.lic-srv-thead span {
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .5px;
    color: var(--text-sub);
}

.lic-srv-thead > *,
.lic-srv-row > * {
    min-width: 0;
}

.lic-srv-thead span {
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
}

.lic-srv-row {
    display: grid;
    grid-template-columns: 2fr 1.8fr 1fr 1.4fr 64px;
    align-items: center;
    padding: 13px 16px;
    border-bottom: 1px solid var(--main-border-color);
    gap: 12px;
    cursor: pointer;
    touch-action: manipulation;
    transition: background .15s;
}

.lic-srv-row:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 3px rgba(var(--main-color-rgb), .35);
}

.lic-srv-row:last-child { border-bottom: none; }

.lic-srv-row:hover {
    background: var(--row-hover-bg);
}

.lic-srv-keycode {
    font-family: monospace;
    font-size: 13px;
    font-weight: var(--font-extra-bold);
    letter-spacing: 1px;
    color: var(--main-text-color);
}

.lic-srv-cell-muted {
    font-size: 12px;
    color: var(--text-sub);
}

/* Server chip filter row above the product keys table */
.lic-srv-filter {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.lic-srv-filter-btn {
    font-size: 11px;
    padding: 4px 10px;
    border-radius: 0;
    border: 1px solid var(--main-border-color);
    background: var(--main-background-color);
    color: var(--text-sub);
    cursor: pointer;
    transition: all .15s;
    white-space: nowrap;
}

.lic-srv-filter-btn:hover {
    border-color: var(--main-color);
    color: var(--main-color);
}

.lic-srv-filter-btn.active {
    background: var(--main-color);
    border-color: var(--main-color);
    color: var(--white-color);
    font-weight: var(--font-bold);
}

/* Icon-only action button shared by server key and product key downloads */
.notif-action-btn {
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    background: var(--main-background-color);
    color: var(--text-sub);
    cursor: pointer;
    transition: border-color .15s, color .15s;
}

.notif-action-btn:hover {
    border-color: var(--main-color);
    color: var(--main-color);
}

.dark-mode .notif-action-btn {
    background: var(--df-dark);
}

/* Product keys table - 13-column grid: checkbox, status, qty, mode, code,
   type, description, maint. ver., days, expiration, key, server, download */
.lkg-wrap {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    overflow-x: auto;
    margin-bottom: 24px;
}

/* The global scrollbar (Style.css) is already a persistent bar, so small screens still
   get the cue that more columns exist to the right. */
.lkg-inner {
    min-width: 940px;
}

.lkg-thead,
.lkg-row,
.lkg-subrow {
    display: grid;
    grid-template-columns: 20px 58px 46px 80px 64px 56px 1.6fr 64px 50px 78px 130px 1fr 32px;
    gap: 8px;
}

.lkg-thead {
    align-items: center;
    padding: 9px 12px;
    background: var(--main-background-color-subtle);
    border-bottom: 1px solid var(--main-border-color);
}

.lkg-thead span {
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-sub);
}

.lkg-thead > *,
.lkg-row > *,
.lkg-subrow > * {
    min-width: 0;
    overflow-wrap: anywhere;
    word-break: break-word;
}

.lkg-thead span {
    white-space: normal;
    /* Headers wrap between words only; the tracks above fit the longest translated word. */
    overflow-wrap: normal;
    word-break: normal;
}

.lkg-row {
    align-items: start;
    padding: 14px 12px;
    font-size: 13px;
    border-bottom: 1px solid var(--main-border-color);
    transition: background .15s;
}

.lkg-row:last-child { border-bottom: none; }

.lkg-row:hover {
    background: var(--row-hover-bg);
}

.lkg-subrow {
    align-items: center;
    padding: 7px 12px;
    font-size: 13px;
    border-bottom: 1px solid var(--main-border-color);
    background: var(--main-background-color-subtle);
}

.lkg-subrow-code {
    font-weight: var(--font-bold);
    color: var(--text-sub);
}

.lkg-subrow-desc { color: var(--text-sub); }

.lkg-code {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.lkg-desc { font-size: 13px; }

.lkg-mono { font-size: 13px; }

.lkg-num { font-size: 13px; }

.lkg-expiry {
    font-size: 13px;
    color: var(--text-sub);
}

/* ── License server detail popup (double-click a Server Keys row) ── */
#licSrvDetailModal > .modal {
    width: 760px;
    padding: 0;
}

.lic-server-hdr {
    /* This popup builds its own header rather than using .modal-header, so the
       facet fill has to be repeated here to keep it matching the other dialogs. */
    background: var(--df-facet-tile);
    padding: 22px 24px;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
}

.lic-server-hdr-right {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 10px;
}

.lic-server-hdr-left {
    min-width: 0;
}

.lic-server-hdr-left h3 {
    color: var(--white-color);
    font-size: 15px;
    font-weight: 700 !important;
    margin-bottom: 4px;
    display: flex;
    align-items: center;
    gap: 6px;
}

.lic-server-hostname {
    font-family: monospace;
    font-size: 12px;
    color: rgba(255, 255, 255, .55);
    background: rgba(255, 255, 255, .07);
    padding: 4px 10px;
    border-radius: 0;
    display: inline-block;
    margin-top: 2px;
}

.lic-server-online {
    border: var(--tag-border);
    display: flex;
    align-items: center;
    gap: 6px;
    background: var(--tag-green-bg);
    /* This header is --df-facet-tile (#1C1C1E) in BOTH themes, but --success-text
       only lightens under dark mode — in light mode it stays a near-black green
       and the label is unreadable here. The on-dark foreground is mode-stable. */
    color: var(--tag-green-fg-on-dark);
    padding: 5px 12px;
    border-radius: 0;
    font-size: 11px;
    font-weight: var(--font-regular);
    flex-shrink: 0;
}

.lic-server-online.no {
    background: var(--tag-red-bg);
    /* Same reason as the activated state above. */
    color: var(--tag-red-fg-on-dark);
}

.lic-server-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--success-color);
}

.lic-server-online.no .lic-server-dot {
    background: var(--error-color);
}

@media (max-width: 480px) {
    .lic-server-hdr {
        position: relative;
        flex-wrap: wrap;
        gap: 10px;
        padding: 16px;
    }

    .lic-server-hdr-left {
        flex: 1 1 100%;
        padding-right: 40px;
    }

    .lic-server-hdr-right {
        flex: 1 1 100%;
        align-items: flex-start;
    }

    .lic-server-hdr-right .modal-close {
        position: absolute;
        top: 16px;
        right: 16px;
    }
}

.lic-server-meta {
    display: flex;
    border-top: 1px solid var(--main-border-color);
}

.lic-server-meta-item {
    flex: 1;
    min-width: 0;
    padding: 12px 20px;
    border-right: 1px solid var(--main-border-color);
}

.lic-server-meta-item:last-child { border-right: none; }

.lic-sml {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .6px;
    color: var(--text-sub);
    font-weight: var(--font-bold);
}

.lic-smv {
    font-size: 13px;
    font-weight: var(--font-bold);
    margin-top: 3px;
    color: var(--main-text-color);
}

.lic-smv.ok { color: var(--success-color-dark); }

@media (max-width: 600px) {
    .lic-server-meta {
        flex-direction: column;
    }

    .lic-server-meta-item {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 12px;
        padding: 10px 16px;
        border-right: none;
        border-bottom: 1px solid var(--main-border-color);
    }

    .lic-server-meta-item:last-child {
        border-bottom: none;
    }

    .lic-smv {
        margin-top: 0;
        text-align: right;
    }
}

.lic-keys-wrap {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    max-height: 300px;
    overflow-y: auto;
    overflow-x: auto;
}

.lic-keys-inner {
    min-width: 480px;
}

.lic-keys-thead {
    display: grid;
    grid-template-columns: 2fr 1.6fr 1fr .8fr;
    align-items: center;
    padding: 10px 16px;
    background: var(--main-background-color-subtle);
    border-bottom: 1px solid var(--main-border-color);
    gap: 12px;
    position: sticky;
    top: 0;
}

.lic-keys-thead span {
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .5px;
    color: var(--text-sub);
}

.lic-key-row {
    display: grid;
    grid-template-columns: 2fr 1.6fr 1fr .8fr;
    align-items: center;
    padding: 12px 16px;
    border-bottom: 1px solid var(--main-border-color);
    gap: 12px;
}

.lic-key-row:last-child { border-bottom: none; }

.lic-prod-cell {
    display: flex;
    align-items: center;
    gap: 8px;
}

.lic-prod-icon {
    width: 26px;
    height: 26px;
    background: rgba(var(--main-color-rgb), .12);
    border-radius: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    flex-shrink: 0;
    color: var(--main-color);
}

.lic-prod-icon .svg-icon > svg {
    width: 16px;
    height: 16px;
}

.lic-prod-label {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.lic-key-code {
    font-size: 11px;
    color: var(--text-sub);
    background: var(--main-background-color-subtle);
    padding: 3px 7px;
    border-radius: 0;
    white-space: nowrap;
}

.lkg-row.lic-filtered-hidden,
.lkg-subrow.lic-filtered-hidden { display: none; }

.lic-act-badge {
    border: var(--tag-border);
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    box-sizing: border-box;
    font-size: 11px;
    font-weight: var(--font-regular);
    padding: 3px 9px;
    border-radius: 0;
}

.lic-act-badge.yes {
    background: var(--tag-green-bg);
    color: var(--tag-green-fg);
}

.lic-act-badge.no {
    background: rgba(var(--warning-color-rgb), .15);
    color: var(--warning-color);
}

.lic-act-badge.unlimited {
    background: rgba(45, 143, 210, .15);
    color: var(--blue-link-hover-color);
}

.lic-type-badge {
    border: var(--tag-border);
    display: inline-block;
    max-width: 100%;
    box-sizing: border-box;
    font-size: 11px;
    font-weight: var(--font-regular);
    padding: 2px 7px;
    border-radius: 0;
    vertical-align: top;
}

.lic-type-badge.base {
    background: rgba(var(--main-color-rgb), .12);
    color: var(--main-color);
}

.lic-type-badge.addon {
    background: rgba(45, 143, 210, .12);
    color: var(--blue-link-hover-color);
}

.lic-type-badge.incl {
    background: rgba(var(--purple-color-rgb), .12);
    color: var(--purple-color-dark);
    border-radius: 0;
}

/* License mode badge - Concurrent (blue) or N Named (purple) */
.lic-mode-badge {
    border: var(--tag-border);
    display: inline-block;
    max-width: 100%;
    box-sizing: border-box;
    font-size: 10px;
    font-weight: var(--font-bold);
    padding: 2px 8px;
    border-radius: 0;
    vertical-align: top;
}

.lic-mode-badge.conc {
    background: rgba(45, 143, 210, .1);
    color: var(--blue-link-hover-color);
}

.lic-mode-badge.named {
    background: rgba(var(--purple-color-rgb), .1);
    color: var(--purple-color-dark);
}

/* Product key row status - Active / Partial / None */
.lkg-status {
    border: var(--tag-border);
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    box-sizing: border-box;
    font-size: 11px;
    font-weight: var(--font-regular);
    padding: 4px 9px;
    border-radius: 0;
}

/* Shared by .lic-act-badge/.lkg-status (the two badge types with an icon):
   both are inline-flex rows now, so the icon and label always sit side by
   side on one line instead of the label wrapping onto its own line below
   the icon when the badge's column is narrow. */
.badge-icon {
    display: inline-flex;
    align-items: center;
    white-space: nowrap;
    margin-right: 3px;
    flex-shrink: 0;
}

/* Square status marker on the licence activation badge, drawn in the badge's colour. */
.badge-icon .lic-dot-square {
    display: inline-block;
    width: 8px;
    height: 8px;
    background: currentColor;
    flex-shrink: 0;
}

.badge-label {
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* .lic-act-badge/.lkg-status are the only two badge types with an icon —
   force their label onto the same line as the icon instead of letting it
   wrap below (the mode/type badges above have no icon and still want the
   label to wrap-anywhere for longer text). */
.lic-act-badge .badge-label,
.lkg-status .badge-label {
    white-space: nowrap;
    min-width: 0;
}

.lkg-status.all {
    background: var(--tag-green-bg);
    color: var(--tag-green-fg);
}

.lkg-status.partial {
    background: var(--tag-orange-bg);
    color: var(--tag-orange-fg);
}

.lkg-status.none {
    background: var(--main-background-color-subtle);
    color: var(--text-sub);
}

/* Maint. Ver. coloring - amber within 3 months, red once expired */
.lkg-maint-warn { color: var(--warning-text) !important; font-weight: var(--font-extra-bold); }
.lkg-maint-exp  { color: var(--error-text) !important; font-weight: var(--font-extra-bold); }

/* ── Knowledge base article feedback (thumbs, helpful bar, reason chips) ──
   Shared by the KB reader, its welcome panel cards and the Licensing help cards. */
.kb-thumbs {
    display: flex;
    align-items: center;
    gap: 8px;
}

.kb-thumb {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 8px;
    border: 1px solid var(--main-border-color);
    border-radius: unset;
    background: transparent;
    color: var(--text-sub);
    font-size: 11px;
    cursor: pointer;
    transition: color .12s, border-color .12s;
}

.kb-thumb:hover {
    border-color: rgba(var(--main-color-rgb), .4);
    color: var(--main-color);
}

/* The reader's own reaction. Colour alone would not survive a colour-blind reader, so the icon
   swaps to its filled variant as well. */
.kb-thumb.active {
    border-color: var(--main-color);
    color: var(--main-color);
}

.kb-thumb .svg-icon {
    width: 14px;
    height: 14px;
}

/* Sits at the end of the article body, so it needs the separating rule the content does not. */
.kb-helpful-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 28px;
    padding-top: 16px;
    border-top: 1px solid var(--main-border-color);
}

.kb-helpful-label {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-color);
}

.kb-feedback-modal {
    width: 560px;
}

.kb-feedback-reasons {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 14px;
}

.kb-reason-chip {
    padding: 7px 12px;
    border: 1px solid var(--main-border-color);
    border-radius: unset;
    background: transparent;
    color: var(--main-text-color);
    font-size: 12px;
    cursor: pointer;
    transition: background .12s, border-color .12s, color .12s;
}

.kb-reason-chip:hover {
    border-color: rgba(var(--main-color-rgb), .4);
}

/* Held on by the comment box rather than greyed out: it is still a live selection, just not one
   the reader can turn off while the words that justify it are there. */
.kb-reason-chip:disabled {
    cursor: default;
    opacity: 1;
}

.kb-reason-chip.active {
    border-color: var(--main-color);
    color: var(--main-color);
    background: rgba(var(--main-color-rgb), .08);
}

.kb-feedback-comment {
    width: 100%;
    min-height: 140px;
    padding: 12px;
    border: 1px solid var(--main-border-color);
    border-radius: unset;
    background: var(--main-background-color);
    color: var(--main-text-color);
    font-family: inherit;
    font-size: 13px;
    resize: vertical;
}

.kb-feedback-count {
    margin-top: 6px;
    text-align: right;
    font-size: 11px;
    color: var(--text-sub);
}

/* ── Licensing Help & Documentation panel (KB article quick links) ── */

/* No active SMP: the cards read as unavailable and swallow clicks, but the list around them
   keeps its pointer events so it still scrolls -- and so a hover still reaches the wrapper,
   whose title carries the reason. */
.lic-help-locked .help-article-list {
    cursor: not-allowed;
}

.lic-help-locked .help-article {
    opacity: .5;
    pointer-events: none;
}

.lic-help-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}

/* Shows roughly the top 4 articles before scrolling for the rest; min-height keeps
   the card's footprint stable while empty (loading) instead of collapsing. */
.help-article-list {
    min-height: 400px;
    max-height: 400px;
    overflow-y: auto;
}

.help-article {
    background: var(--main-background-color-subtle);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    padding: 16px 20px;
    display: flex;
    align-items: flex-start;
    gap: 14px;
    cursor: pointer;
    margin-bottom: 10px;
    text-decoration: none;
    color: inherit;
}

.help-article:hover {
    border-color: rgba(var(--main-color-rgb), .4);
    box-shadow: var(--shadow-sm);
}

/* Brand red on hover, via the token so dark mode gets its lighter variant. */
.help-article:hover .ha-arrow {
    color: var(--main-color);
}

/* The KB welcome panel uses the same cards, so it takes the same dark-mode surface. */
.dark-mode .lic-help-grid .help-article,
.dark-mode .help-articles-grid .help-article {
    background: var(--sidebar-bg);
}

.help-article .ha-body h4 {
    font-size: 14px;
    /* !important: the global h4 rule sets font-weight:300 !important, which
       otherwise wins here regardless of this selector's higher specificity. */
    font-weight: var(--font-bold) !important;
    color: var(--main-text-color);
}

/* min-width:0 because .help-article is a flex row: a flex item's automatic minimum size is its
   content, so without this the text refuses to shrink and overflows instead of wrapping. */
.help-article .ha-body {
    min-width: 0;
}

.help-article .ha-body p {
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 3px;
    /* Three lines at any width, with the browser placing the ellipsis. A server-side character
       cut is not enough on its own -- how many lines it fills depends on the column width. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
}

.ha-arrow {
    margin-left: auto;
    color: var(--text-sub);
    align-self: center;
    flex-shrink: 0;
    transition: color .12s;
}

@media (max-width: 600px) {
    .lic-help-grid { grid-template-columns: 1fr; }
}

/* ── Feature Flags rows (Configuration → Feature Flags) ─────────── */
.feature-row {
    position: relative;
    display: flex;
    align-items: center;
    padding: 13px 20px;
    border-bottom: 1px solid var(--main-border-color);
    gap: 14px;
}

.feature-row:last-child { border-bottom: none; }

.feature-row:hover {
    background: var(--row-hover-bg);
}

.feature-info { flex: 1; min-width: 0; }

.feature-name {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.feature-desc {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 2px;
}

@media (max-width: 600px) {
    .feature-row {
        flex-wrap: wrap;
        gap: 10px 14px;
    }

    .feature-info {
        flex: 1 1 100%;
    }
}

/* Toggle switch — used by Feature Flags + other config rows.
   Compact rectangle with a small square knob that slides: track and knob both
   take var(--radius) (0) so the control matches the theme's sharp-cornered
   controls rather than the pill/iOS look it used to have — and matches the
   dxSwitch, which dx.alterations.css squares corner-for-corner for the same
   reason. 36x18 track with a solid 14x14 handle inset 2px on all four sides:
   2+14+18+2 = 36, so the throw is 18px and the gap stays an even 2px on every
   edge in both states.
   .toggle is a <label>, so the global `label { display:block; margin-top:1em }` in
   Style.css applies to it. In a flex row the margin lands *after* centring, so the
   switch was sitting ~16px below the Edit/Delete buttons beside it — hence the
   explicit margin:0 here. vertical-align keeps it centred in the inline/table-cell
   contexts too (the notification grid drops one straight into a <td>). */
.toggle {
    position: relative;
    width: 36px;
    height: 18px;
    flex-shrink: 0;
    display: inline-block;
    margin: 0;
    vertical-align: middle;
}

.toggle input {
    opacity: 0;
    width: 0;
    height: 0;
}

.toggle-slider {
    position: absolute;
    inset: 0;
    background: var(--main-background-color-subtle-lowlight);
    border-radius: var(--radius);
    cursor: pointer;
    transition: background .2s cubic-bezier(.4, 0, .2, 1);
}

/* Square handle in a square track — the handle takes the same var(--radius) as the
   track it rides in, so nothing on this control is rounded. It was a 50% circle,
   which made the in-house switch the only round-cornered control left in a theme
   that squares everything else, the dxSwitch handle included.
   No box-shadow: a drop shadow reads as a smudge against the flat theme. */
.toggle-slider::before {
    content: '';
    position: absolute;
    height: 14px;
    width: 14px;
    left: 2px;
    top: 2px;
    background: var(--white-color);
    border-radius: var(--radius);
    transition: transform .2s cubic-bezier(.4, 0, .2, 1);
}

.toggle input:checked + .toggle-slider {
    background: var(--main-color);
}

.toggle input:checked + .toggle-slider::before {
    transform: translateX(18px);
}

/* ── Scheduling controls (notification / banner / news modals) ───────
   "Leave blank to start immediately" hints under the date fields, plus the live
   recipient count pinned to the right of the Audience heading. Shared by all three
   admin modals so the three schedule blocks stay identical. */

/* Puts the recipient count at the far right of the Audience heading row. The label
   keeps its own baseline, so the count aligns with the text rather than the box.
   flex-direction MUST be stated: this sits on .form-group-sh, which is already
   `display:flex; flex-direction:column`, so declaring only `display:flex` inherits
   the column direction and stacks the count under the label instead. */
.audience-head {
    display: flex;
    flex-direction: row;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

/* Same green tint/text pairing .pill-green uses, so it inverts correctly in dark
   mode. Turns red via .is-warning when the selection would not be delivered. */
.audience-count {
    flex-shrink: 0;
    font-size: 11.5px;
    line-height: 1.4;
    padding: 2px 8px;
    border-radius: var(--radius);
    background: rgba(var(--success-color-rgb), .12);
    color: var(--success-text);
    white-space: nowrap;
}

.audience-count:empty {
    display: none;
}

/* Sits under the audience chips: an unset dimension is not a restriction, so no
   selection at all reaches every active user. Worth stating outright — an empty
   picker otherwise reads as "nobody selected yet". */
.audience-hint {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 6px;
}

.audience-count b {
    font-weight: var(--font-bold);
}

.audience-count.is-warning {
    background: rgba(var(--error-color-rgb), .1);
    /* --error-text, not --error-color-dark: only the former has a dark-mode value. */
    color: var(--error-text);
    cursor: help;
}

/* Bordered callout for an optional field with no backend behind it yet (e.g.
   Auto-translate). Deliberately outside .form-group-sh so its checkbox label
   reads as a sentence instead of inheriting the field-label uppercase treatment. */
.callout-box {
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    padding: 12px 14px;
}

.callout-box .check-row label {
    font-size: 13px;
    font-weight: var(--font-bold);
}

.callout-box .callout-hint {
    font-size: 11px;
    color: var(--text-sub);
    margin: 4px 0 0 25px;
    line-height: 1.5;
}

.at-deepl-warning {
    font-size: 11px;
    color: var(--error-text);
    margin: 6px 0 0 25px;
    line-height: 1.5;
}

/* Same "show a few, +N more" truncation toggle as ContactsConfig's page-scoped
   .role-pills-more-toggle — promoted here so a pill-list cell elsewhere (e.g. the
   Notifications table's Countries/Lang columns) can reuse it without copying the
   rule into its own page-scoped <style> block. */
.role-pills-more-toggle {
    background: none;
    border: none;
    padding: 0;
    margin-left: 2px;
    color: var(--main-color);
    font-size: 12px;
    font-family: inherit;
    cursor: pointer;
    text-decoration: underline;
}

    .role-pills-more-toggle:hover {
        opacity: .8;
    }

/* Also reaches the copy in ContactsConfig, which sets neither display nor min-height. */
@media (max-width: 600px) {
    .role-pills-more-toggle {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }
}

/* Auto-translate coverage preview: which languages a checked Auto-translate box
   will cover, computed client-side from the language blocks already present.
   The real DeepL translation happens server-side on save. */
.at-cover-line {
    font-size: 12px;
    color: var(--text-sub);
    margin: 10px 0 8px;
}

.at-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.at-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    font-size: 12px;
    font-family: inherit;
    color: var(--text-sub);
    background: var(--main-background-color);
    /* Grid items don't shrink below their content's natural width by default --
       needed so .at-chip-label can actually ellipsis instead of the chip just
       overflowing its column. */
    min-width: 0;
}

/* The language name truncates before the tag ever wraps -- the tag (AUTO/SOURCE/
   SHOWS SOURCE/MANUAL) is the one piece of information every chip in the grid must
   show fully and on one line for the row to scan cleanly. */
.at-chip-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

button.at-chip {
    cursor: pointer;
}

.at-chip.manual {
    color: var(--main-text-color);
    font-weight: var(--font-bold);
}

.at-chip.src {
    border-color: var(--main-color);
    color: var(--main-color);
    font-weight: var(--font-bold);
}

.at-chip-tag {
    border: var(--tag-border);
    font-size: 11px;
    font-weight: var(--font-regular);
    color: var(--tag-neutral-fg);
    background: var(--tag-neutral-bg);
    padding: 3px 7px;
    border-radius: 0;
    white-space: nowrap;
    flex-shrink: 0;
}

.at-chip.src .at-chip-tag {
    color: var(--main-color);
    background: var(--tag-brand-bg);
}

/* Neutral while the count is in flight — a pending number must not wear the green
   "confirmed" tint. Holds the 5-dot .cd-spinner (the app's only loader) plus the
   "Calculating recipients" label. */
.audience-count.is-calculating {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--gray-bg);
    color: var(--text-sub);
}

/* ── "Select date" placeholder for datetime-local fields ─────────────
   A datetime-local input cannot carry a placeholder: browsers render a fixed
   "dd-mm-yyyy --:--" mask whenever it is empty. Wrapping it in .dt-field lets us blank
   that mask while the field is empty and unfocused, and show a plain label over it.
   Focusing reveals the real mask so typing/picking behaves natively, and clearing a
   value stays plain select-and-delete — no custom clear button or state to manage. */
.dt-field {
    position: relative;
    display: block;
    width: 100%;
}

/* Hidden by default so browsers that skip the @supports block below (no WebKit
   datetime pseudo-elements) keep their native mask instead of showing both. */
.dt-placeholder {
    display: none;
}

@supports selector(::-webkit-datetime-edit) {
    .dt-field > input:not(.has-value):not(:focus)::-webkit-datetime-edit {
        color: transparent;
    }

    .dt-field > input:not(.has-value):not(:focus) + .dt-placeholder {
        display: block;
    }

    .dt-placeholder {
        position: absolute;
        /* Matches .form-group-sh input's 12px horizontal padding + 1.5px border, so the
           label starts exactly where the real value will appear. */
        left: 13.5px;
        top: 50%;
        transform: translateY(-50%);
        font-size: 13px;
        color: var(--text-sub);
        pointer-events: none;
    }
}

.sched-start-hint {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 4px;
}

/* Colour, size and badge: shared field-message rule in Style.css. */
.sched-start-error {
    margin-top: 4px;
}

/* ── Dashboard card grid ────────────────────────────────────────── */
.dashboard-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px;
}

.dash-card .card-body {
    padding: 14px 18px 18px;
}

.dash-link-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.dash-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    border-radius: var(--radius);
    color: var(--main-text-color);
    text-decoration: none;
    font-size: 13px;
    transition: background .12s, color .12s;
}

.dash-link:hover {
    background: var(--main-background-color-subtle);
    color: var(--main-color);
}

.dash-link.home-link-disabled {
    opacity: .55;
    cursor: not-allowed;
}

.dash-link-icon {
    width: 18px;
    flex-shrink: 0;
    color: var(--text-sub);
    text-align: center;
    font-size: 14px;
}

/* ── Dashboard grid-main (2-col) + activity timeline + data-table ── */
.grid-main {
    display: grid;
    grid-template-columns: 1fr 380px;
    gap: 16px;
    margin-bottom: 20px;
    align-items: stretch;
}

/* Dashboard's Recent Cases / Recent Activity cards: stretch to match each other's height
   (align-items:stretch above) instead of each sizing to its own content, then let the body
   fill the remaining space and scroll if its list is the taller of the two. */
.grid-main > .card {
    display: flex;
    flex-direction: column;
}

.grid-main > .card > .card-body {
    flex: 1;
    overflow-y: auto;
}

/* Dark mode: raise the dashboard cards to the same #2A2E33 as the stat tiles above
   them. Style.css paints dark .card, .card-header and .card-body with the #141414
   canvas colour, so all three have to be restated here. */
.dark-mode .grid-main > .card,
.dark-mode .grid-main > .card > .card-header,
.dark-mode .grid-main > .card > .card-body {
    background-color: var(--list-surface-bg);
}

/* Recent Activity has no table, so its list matched the grey title bar; use the dark body
   the My Cases rows have. */
.dark-mode .grid-main > .card:has(.activity-list) > .card-body {
    background-color: var(--main-background-color);
}

/* Latest Downloads and News & Updates sit outside .grid-main, so they missed the grey title
   bar the My Cases card gets. One selector per body type: table, news list. */
.dark-mode #home-page .card:has(> .card-body > .data-table) > .card-header,
.dark-mode #home-page .card:has(> .card-body .nf-widget-item) > .card-header {
    background-color: var(--list-surface-bg);
}

/* In dark mode the title bar and the column-heading row are too close in tone to read as
   separate bands; lift the heading row a step (about 8% toward the text colour). */
.dark-mode #home-page .data-table th {
    background-color: color-mix(in srgb, var(--list-surface-bg) 92%, var(--main-text-color));
}

@media (max-width: 1024px) {
    .grid-main { grid-template-columns: 1fr; }
}

/* table-layout:auto has no minimum, so columns crush by wrapping instead of
   overflowing and the scroll never triggers. max-content sizes each table to
   its own content rather than a px guess that suits only one of them. */
.card-body:has(> .data-table) {
    overflow-x: auto;
    overflow-y: hidden;
}

.data-table {
    min-width: max-content;
}

@media (max-width: 600px) {
    /* One label, not two words — the arrow was wrapping above the text. */
    #home-page .download-link {
        white-space: nowrap;
    }
}

/* Pinned count, not auto-fit, which orphans a 4th card at some width. Folds
   where 4x180px + gaps stops fitting — twice, because the sidebar reclaims
   240px above 900px. Recompute if sidebar, padding, gap or count change. */
.stat-row {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 20px;
}

@media (max-width: 812px) {
    .stat-row {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (width > 900px) and (max-width: 1052px) {
    .stat-row {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

.stat-row .stat-card {
    cursor: default;
    transition: box-shadow .15s;
}

.stat-row .stat-card:hover {
    box-shadow: var(--shadow-md);
}

body.dark-mode .stat-row .stat-card:hover {
    box-shadow: 0 0 18px rgba(255, 255, 255, 0.16), 0 6px 32px rgba(0, 0, 0, 0.6);
}

.stat-row a.stat-card {
    text-decoration: none;
    color: inherit;
    cursor: pointer;
}

.stat-card .stat-icon {
    position: absolute;
    top: 14px;
    right: 14px;
    display: inline-flex;
    color: var(--main-color);
    opacity: .45;
}

body.dark-mode .stat-card .stat-icon {
    color: var(--main-color-disabled);
}

.stat-card .stat-icon .svg-icon > svg {
    width: 20px;
    height: 20px;
}

/* Stacked so nothing competes for horizontal room. The hint line stays: it can
   carry a status alert, not just a routine trend. */
@media (max-width: 600px) {
    .stat-row .stat-card {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 4px;
        padding: 10px 12px;
    }

    .stat-row .stat-card .stat-icon {
        position: static;
        opacity: 1;
        order: 1;
    }

    .stat-row .stat-card .stat-value {
        order: 2;
        font-size: 18px;
        white-space: nowrap;
    }

    .stat-row .stat-card .stat-value.stat-value-text {
        min-height: 0;
        font-size: 14px;
        /* Status words ("No Active Licenses") are longer than counts and must wrap, not clip. */
        white-space: normal;
        overflow-wrap: anywhere;
    }

    /* Wrap rather than side-scroll: a hidden scrollbar on touch just looks clipped. */
    .stat-row .stat-card .stat-label {
        order: 3;
        margin-bottom: 0;
        padding-right: 0;
        font-size: 10px;
        max-width: 100%;
        overflow-wrap: anywhere;
    }

    /* Same treatment as the label, a status alert must stay fully readable. */
    .stat-row .stat-card .stat-hint {
        order: 4;
        margin-top: 0;
        max-width: 100%;
        overflow-wrap: anywhere;
    }

    /* Top accent stripe becomes a left stripe to suit the row orientation. */
    .stat-row .stat-card::before {
        top: 0;
        bottom: 0;
        left: 0;
        right: auto;
        width: 3px;
        height: auto;
    }
}

.card:has(.activity-list) {
    display: flex;
    flex-direction: column;
}

.card:has(.activity-list) .card-body {
    flex: 1;
}

.activity-list {
    padding: 6px 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    height: 100%;
}

.activity-empty {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 18px 20px;
    color: var(--text-sub);
    font-size: 13px;
}

.activity-item {
    display: grid;
    grid-template-columns: 32px 1fr auto;
    gap: 12px;
    align-items: center;
    padding: 10px 18px;
    border-bottom: 1px solid var(--main-border-color);
}

.activity-item:last-child { border-bottom: 0; }

.act-icon {
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text);
    background: var(--main-background-color-subtle);
    flex-shrink: 0;
}

.act-icon .svg-icon > svg {
    width: 16px;
    height: 16px;
}

.act-body strong {
    display: block;
    font-size: 13px;
    font-weight: var(--font-regular);
    color: var(--main-text-color);
    line-height: 1.3;
}

.act-body p {
    font-size: 11px;
    color: var(--text-sub);
    margin: 2px 0 0;
    line-height: 1.3;
}

.act-time {
    font-size: 11px;
    color: var(--text-sub);
    white-space: nowrap;
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    border: 1px solid var(--main-border-color);
}

.data-table th {
    text-align: left;
    padding: 10px 18px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .6px;
    color: var(--text-sub);
    font-weight: var(--font-bold);
    border-bottom: 1px solid var(--main-border-color);
    background: var(--main-background-color-subtle);
}

.data-table td {
    padding: 11px 18px;
    border-bottom: 1px solid var(--main-border-color);
    color: var(--main-text-color);
}

.data-table tr:last-child td { border-bottom: 0; }

.data-table tr:hover td { background: var(--row-hover-bg); }

/* Partner Dev Items tab (.di-table) — the title column needs a guaranteed minimum so a long
   heading doesn't get squeezed to one letter per line once the other columns claim their
   natural (auto) width; scoped to .di-table rather than .data-table so every other table
   sharing that class is unaffected. */
.di-table .di-title-col { min-width: 218px; }

.td-muted {
    color: var(--text-sub);
    font-size: 12px;
}

/* ── Maintenance / license alert banners ─────────────────────────── */
.maint-alert {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding: 14px 18px;
    border-radius: var(--radius);
    border: 1px solid var(--main-border-color);
    background: var(--main-background-color);
    margin-bottom: 16px;
    position: relative;
}

/* State variants — full hairline border tint + bg tint. The icon and
   strong heading carry the meaning; no colored side-stripe (cross-system
   absolute ban). */
.maint-alert.warn {
    border-color: rgba(var(--warning-color-rgb), .35);
    background: rgba(var(--warning-color-rgb), .06);
}

.maint-alert.info {
    border-color: rgba(45, 143, 210, .35);
    background: rgba(45, 143, 210, .06);
}

.maint-alert-icon { font-size: 22px; flex-shrink: 0; }

.maint-alert.info .maint-alert-icon { color: rgb(45, 143, 210); }

.maint-alert.info .maint-alert-icon svg { stroke-width: 2.25; }

.maint-alert-body strong {
    display: block;
    font-size: 14px;
    color: var(--main-text-color);
    margin-bottom: 4px;
}

.maint-alert-body p {
    font-size: 13px;
    color: var(--text-sub);
    margin: 0 0 8px;
}

/* Cases tab on a phone: Create Support Case spans the row under the filters,
   with a 44px touch target. */
@media (max-width: 600px) {
    [data-cd-panel="cases"] .section-action-btn {
        width: 100%;
        min-height: 44px;
    }

    /* Status and product filters split the row 50/50. */
    [data-cd-panel="cases"] div:has(> .sh-select),
    [data-cd-panel="invoices"] div:has(> .sh-select) {
        flex: 1 1 100%;
    }

    [data-cd-panel="cases"] div > .sh-select,
    [data-cd-panel="invoices"] div > .sh-select {
        flex: 1 1 0;
        min-width: 0;
    }
}

/* Unregistered-users banner on a phone: Invite All drops to its own full-width
   row so the message isn't squeezed into a narrow column beside it. */
@media (max-width: 600px) {
    #unregBanner {
        flex-wrap: wrap;
    }

    #unregBanner .maint-alert-body {
        flex: 1 1 0;
        min-width: 0;
    }

    #unregBanner .btn-primary-sh {
        flex: 1 1 100%;
    }
}

.maint-alert-actions {
    display: flex;
    gap: 8px;
    margin-top: 6px;
    flex-wrap: wrap;
}

.alert-dismiss {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 24px;
    height: 24px;
    border: 0;
    background: transparent;
    color: var(--text-sub);
    cursor: pointer;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
}

.alert-dismiss:hover { background: var(--main-background-color-subtle); color: var(--main-text-color); }

/* ── Alert cards ───────────────────────────────────────
   The shared card for operational alerts that need the user to DO something:
   maintenance expiring, a case awaiting reply, a failed sync. Raised through
   AlertService and rendered by Views/Shared/_AlertCard.cshtml — no module
   writes its own alert markup or picks its own colour.

   Treatment: a saturated severity block anchors the card while the body stays
   on the ordinary card surface, so an alert sits in the same visual family as
   the rest of the dashboard rather than overriding it. Selected from a set of
   nine on #364116.

   The block is a solid fill, not the 4px side-stripe the redesign removed
   — it is wide enough to carry the category icon, which is what makes it a
   surface rather than a rule.

   There is no Success variant. Alerts are for outstanding work; a completed
   operation is a toast (window.showMessage), not a card that has to be
   dismissed. .maint-alert is unrelated and stays as it is — it carries passive
   page state, which is a quieter job. */

.sh-alert-stack {
    display: flex;
    flex-direction: column;
    /* The 12px between cards lives on the cards themselves rather than in a flex gap.
       A gap belongs to the container, so it would hold its 12px open underneath a card
       collapsing to zero height and the stack would close the rest of the way in a jump
       the moment the card was removed; a margin the card owns animates away with its
       height. This 12px plus the last card's own margin is the 24px the stack has always
       kept above the dashboard grid. */
    margin-bottom: 12px;
    /* A dismissing card slides out to the right (see .sh-dismissing). Without this the
       transform would widen the document and flash a horizontal scrollbar on the page. */
    overflow-x: hidden;
}

/* Shared dismiss motion - driven by shDismissElement() in utils.js, which pins the
   element's measured height before adding this class. Used by the dashboard alert
   stack and by the notification bell's rows, so a dismissal reads the same wherever
   the user performs it: the card slides out towards the button that dismissed it
   while the gap it leaves closes behind it.

   This is the one place that animates height, against the rule recorded on the motion
   tokens at the top of this file. Closing the gap is the point of the transition, and
   nothing reachable through transform does it: the items below have to move up. The
   !important flags exist because the collapsed values must beat whatever padding,
   margin and border the dismissing component sets on itself - .sh-alert and
   .bell-drop-item have different box models and neither should have to know about
   this class.

   Style.css's prefers-reduced-motion block collapses the durations app-wide, so a
   motion-sensitive user gets the same dismissal instantly. */
.sh-dismissing {
    overflow: hidden !important;
    opacity: 0;
    transform: translateX(105%);
    height: 0 !important;
    min-height: 0 !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    margin-top: 0 !important;
    margin-bottom: 0 !important;
    border-top-width: 0 !important;
    border-bottom-width: 0 !important;
    /* Opacity runs shorter than the slide so the card is gone before the gap finishes
       closing - a card still faintly visible while the stack reflows reads as a glitch. */
    transition: transform var(--dur-slow) var(--ease-out), height var(--dur-slow) var(--ease-out), margin var(--dur-slow) var(--ease-out), padding var(--dur-slow) var(--ease-out), opacity var(--dur-base) linear;
    pointer-events: none;
}

.sh-alert {
    display: flex;
    align-items: stretch;
    position: relative;
    /* Carried by the card, not the stack - see .sh-alert-stack. */
    margin-bottom: 12px;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    /* Keeps any long unbroken content inside the card's own border. */
    overflow: hidden;
}

/* The whole card takes a wash of its state hue, not just the icon tile, so an
   alert is legible as an alert from across the page rather than only once the
   eye reaches the mark. Kept low: the title, the tile and the button carry the
   signal, and a heavier fill would put body text on saturated colour - which is
   the objection to the fully-solid treatment.

   Both stops come from the same stable rgb triples the tile uses, so the wash
   holds its hue in dark mode instead of washing out to grey. --*-text on a
   15-18% wash of its own hue is the case Style.css records as verified:
   7.90:1, 5.33:1 and 6.38:1 in light. */
.sh-alert-critical {
    background: rgba(var(--error-color-rgb), .07);
    border-color: rgba(var(--error-color-rgb), .30);
}

.sh-alert-warning {
    background: rgba(var(--warning-color-rgb), .09);
    border-color: rgba(var(--warning-color-rgb), .32);
}

.sh-alert-info {
    background: rgba(var(--blue-color-rgb), .08);
    border-color: rgba(var(--blue-color-rgb), .30);
}

/* The severity tile. A soft wash of the state hue carrying the glyph in that
   state's TEXT colour — the same construction as the --tag-* chips, which are
   the app's single source of truth for tinted marks. Inset rather than
   full-height so the card reads as a card with a mark on it, not a card with a
   coloured edge.

   Both halves flip with the theme: the wash is an alpha of a stable fill hue,
   and the glyph colour comes from the --*-text tokens, which are the only state
   colours cleared for a `color:` declaration. Style.css records these verified
   on a 15-18% wash of their own hue in both themes — 7.90:1, 5.33:1 and 6.38:1
   in light. */
.sh-alert-block {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    /* Inset from the card edge rather than stretching, which is what turns the
       block into a tile. Left margin doubles as the card's left padding. */
    margin: 14px 0 14px 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius);
    background: rgba(var(--blue-color-rgb), .22);
    color: var(--blue-link-hover-color);
}

.sh-alert-critical .sh-alert-block {
    background: rgba(var(--error-color-rgb), .20);
    color: var(--error-text);
}

.sh-alert-warning .sh-alert-block {
    background: rgba(var(--warning-color-rgb), .24);
    color: var(--warning-text);
}

.sh-alert-info .sh-alert-block {
    background: rgba(var(--blue-color-rgb), .22);
    color: var(--blue-link-hover-color);
}

.sh-alert-block .svg-icon svg { width: 20px; height: 20px; }

.sh-alert-body {
    flex: 1 1 auto;
    /* Without an explicit zero basis a long unbroken string (a case subject, a
       customer name) forces the flex item wider than the card and pushes the
       actions off the edge. */
    min-width: 0;
    padding: 14px 16px 14px 13px;
}

/* The title is the one place severity reaches the type, so it uses the TEXT
   tokens rather than the block fills — those flip with the theme and are the
   only state colours cleared for use in a `color:` declaration. */
.sh-alert-title {
    font-size: 14px;
    font-weight: var(--font-extra-bold);
    line-height: 1.3;
    overflow-wrap: anywhere;
}

.sh-alert-critical .sh-alert-title { color: var(--error-text); }
.sh-alert-warning  .sh-alert-title { color: var(--warning-text); }
.sh-alert-info     .sh-alert-title { color: var(--blue-link-hover-color); }

.sh-alert-message {
    margin: 3px 0 0;
    font-size: 13px;
    line-height: 1.45;
    color: var(--text-sub);
    overflow-wrap: anywhere;
}

/* Timestamp. Deliberately the quietest thing on the card — it qualifies the
   alert, it is not part of the demand. Sits below the message so the title and
   action keep the top of the reading order. */
.sh-alert-time {
    display: flex;
    align-items: center;
    gap: 5px;
    margin: 7px 0 0;
    font-size: 12px;
    color: var(--text-sub);
}

.sh-alert-time-icon { display: flex; flex-shrink: 0; }
.sh-alert-time-icon svg { width: 13px; height: 13px; }

.sh-alert-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
    padding: 14px 16px 14px 0;
    /* Clears the absolutely-positioned dismiss button in the corner. */
    margin-right: 22px;
}

/* The action control is .btn-primary-sh from the shared button family rather
   than a bespoke rule — the card body is an ordinary light surface, so the
   app's own primary button is already correct here, hover and press included. */

.sh-alert-dismiss {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 24px;
    height: 24px;
    flex-shrink: 0;
    border: 0;
    background: transparent;
    color: var(--text-sub);
    cursor: pointer;
    border-radius: var(--radius);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    line-height: 1;
    padding: 0;
}

.sh-alert-dismiss:hover,
.sh-alert-dismiss:focus-visible {
    background: var(--main-background-color);
    color: var(--main-text-color);
}

/* The "+N more" line under a capped stack. Deliberately quiet — it is a pointer
   to the bell, not another thing demanding attention. */
.sh-alert-overflow {
    margin: 0;
    padding: 2px 2px 0;
    font-size: 12.5px;
    color: var(--text-sub);
}

/* Below the sm breakpoint the action would be squeezed to a few characters
   beside a wrapped title. The block and body stay on the first row — wrapping
   the block would stretch it to full width — and the action drops beneath,
   indented to line up with the body rather than the block. */
@media (max-width: 575.98px) {
    .sh-alert { flex-wrap: wrap; }

    .sh-alert-block { margin-left: 14px; }

    .sh-alert-body {
        flex: 1 1 calc(100% - 54px);
        padding: 13px 14px 13px 12px;
    }

    .sh-alert-actions {
        flex-basis: 100%;
        padding: 0 14px 14px 66px;
        margin-right: 0;
        flex-wrap: wrap;
    }
}

/* Re-skin legacy details-header-container to look like the prototype's co-hero card */
.details-header-container {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    padding: 18px 22px;
    margin-bottom: 16px;
    box-shadow: var(--shadow-sm);
}

.details-headers-title {
    font-size: 14px;
    text-transform: uppercase;
    letter-spacing: .8px;
    color: var(--text-sub);
    font-weight: var(--font-bold);
    margin: 0 0 4px;
}

.details-header-text {
    font-size: 22px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
    line-height: 1.2;
}

.details-header-subtitle-container {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
}

/* ── Cases list — toolbar + cards (Cases/ViewAll) ───────────────── */
/* Pins tabs/filters/New Case under the topbar while cards scroll beneath.
   --sticky-top is set inline per instance (Index.cshtml) to stack correctly under the partner view-bar when present. */
/* Paints the page canvas, not white: the band is a slice of the page that happens to
   stay pinned, so it has to read as page. --main-background-color (#FFFFFF) turned it
   into a white panel sitting on the grey canvas, which made the tabs and the toolbar
   look like a card that stops at the container edge. Opaque either way, so cards still
   scroll under it cleanly. */
.cases-sticky-header {
    position: sticky;
    top: var(--sticky-top, var(--topbar-h));
    z-index: 20;
    background: var(--workspace-bg);
    padding-top: 16px;
    margin-top: -16px;
}

.pcase-view-bar.cases-sticky-header {
    padding-top: 0;
    margin-top: 24px;
}

/* The shared .tab-bar carries margin: 24px 0 20px. Inside this header that 24px landed
   on top of .page-header's own 24px bottom margin — 48px of empty canvas between the
   page subtitle and the tabs, before the tab's own 10px padding. The header's padding-top
   already supplies the separation, so the tabs take none of their own and the gap is the
   page rhythm's 24px. Also keeps the partner variant's tabs from collapsing their margin
   through the wrapper above and reopening the seam under the Your/Customer switcher —
   this replaces the .pcase-view-bar-scoped rule that used to do only that. */
.cases-sticky-header > .tab-bar {
    margin-top: 0;
}

/* Fallback height of .pcase-view-bar; syncPcaseViewBarHeight() (Cases/Index.cshtml)
   overwrites this with the actual rendered height on load and on resize. */
:root {
    --pcase-view-bar-h: 49px;
}

/* Search, filters and the New Case button share one line all the way down to
   600px; the filters absorb the loss by shrinking to a common floor rather than
   wrapping. Below 600px the row stacks — see the mobile block further down. */
.cases-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 4px;
    padding-bottom: 16px;
    flex-wrap: nowrap;
}

.cases-toolbar input::placeholder { color: var(--text-sub); }

/* The two filters carry long option text ("All Products", product names) in what was a
   130px-wide, 13px control with a 10px caret — narrower and quieter than the search box
   sitting next to them. Sized up here so the whole toolbar row reads as one set of
   controls and the caret is an obvious "this opens" affordance.

   Sizing only — no widths. This rule used to carry min-width:200px on the toggle, which
   would now pin the row open and stop it collapsing; the .cases-toolbar-filters rules
   below own every width in this toolbar. */
.cases-toolbar .sh-select .sh-select-toggle {
    padding: 10px 14px;
    font-size: 14px;
}

.cases-toolbar .sh-select .sh-select-caret {
    font-size: 16px;
    line-height: 1;
}

.cases-toolbar .sh-select .sh-select-option {
    font-size: 14px;
}

/* Match the search box to the taller filters so the row lines up. Its max-width lives
   with the other widths below (200px), not here. */
.cases-toolbar input.list-filter-input {
    padding: 10px 14px;
    font-size: 14px;
}

/* Style.css has a global `input:not([type="checkbox"]):not([type="radio"])` reset that
   sets background-color: var(--main-background-color-subtle). Its specificity (0,2,1)
   outranks .list-filter-input (0,1,0), so the search box rendered grey — the only tinted
   control in a row of white .sh-select toggles, and against the canvas it read as a filled
   block rather than a field. Mirroring the reset's shape beats it without !important.
   --control-bg is the form-control fill the toggles beside it already use: white in light
   mode, #202024 in dark, which is exactly what the reset gave it there. */
.cases-toolbar input.list-filter-input:not([type="checkbox"]):not([type="radio"]) {
    background: var(--control-bg);
}

.cases-toolbar-filters {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1 1 auto;
    min-width: 0;
    flex-wrap: wrap;
}

/* Mobile-only icon toggles (see the 600px block below) - no job above that width. */
.cases-search-toggle,
.cases-filter-toggle-wrap {
    display: none;
}

/* Its box is stripped entirely so the selects lay out inline with the rest of
   .cases-toolbar-filters; the 600px block re-adds a real box to make it toggle. */
.cases-filter-panel {
    display: contents;
}

.cases-toolbar-filters > input.list-filter-input,
.cases-toolbar-filters .sh-select {
    flex: 1 1 0;
    min-width: 120px;
}

/* 200px, down from 220px: the two filters beside it render ~126px, and at 220 the search
   box was 1.75x their width, heavier than the prototype's ~1.5x. */
.cases-toolbar-filters > input.list-filter-input {
    max-width: 200px;
}

.cases-toolbar-filters .sh-select {
    max-width: var(--sh-selected-width, none);
}

.cases-toolbar-filters .sh-select-toggle {
    min-width: 0 !important;
    width: 100%;
}

.cases-toolbar-new {
    flex-shrink: 0;
    margin-left: auto;
}

.case-card {
    position: relative;
    background: var(--list-surface-bg);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    padding: 18px 20px;
    margin-bottom: 10px;
    /* Wraps by construction rather than at a breakpoint
       the ID heads the card, .case-body takes a full-width line of its own and
       .case-meta lands on a third line at the bottom right. Reflows
       continuously, so it cannot break at an untested width. */
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 8px;
    cursor: pointer;
    transition: box-shadow .2s, border-color .15s;
    text-decoration: none;
    color: inherit;
}

/* Shown briefly while a click navigates to Cases/ViewCase — the navigation itself is a full
   page load, but this avoids the click feeling dead while the next page starts loading. */
.case-card-loading {
    pointer-events: none;
}

.case-card-loading-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--list-surface-bg) 70%, transparent);
    border-radius: unset;
}

/* Positioning context for .case-row-loading-overlay, which JS sizes/places
   from the clicked row's own rect (a <tr> can't host it directly).
   #CasesList is the Company Details Cases tab, which uses the same overlay;
   unlike .dash-cases-body it scrolls, so its handler adds scrollTop to the offset. */
.dash-cases-body,
#CasesList {
    position: relative;
}

.case-row-loading-overlay {
    position: absolute;
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--main-background-color) 70%, transparent);
    /* Sized from the row's rect, which includes its separator lines; keep those visible. */
    clip-path: inset(1px 0);
}

.case-card:hover {
    box-shadow: var(--shadow-sm);
    border-color: rgba(var(--main-color-rgb), .25);
}

.case-id {
    font-size: 12px;
    font-weight: var(--font-extra-bold);
    color: var(--main-color);
    flex-shrink: 0;
}

/* flex-basis:100% forces the title/description/product line beneath the ID;
   min-width:0 lets its text truncate instead of pushing the card wider. */
.case-body {
    flex-basis: 100%;
    width: 100%;
    min-width: 0;
    margin-top: 4px;
}

.case-body h4 {
    font-size: 14px;
    /* !important: the global h4 rule sets font-weight:300 !important, which
       otherwise wins and left case titles rendering thin/light instead of
       matching the card titles used elsewhere in the app. */
    font-weight: var(--font-bold) !important;
    margin: 0 0 4px;
    color: var(--main-text-color);
    /* Wraps onto additional lines instead of overflowing the card — word-break covers long
       unbroken strings (e.g. a case number with no spaces) that white-space:normal alone
       wouldn't wrap. */
    white-space: normal;
    overflow-wrap: break-word;
    word-break: break-word;
}

.case-body p {
    font-size: 13px;
    font-weight: var(--font-regular);
    line-height: 1.5;
    color: var(--text-sub);
    margin: 0;
}

/* Single line, ellipsized if it overflows — .case-body's min-width:0 (above) lets this
   shrink with the card instead of pushing it wider, so the ellipsis point is responsive. */
.case-body p.case-desc-clamp {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

.case-product-pill {
    display: inline-block;
    margin-top: 8px;
}

/* Follows .case-body, which is a full-width flex line, so this lands on a line of
   its own at the bottom of the card; margin-left:auto pins it to the right edge.
   Status/priority/customer pills, the updated date and the feedback link all read
   as one row — wrap is a safety valve for the narrowest phones only. */
.case-meta {
    margin-left: auto;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 4px 8px;
    text-align: right;
}

.case-meta-pills {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 6px;
}

.case-date {
    font-size: 11px;
    color: var(--text-sub);
}

/* ───── Cases toolbar below the mobile boundary ─────
   Search and the filter dropdowns collapse to icon toggles (toggleCaseSearch()/
   toggleCaseFilters()) sharing a row with New Case. */
@media (max-width: 600px) {
    .cases-toolbar {
        display: grid;
        grid-template-columns: auto auto 1fr;
        align-items: center;
        gap: 10px;
    }

    .cases-toolbar-filters {
        display: contents;
    }

    .cases-search-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 34px;
        height: 34px;
        flex-shrink: 0;
        padding: 0;
        grid-row: 1;
        grid-column: 1;
        border: 1.5px solid var(--main-border-color);
        background: var(--main-background-color);
        color: var(--text-sub);
        cursor: pointer;
    }

    .cases-search-toggle:hover,
    .cases-toolbar.cases-search-open .cases-search-toggle {
        border-color: var(--main-color-outline);
        color: var(--main-color);
    }

    .cases-filter-toggle-wrap {
        display: inline-flex;
        grid-row: 1;
        grid-column: 2;
        border: 1.5px solid var(--main-border-color);
    }

    .cases-filter-toggle {
        display: inline-flex;
        align-items: center;
        gap: 5px;
        white-space: nowrap;
        padding: 4px 8px 4px 10px;
        height: 34px;
        font-size: 12px;
        font-weight: 600;
        border: none;
        background: var(--main-background-color);
        color: var(--text-sub);
        cursor: pointer;
    }

    .cases-filter-toggle:hover,
    .cases-toolbar.cases-filters-open .cases-filter-toggle {
        color: var(--main-color);
    }

    .cases-filter-clear-x {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 24px;
        border: none;
        border-left: 1.5px solid var(--main-border-color);
        background: var(--main-background-color);
        color: var(--text-sub);
        cursor: pointer;
    }

    .cases-filter-clear-x:hover {
        color: var(--main-color);
    }

    .cases-filter-clear-x > .svg-icon > svg {
        width: 12px;
        height: 12px;
    }

    /* Part of the toggle's own label, not a separate control - only exists to be read. */
    .cases-filter-count {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 15px;
        height: 15px;
        padding: 0 3px;
        background: var(--main-color);
        color: var(--white-color);
        font-size: 10px;
        font-weight: 700;
        line-height: 1;
    }

    .cases-toolbar-new {
        grid-column: 3;
        grid-row: 1;
        justify-self: end;
        width: auto;
        margin-left: 0;
    }

    /* Hidden until its toggle opens it; then drops to its own full-width row below
       the icon row rather than fighting the grid's three narrow columns. */
    .cases-toolbar-filters > input.list-filter-input {
        display: none;
        grid-column: 1 / -1;
        grid-row: 2;
        max-width: none;
    }

    .cases-toolbar.cases-search-open .cases-toolbar-filters > input.list-filter-input {
        display: block;
    }

    .cases-filter-panel {
        display: none;
        grid-column: 1 / -1;
        grid-row: 3;
        flex-direction: column;
        gap: 10px;
    }

    .cases-toolbar.cases-filters-open .cases-filter-panel {
        display: flex;
    }

    .cases-filter-panel > .sh-select {
        width: 100%;
        max-width: none;
    }
}

.case-prod-tag {
    display: inline-block;
    margin-top: 6px;
    padding: 2px 8px;
    background: var(--main-background-color-subtle);
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    font-size: 11px;
    color: var(--text-sub);
    font-weight: 500;
}

/* "Your Cases" / "My Cases" section divider used above the cases list */
.psec-my-label {
    font-size: 13px;
    font-weight: var(--font-extra-bold);
    color: var(--text-sub);
    text-transform: uppercase;
    letter-spacing: .5px;
    margin-bottom: 14px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.psec-my-label::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--main-border-color);
}

/* Legacy aliases — preserve any callers from earlier phases that emitted
   .case-card-head / .case-card-id / .case-card-title / .case-card-meta. */
.case-card-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 6px; }
.case-card-id { font-size: 12px; color: var(--text-sub); font-weight: var(--font-bold); }
.case-card-title { font-size: 14px; font-weight: var(--font-extra-bold); color: var(--main-text-color); margin: 0 0 4px; }
.case-card-meta { font-size: 12px; color: var(--text-sub); display: flex; flex-wrap: wrap; gap: 14px; }

/* ── Product download card (Downloads pages) ─────────────────────── */
.product-dl-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
}

.product-dl-card {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.product-dl-card-title {
    font-size: 16px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
}

.product-dl-card-meta {
    font-size: 12px;
    color: var(--text-sub);
}

.product-dl-card .btn-dl {
    margin-top: auto;
}

/* ── Downloads page (prototype dl-* pattern) ──────────────────────── */
.filter-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 20px;
    flex-wrap: wrap;
}

.filter-bar input,
.filter-bar select {
    padding: 8px 12px;
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    font-size: 13px;
    background: var(--contrast-bg);
    color: var(--main-text-color);
    outline: none;
}

.filter-bar input[type="search"] {
    width: 220px;
}

.filter-tag {
    padding: 6px 12px;
    border-radius: 0;
    border: 1px solid var(--main-border-color);
    font-size: 12px;
    font-weight: 500;
    color: var(--text-sub);
    background: var(--contrast-bg);
    cursor: pointer;
    font-family: inherit;
    line-height: normal;
}

.filter-tag:hover {
    border-color: var(--main-color-outline);
    color: var(--main-text-color);
}

.filter-tag.active {
    background: var(--main-color);
    border-color: var(--main-color);
    color: var(--white-color);
}

.filter-tag:focus-visible {
    outline: 2px solid var(--main-color);
    outline-offset: 2px;
}

.dl-filter-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* Below 900px the Downloads toolbar is search (70%) beside the Filter button (30%); the pills sit in the panel. */
@media (max-width: 900px) {
    .filter-bar.has-filter-toggle #dlSearchInput {
        flex: 7 1 0;
        width: auto;
        min-width: 0;
    }

    .filter-bar.has-filter-toggle .list-filter-toggle-wrap {
        flex: 3 1 0;
        min-width: 0;
    }

    .filter-bar.has-filter-toggle .list-filter-toggle-btn {
        flex: 1 1 auto;
        justify-content: center;
        height: 36px;
    }

    .filter-bar.has-filter-toggle .list-filter-clear-x {
        height: 36px;
    }
}

@media (max-width: 600px) {
    .filter-bar input[type="search"] {
        flex: 1 1 100%;
        width: auto;
    }

    .dl-filter-pills {
        flex: 1 1 100%;
        gap: 8px;
    }
    .dl-filter-pills .filter-tag {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-height: 40px;
        min-width: 0;
    }
}

.dl-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
    gap: 16px;
}

.dl-cat-hdr {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 20px 0 10px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .7px;
    color: var(--text-sub);
}

.dl-cat-hdr::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--main-border-color);
}

.dl-release-hdr {
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    width: 100%;
    padding: 11px 16px;
    border-radius: var(--radius);
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    margin: 24px 0 0;
    font-family: inherit;
    font-size: inherit;
    text-align: left;
    user-select: none;
    transition: background .15s, border-color .15s;
}

.dl-release-hdr:focus-visible {
    outline: 2px solid var(--main-color);
    outline-offset: 2px;
}

.dl-release-hdr:hover {
    background: var(--gray-bg);
    border-color: rgba(var(--main-color-rgb), .3);
}

.dl-release-hdr-label {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    row-gap: 2px;
    min-width: 0;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .7px;
    color: var(--text-sub);
}

.dl-release-hdr-label .svg-icon {
    flex-shrink: 0;
}

.dl-chevron {
    color: var(--text-sub);
    transition: transform .22s;
    display: inline-flex;
    margin-left: 10px;
    flex-shrink: 0;
}

.dl-chevron .svg-icon {
    width: 15px;
    height: 15px;
}

.dl-chevron.collapsed {
    transform: rotate(-90deg);
}

.dl-release-body {
    padding-top: 14px;
}

.dl-release-body.collapsed {
    display: none;
}

.dl-bundle {
    background: var(--df-facet-tile);
    border-radius: var(--radius);
    padding: 20px 24px;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 20px;
    margin-bottom: 8px;
    color: var(--white-color);
    position: relative;
    overflow: hidden;
}


.dl-bundle > * {
    position: relative;
    z-index: 1;
}

.dl-bundle.archived + .dl-bundle.archived { margin-top: 16px; }

.dl-bundle-info {
    flex: 1 1 240px;
    min-width: 0;
}

.dl-bundle > .dl-icon {
    flex-shrink: 0;
}

.dl-bundle-info h3 {
    font-size: 16px;
    font-weight: 700;
    color: var(--white-color);
    margin-bottom: 3px;
}

.dl-bundle-info p {
    font-size: 12px;
    color: rgba(255, 255, 255, .55);
}

.dl-bundle-meta {
    display: flex;
    align-items: center;
    gap: 16px;
    flex: 1 1 auto;
    justify-content: flex-end;
}

.dl-bundle-size-block {
    text-align: right;
}

.dl-bundle-size {
    font-size: 13px;
    font-weight: 600;
    color: rgba(255, 255, 255, .7);
}

.dl-bundle-version {
    font-size: 11px;
    color: rgba(255, 255, 255, .4);
    margin-top: 2px;
}

.dl-bundle-notes {
    display: inline-block;
    margin-top: 8px;
    color: rgba(255, 255, 255, .85);
    font-size: 12px;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.dl-bundle-notes:hover {
    color: var(--white-color);
}

@media (max-width: 600px) {
    .dl-bundle {
        padding: 16px;
        gap: 14px;
    }

    .dl-bundle-meta {
        flex-wrap: wrap;
        gap: 12px;
    }

    .dl-bundle-size-block {
        flex: 1 1 100%;
        text-align: left;
    }

    .dl-bundle-meta .btn-primary-sh {
        flex: 1 1 100%;
    }
}

.dl-card {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    transition: box-shadow .2s, border-color .2s;
    min-width: 0;
}

/* Downloads pages, dark side: cards take the side navigation's grey (--sidebar-bg,
   #2A2E33), matching the System Health cards, instead of the #141414 card colour
   that blended into the page. Covers the download cards and support-link cards
   (current / previous / archived), the collapsible release headers, and the older
   product and package boxes the Release Candidates page still uses. Light mode is
   unchanged. */
.dark-mode .dl-card,
.dark-mode .support-link-card,
.dark-mode .dl-release-hdr,
.dark-mode .productBlock,
.dark-mode .package-container {
    background: var(--sidebar-bg);
}

/* The release header's hover normally drops to --gray-bg, which in dark mode is
   darker than the new grey, so hover lifts one step to DF Mid instead. */
.dark-mode .dl-release-hdr:hover {
    background: var(--df-mid);
}

.dl-card:hover {
    box-shadow: var(--shadow-md);
    border-color: rgba(var(--main-color-rgb), .3);
}

.dl-card.patches-available {
    border-color: rgba(var(--main-color-rgb), .35);
}

.dl-card-top {
    display: flex;
    align-items: flex-start;
    gap: 12px;
}

/* ── Download icon box ────────────────────────────────────────────────
   Two different things sit in this box. Product artwork is a finished mark
   with its own silhouette and transparent corners; the package glyph
   fallback is a bare stroke that needs a ground under it. So the box stays
   neutral by default and tints only when it holds the glyph - a tint behind
   artwork shows through the mark's corners and reads as a shadow around it.
   Size belongs to the box, tint to the modifier. */
.dl-icon {
    width: 44px;
    height: 44px;
    padding: 2px;
    background: none;
    border-radius: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    flex-shrink: 0;
    color: var(--main-color);
    overflow: hidden;
}

.dl-icon:not(:has(img)) {
    background: rgba(var(--main-color-rgb), .1);
}

/* Release-bundle rows: a larger box on a darker header than the download cards.
   Both tints used to be inline on the div, which outranks any rule here. */
.dl-icon-lg {
    width: 52px;
    height: 52px;
    font-size: 24px;
}

.dl-icon-plate:not(:has(img)) {
    background: rgba(255, 255, 255, .12);
}

.dl-icon-plate-rc:not(:has(img)) {
    background: rgba(156, 111, 214, .25);
}

.dl-icon img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.dl-info {
    min-width: 0;
    overflow-wrap: anywhere;
}

.dl-info h3 {
    font-size: 14px;
    font-weight: 600;
}

.dl-info p {
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 3px;
}

.dl-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 12px;
}

.dl-version {
    font-size: 11px;
    color: var(--text-sub);
}

.dl-size {
    font-size: 11px;
    font-weight: 600;
}

.dl-patches-link {
    font-size: 11px;
    color: var(--main-color);
    font-weight: 600;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
    display: block;
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    font-family: inherit;
    text-align: left;
}

@media (max-width: 900px) {
    .dl-patches-link {
        min-height: 40px;
    }
}

.dl-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}

.dl-tag {
    border: var(--tag-border);
    display: inline-block;
    font-size: 11px;
    font-weight: var(--font-regular);
    padding: 3px 9px;
    border-radius: 0;
    background: rgba(45, 143, 210, .12);
    color: var(--blue-link-hover-color);
}

.dl-tag.doc {
    background: var(--main-background-color-subtle);
    color: var(--text-sub);
}

.dl-tag.current {
    background: var(--tag-green-bg);
    color: var(--tag-green-fg);
}

/* ============================================================================
   What Is New panel: a second lane beside the Downloads. Below 1280px (must
   match WN_SIDE_BY_SIDE in whats-new.js) it starts hidden and stacks under them.
   ============================================================================ */
.dl-lanes {
    display: grid;
    grid-template-columns: 1fr;
    gap: 24px;
    align-items: start;
}

/* Container query target: the lane's width depends on the nav rail and the
   panel size, so .dl-grid sizes from the lane, not the viewport. */
.dl-main-lane {
    container-type: inline-size;
    min-width: 0;
}

/* The downloads lane's outer margins move onto the lanes, so the panel lines up
   with the lane's visible top and bottom edges. */
.dl-lanes { margin: 24px 0 56px; }
.dl-main-lane > :first-child { margin-top: 0; }
.dl-main-lane > :last-child { margin-bottom: 0; }

/* The floating button shows only while the panel is hidden. */
body.wn-size-hidden .wn-lane { display: none; }
body:not(.wn-size-hidden) .wn-fab { display: none; }

@media (min-width: 1280px) {
    body.wn-size-half .dl-lanes {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }

    body.wn-size-full .dl-lanes {
        grid-template-columns: 1fr;
    }

    /* Full replaces the downloads lane; below 1280px the lanes already stack. */
    body.wn-size-full .dl-main-lane { display: none; }

    /* Matches the downloads lane's height, capped to the space between the top bar
       and fixed footer so it stays in view (sticky) while a long lane scrolls. */
    .wn-lane {
        position: sticky;
        top: var(--topbar-h);
        align-self: stretch;
        max-height: calc(100vh - var(--topbar-h) - var(--site-footer-h));
    }

    body.impersonating .wn-lane {
        max-height: calc(100vh - var(--topbar-h) - var(--site-footer-h) - var(--imp-banner-h, 48px));
    }

    /* Full has no lane to match, so it takes all the height max-height allows. */
    body.wn-size-full .wn-lane { height: 100vh; }
}

@media (max-width: 1279px) {
    .wn-lane {
        height: min(70vh, 620px);
        scroll-margin-top: calc(var(--topbar-h) + 16px);
    }

    /* Full replaces the downloads and fills the space between the top bar and footer. */
    body.wn-size-full .dl-main-lane { display: none; }
    body.wn-size-full .dl-lanes { margin-bottom: 0; }
    body.wn-size-full .wn-lane { height: calc(100vh - var(--topbar-h) - var(--site-footer-h) - 24px); }
    body.wn-size-full.impersonating .wn-lane { height: calc(100vh - var(--topbar-h) - var(--site-footer-h) - var(--imp-banner-h, 48px) - 24px); }
}

/* Three card columns beside the half-size panel; otherwise .dl-grid keeps its auto-fill layout. */
@container (min-width: 900px) {
    body.wn-size-half .dl-main-lane .dl-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

/* whats-new.js scales the document's text to this font-size, so changing it resizes the document. */
.wn-lane {
    display: flex;
    flex-direction: column;
    font-size: 14px;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    overflow: hidden;
}

.wn-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 11px 16px;
    border-bottom: 1px solid var(--main-border-color);
}

.wn-head h3 {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 16px;
    font-weight: 700;
    color: var(--main-text-color);
    margin: 0;
    white-space: nowrap;
}

.wn-chip {
    font-size: 11px;
    font-weight: 600;
    padding: 3px 9px;
    border-radius: var(--radius);
    background: var(--tag-blue-bg);
    color: var(--tag-blue-fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.wn-chip.range {
    background: var(--main-color);
    color: var(--white-color);
}

/* Chips and controls grouped at the header's right edge; a long product name
   ellipsizes rather than pushing the controls. */
.wn-head-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    min-width: 0;
}

.wn-head-actions > :not(.wn-chip) { flex-shrink: 0; }

.wn-mini {
    background: transparent;
    border: none;
    color: var(--text-sub);
    cursor: pointer;
    display: inline-flex;
    padding: 4px;
    border-radius: var(--radius);
}

.wn-mini[hidden] { display: none; }

.wn-mini:hover {
    background: var(--gray-bg);
    color: var(--main-text-color);
}

.wn-mini:focus-visible {
    outline: 2px solid var(--main-color);
    outline-offset: 1px;
}

.wn-seg {
    display: flex;
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    overflow: hidden;
}

.wn-seg-btn {
    background: var(--main-background-color);
    border: none;
    color: var(--text-sub);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    padding: 5px 8px;
}

.wn-seg-btn + .wn-seg-btn { border-left: 1px solid var(--main-border-color); }

.wn-seg-btn[aria-pressed="true"] {
    background: var(--main-color);
    color: var(--white-color);
}

.wn-seg-btn:focus-visible {
    outline: 2px solid var(--main-color);
    outline-offset: 1px;
}

.wn-body {
    position: relative;
    flex: 1;
    min-height: 0;
}

#wnFrame {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}

.wn-ph {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 24px;
    text-align: center;
    color: var(--text-sub);
    font-size: 13px;
}

/* Its display: flex would otherwise override the hidden attribute. */
.wn-ph[hidden] {
    display: none;
}

/* Covers the frame while the document loads and Connect sets it up. */
.wn-loading {
    background: var(--main-background-color);
}

.wn-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 14px;
    border-top: 1px solid var(--main-border-color);
    font-size: 11.5px;
    color: var(--text-sub);
}

/* Sits above the fixed #siteFooter (and the impersonation banner, when shown). */
.wn-fab {
    position: fixed;
    right: 24px;
    bottom: calc(var(--site-footer-h) + 16px);
    z-index: 90;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    border-radius: var(--radius);
    border: none;
    background: var(--main-color);
    color: var(--white-color);
    font-weight: 600;
    font-size: 13px;
    cursor: pointer;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .2);
}

body.impersonating .wn-fab {
    bottom: calc(var(--imp-banner-h, 48px) + var(--site-footer-h) + 16px);
}

/* Below 601px the footer is in the page flow (layout.css), so the button sticks to the
   viewport bottom only until the content ends and then stops above the footer. */
@media (max-width: 600px) {
    .wn-fab {
        position: sticky;
        display: flex;
        width: fit-content;
        margin: 0 16px 0 auto;
        bottom: 16px;
    }

    body.impersonating .wn-fab {
        bottom: calc(var(--imp-banner-h, 48px) + 16px);
    }

    /* The button ends the grid while the panel is hidden, so the 56px trailing margin only adds space. */
    body.wn-size-hidden .dl-lanes {
        margin-bottom: 16px;
    }
}

/* Neutral like the category pills (.filter-tag) at rest, Connect red on hover. */
.wn-rel-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--contrast-bg);
    border: 1px solid var(--main-border-color);
    color: var(--text-sub);
    border-radius: var(--radius);
    font-family: inherit;
    font-size: 12px;
    font-weight: 500;
    line-height: normal;
    padding: 6px 12px;
    cursor: pointer;
    text-transform: none;
    letter-spacing: 0;
    margin-left: 8px;
    transition: color .15s, border-color .15s;
}

.wn-rel-btn:hover,
.wn-rel-btn:focus-visible {
    color: var(--main-color);
    border-color: var(--main-color);
}

/* The box moves from .dl-release-hdr to this wrapper so the "New in X.Y" button
   can sit at its right edge without being nested inside the header button. */
.dl-release-hdr-row {
    display: flex;
    align-items: center;
    margin: 24px 0 0;
    border-radius: var(--radius);
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    transition: background .15s, border-color .15s;
}

.dl-release-hdr-row:hover {
    background: var(--gray-bg);
    border-color: rgba(var(--main-color-rgb), .3);
}

.dl-release-hdr-row > .dl-release-hdr {
    flex: 1;
    min-width: 0;
    margin: 0;
    border: none;
    background: transparent;
}

/* Dark mode: the whole row, beside the "New in X.Y" button too, takes the header's colours. */
.dark-mode .dl-release-hdr-row {
    background: var(--sidebar-bg);
}

.dark-mode .dl-release-hdr-row:hover {
    background: var(--df-mid);
}

.dark-mode .dl-release-hdr-row > .dl-release-hdr,
.dark-mode .dl-release-hdr-row > .dl-release-hdr:hover {
    background: transparent;
}

.dl-release-hdr-row > .wn-rel-btn {
    flex-shrink: 0;
    margin: 0 12px 0 0;
}

@media (max-width: 600px) {
    .dl-release-hdr-row {
        flex-wrap: wrap;
    }

    .dl-release-hdr-row > .dl-release-hdr {
        flex-basis: 100%;
    }

    .dl-release-hdr-row > .wn-rel-btn {
        margin: 0 12px 10px;
    }
}

.dl-wn-icon {
    margin-left: auto;
    align-self: flex-start;
    background: transparent;
    border: none;
    color: var(--text-sub);
    cursor: pointer;
    padding: 4px;
    border-radius: var(--radius);
    display: inline-flex;
    flex-shrink: 0;
}

.dl-wn-icon:hover {
    background: var(--gray-bg);
    color: var(--main-color);
}

.dl-wn-icon:focus-visible {
    outline: 2px solid var(--main-color);
    outline-offset: 1px;
}

/* The release or card button whose scope the panel is showing (set by wnChips). */
.wn-rel-btn[aria-pressed="true"],
.dl-wn-icon[aria-pressed="true"],
.wn-rel-btn[aria-pressed="true"]:hover,
.dl-wn-icon[aria-pressed="true"]:hover {
    background: var(--main-color);
    border-color: var(--main-color);
    color: var(--white-color);
}

/* ── Support link cards (Downloads footer → Continued/Expired Support) ── */
.support-links {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin: 28px 0 56px;
}

.support-link-card {
    flex: 1 1 320px;
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 18px 22px;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    text-decoration: none;
    color: inherit;
    transition: box-shadow .2s, border-color .2s;
}

.support-link-card:hover {
    box-shadow: var(--shadow-md);
    border-color: rgba(var(--main-color-rgb), .3);
}

.support-link-card:focus-visible {
    outline: none;
    border-color: var(--main-color);
    box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .35);
}

.support-link-icon {
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    color: var(--text-sub);
}

.support-link-body {
    flex: 1;
    min-width: 0;
}

.support-link-title {
    display: block;
    font-size: 14px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    margin-bottom: 2px;
}

.support-link-desc {
    display: block;
    font-size: 12px;
    color: var(--text-sub);
    line-height: 1.4;
}

.support-link-arrow {
    font-size: 16px;
    color: var(--text-sub);
    flex-shrink: 0;
    transition: transform .15s, color .15s;
}

.support-link-card:hover .support-link-arrow {
    color: var(--main-color);
    transform: translateX(3px);
}
.support-link-card.expired {
    opacity: .85;
}

/* ── Patches available modal (Downloads → Patches) ──────────────── */

.patches-modal {
    background: var(--dialog-bg);
    border-radius: var(--radius);
    width: 100%;
    max-width: 860px;
    max-height: min(85vh, 100%);
    display: flex;
    flex-direction: column;
    box-shadow: var(--shadow-md), 0 0 0 1px var(--dialog-frame);
    overflow: hidden;
}

/* Header chrome comes from the shared .modal-header / .modal-header-sub /
   .modal-close — the private .patches-modal-head/-title/-sub/-close rules that
   lived here were a lighter, larger variant of the same header. */

.patches-modal-body {
    overflow-y: auto;
    flex: 1;
    padding: 0 8px;
}

.patch-version-hdr {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--text-sub);
    padding: 14px 20px 8px;
    position: sticky;
    top: 0;
    background: var(--main-background-color);
    z-index: 1;
    border-bottom: 1px solid var(--main-border-color);
}

.patch-version-hdr .svg-icon {
    width: 13px;
    height: 13px;
}

/* The patch count reuses the shared .tab-count chip (same as the Cases tabs); only the
   spacing from the label is set here so the badge colours stay on the shared tokens. */
.patch-version-hdr .pv-badge {
    margin-left: 4px;
}

.patches-modal-body .patch-row {
    padding: 16px 20px;
    border-bottom: 1px solid var(--main-border-color);
    transition: background .15s;
}

.patches-modal-body .patch-row:last-child { border-bottom: none; }

.patches-modal-body .patch-row:hover {
    background: var(--row-hover-bg);
}

.patch-row-top {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.patch-row-desc {
    font-size: 12px;
    color: var(--text-sub);
    line-height: 1.5;
    margin-top: 6px;
}

.patch-ver-chip {
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    font-family: monospace;
    background: var(--main-background-color-subtle);
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    padding: 2px 7px;
    color: var(--main-text-color);
    white-space: nowrap;
}

.patch-row-name {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.patch-row-date {
    font-size: 11px;
    color: var(--text-sub);
    margin-left: auto;
    white-space: nowrap;
}

.patch-row-dl {
    margin-left: 8px;
    padding: 4px 10px;
    font-size: 11px;
}

.patch-status-chip {
    border: var(--tag-border);
    font-size: 11px;
    font-weight: var(--font-regular);
    padding: 2px 8px;
    border-radius: 0;
    white-space: nowrap;
}

.patch-status-chip.release { background: var(--tag-green-bg); color: var(--tag-green-fg); }
.patch-status-chip.rc { background: rgba(45, 143, 210, .15); color: var(--blue-link-hover-color); }
.patch-status-chip.other { background: var(--main-background-color-subtle); color: var(--text-sub); }

.patches-modal-footer {
    padding: 14px 24px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    flex-shrink: 0;
}

.patches-modal-footer-note {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex: 1 1 260px;
    font-size: 11px;
    line-height: 1.5;
    color: var(--text-sub);
}

.patches-modal-footer-note .svg-icon {
    width: 13px;
    height: 13px;
    flex-shrink: 0;
}

.patches-modal-footer .btn-primary-sh,
.patches-modal-footer .btn-ghost-sh {
    min-width: 150px;
    height: 38px;
    padding: 0 18px;
    white-space: nowrap;
}

.patches-modal-footer .btn-primary-sh .svg-icon {
    width: 15px;
    height: 15px;
}

.patches-modal-footer-actions {
    display: flex;
    gap: 8px;
}

@media (max-width: 600px) {
    .patches-modal-overlay {
        padding: 12px;
    }

    .patches-modal {
        max-height: min(92vh, 100%);
    }

    .patches-modal-body {
        padding: 0;
    }

    .patch-version-hdr {
        padding: 12px 16px 8px;
    }

    .patches-modal-body .patch-row {
        padding: 14px 16px;
    }

    .patch-row-name {
        flex: 1 1 100%;
    }

    .patch-row-date {
        margin-left: 0;
    }

    .patch-row-dl {
        margin-left: 0;
        min-height: 40px;
        padding: 4px 14px;
    }

    .patches-modal-footer {
        padding: 12px 16px;
    }

    /* Disclaimer collapses into the .dlg-note-tip info button beside Cancel. */
    .patches-modal-footer-note {
        display: none;
    }

    .patches-modal-footer-actions {
        flex: 1 1 100%;
    }

    .patches-modal-footer .btn-primary-sh,
    .patches-modal-footer .btn-ghost-sh {
        flex: 1 1 0;
        min-width: 0;
    }

    .modal-close {
        width: 40px;
        height: 40px;
    }
}

/* ── Alpha-grouped card list (Customer/User search target pattern) */
.alpha-list {
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.alpha-group-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    font-weight: var(--font-extra-bold);
    color: var(--text-sub);
    padding-bottom: 4px;
    border-bottom: 1px solid var(--main-border-color);
    margin-bottom: 6px;
}

.alpha-group-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 10px;
}

.alpha-card {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: var(--list-surface-bg);
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    text-decoration: none;
    color: inherit;
    transition: border-color .15s, box-shadow .15s;
}

.alpha-card:hover {
    border-color: rgba(var(--main-color-rgb), .35);
    box-shadow: var(--shadow-sm);
}

.alpha-card-avatar {
    width: 36px;
    height: 36px;
    border-radius: var(--radius);
    background: var(--avatar-user-bg);
    color: var(--white-color);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: var(--font-extra-bold);
    font-size: 13px;
    flex-shrink: 0;
}

.alpha-card-body {
    flex: 1;
    min-width: 0;
}

.alpha-card-name {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    line-height: 1.2;
}

.alpha-card-sub {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 2px;
}

/* ── Pagination controls ───────────────────────────────────────── */
/* ── Pagination (single source) ──────────────────────────────────────
   One control for the whole app. Design adopted from the Registration
   Information pager: a table-footer row with a hairline top border and
   compact 28px buttons, aligned right. An optional .pager-count on the left
   holds a result summary; without one the buttons keep the right edge.

   Both a bare <button> child and an explicit .pager-btn are styled, so the
   three existing renderers (Customer Details grids, the company/user search
   scripts, and the Registration attempts table) all work unchanged.

   Replaced .grid-pagination (Customer Details, inline, hardcoded #fff/#ddd/
   #d32f2f — broken in dark mode) and .reg-pag* (Registration Information,
   inline). Neither was reusable where it lived. */
.pager {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 6px;
    padding: 12px 20px;
    border-top: 1px solid var(--main-border-color);
    font-size: 13px;
    color: var(--text-sub);
    margin-bottom:6px;
}

/* Every renderer blanks the container when there is only one page of results
   (`innerHTML = ''`). Without this the row's own border and padding would still
   paint — a stray rule under any short result list. */
.pager:empty {
    display: none;
}

/* Result count, e.g. "1–25 of 412" — opposite the buttons when present. */
.pager-count {
    margin-right: auto;
}

/* Optional wrapper keeping the buttons together when a count shares the row. */
.pager-btns {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.pager button,
.pager-btn {
    min-width: 28px;
    height: 28px;
    padding: 0 8px;
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    background: var(--main-background-color);
    color: var(--main-text-color);
    font-family: inherit;
    font-size: 12px;
    cursor: pointer;
    transition: border-color .15s;
}

/* Hover and current page share ONE band — the same --select-selected-bg the
   grid rows, list rows and dropdown options use, so pagination reads as part
   of the same selection language in both themes. The brand red that used to
   fill the current page (and tint the hover border) is gone: red on a control
   reads as "flagged / in error", not "selected". The current page stays
   identifiable without hover through its bolder weight. */
.pager button:not(:disabled):hover,
.pager-btn:not(:disabled):hover {
    background: var(--select-selected-bg);
    border-color: var(--main-border-color);
}

.pager button.active,
.pager-btn.active {
    background: var(--select-selected-bg);
    border-color: var(--main-border-color);
    color: var(--main-text-color);
    font-weight: var(--font-bold);
}

.pager button:disabled,
.pager-btn:disabled {
    opacity: .4;
    cursor: default;
}

/* ── Employee-only notice banner ────────────────────────────────── */
/* Same block as .notice-info (see "Status notice" above) — it is the advisory
   tone, just server-rendered with its own <icon> rather than the masked one, so
   the OTP and Dev Items tabs can each pick a fitting glyph. Sharing the geometry
   is what keeps the employee banner and a failed-fetch notice on the same tab
   from reading as two unrelated components. It carried warning orange before,
   which overstated it: nothing is wrong, the tab is simply staff-only. */
.emp-only-notice {
    display: flex;
    align-items: center;
    gap: 10px;
    background: rgba(var(--blue-color-rgb), .07);
    border-left: 3px solid var(--blue-color);
    border-radius: var(--radius);
    padding: 12px 18px;
    font-size: 12px;
    color: var(--main-text-color);
    margin-bottom: 16px;
}
.emp-only-notice strong { font-weight: var(--font-bold); }
.emp-only-notice .svg-icon { color: var(--blue-color); }

/* ── OTP grid pill badges ────────────────────────────────────────── */
.otp-code { font-family: 'Courier New', Courier, monospace; font-size: 13px; font-weight: 700; letter-spacing: .08em; color: var(--main-text-color); }
.dark-mode .otp-code { color: var(--main-text-color); }

/* ── Dev Items grid badges & links ───────────────────────────────── */
/* Severity: the cell text already states the level (see the severityNumber
   cellTemplate in Customers/Details.cshtml), so colour reinforces rather than
   carries the meaning. Only the two genuinely alarming levels take a state hue;
   medium and low stay neutral, per "a number is not good news". */
.di-sev-1 { color: var(--error-text); font-weight: 700; }
.di-sev-2 { color: var(--warning-text); font-weight: 700; }
.di-sev-3 { color: var(--main-text-color); font-weight: 700; }
.di-sev-4 { color: var(--text-sub); }
.di-link    { color: var(--main-color); text-decoration: none; font-weight: 600; font-size: 12px; }
.di-link:hover { text-decoration: underline; }
.di-id-link { text-decoration: none; font-weight: 700; font-size: 12px; font-family: 'Courier New', Courier, monospace; }
.di-id-link:hover { text-decoration: underline; }

/* ══════════════════════════════════════════════════════════════════════
   Partner Company Overview — Customers split panel (.pcs-*) and Partner
   Profile card (.pp-*). Spec: MySigmaTEK Partner Development Guidelines
   §5.4–§5.6, rendered by Views/Customers/Details.cshtml.

   Every colour here is a token, so the dark theme comes for free: --surface,
   --gray-bg, --border, --text and --text-sub all alias --main-* tokens that
   .dark-mode already overrides. No .dark-mode counterpart is needed, and none
   should be added — a hardcoded dark hex would be the thing that breaks it.
   ══════════════════════════════════════════════════════════════════════ */

/* ── Customers tab: master/detail shell ──────────────────────────── */
.pcs-split {
    display: flex;
    border: 1px solid var(--border);
    border-radius: unset;
    overflow: hidden;
    min-height: 480px;
    max-height: max(260px, calc(100vh - 420px));
}

.pcs-list-panel {
    width: 272px;
    min-width: 200px;
    flex-shrink: 0;
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    background: var(--gray-bg);
}

.pcs-list-header { padding: 12px; border-bottom: 1px solid var(--border); }

.pcs-list-search-wrap { position: relative; }

.pcs-list-search-wrap input {
    width: 100%;
    height: 36px;
    padding: 0 12px 0 32px;
    border: 1px solid var(--main-border-color);
    border-radius: unset;
    font-size: 13px;
    outline: none;
    background: var(--main-background-color);
    color: var(--main-text-color);
    box-sizing: border-box;
}

/* Style.css's global `input:not([type=checkbox]):not([type=radio])` reset
   (specificity 0,2,1) paints the box with the subtle grey, beating the plain
   background above; restated at a higher specificity, as .cd-filter-input does,
   so the field is the same white as the Users tab search. */
.pcs-list-search-wrap input[type="search"]:not([type="checkbox"]):not([type="radio"]) {
    background-color: var(--main-background-color);
}

/* Box matches the Users tab search (.cd-filter-input): 36px, 1px border, 13px text. */
.pcs-list-search-wrap input:focus { border-color: var(--main-color); }

.pcs-list-search-wrap input::placeholder { color: var(--text-sub); }

/* The tag helper wraps the SVG in a <span class="svg-icon">, so the absolute
   positioning goes on that wrapper rather than the <svg> the prototype targeted. */
.pcs-list-search-wrap > .svg-icon {
    position: absolute;
    left: 8px;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    opacity: .45;
    display: flex;
    color: var(--text-sub);
}

.pcs-list-search-wrap > .svg-icon svg { width: 13px; height: 13px; }

.pcs-list-filters {
    display: flex;
    gap: 4px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}

.pcs-filter-pill {
    font-size: 10px;
    padding: 3px 9px;
    border-radius: unset;
    border: 1px solid var(--border);
    cursor: pointer;
    background: transparent;
    color: var(--text-sub);
    transition: all .15s;
    white-space: nowrap;
    line-height: 1.5;
}

.pcs-filter-pill:hover { border-color: var(--red); color: var(--red); }
.pcs-filter-pill.active { background: var(--red); color: var(--white-color); border-color: var(--red); }

.pcs-list-rows { overflow-y: auto; flex: 1; }

.pcs-list-pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    border-top: 1px solid var(--border);
    flex-shrink: 0;
    background: var(--gray-bg);
}

.pcs-page-btn {
    background: none;
    border: 1px solid var(--border);
    border-radius: unset;
    font-size: 11px;
    padding: 3px 8px;
    cursor: pointer;
    color: var(--text-sub);
    font-weight: var(--font-semi-bold);
    line-height: 1.5;
    transition: all .15s;
}

.pcs-page-btn:hover:not(:disabled) { background: var(--select-selected-bg); border-color: var(--border); color: var(--main-text-color); }
.pcs-page-btn:disabled { opacity: .35; cursor: default; }

.pcs-page-info { font-size: 11px; color: var(--text-sub); font-variant-numeric: tabular-nums; }

.pcs-list-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    cursor: pointer;
    border-bottom: 1px solid var(--border);
    position: relative;
    transition: background .12s;
}

.pcs-list-row:last-child { border-bottom: none; }
.pcs-list-row:hover { background: var(--row-hover-bg); }

.pcs-list-row.active {
    background: var(--surface);
    border-left: 3px solid var(--red);
    padding-left: 9px;
}

.pcs-list-row:focus-visible { outline: 2px solid var(--red); outline-offset: -2px; }

.pcs-row-info { flex: 1; min-width: 0; }

.pcs-row-name {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pcs-list-row.active .pcs-row-name { color: var(--red); }

.pcs-row-meta {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 1px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pcs-row-badges {
    display: flex;
    flex-direction: column-reverse;
    align-items: flex-end;
    gap: 4px;
    flex-shrink: 0;
}

.pcs-row-case-badge {
    border: var(--tag-border);
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    background: var(--tag-brand-bg);
    color: var(--tag-brand-fg);
    border-radius: unset;
    padding: 2px 7px;
    line-height: 1.4;
    white-space: nowrap;
}

.pcs-row-case-badge.clear { background: var(--tag-green-bg); color: var(--tag-green-fg); }

/* Status dot is decoration only — every row also carries the expiry date as a
   title, and the detail pane states it in words, so colour is never the sole cue. */
.pcs-row-maint-dot { width: 6px; height: 6px; border-radius: 0; flex-shrink: 0; }
.pcs-row-maint-dot.ok { background: var(--success-color); }
.pcs-row-maint-dot.warn { background: var(--warning-color); }
.pcs-row-maint-dot.expired { background: var(--error-color); }
.pcs-row-maint-dot.none { background: var(--light-grey-color); }

.pcs-list-empty {
    padding: 24px;
    text-align: center;
    font-size: 12px;
    color: var(--text-sub);
    font-style: italic;
}

.pcs-avatar,
.pcs-detail-avatar {
    background: var(--avatar-company-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--white-color);
    font-weight: var(--font-extra-bold);
    border-radius: unset;
    flex-shrink: 0;
}

.pcs-avatar { width: 36px; height: 36px; font-size: 13px; }
.pcs-detail-avatar { width: 44px; height: 44px; font-size: 15px; }

/* ── Customers tab: detail pane ──────────────────────────────────── */
.pcs-detail-panel { flex: 1; min-width: 0; overflow-y: auto; background: var(--surface); }

.pcs-detail-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 280px;
    gap: 10px;
    color: var(--text-sub);
}

.pcs-detail-empty-icon { opacity: .25; }
.pcs-detail-empty-icon svg { width: 32px; height: 32px; }

.pcs-detail-header {
    padding: 16px 20px 12px;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: 12px;
}

.pcs-detail-co-name { font-size: 15px; font-weight: var(--font-extra-bold); color: var(--text); }
.pcs-detail-co-sub { font-size: 12px; color: var(--text-sub); margin-top: 2px; }
.pcs-detail-pills { display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; }

.pcs-sub-title {
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .7px;
    color: var(--text-sub);
    padding: 12px 18px 6px;
}

.pcs-empty-note {
    padding: 14px 18px;
    font-size: 13px;
    color: var(--text-sub);
    font-style: italic;
}

.pcs-case-table { font-size: 12px; margin: 0; }
.pcs-case-link { color: var(--main-color); font-weight: var(--font-bold); text-decoration: none; }
.pcs-case-link:hover { text-decoration: underline; }

.pcs-lic-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 18px 14px;
    gap: 12px;
    flex-wrap: wrap;
}

.pcs-lic-status { display: flex; align-items: center; gap: 10px; }
.pcs-lic-info { font-size: 12px; color: var(--text-sub); }
.pcs-lic-info strong { color: var(--text); font-weight: var(--font-bold); }
.pcs-lic-dot { width: 8px; height: 8px; border-radius: 0; flex-shrink: 0; }
.pcs-lic-dot.ok { background: var(--success-color); }
.pcs-lic-dot.warn { background: var(--warning-color); }

.pcs-detail-actions { padding: 4px 18px 16px; }

.pcs-view-lic,
.pcs-view-overview {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-size: 12px;
    font-weight: var(--font-bold);
    padding: 6px 12px;
    border: 1px solid var(--main-color);
    border-radius: unset;
    background: var(--main-color);
    color: var(--white-color);
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
    flex-shrink: 0;
    transition: all .15s;
}

.pcs-view-overview { width: 100%; }
.pcs-view-lic:hover { background: var(--main-color-dark); border-color: var(--main-color-dark); color: var(--white-color); text-decoration: none; }

/* View Company Overview follows the login page's Sign Up button (auth.css
   a.btn-signup): outlined in brand red at rest, filling solid red with white
   text, a soft red shadow and a 1px lift on hover. */
.pcs-view-overview {
    background: var(--surface);
    color: var(--main-color);
    border: 1.5px solid var(--main-color);
    transition: background .15s, color .15s, border-color .15s, box-shadow .15s, transform .1s;
}

.dark-mode .pcs-view-overview {
    color: var(--brand-text);
    border-color: rgba(var(--main-color-rgb), .55);
}

.pcs-view-overview:hover,
.dark-mode .pcs-view-overview:hover {
    background: var(--main-color);
    border-color: var(--main-color);
    color: var(--white-color);
    text-decoration: none;
    box-shadow: 0 4px 12px rgba(var(--main-color-rgb), .25);
}

@media (prefers-reduced-motion: no-preference) {
    .pcs-view-overview:hover { transform: translateY(-1px); }
    .pcs-view-overview:active { transform: translateY(0) scale(.98); box-shadow: none; }
}
.pcs-view-lic svg,
.pcs-view-overview svg { width: 13px; height: 13px; }

/* Section head for Key Contacts / Open Cases: the existing .pcs-sub-title keeps
   its own type and padding, and the "+N more" toggle rides on the opposite edge
   of the same row. The title's padding is what sets the row's height, so the
   badge is aligned to the baseline area rather than given padding of its own. */
.pcs-sub-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.pcs-sub-head .pcs-more-slot {
    padding: 12px 18px 6px;
    flex-shrink: 0;
}

/* Reveal toggle. Borrows .pcs-row-case-badge's tag vocabulary (same 10px
   extra-bold, same square corners) so it reads as a badge rather than a second
   button competing with the panel's outlined actions. Neutral rather than brand
   tinted: it is a disclosure control, not a status. */
.pcs-more-badge {
    border: var(--tag-border);
    font-size: 11px;
    font-weight: var(--font-regular);
    font-family: inherit;
    background: var(--tag-neutral-bg);
    color: var(--tag-neutral-fg);
    border-radius: unset;
    padding: 3px 9px;
    line-height: 1.4;
    white-space: nowrap;
    cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}

.pcs-more-badge:hover {
    background: var(--red-tint);
    color: var(--brand-text);
    border-color: var(--main-color);
}

/* [hidden] alone is only a UA-stylesheet display:none, which any later
   display rule on a row or table row would beat — stated explicitly so the
   collapsed records stay collapsed. */
.pcs-collapse-rows[hidden],
tr.pcs-collapse-row[hidden] {
    display: none !important;
}

/* Impersonate control deliberately carries its own class rather than
   .u-action-btn.impersonate (spec §9.2): that class is hidden by employee-only
   visibility rules, which would also hide the partner's copy of the button. */
.pcs-imp-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: var(--font-bold);
    padding: 4px 10px;
    border: 1px solid var(--border);
    border-radius: unset;
    background: var(--surface);
    color: var(--main-color);
    cursor: pointer;
    white-space: nowrap;
    transition: all .15s;
}

.pcs-imp-btn .svg-icon {
    flex-shrink: 0;
}

.pcs-imp-btn .svg-icon > svg {
    width: 13px;
    height: 13px;
}

.pcs-imp-btn:hover { background: var(--red-tint); border-color: var(--red); }

/* Below 600px: matches the Users tab row, collapses to icon-only. */
@media (max-width: 600px) {
    .pcs-imp-btn {
        width: 40px;
        height: 40px;
        padding: 0;
        justify-content: center;
        gap: 0;
    }

    .pcs-imp-btn .svg-icon > svg {
        width: 17px;
        height: 17px;
    }
}

/* Mobile back link — hidden until the split collapses to one column. */
.pcs-mobile-back {
    display: none;
    align-items: center;
    gap: 6px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    font-size: 12px;
    font-weight: var(--font-bold);
    color: var(--main-color);
    background: var(--gray-bg);
    cursor: pointer;
}

.pcs-back-chevron { display: inline-flex; transform: rotate(180deg); }
.pcs-back-chevron svg { width: 14px; height: 14px; }

/* Pager chevrons — same rotate-chevron-right trick as .pcs-back-chevron above,
   since the icon set has no chevron-left. Sized down from 14px to sit inside the
   pager buttons' smaller type. */
.pcs-page-chevron-prev,
.pcs-page-chevron-next { display: inline-flex; vertical-align: middle; }
.pcs-page-chevron-prev { transform: rotate(180deg); }
.pcs-page-chevron-prev svg,
.pcs-page-chevron-next svg { width: 12px; height: 12px; }

@media (max-width: 680px) {
    .pcs-split { flex-direction: column; min-height: 0; }
    .pcs-list-panel { width: 100%; border-right: none; }
    .pcs-list-rows { max-height: 180px; }
    .pcs-detail-panel { max-height: none; }
    .pcs-split.mobile-detail .pcs-list-panel { display: none; }
    .pcs-mobile-back { display: flex; }
}

/* ── Profile tab: partner profile card ───────────────────────────── */
.pp-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: unset;
    margin-bottom: 20px;
    overflow: hidden;
}

.pp-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 13px 18px;
    border-bottom: 1px solid var(--border);
    background: var(--gray-bg);
    flex-wrap: wrap;
}

.pp-card-title {
    font-size: 13px;
    font-weight: var(--font-extra-bold);
    color: var(--text);
    display: flex;
    align-items: center;
    gap: 7px;
}

.pp-card-title svg { width: 14px; height: 14px; opacity: .6; }

.pp-header-actions { display: flex; gap: 8px; }
.pp-btn-sm { font-size: 12px; padding: 5px 12px; margin: 0; }

.pp-edit-btn {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    font-weight: var(--font-bold);
    color: var(--white-color);
    background: var(--main-color);
    border: 1px solid var(--main-color);
    border-radius: unset;
    padding: 5px 12px;
    cursor: pointer;
    white-space: nowrap;
    transition: all .15s;
}

.pp-edit-btn:hover { background: var(--main-color-dark); border-color: var(--main-color-dark); color: var(--white-color); }
.pp-edit-btn svg { width: 12px; height: 12px; }

.pp-info-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 18px 6px;
    font-size: 13px;
    color: var(--text);
}

.pp-info-row a { color: var(--main-color); text-decoration: none; }
.pp-info-row a:hover { text-decoration: underline; }

/* overflow-wrap/word-break only kick in once a run of text has no natural break
   point (a normal sentence with spaces never reaches them) — that's exactly the
   "continuous text, no spaces" case this is for, so ordinary descriptions are
   unaffected. hyphens:auto adds the visible dash at the break, same as this
   partner-authored field is free text, so there's no guarantee it won't be one
   long unbroken run. */
.pp-description {
    padding: 4px 18px 12px;
    font-size: 13px;
    color: var(--text-sub);
    line-height: 1.55;
    overflow-wrap: break-word;
    word-break: break-word;
    hyphens: auto;
}

.pp-contact-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 18px;
    border-top: 1px solid var(--border);
}

/* Read-only Partner Profile view — 2-column grid with a full box border per
   cell (not just .pp-contact-row's top-border); scoped so the Edit Profile
   form's .pp-row-list keeps its original stacked look. */
.pp-contact-view-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
    padding: 10px 18px;
}

.pp-contact-view-grid .pp-contact-row {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 10px 12px;
}

@media (max-width: 700px) {
    .pp-contact-view-grid {
        grid-template-columns: 1fr;
    }
}

.pp-contact-avatar {
    width: 36px;
    height: 36px;
    background: var(--avatar-user-bg);
    border-radius: unset;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--white-color);
    font-size: 12px;
    font-weight: var(--font-extra-bold);
    flex-shrink: 0;
}

.pp-contact-info { flex: 1; min-width: 0; }
.pp-contact-name { font-size: 13px; font-weight: var(--font-bold); color: var(--text); }

.pp-contact-sub {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 2px;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.pp-contact-sub a { color: inherit; text-decoration: none; }
.pp-contact-sub a:hover { color: var(--main-color); text-decoration: underline; }

.pp-empty-notice {
    padding: 22px 18px;
    font-size: 13px;
    color: var(--text-sub);
    font-style: italic;
    text-align: center;
}

/* Section dots only. These used to tint .pp-contact-avatar as well, which now
   takes the flat --avatar-user-bg like every other person avatar — the contact type
   was always spelled out in the adjacent chip, so nothing is lost by dropping the
   hue from the tile. The dot keeps it: it sits beside that chip as its bullet. */
.pp-tone-brand { background: var(--main-color); }
.pp-tone-blue { background: var(--blue-color); }
.pp-tone-green { background: var(--success-color); }
.pp-tone-yellow { background: var(--yellow-color); }

.pp-tone-dot { width: 8px; height: 8px; display: inline-block; flex-shrink: 0; }

/* Contact editor (edit form) — a row of tabs picks Support/Sales/Billing/
   General; a full-width tinted heading band below it (.pp-section-heading,
   colored via the same .pill-{tone} classes used elsewhere) names the section
   and doubles as a title for the fields under it. Support/Sales/Billing show
   that one fixed contact's fields; General shows a grid instead (.pp-row-list,
   reusing the same avatar/name/sub/pill look as the read-only view above),
   since it's the one type that can hold any number of contacts — each with
   its own edit/delete, plus "+ Add general contact". */
.pp-section-tabs {
    display: flex;
    border-bottom: 2px solid var(--main-border-color);
    margin-bottom: 12px;
}

.pp-section-heading {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    font-size: 13px;
    font-weight: var(--font-bold);
    margin-bottom: 16px;
}

.pp-row-list .pp-contact-row:first-child { border-top: none; }
.pp-row-list .pp-contact-row .pill { margin-left: auto; }

.pp-row-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: unset;
    background: var(--surface);
    color: var(--text-sub);
    cursor: pointer;
    flex-shrink: 0;
}

.pp-row-btn:hover { color: var(--text); border-color: var(--main-color-outline); }
.pp-row-btn .svg-icon { width: 13px; height: 13px; }

.pp-row-editing { padding: 14px 18px; border-top: 1px solid var(--border); }
.pp-row-list .pp-row-editing:first-child { border-top: none; }

/* .pp-row-error / .pp-form-error: colour, size and badge from the shared
   field-message rule in Style.css; only spacing is local. */
.pp-row-error { margin-top: 6px; }
.pp-row-error:empty { display: none; }
.pp-row-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 10px; }

.pp-add-general-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    padding: 10px;
    margin-bottom: 10px;
    font-size: 12px;
    font-weight: var(--font-bold);
    color: var(--main-color);
    background: none;
    border: 1px dashed var(--border);
    border-radius: unset;
    cursor: pointer;
}

.pp-add-general-btn:hover { background: var(--red-tint); border-color: var(--red); }

/* ── Profile tab: edit form ──────────────────────────────────────── */
.pp-form-section { padding: 14px 18px; }
.pp-form-section ~ .pp-form-section { border-top: 1px solid var(--border); }

.pp-form-section-label {
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .7px;
    color: var(--text-sub);
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 7px;
}

.pp-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 16px; }
.pp-form-group { display: flex; flex-direction: column; gap: 4px; }
.pp-form-group.full { grid-column: 1 / -1; }

.pp-form-label {
    font-size: 11px;
    font-weight: var(--font-bold);
    color: var(--text-sub);
    text-transform: uppercase;
    letter-spacing: .4px;
}

.pp-form-input,
.pp-form-textarea {
    padding: 7px 10px;
    border: 1px solid var(--border);
    border-radius: unset;
    font-size: 13px;
    font-family: inherit;
    outline: none;
    background: var(--surface);
    color: var(--text);
    transition: border-color .15s;
}

.pp-form-textarea { min-height: 72px; line-height: 1.5; resize: vertical; }
.pp-form-input:focus,
.pp-form-textarea:focus { border-color: var(--red); }
.pp-form-input.has-error,
.pp-form-textarea.has-error { border-color: var(--error-color); }

.pp-form-error:empty { display: none; }

.pp-form-footer {
    padding: 12px 18px;
    border-top: 1px solid var(--border);
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    background: var(--gray-bg);
}

@media (max-width: 680px) {
    .pp-form-grid { grid-template-columns: 1fr; }
    .pp-form-group.full { grid-column: 1; }
}

/* ── Partner Dev Items: read-only work item id (spec §5.6.1) ─────── */
/* Plain text, not .di-id-link: partners get IDs for reference only, so the cell
   must not look or behave like a link out to Azure DevOps. */
.di-id-plain {
    font-family: 'Courier New', Courier, monospace;
    font-size: 12px;
    font-weight: var(--font-extra-bold);
    color: var(--text-sub);
    cursor: default;
}

/* Compat aliases — old layout helpers map to new flex pattern */
.flex-row-search-bar,
.flex-row-case {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
    flex-wrap: wrap;
}

/* ── Responsive: touch-target overrides ────────────────────────────
   Per the responsive spec's §15.4 "Tap targets" defect, the 44px-floor
   remediation is scoped to five specific small/icon controls that were
   genuinely undersized (14-30px): .inv-cb, .cf-ctx-btn, .u-action-btn,
   .modal-close, .cop-msg-fb-btn — at <=600px, not <=900px. The general
   .btn-primary/-ghost/-outline family was never in that list (they're
   already ~36px) and was removed from here for exactly that reason: it
   was scope creep past what §15.4 called for, on top of using this
   project's *desktop* breakpoint (>900px = full desktop layout per §3)
   rather than the spec's mobile one — so it fired on any ordinary
   narrowed browser window, not just phone-width screens. */
@media (max-width: 900px) {
    .cd-back-btn,
    #cdSidebarToggle {
        padding: 12px 14px;
    }

    .tab-item {
        padding-top: 14px;
        padding-bottom: 14px;
    }
}

/* ══════════════════════════════════════════════════════════════════════
   CENTRALIZED DIALOG POLISH (no size changes)
   Colour (gradient header + sidebar-tone body) and title styling are already
   centralized on the shared .modal-header / .dx-popup-title rules above. This
   block adds two cross-dialog rules only: uppercase field labels, and the
   outside-click "shake" reaction. Nothing here alters dialog dimensions.
   ══════════════════════════════════════════════════════════════════════ */

/* Field titles UPPERCASE across every dialog. Text-transform only — no
   font-size/spacing that would resize anything. */
.modal-body .form-group-sh label,
.dx-popup-content .form-group-sh label {
    text-transform: uppercase;
    letter-spacing: .5px;
}

/* Consistent dialog shell: identical hairline outline + shadow on every dialog
   type (custom .modal, DevExpress dxPopup, Bootstrap), using existing tokens so
   colours/branding are unchanged. The outline is a box-shadow RING (not a real
   border): a real border sits inside the modal's overflow clip and framed the
   dark gradient header, leaving a light line at the top corners. As a ring it
   sits OUTSIDE the shell, so the header bleeds fully into the top-left/right
   corners while we still get a uniform 1px outline. Size-preserving. */
/* :not(.dx-tooltip-wrapper) for the same reason as the --page-bg rule above — a
   dxTooltip carries .dx-popup-wrapper, and this rule's border:0 was stripping
   the tooltip chip's red keyline while its shadow replaced the chip's own. */
/* The ring is the second box-shadow layer, and the reason it is a shadow rather
   than a border is unchanged from the note above — a real border sits inside the
   shell's overflow clip and frames the facet header, leaving a light line at the
   top corners. As a spread-only shadow it sits OUTSIDE the shell, so the header
   still bleeds into the corners. Size-preserving either way. */
.modal-overlay > .modal,
.dx-popup-wrapper:not(.dx-dropdowneditor-overlay):not(.dx-dropdownlist-popup-wrapper):not(.dx-tooltip-wrapper) > .dx-overlay-content:not(.dx-popup-fullscreen),
.modal-content {
    border: 0 !important;
    box-shadow: var(--shadow-lg), 0 0 0 1px var(--dialog-frame) !important;
}

/* ══════════════════════════════════════════════════════════════════════
   PUNCHY TABLES (centralized)
   Header/title row uses a contrast tone — Fog (light) / DF Light (dark) — with
   readable dark text, and cell borders stay clearly visible in dark mode. One
   set of tokens drives every table so the look is consistent and easy to tune.
   ══════════════════════════════════════════════════════════════════════ */
:root {
    --table-head-text: #2A2E33;      /* DF Dark — readable on the light #ebedf0 header */
    --table-border: var(--main-border-color);
    --table-row-bg: var(--page-bg);  /* Snow #FAFAFA (light) */
}

.dark-mode {
    --table-head-text: #C5C8CC;      /* Fog — readable on the dark #2A2E33 header */
    /* Interior cell rules, one step softer than the frame. Was DF Highlight #52575E —
       an opaque mid-grey several times the frame's weight, so a table read as a drawn
       grid pasted onto the dark chassis while the panel beside it had barely-there
       rules. .06 is measured off the reference design: its row hairline composites to
       #222222 on the #141414 row — (0x22-0x14)/(0xFF-0x14) = .06, against .08 for the
       card frame around it. That gap is deliberate and is what makes rows read as
       separated rather than boxed; keep --table-border below --main-border-color if
       either is ever retuned. */
    --table-border: rgba(255, 255, 255, 0.06);
    --table-row-bg: var(--df-jet);  /* #141414 — the dark side-navigation tone */
}

/* Title row: contrast tone (#EBEDF0 light / #2A2E33 dark) so it stands off the
   #FAFAFA data rows below it. Deliberately NOT --sidebar-bg any more: the nav
   pane and the page canvas are both #F4F5F6 now, which is within a hair of the
   row tone and would have flattened the header. */
.sh-table thead th,
.data-table th,
.role-table th {
    background: var(--contrast-bg) !important;
    color: var(--table-head-text) !important;
}

/* Customer Details' Cases tab table only — header text uses --text-sub instead of the
   site-wide --table-head-text token. Scoped to #CasesList so every other .data-table on the
   site (Invoices, Work Items, etc.) keeps the standard header color. */
#CasesList .data-table th {
    color: var(--text-sub) !important;
}

/* Data rows: page tone (Snow #FAFAFA light / Jet #141414 dark). Set on the row
   so existing hover states (which target tr or td) still win. */
.sh-table tbody tr,
.data-table tbody tr,
.role-table tbody tr {
    background: var(--table-row-bg);
    /* Row reveal. Results currently appear as one block, which reads as a repaint
       rather than a load; a short stagger says "these arrived". It reuses
       layout.css's page-enter keyframes on purpose — same 8px rise as the page
       itself, and because those keyframes are declared inside
       @media (prefers-reduced-motion: no-preference), the animation name simply
       does not resolve for reduced-motion users and no fallback guard is needed.

       Rows re-render on filter/search, so this fires again on each new result set,
       which is exactly when it carries information. */
    animation: page-enter var(--dur-base) var(--ease-out) backwards;
}

/* Only the first twelve rows stagger — the rest come in together at 0ms. An
   uncapped 30ms stagger on a 200-row grid animates for six seconds, and rows past
   the fold have nothing to announce anyway. :is() keeps this to eleven rules
   instead of thirty-three; it is as widely supported as the color-mix() already in
   Style.css. */
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(2) { animation-delay: 30ms; }
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(3) { animation-delay: 60ms; }
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(4) { animation-delay: 90ms; }
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(5) { animation-delay: 120ms; }
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(6) { animation-delay: 150ms; }
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(7) { animation-delay: 180ms; }
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(8) { animation-delay: 210ms; }
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(9) { animation-delay: 240ms; }
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(10) { animation-delay: 270ms; }
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(11) { animation-delay: 300ms; }
:is(.sh-table, .data-table, .role-table) tbody tr:nth-child(12) { animation-delay: 330ms; }

/* Same reveal for the list/grid results that aren't tables — Customer Search's
   company rows and person rows, and the card grid both tabs share. These are the
   only other places in the app where a result set replaces itself in place, so they
   get the identical keyframes, duration and 12-item stagger cap as the tables
   above; anything else would have the same page announcing results two ways.

   Companies are rendered one .cs-list per alpha letter, so the stagger restarts at
   each header. That is deliberate: the count is what reads, not a single ramp
   across a list whose groups are already visually separate. */
.cs-list > .cs-row,
.cs-list > .ppl-row,
.alpha-group-list > .alpha-card {
    animation: page-enter var(--dur-base) var(--ease-out) backwards;
}

:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(2) { animation-delay: 30ms; }
:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(3) { animation-delay: 60ms; }
:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(4) { animation-delay: 90ms; }
:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(5) { animation-delay: 120ms; }
:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(6) { animation-delay: 150ms; }
:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(7) { animation-delay: 180ms; }
:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(8) { animation-delay: 210ms; }
:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(9) { animation-delay: 240ms; }
:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(10) { animation-delay: 270ms; }
:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(11) { animation-delay: 300ms; }
:is(.cs-list, .alpha-group-list) > :is(.cs-row, .ppl-row, .alpha-card):nth-child(12) { animation-delay: 330ms; }

/* Borders visible in both modes. */
.sh-table th,
.sh-table td,
.data-table th,
.data-table td,
.role-table th,
.role-table td {
    border-bottom-color: var(--table-border) !important;
}

/* ══════════════════════════════════════════════════════════════════════
   PROFILE / SETTINGS MODAL — bring the pm-* modal onto the shared dialog theme
   (gradient header, page-tone body, sharp shell, single shadow) so it matches
   every other dialog in both light and dark mode.
   ══════════════════════════════════════════════════════════════════════ */
.pm-modal {
    background: var(--dialog-bg) !important;
    border: 0 !important;
    border-radius: 0 !important;
    /* Frame ring as the shadow's second layer — see the shared shell rule above. */
    box-shadow: var(--shadow-lg), 0 0 0 1px var(--dialog-frame) !important;
    overflow: hidden;
}

/* (The pm-modal-header/-title/-close-btn retrofit that lived here is gone:
   the Settings dialog now uses .modal-header / .modal-close directly.) */

/* Loader shown while the profile/settings modal is being fetched — reuses the
   Configuration module's 5-dot .cd-spinner for a consistent app-wide loader. */
#profileLoader {
    position: fixed;
    inset: 0;
    z-index: 1300;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.35);
}

    #profileLoader .cd-spinner .dot {
        width: 10px;
        height: 10px;
    }


/* ══════════════════════════════════════════════════════════════════════
   INVOICE DETAILS — one bordered sheet standing in for the printed invoice
   The page was Bootstrap defaults over inline flex rows: rounded
   .btn-secondary/.btn-success buttons, `Label: value` in <p> tags, and a
   bare table whose totals block carried the wire instructions in its last
   cell. Everything below puts it on the app's system — square corners, Fog
   header bands, 11px uppercase micro-labels, fixed pitch for every figure —
   while keeping the reading order of the paper invoice it stands in for, so it
   still reads as a document and not as a dashboard.
   ══════════════════════════════════════════════════════════════════════ */
.inv-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* The Pay button is wrapped in an <a> (it navigates to the pay flow), and the
   anchor is what the flex row lays out — without this the button inside sits on
   the row's baseline instead of centred against its siblings. */
.inv-actions > a {
    display: inline-flex;
}

.inv-actions .svg-icon svg {
    width: 15px;
    height: 15px;
}

.inv-doc {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
}

/* Issuer block: the mark on the left, the subsidiary's own address opposite it. */
.inv-doc-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 24px;
    padding: 20px 22px;
    border-bottom: 1px solid var(--main-border-color);
}

.inv-doc-top .brand-logo {
    max-width: 220px;
    flex-shrink: 0;
}

.inv-issuer {
    margin: 0;
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-sub);
    text-align: right;
}

/* Title band — what the document is, its number, and its status. */
.inv-band {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 12px 22px;
    background: var(--main-background-color-subtle);
    border-bottom: 1px solid var(--main-border-color);
}

.inv-band h3 {
    margin: 0;
    font-size: 15px;
    /* !important: the global h3 rule sets font-weight:300 !important, which beats
       this selector regardless of specificity. */
    font-weight: var(--font-extra-bold) !important;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--main-text-color);
}

.inv-band-num {
    font-family: Consolas, 'Courier New', monospace;
    font-size: 14px;
    font-weight: var(--font-bold);
    letter-spacing: .5px;
}

/* Header facts. auto-fit rather than the old pair of flex columns, so six short
   values use the width they have instead of stacking into two tall columns. */
.inv-meta {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 14px 22px;
    padding: 16px 22px;
    border-bottom: 1px solid var(--main-border-color);
}

.inv-label {
    display: block;
    margin-bottom: 3px;
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    letter-spacing: .4px;
    text-transform: uppercase;
    color: var(--text-sub);
}

.inv-value {
    display: block;
    font-size: 13px;
    line-height: 1.55;
    color: var(--main-text-color);
    word-break: break-word;
}

.inv-value a {
    color: var(--main-color);
}

/* Bill to / Ship to, split by a rule that runs the full height of the band. */
.inv-parties {
    display: grid;
    grid-template-columns: 1fr 1fr;
    border-bottom: 1px solid var(--main-border-color);
}

.inv-party {
    padding: 16px 22px;
}

.inv-party + .inv-party {
    border-left: 1px solid var(--main-border-color);
}

.inv-items {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    /* Style.css puts a drop shadow on every bare <table>; the sheet is the frame. */
    box-shadow: none;
}

.inv-items th {
    padding: 10px 22px;
    background: var(--contrast-bg);
    border-bottom: 1px solid var(--main-border-color);
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    letter-spacing: .4px;
    text-transform: uppercase;
    color: var(--table-head-text);
    text-align: left;
    white-space: nowrap;
}

.inv-items td {
    padding: 11px 22px;
    border-bottom: 1px solid var(--main-border-color);
    vertical-align: top;
}

.inv-items .inv-qty {
    text-align: center;
    width: 90px;
}

/* Figures are fixed pitch and right-aligned so a column of money lines up on the
   decimal point rather than on the digit count. */
.inv-items .inv-num {
    text-align: right;
    width: 150px;
    font-family: Consolas, 'Courier New', monospace;
    font-size: 12px;
    white-space: nowrap;
}

/* .inv-num is brand red because on the company page it is the invoice-number *link*
   (a.inv-num, Customers/Details). Here it is a money cell, not a link, and a whole
   column of red figures read as errors or as amounts owing. Bold black instead —
   the bold comes from .inv-num itself. Scoped to td so the AMOUNT column header
   keeps the shared table-head colour. */
.inv-items td.inv-num {
    color: var(--main-text-color);
}

.inv-summary {
    display: flex;
    justify-content: flex-end;
    padding: 16px 22px;
}

.inv-totals {
    width: 340px;
    max-width: 100%;
    border: 1px solid var(--main-border-color);
}

.inv-total-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 20px;
    padding: 8px 14px;
    font-size: 13px;
    border-bottom: 1px solid var(--main-border-color);
}

    .inv-total-row > span:last-child {
        font-family: Consolas, 'Courier New', monospace;
        font-size: 12px;
        white-space: nowrap;
    }

.inv-total-row.strong {
    font-weight: var(--font-bold);
}

/* Amount due: the figure the reader opened the page for. Size, weight and the tinted
   band carry that emphasis; the figure itself is the same black as every other amount
   on the sheet, since a red total reads as overdue even when the invoice is settled. */
.inv-total-row.due {
    padding: 11px 14px;
    background: rgba(var(--main-color-rgb), .06);
    border-bottom: none;
}

    .inv-total-row.due > span:first-child {
        font-size: 11px;
        font-weight: var(--font-extra-bold);
        letter-spacing: .4px;
        text-transform: uppercase;
    }

    .inv-total-row.due > span:last-child {
        font-size: 18px;
        font-weight: var(--font-extra-bold);
        color: var(--main-text-color);
    }

/* How to pay. These two were a colspan pair inside the totals table, which made
   the payment instructions read as another total. */
.inv-foot {
    display: grid;
    grid-template-columns: 1fr 1fr;
    border-top: 1px solid var(--main-border-color);
}

.inv-foot-cell {
    padding: 14px 22px;
}

.inv-foot-cell + .inv-foot-cell {
    border-left: 1px solid var(--main-border-color);
}

.inv-foot-body {
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-sub);
}

    /* The wire instructions and remittance address arrive from NetSuite as HTML and
       carry their own <p> tags, which would otherwise add a blank line at the top of
       each cell and leave the last line hanging below the padding. */
    .inv-foot-body p {
        margin: 0;
        font-size: 12px;
    }

/* The sheet holds its own two-column bands on a narrow window, but not below the
   point where an address has nowhere to wrap. */
@media (max-width: 700px) {
    .inv-parties,
    .inv-foot {
        grid-template-columns: 1fr;
    }

    /* The issuer block is a flex row, not a grid — it stacks by direction. */
    .inv-doc-top {
        flex-direction: column;
        gap: 14px;
    }

    .inv-issuer {
        text-align: left;
    }

    .inv-party + .inv-party,
    .inv-foot-cell + .inv-foot-cell {
        border-left: none;
        border-top: 1px solid var(--main-border-color);
    }

    .inv-totals {
        width: 100%;
    }

    /* Fixed 90/150px columns plus 22px padding overflow a 375px viewport. */
    .inv-items th,
    .inv-items td {
        padding-left: 10px;
        padding-right: 10px;
    }

    .inv-items .inv-qty,
    .inv-items .inv-num {
        width: auto;
    }
}

/* The logo swaps for the dark theme; the light one would disappear against it.
   Moved here from the view's inline <style> with the rest of this page. */
.brand-logo .logo {
    max-width: 100%;
    height: auto;
}

.brand-logo .logo-dark {
    display: none;
}

body.dark-mode .brand-logo .logo-light {
    display: none;
}

body.dark-mode .brand-logo .logo-dark {
    display: inline-block;
}

/* ── Contacts ───────────────────────────────────────────────────── */
.contact-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 16px;
    padding: 16px;
}

@media (max-width: 600px) {
    .contact-card-grid {
        grid-template-columns: 1fr;
    }
}

.contact-card {
    display: flex;
    gap: 12px;
    padding: 14px;
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
    background: var(--main-background-color);
}

.contact-card-avatar {
    flex: none;
    width: 36px;
    height: 36px;
    border-radius: var(--radius);
    background: var(--avatar-user-bg);
    color: var(--white-color);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: var(--font-bold);
}

.contact-card-body {
    min-width: 0;
}

.contact-card-name {
    font-size: 14px;
    font-weight: var(--font-bold);
    /* Bootstrap's .card sets its own color: var(--bs-body-color), which this app
       never re-themes for dark mode — anything here with no color of its own
       silently inherits that (light-theme-only) value instead of --main-text-color. */
    color: var(--main-text-color);
}

.contact-card-role {
    font-size: 12px;
    color: var(--text-sub);
    margin-bottom: 4px;
}

.contact-card-line {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 4px;
    min-width: 0;
}

.contact-card-line .svg-icon {
    flex: none;
    width: 14px;
    height: 14px;
}

.contact-card-line a {
    min-width: 0;
    overflow-wrap: anywhere;
    color: var(--blue-link-color);
    text-decoration: none;
}

    .contact-card-line a:hover {
        text-decoration: underline;
    }

/* ── Contacts page (read-only, customer/employee facing) ────────────
   The page-level card gets more room than the denser .contact-card--config
   rows in Configuration: it sits alone on a full page rather than inside a
   configuration card, so the same markup is spaced out here instead of being
   packed. Configuration keeps the base sizing. */
.contacts-page-group + .contacts-page-group {
    margin-top: 28px;
}

/* Group heading is a small caps eyebrow, not the 18px .section-title used
   inside Configuration cards — the cards below carry the visual weight, so the
   label only has to name the group. Caps and tracking are presentation only
   (the .resx keys stay sentence case), and the weight needs !important to beat
   the global h3 {font-weight:300 !important}. */
.contact-group-label {
    margin: 0 0 12px;
    font-size: 11px;
    font-weight: var(--font-extra-bold) !important;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-sub);
}

/* Cards line up with the group label and the page header above, so the shared
   grid's own 16px inset is dropped here. Column count stays on the shared
   auto-fill track (and its 1-column mobile rule) rather than a fixed 3 per row —
   at page width that fills the row with 2 wide cards instead of 3 narrow ones,
   which is what leaves the name, role and contact lines on their own lines. */
.contacts-page-grid {
    padding: 0;
}

.contacts-page-grid .contact-card {
    padding: 20px;
    gap: 14px;
    transition: box-shadow var(--dur-base) var(--ease-out);
}

    .contacts-page-grid .contact-card:hover {
        box-shadow: var(--shadow-md);
    }

    /* Contacts page, dark side: cards take the side navigation's grey (--sidebar-bg,
       #2A2E33), matching the System Health, Downloads and Chat Feedback cards, instead
       of the #141414 card colour that blended into the page. Scoped to the page grid:
       the same .contact-card also renders inside the manage-contacts dialogs and the
       partner profile, which keep their dialog surface. */
    .dark-mode .contacts-page-grid .contact-card {
        background: var(--sidebar-bg);
    }

    .contacts-page-grid .contact-card-avatar {
        width: 44px;
        height: 44px;
        font-size: 15px;
    }

    .contacts-page-grid .contact-card-role {
        font-size: 11px;
        margin-bottom: 8px;
    }

    /* The row's own colour dims the leading icon; the value itself stays at full
       strength so name and contact details read as one block. The email keeps
       its mailto link — only the link colour is dropped, since a blue email on
       an otherwise grey/black card was the one thing pulling the eye off the
       name. */
    .contacts-page-grid .contact-card-line > span,
    .contacts-page-grid .contact-card-line a {
        color: var(--main-text-color);
    }

/* Configuration > Contacts grid rows and the Manage Contacts dialog's Added
   Contacts list share this row layout for a picked/configured employee or
   manual entry. */
.employee-picker-added-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 12px;
    max-height: 260px;
    overflow-y: auto;
}

.employee-picker-added-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border: 1px solid var(--main-border-color);
    border-radius: var(--radius);
}

.employee-picker-added-row .contact-card-avatar {
    width: 28px;
    height: 28px;
    font-size: 11px;
}

.employee-picker-added-info {
    flex: 1;
    min-width: 0;
}

.employee-picker-added-name {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

.employee-picker-added-role {
    font-size: 12px;
    color: var(--text-sub);
}

/* Customer scope badge on an Added Contacts row — a clickable toggle that swaps
   in its own inline dropdown/picker on click. */
.added-row-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 4px;
}

.added-row-toggle {
    cursor: pointer;
    border: 1.5px solid transparent;
    background: var(--gray-bg);
    color: var(--text-sub);
}

    .added-row-toggle:hover {
        border-color: var(--main-border-color);
    }

.employee-picker-added-actions {
    flex: none;
    display: flex;
    gap: 6px;
}

    .employee-picker-added-actions .btn-ghost-sh {
        font-size: 12px;
        padding: 0 10px;
        height: 26px;
    }

.picker-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
}

    .picker-chip-row:empty {
        margin-top: 0;
    }

.picker-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    border: 1.5px solid var(--main-border-color);
    border-radius: var(--radius);
    font-size: 12px;
    color: var(--main-text-color);
    background: var(--main-background-color);
}

    .picker-chip button {
        background: none;
        border: none;
        padding: 0;
        color: var(--text-sub);
        cursor: pointer;
        font-size: 13px;
        line-height: 1;
    }

        .picker-chip button:hover {
            color: var(--main-text-color);
        }

.employee-picker-empty {
    font-size: 12px;
    color: var(--text-sub);
    padding: 12px 0;
}

.manage-contacts-modal .modal {
    width: 560px;
}

@media (max-width: 600px) {
    .manage-contacts-modal .modal {
        width: 100%;
    }
}

/* ══ Tooltips ══════════════════════════════════════════════════════════════
   Two mechanisms render hover hints in this app and they must not drift apart:
   DevExtreme's dxTooltip (.prospect-tooltip and a handful of widgets) and
   .sn-tooltip, the single element utils.js substitutes for native title=""
   attributes so the OS tooltip never appears. Both read the tokens below.

   Values are stated here rather than inherited. A dxTooltip's wrapper also
   carries .dx-popup-wrapper, so the dialog rules earlier in this file used to
   paint tooltips as though they were dialogs. Those rules now carry
   :not(.dx-tooltip-wrapper) so they no longer reach here — see the note on the
   --page-bg rule. The !important below is kept only to beat the vendor theme's
   own grey-pill treatment, not our own dialog styling. */
:root {
    --tooltip-bg: var(--contrast-bg);
    --tooltip-text: var(--sidebar-text);
    --tooltip-border-color: var(--sidebar-border);
    --tooltip-max-width: 320px;
}

.dark-mode {
    --tooltip-text: var(--main-text-color-a75); /* --sidebar-text's .6 opacity is too faint at tooltip size */
}

/* ── DevExtreme dxTooltip ────────────────────────────────────────────────── */
/* The four defining properties are !important deliberately. Two separate dialog
   rules above (the --page-bg surface and the border:0 + shadow ring) each reach
   .dx-popup-wrapper and had silently stripped the fill, then the keyline; both
   now exclude tooltips, but nothing legitimately needs to override a tooltip's
   own chrome, so pinning them here stops a third dialog tweak from doing it
   again. */
.dx-tooltip-wrapper.dx-popup-wrapper > .dx-overlay-content,
.dx-tooltip-wrapper > .dx-overlay-content {
    min-width: 0;
    max-width: var(--tooltip-max-width);
    background: var(--tooltip-bg) !important;
    color: var(--tooltip-text);
    border: 1px solid var(--tooltip-border-color) !important;
    border-radius: unset !important;
    box-shadow: var(--shadow-md) !important;
}

.dx-tooltip-wrapper.dx-popup-wrapper > .dx-overlay-content .dx-popup-content,
.dx-tooltip-wrapper > .dx-overlay-content .dx-popup-content {
    padding: 4px 10px !important;
    font-size: 12px;
    line-height: 1.3;
    text-align: left;
    white-space: normal;
    overflow-wrap: anywhere; /* wrap long file names / URLs */
    color: var(--tooltip-text);
    background: transparent !important;
}

/* Opt-in rounding for select dxTooltip instances; every other dxTooltip
   stays square per the border-radius: unset rule above. */
.dx-tooltip-wrapper.prospect-tooltip-rounded.dx-popup-wrapper > .dx-overlay-content,
.dx-tooltip-wrapper.prospect-tooltip-rounded > .dx-overlay-content {
    border-radius: 8px !important;
}

/* No pointer on either implementation — the chip is read as a label beside what
   it describes. Hidden rather than restyled because DevExtreme's arrow is a
   rotated square clipped by its parent, so any fill or keyline on it reappears
   as a notch across the box edge. */
.dx-tooltip-wrapper .dx-popover-arrow,
.dx-tooltip-wrapper.dx-popover-wrapper .dx-popover-arrow {
    display: none !important;
}

/* ── Native title="" replacement (see initGlobalTooltips in utils.js) ─────── */
.sn-tooltip {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 6000;
    max-width: var(--tooltip-max-width);
    padding: 4px 10px;
    background: var(--tooltip-bg);
    color: var(--tooltip-text);
    border: 1px solid var(--tooltip-border-color);
    border-radius: unset;
    box-shadow: var(--shadow-md);
    font-size: 12px;
    line-height: 1.3;
    text-align: left;
    overflow-wrap: anywhere; /* wrap long file names / URLs */
    /* Never intercept a click aimed at the thing it describes. */
    pointer-events: none;
    /* visibility rather than display: the element has to have layout for its
       size to be measurable before it is positioned. */
    opacity: 0;
    visibility: hidden;
    transition: opacity .1s ease;
}

.sn-tooltip.is-visible {
    opacity: 1;
    visibility: visible;
}

/* Deliberately no ::before/::after arrow — see the note on the DevExtreme rule
   above. .is-below is still set by the positioner and kept as a styling hook. */

/* ── Files tab (.files-shell) — 900 / 680 / 600 / 480 ──────────────────
   Breakpoints mirrored in applyFilesLayout(); keep both in sync. */

.files-shell {
    position: relative;
}

/* Scope every .dx-drawer-* rule through .dx-filemanager-container: outside it
   those classes belong to the notification drawer, not the folder tree. */
.files-shell .dx-filemanager-container .dx-drawer-panel-content {
    display: flex;
    flex-direction: column;
}

.files-shell .dx-filemanager-container .dx-filemanager-dirs-panel {
    flex: 1 1 auto;
    min-height: 0;
    height: auto !important;
    /* Theme sets 5px 10px, which opens a gap under the FOLDERS caption. Tree
       items carry their own inset. */
    padding: 0 !important;
}

.files-shell .dx-filemanager-dirs-tree .dx-treeview-node-loadindicator,
.files-shell .dx-filemanager-dirs-tree .dx-loadindicator {
    display: none !important;
}

/* dx.alterations cancels the theme's vertical cell borders for header and data
   rows only, leaving them drawn in the freespace filler below the last row —
   a column line visible only where there is no data. */
.files-shell #file-manager .dx-filemanager-details .dx-freespace-row > td,
.files-shell #file-manager .dx-filemanager-details .dx-virtual-row > td {
    border-left: none !important;
    border-right: none !important;
}

.files-shell .files-scrim {
    display: none;
    position: absolute;
    inset: 0;
    z-index: 4;
    background: rgba(0, 0, 0, .34);
    border: 0;
    padding: 0;
}

.files-shell .files-action-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    background: var(--main-background-color);
    color: var(--main-text-color);
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    font-size: 11px;
    font-weight: var(--font-bold);
    letter-spacing: .3px;
    text-transform: uppercase;
    cursor: pointer;
}

.files-shell .files-action-btn .dx-icon {
    font-size: 15px;
}

.files-shell .files-action-btn:focus-visible {
    outline: 2px solid var(--main-color);
    outline-offset: 1px;
}

.files-shell .files-folders-toggle {
    display: none;
}

.files-shell.files-tree-hidden .files-folders-toggle {
    display: inline-flex;
}

.files-shell.files-open .files-folders-toggle {
    background: rgba(var(--main-color-rgb), .08);
    border-color: var(--main-color);
    color: var(--main-color);
}

@media (max-width: 900px) {
    /* Width belongs on the drawer wrapper — the dirs panel inside it is
       width:100% and cannot drive its own size. */
    .files-shell .dx-filemanager-container .dx-drawer-panel-content {
        width: clamp(160px, 26vw, 264px) !important;
        min-width: 0 !important;
        max-width: none !important;
        flex: 0 0 auto !important;
    }

    .files-shell .dx-filemanager-dirs-panel .dx-treeview-item {
        padding: 0 8px;
    }

    .files-shell #file-manager .dx-filemanager-details .dx-header-row > td,
    .files-shell #file-manager .dx-filemanager-details .dx-data-row > td {
        padding-left: 10px;
        padding-right: 10px;
    }
}

@media (max-width: 680px) {
    .files-shell .dx-filemanager-container .dx-drawer-panel-content {
        display: none !important;
    }

    .files-shell .dx-filemanager-container .dx-splitter {
        display: none !important;
    }

    .files-shell .dx-filemanager-container .dx-drawer-content,
    .files-shell .dx-filemanager-items-panel {
        width: 100% !important;
        max-width: 100% !important;
        margin-left: 0 !important;
    }

    .files-shell .files-actions {
        flex-wrap: wrap;
        row-gap: 8px;
    }

    .files-shell .files-actions-right {
        order: 3;
        width: 100%;
        margin-left: 0;
        padding-top: 7px;
        border-top: 1px solid var(--main-border-color);
    }

    .files-shell #custom-breadcrumb {
        min-width: 0;
        overflow-x: auto;
        overflow-y: hidden;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }

    .files-shell #custom-breadcrumb::-webkit-scrollbar {
        display: none;
    }

    .files-shell #custom-breadcrumb .dx-menu-horizontal,
    .files-shell #custom-breadcrumb .dx-menu-items-container {
        flex-wrap: nowrap !important;
        white-space: nowrap;
    }
}

/* Outside the media queries on purpose: the overlay follows .files-tree-hidden,
   which applyFilesLayout() sets by measuring the tree, because dxFileManager
   also collapses it on a threshold of its own above 680. */
.files-shell .dx-filemanager-container {
    position: relative;
}

.files-shell.files-open .dx-filemanager-container .dx-drawer-panel-content {
    display: flex !important;
    flex-direction: column;
    position: absolute;
    inset: 0 auto 0 0;
    width: min(272px, 78vw) !important;
    max-width: none !important;
    flex: none !important;
    transform: none !important;
    z-index: 5;
    box-shadow: 4px 0 18px rgba(0, 0, 0, .22);
    background: var(--main-background-color);
}

.files-shell.files-open .dx-filemanager-container .dx-filemanager-dirs-panel {
    display: flex !important;
    flex-direction: column;
    height: 100%;
}

.files-shell.files-open .files-scrim {
    display: block;
}

@media (max-width: 600px) {
    .files-shell #file-manager {
        height: max(420px, calc(100vh - 300px));
    }

    .files-shell #file-manager .dx-treeview-item {
        height: 44px;
        min-height: 44px;
    }

    .files-shell #file-manager .dx-filemanager-details .dx-data-row > td {
        padding-top: 12px;
        padding-bottom: 12px;
    }

    .files-shell .files-action-btn {
        padding: 10px 14px;
    }

    .files-shell #fileuploader-container .dx-fileuploader-input-label {
        display: none;
    }

    .files-shell #fileuploader-container .dx-fileuploader-button.dx-button {
        min-height: 40px;
        height: 40px;
    }

    .files-shell + #file-uploader-storage-remaining {
        border: 1px solid var(--main-border-color);
        border-top: 0;
    }
}

@media (max-width: 480px) {
    .files-shell #file-manager .dx-filemanager-details .dx-header-row > td,
    .files-shell #file-manager .dx-filemanager-details .dx-data-row > td {
        padding-left: 12px;
        padding-right: 12px;
    }

    .files-shell .files-actions-left {
        width: 100%;
    }

    .files-shell .files-actions-left > .files-action-btn {
        flex: 1;
        width: 100%;
        justify-content: center;
    }
}

/* ── Files — card shell (.files-shell.fm-v2) ───────────────────────────
   The folder tree and the file grid as two separate rounded cards on the
   page's recessed ground, on a 14px / 48px scale rather than the compact
   11px / 32px one the Company Details tab previously used on its own.

   Applied as a MODIFIER beside .files-shell, never in place of it: the
   responsive rules above keep working untouched, and the whole restyle
   reverts by deleting one class from the two views. Shared by /Files and
   the Company Details Files tab — which is why it lives here rather than
   in either view's own <style> block.

   Note this deliberately reverses the compact scale: on Company Details the
   Files tab now reads a size larger than its sibling tabs. That was asked
   for, so the two file managers match. */

/* ONE card with an internal divider between the folder column and the grid —
   not two separate cards with a gap between them. */
.files-shell.fm-v2 #file-manager {
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-radius: 10px;
    overflow: hidden;
    box-shadow: none;
}

/* DevExtreme's own toolbar is replaced by .files-actions, but must stay in the
   DOM rather than be removed: the New Folder button works by proxying a click
   to the widget's hidden newfolder command. */
.files-shell.fm-v2 .dx-filemanager-toolbar {
    display: none !important;
}

.files-shell.fm-v2 .dx-filemanager-container {
    background: transparent;
}

.files-shell.fm-v2 .dx-filemanager-items-panel {
    background: transparent;
    border: 0;
    border-radius: 0;
}

/* The folder column's ground is the recessed grey, so the reserved space between
   the last folder and the quota reads as empty panel rather than as more card.
   The header, the tree and the quota each lay their own white over it. */
.files-shell.fm-v2 .dx-filemanager-container .dx-drawer-panel-content {
    background: var(--main-background-color-subtle);
    border: 0;
    border-radius: 0;
}

/* The dirs panel grows to fill the column, so it has to carry the grey too —
   otherwise the theme's own white on it covers the panel behind. */
.files-shell.fm-v2 #file-manager .dx-filemanager-dirs-panel {
    background: var(--main-background-color-subtle);
}

/* ...and the tree keeps that same grey rather than laying the theme's white over
   it: the tree is content-height, so a white tree made the column change colour
   at the last folder — collapsed it read as one grey panel, expanded the child
   rows turned white. Only the FOLDERS caption and the quota are white bands. */
.files-shell.fm-v2 .dx-filemanager-tree-view,
.files-shell.fm-v2 #file-manager .dx-filemanager-dirs-tree {
    background: var(--main-background-color-subtle);
}

/* The card's own edge replaces the theme's divider on the tree itself; the
   single rule between the two columns is drawn by the panel wrapper below. */
.files-shell.fm-v2 .dx-filemanager-tree-view {
    border-right: 0;
}

@media (min-width: 681px) {
    .files-shell.fm-v2 .dx-filemanager-container .dx-drawer-panel-content {
        border-right: 1px solid var(--main-border-color);
    }

    /* The splitter stays in the flow — it is the drag handle that resizes the
       tree — and is only made transparent, so the affordance survives. */
    .files-shell.fm-v2 .dx-filemanager-container .dx-splitter {
        background: transparent;
        border: 0;
    }
}

/* FOLDERS caption — on the card's own white rather than the grey the compact
   scale used, so the tree card reads as a single surface.

   The label metrics are declared in full here, not just the overrides: the base
   .folders-header rule lives in the Company Details <style> block, so /Files —
   which injects the same caption from initShellLayout() — would otherwise render
   it as plain body text. */
.files-shell.fm-v2 .folders-header {
    display: flex;
    align-items: center;
    padding: 0 16px;
    font-size: 11px;
    font-weight: var(--font-extra-bold);
    color: var(--text-sub);
    letter-spacing: .8px;
    text-transform: uppercase;
    background: var(--main-background-color);
    border-bottom: 1px solid var(--main-border-color);
}

/* dx.alterations.css insets every tree row with `margin: 2px 8px`, which would
   hold the selected row's left rule off the card edge. */
.files-shell.fm-v2 #file-manager .dx-treeview-item {
    height: 38px;
    min-height: 38px;
    margin: 0;
    padding: 0 16px;
    font-size: 13px; /* matches file rows and sidebar */
}

/* A long folder name truncates rather than wrapping — the row is a fixed 38px,
   so a second line would overflow it instead of growing the row. */
.files-shell.fm-v2 #file-manager .dx-treeview-item-content {
    display: flex;
    align-items: center;
    min-width: 0;
    white-space: nowrap;
}

.files-shell.fm-v2 #file-manager .dx-treeview-item-content > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Selected row, both panels ────────────────────────────────────────────
   --row-selected-bg (layout.css) — the navigation pane's own selected shade —
   for the selected folder in the tree AND the selected rows in the grid, so the
   two panels read as one control and agree with every other row in the app.

   Deliberately NOT the brand tint: selection here is a working state on a row
   of files, not an accent, and the theme's red wash made the grid look like it
   was flagging an error. dx.alterations.css now neutralises those theme states
   globally; these rules stay because the tree still needs the !important below.

   The state class sits on .dx-treeview-node, not on .dx-treeview-item — which
   is why dx.alterations.css's `.dx-filemanager .dx-treeview-item.dx-state-selected`
   (a hardcoded Material indigo, #eef2ff/#4338ca) never actually matched and the
   tree has been falling through to the theme's own grey all along. The node
   selector below is the live one; the item selectors are kept only to keep that
   indigo from waking up, hence the !important. */
.files-shell.fm-v2 #file-manager .dx-treeview-node.dx-state-selected > .dx-treeview-item,
.files-shell.fm-v2 #file-manager .dx-treeview-item.dx-state-focused,
.files-shell.fm-v2 #file-manager .dx-treeview-item.dx-state-selected {
    background: var(--row-selected-bg) !important;
    color: var(--main-text-color) !important;
}

/* The grid's own selected/focused states, matched to the tree row above.
   Two separate theme states land on the same row and both are brand-tinted:
   .dx-selection is a faint rgba(red,.04) wash, and .dx-row-focused — the row
   you last clicked — is the solid --main-color-highlight pink. Both are
   restated here rather than only the focused one, or clicking then selecting
   would shift the row between two greys. */
.files-shell.fm-v2 #file-manager .dx-datagrid-rowsview .dx-selection.dx-row:not(.dx-row-focused) > td,
.files-shell.fm-v2 #file-manager .dx-datagrid-rowsview .dx-selection.dx-row:not(.dx-row-focused):hover > td,
.files-shell.fm-v2 #file-manager .dx-datagrid-rowsview .dx-row-focused.dx-data-row > td:not(.dx-focused):not(.dx-cell-modified):not(.dx-datagrid-invalid) {
    background-color: var(--row-selected-bg);
    color: var(--main-text-color);
}

/* Without row lines the theme draws the focused row's top and bottom edges in
   --main-color-outline, so the red survives the fill above as two rules. */
.files-shell.fm-v2 #file-manager .dx-datagrid-rowsview .dx-row-focused.dx-data-row:not(.dx-row-lines) > td,
.files-shell.fm-v2 #file-manager .dx-datagrid-rowsview .dx-row-focused.dx-data-row:not(.dx-row-lines) > tr:first-child > td {
    border-top-color: transparent;
    border-bottom-color: var(--main-border-color);
}

/* Same for the selected row's top/bottom rules, tinted rgba(red,.04). */
.files-shell.fm-v2 #file-manager .dx-datagrid-rowsview .dx-selection.dx-row:not(.dx-row-focused):not(.dx-row-lines) > td,
.files-shell.fm-v2 #file-manager .dx-datagrid-rowsview .dx-selection.dx-row:not(.dx-row-focused):hover:not(.dx-row-lines) > td {
    border-top-color: transparent;
    border-bottom-color: var(--main-border-color);
}

/* The tree card draws its own edge, so drop the one dx.alterations.css forces,
   and let the card set the width rather than its hardcoded 260px. */
.files-shell.fm-v2 .dx-filemanager .dx-filemanager-dirs-panel {
    border-right: 0 !important;
    width: auto;
}

/* Base layout for the action bar, not just its padding: the flex rules live in
   the Company Details <style> block, so /Files would otherwise stack the
   buttons and lose the right-aligned breadcrumb.

   The fixed height is shared with .folders-header below so the two bands end on
   the same line — that is what puts the tree's first folder and the grid's
   column header on one baseline across the divider. Change one, change both. */
.files-shell.fm-v2 .files-actions,
.files-shell.fm-v2 .folders-header {
    box-sizing: border-box;
    height: 48px;
    /* Both sit in fixed-height flex columns (the tree column and the items panel).
       Without this, the grid or tree below them claimed part of the band once it
       outgrew the column, and the two rules stopped meeting on one line. */
    flex-shrink: 0;
}

.files-shell.fm-v2 .files-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 0 16px;
    background: var(--main-background-color);
    border-bottom: 1px solid var(--main-border-color);
}

.files-shell.fm-v2 .files-actions-left {
    display: flex;
    align-items: center;
    gap: 8px;
}

.files-shell.fm-v2 .files-actions-right {
    margin-left: auto;
    display: flex;
    align-items: center;
}

/* The breadcrumb is relocated into the action bar, so the panel padding and
   bottom rule dx.alterations.css gives it would double the bar's own. */
.files-shell.fm-v2 #custom-breadcrumb .dx-filemanager-breadcrumbs {
    background: transparent !important;
    border: 0 !important;
    padding: 0;
}

.files-shell.fm-v2 #custom-breadcrumb .dx-menu-item .dx-menu-item-text {
    font-size: 13px;
}

/* Sentence case, square corners. The button labels are already sentence case in
   the resx — only the compact scale's text-transform uppercased them. */
/* Explicit height rather than vertical padding, so the button can't outgrow the
   48px band and cross its bottom rule. .files-action-btn is already an
   inline-flex row, so the label and icon stay centred. */
.files-shell.fm-v2 .files-action-btn {
    height: 32px;
    padding: 0 14px;
    border-radius: 0;
    font-size: 13px;
    letter-spacing: normal;
    text-transform: none;
}

/* Grid header — likewise sentence case; the captions come from
   FileManager.FileName / .DateModified / .FileSize as written. */
.files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-header-row > td,
.files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-header-row > td .dx-datagrid-text-content {
    font-size: 12px;
    letter-spacing: .3px;
    text-transform: none;
    color: var(--df-mid);
}

/* --df-mid has no dark value, so headers vanished in dark mode. */
.dark-mode .files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-header-row > td,
.dark-mode .files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-header-row > td .dx-datagrid-text-content {
    color: var(--text-sub);
}

.files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-header-row > td {
    padding: 9px 16px;
}

.files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-data-row > td {
    padding: 9px 16px;
    font-size: 13px;
}

/* Regular weight, overriding dx.alterations' app-wide semi-bold (as .cd-grid-card does). */
.files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-data-row > td,
.files-shell.fm-v2 #file-manager .dx-treeview-item {
    font-weight: var(--font-regular);
}
/* Upload strip — a slim bar above the cards rather than DevExtreme's ~100px
   dashed dropzone. Trimmed here rather than in either view, for the same reason
   as the action bar: the Company Details rules are page-local. */
/* ── Upload strip ─────────────────────────────────────────────────────────
   One square-cornered dashed drop target holding the button and the drop hint on
   a single row. The border sits on #fileuploader-container — the view's own div —
   so everything the widget renders is inside the dashed box.

   DO NOT make this a flex container, and do not shrink-wrap #file-uploader.
   dxFileUploader gives its native file input a width of 500000px on purpose, so
   that a click anywhere in the dropzone opens the picker. Shrink-wrapping the
   widget resolves its max-content width against that input and throws the whole
   thing ~250000px off-screen, taking the Select Files button with it. The widget
   needs a definite width, which it gets from this block-level container.

   The hint is therefore DevExtreme's own label, repositioned rather than
   replaced: the theme renders it as an absolute full-width overlay
       position: absolute; top: 0; left: 0; height: 100%; width: 100%
   with the text centred, so left-aligning it and clearing the button's width
   with padding is all that is needed — no layout is disturbed and the huge input
   keeps doing its job. */
.files-shell.fm-v2 #fileuploader-container {
    position: relative;
    /* The left inset keeps the button off the rule rather than flush against it. */
    padding: 7px 12px 7px 16px;
    margin: 0 0 14px;
    background: transparent;
    /* --main-border-color, not --main-background-color-subtle-highlight: the
       latter is a background token (#0a0a0c in dark), which left the dashed
       outline invisible against the card. */
    border: 1px dashed var(--main-border-color);
    border-radius: 0;
}

/* Sits immediately right of the button instead of centred in the strip.

   The label is absolutely positioned against .dx-fileuploader-input-container —
   the table cell that follows the button's own cell — so its left edge is
   already the button's right edge. The inset here is a gutter, not a clearance;
   anything button-width over-indents the hint and, because the cell is narrow,
   ellipsises it.

   Vertical padding is zeroed rather than left at the theme's 7.75px: with the
   label filling the 34px row the 34px line-height centres the text on the
   button, whereas the theme padding pushed it below centre. */
.files-shell.fm-v2 #fileuploader-container .dx-fileuploader-input-label {
    padding: 0 0 0 12px;
    font-size: 13px;
    line-height: 34px;
    text-align: left;
    white-space: nowrap;
    color: var(--text-sub);
}

.files-shell.fm-v2 #fileuploader-container .dx-fileuploader-wrapper,
.files-shell.fm-v2 #fileuploader-container .dx-fileuploader-input-wrapper {
    padding: 0;
    border: 0;
    border-radius: 0;
}

/* Select Files only — hence the .dx-fileuploader-input-wrapper ancestor rather
   than .dx-fileuploader-button alone.

   dxFileUploader gives the per-file cancel button the SAME .dx-fileuploader-button
   class (`<div class="dx-fileuploader-button dx-fileuploader-cancel-button">`,
   icon "close", no text) and the theme sizes it 28x28. Without the wrapper in
   the selector the rules below caught it too: the 34px height stretched a
   28px-wide box, and the 18px side padding on an icon with no label pushed the
   × out of the button and over the file name. The two live in different
   subtrees — the select button under .dx-fileuploader-input-wrapper, the cancel
   button under .dx-fileuploader-files-container — so the ancestor separates
   them cleanly and the cancel button keeps the theme's own sizing. */
.files-shell.fm-v2 #fileuploader-container .dx-fileuploader-input-wrapper .dx-fileuploader-button.dx-button {
    min-height: 34px;
    height: 34px;
    border-radius: 0;
    /* dx.alterations nudges the button down 3px for the old dropzone. */
    top: 0;
}

/* Padding on the content only — putting it on .dx-button-text as well pads the
   label twice over and clips it inside the fixed-height button. */
.files-shell.fm-v2 #fileuploader-container .dx-fileuploader-input-wrapper .dx-fileuploader-button .dx-button-content {
    padding: 0 18px;
    font-size: 13px;
    font-weight: var(--font-bold);
    line-height: 34px;
    white-space: nowrap;
}

/* Sentence case: the theme uppercases DevExtreme button labels. */
.files-shell.fm-v2 #fileuploader-container .dx-fileuploader-input-wrapper .dx-fileuploader-button .dx-button-text {
    text-transform: none;
}

/* Quota pinned to the foot of the tree card, so it reads as a summary of the card
   above it rather than floating under the last folder. The flex column and the
   auto margin are both page-local in Company Details, which is why /Files left it
   mid-card with empty card below. */
.files-shell.fm-v2 .dx-filemanager-dirs-panel {
    display: flex;
    flex-direction: column;
}

/* No min-height:100% here. That belonged to the superseded margin-top:auto
   approach and only stretches the tree's own white over the grey below it. */
.files-shell.fm-v2 #file-manager .dx-filemanager-dirs-panel .dx-scrollable-content {
    display: flex;
    flex-direction: column;
}

/* The tree is only as tall as its folders — NOT flex:1 — so the grey ground shows
   through below it when a company has few folders. It still shrinks (and scrolls
   internally) once the folders outgrow the column.
   #file-manager is in the selector to outrank dx.alterations.css:856, which sets
   `flex: 1 1 0%` on the same element through an id and would otherwise win. */
.files-shell.fm-v2 #file-manager .dx-filemanager-dirs-tree {
    flex: 0 1 auto;
    min-height: 0;
}

.files-shell.fm-v2 #file-manager .dx-filemanager-dirs-tree .dx-scrollable {
    max-height: 100%;
}

/* Pinned, not flexed. The flex/auto-margin approach depends on dxFileManager's
   exact panel nesting and did not survive contact with the real widget — the
   quota kept sitting under the last folder with dead card below it. Anchoring it
   to the folder column instead is independent of how many wrappers DevExtreme
   puts in between, and the reserved padding below keeps the tree from scrolling
   under it. Above the mobile breakpoint only: below it initShellLayout() moves
   the quota out of the shell entirely. */
@media (min-width: 601px) {
    .files-shell.fm-v2 .dx-filemanager-container .dx-drawer-panel-content {
        position: relative;
        padding-bottom: 78px;
    }

    .files-shell.fm-v2 #file-uploader-storage-remaining {
        position: absolute;
        inset: auto 0 0 0;
        background: var(--main-background-color);
    }
}

.files-shell.fm-v2 #file-uploader-storage-remaining {
    padding: 14px 16px;
    font-size: 12px;
    border-top: 1px solid var(--main-border-color);
}

/* The quota is only filled for users who can upload, so without this a
   view-only user gets an empty bordered strip under the tree. */
.files-shell.fm-v2 #file-uploader-storage-remaining:empty {
    display: none;
}

/* Style.css gives this its own border, shadow and -4px pull so it can sit under
   a square widget. Inside the tree card that reads as a second card nested in
   the first, so it is flattened here. */
.files-shell.fm-v2 .file-uploader-storage-remaining-container {
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    color: inherit;
    font-size: 12px;
}

.files-shell.fm-v2 .file-uploader-storage-bar {
    height: 5px;
    border-radius: 99px;
    background: var(--main-border-color);
    margin: 7px 0;
}

.files-shell.fm-v2 .file-uploader-storage-bar-fill {
    border-radius: 99px;
}

.files-shell.fm-v2 .file-uploader-storage-location {
    font-size: 11px;
}

/* The grid's loading overlay ships as a circular white card drawn behind the
   dots — border, border-radius:50%, 13px padding and a drop shadow, all from the
   theme's .dx-loadpanel-content.

   dx.alterations.css already strips that chrome, but only through
   `.dx-loadpanel .dx-loadpanel-content`, which needs the content to sit inside
   the widget root. DevExtreme builds this one with `container: <grid parent>`,
   so the overlay wrapper is appended to the grid instead and the root is never
   an ancestor — the rule simply doesn't match here, which is why the card
   survives on this page and not in the dropdown loaders.

   Stripped shell-scoped rather than by broadening the global rule: the
   notification panel (_NotificationList.cshtml) keeps its card on purpose, and
   its opt-out marker lives on the .dx-loadpanel root, so it cannot be excluded
   from a wrapper-based selector. Only the 5-dot pulse is left, as elsewhere. */
.files-shell.fm-v2 .dx-loadpanel-content {
    border: none;
    background: transparent;
    box-shadow: none;
    padding: 0;
    border-radius: 0;
}

/* ── Notification popup, moved to the toast corner ────────────────────────
   dxFileManager reports every operation — "Created a directory inside My Files",
   upload progress, delete/move/copy results and their errors — through its own
   popup, which it pins to the top-right of the widget:

       position: { my: "right top", at: "right top", of: positionTarget }

   over the grid's own toolbar row. That is a different corner from every other
   message in the app: window.showMessage (_ToastNotification.cshtml) and the
   dxToast defaults in utils.js both put messages bottom-right, 20px in.

   Moved rather than replaced by a real toast, deliberately. The popup is not
   only a message: it carries the upload progress bar and the per-item error
   detail box, and it aggregates a batch into one line ("Deleted 3 items") where
   the widget's public events fire once per item. Re-raising these through
   window.showMessage would cost all three. Re-pointing it costs nothing.

   The popup is created with `container: this.$element()` and appended to the
   widget root, so it is reachable from the shell scope — no body-level marker
   class is needed the way the shared toast needs .sh-toast.

   !important is required: DevExtreme's positioning engine writes top/left onto
   .dx-overlay-content as inline styles, and only an important declaration
   outranks those. position:fixed also takes the popup out of the card's
   `overflow: hidden` — fixed elements are not clipped by an ancestor's
   overflow — and anchors it to the viewport, which is what the toast does too,
   so the two land in the same place whichever shows. */
.files-shell.fm-v2 #file-manager .dx-filemanager-notification-popup .dx-overlay-content {
    position: fixed !important;
    top: auto !important;
    left: auto !important;
    right: 20px !important;
    bottom: 20px !important;
    max-width: min(400px, calc(100vw - 40px)) !important;
    /* Square corners and the shared toast's shadow, so it reads as the same kind
       of message the rest of the app raises. The radius mirrors
       dx.alterations.css:14, which flattens .dx-toast-content app-wide; the
       shadow is the theme's own toast shadow, which survives that rule. */
    border-radius: 0;
    box-shadow: 0 2px 3px rgba(var(--black-color-rgb), .15);
}

.files-shell.fm-v2 #file-manager .dx-filemanager-notification-popup .dx-popup-content {
    padding: 12px 14px;
    font-size: 13px;
}

/* The progress bar and the error detail box keep the breathing room the theme
   gives them; only the outer padding above is tightened to toast proportions. */
.files-shell.fm-v2 #file-manager .dx-filemanager-notification-popup .dx-filemanager-notification-common {
    padding-bottom: 8px;
}

/* ── Files card shell — parity with the Company Details inline block ────
   Details.cshtml carries a long inline <style> for the file manager. Most of it
   is superseded by the .fm-v2 rules above, but these had no counterpart here, so
   /Files — which has no inline block of its own — rendered them at DevExtreme's
   own defaults while the Company Details tab did not. Values copied verbatim from
   that block so the tab keeps rendering exactly as signed off.

   Scoped through #file-manager on purpose. The rowsview rule is UNSCOPED in
   Details.cshtml, where it reaches all eleven dxDataGrids on that page; that is a
   latent bug and is deliberately not reproduced. */

.files-shell.fm-v2 #file-manager .dx-datagrid > .dx-datagrid-rowsview {
    background-color: var(--main-background-color);
}

/* The breadcrumb's own .dx-menu sits inside .dx-filemanager-breadcrumbs and
   carries the theme's background and border in its own right, so clearing the
   wrapper above is not enough to sink it into the action bar. */
.files-shell.fm-v2 #custom-breadcrumb .dx-menu {
    background: transparent !important;
    border: none !important;
    padding: 0;
}

.files-shell.fm-v2 #custom-breadcrumb .dx-menu-horizontal {
    margin: 0;
}

.files-shell.fm-v2 #file-manager .dx-filemanager-details-item-thumbnail {
    font-size: 16px !important;
}

.files-shell.fm-v2 #file-manager .dx-filemanager-dirs-tree .dx-icon-folder {
    font-size: 15px !important;
}

/* ── Files card shell — responsive (.files-shell.fm-v2) ────────────────────
   .files-shell already carries this page's responsive contract at 900/680/600/480
   (mirrored in applyFilesLayout(), so these four values are not free to change
   without touching the JS). Every .fm-v2 rule above is written outside a media
   query, sits later in the file and adds one class of specificity — so each one
   silently outranked its mobile counterpart and the page rendered at desktop
   metrics all the way down.

   This block re-asserts those mobile values at .fm-v2 specificity rather than
   rewriting the card rules into min-width guards: the desktop rendering above is
   the signed-off one and stays byte-identical, and the whole adaptation still
   reverts by deleting a block.

   Nothing here is zoom-specific. Browser zoom scales the CSS pixel, so zooming in
   narrows the effective viewport and lands on these same breakpoints; there is no
   second mechanism to build.

   Only properties .fm-v2 actually clobbered are repeated. Where the base rule
   still wins on its own — the drawer width clamp at 900 (it is !important), the
   off-canvas tree at 680 (already guarded by min-width: 681), the shell height
   and the full-width buttons at 480 — nothing is restated. */

@media (max-width: 900px) {
    /* .fm-v2 sets `padding: 9px 16px` as a shorthand, which reinstates the 16px
       sides the base rule trims here. Left/right only, so the 9px rhythm and the
       12px the 600 block adds to it both survive. */
    .files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-header-row > td,
    .files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-data-row > td {
        padding-left: 10px;
        padding-right: 10px;
    }

    .files-shell.fm-v2 #file-manager .dx-treeview-item {
        padding: 0 8px;
    }

    /* The caption is the tree column's header — it has to keep the same inset as
       the rows under it, or the label hangs off them. */
    .files-shell.fm-v2 .folders-header {
        padding: 0 8px;
    }
}

@media (max-width: 680px) {
    /* The action bar wraps to a second row here (base rule: flex-wrap: wrap),
       and .fm-v2 pinned it to a hard 48px — so the wrapped row was clipped by
       the bar's own bottom rule. min-height keeps the one-row bar at exactly the
       height it has on desktop while letting the wrapped bar grow.

       The block padding replaces the 48px box's optical centring once the bar is
       two rows tall; .files-actions-right brings its own padding-top and rule
       from the base block. */
    .files-shell.fm-v2 .files-actions {
        height: auto;
        min-height: 48px;
        padding-top: 8px;
        padding-bottom: 8px;
    }

    /* .fm-v2's `margin-left: auto` pushes the right-hand group off the end of its
       own row once the base block has given it `width: 100%` and `order: 3`. */
    .files-shell.fm-v2 .files-actions-right {
        margin-left: 0;
    }
}

@media (max-width: 600px) {
    /* 44px is the touch floor the base block sets for this row; .fm-v2's 38px is
       a pointer-only metric. The fixed row height is why .dx-treeview-item-content
       truncates rather than wraps, so it is raised, never released to auto. */
    .files-shell.fm-v2 #file-manager .dx-treeview-item {
        height: 44px;
        min-height: 44px;
    }

    /* Both buttons are given explicit heights by .fm-v2 (32px / 34px) rather than
       vertical padding, so the base block's padding-based enlargement had nothing
       to act on — the heights themselves have to move. 40px matches the base
       block's own target for the uploader button. */
    .files-shell.fm-v2 .files-action-btn {
        height: 40px;
    }

    .files-shell.fm-v2 #fileuploader-container .dx-fileuploader-input-wrapper .dx-fileuploader-button.dx-button {
        min-height: 40px;
        height: 40px;
    }

    /* The label is hidden by the base block at this width, so the button is the
       only thing in the strip and the desktop asymmetry has nothing to balance. */
    .files-shell.fm-v2 #fileuploader-container {
        padding: 8px;
    }

    .files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-data-row > td {
        padding-top: 12px;
        padding-bottom: 12px;
    }
}

@media (max-width: 480px) {
    .files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-header-row > td,
    .files-shell.fm-v2 #file-manager .dx-filemanager-details .dx-data-row > td {
        padding-left: 12px;
        padding-right: 12px;
    }
}

/* ============================================================================
   Direct download page (Products/ and Files/DirectDownload.cshtml)
   ============================================================================ */
/* Centred in the content area as one bordered card, in the same frame as the
   full-page error card, with a brand-red top rule instead of the error rail. */
.direct-download {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: calc(100vh - var(--topbar-h, 56px));
}

.direct-download-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    width: 100%;
    max-width: 440px;
    padding: 32px 28px;
    text-align: center;
    background: var(--main-background-color);
    border: 1px solid var(--main-border-color);
    border-top: 3px solid var(--main-color);
}

.direct-download-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    color: var(--main-color);
    background: rgba(var(--main-color-rgb), .1);
}

.direct-download-icon .svg-icon:not(.nav-icon):not(.ui-icon) > svg {
    width: 22px;
    height: 22px;
}

.direct-download-title {
    margin: 0;
    font-size: 18px;
    font-weight: var(--font-bold);
    line-height: 1.35;
    color: var(--main-text-color);
}

.direct-download-text {
    margin: -8px 0 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-sub);
}

/* ============================================================================
   Responsive
   ============================================================================ */
@media (max-width: 480px) {
    .direct-download-card {
        padding: 24px 16px;
    }
}

/* ============================================================================
   Inline SVG glyphs that replaced emoji / text symbols (download links,
   search buttons).
   ============================================================================ */
.inline-icon {
    vertical-align: -2px;
}

.inline-icon > svg {
    width: 14px;
    height: 14px;
}

.search-button .svg-icon {
    width: 20px;
    height: 20px;
    color: var(--main-color);
}

.search-button .svg-icon > svg {
    width: 20px;
    height: 20px;
}

.search-button .search-button-partner {
    color: var(--success-color);
}

.inline-icon-lg {
    vertical-align: -3px;
}

.inline-icon-lg > svg {
    width: 18px;
    height: 18px;
}

.files-shell .files-action-btn .svg-icon > svg {
    width: 15px;
    height: 15px;
}

.icon-spin {
    display: inline-flex;
}

.icon-spin > .svg-icon > svg {
    animation: icon-spin 0.9s linear infinite;
}

@keyframes icon-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .icon-spin > .svg-icon > svg { animation: none; }
}

/* System Health status glyphs (Azure Functions summary/cards, service overrides). */
.status-ico-ok,
.status-ico-available { color: var(--success-color); }

.status-ico-warn,
.status-ico-degraded { color: var(--warning-color, #d97706); }

.status-ico-bad,
.status-ico-unavailable { color: var(--error-text, var(--error-color, #dc2626)); }

.status-ico-unknown { color: var(--text-sub, currentColor); }

/* CSAT star rating built by buildCsatStarString (utils.js). */
.csat-stars {
    display: inline-flex;
    align-items: center;
    gap: 1px;
    vertical-align: middle;
}

.csat-stars > .svg-icon > svg {
    width: 14px;
    height: 14px;
}

/* CSAT stars on Customer Details (average rating and the cases grid column). */
.avg-csat-stars,
.csat-grid-stars {
    display: inline-flex;
    align-items: center;
    gap: 1px;
    vertical-align: middle;
}

.avg-csat-stars > .svg-icon > svg {
    width: 16px;
    height: 16px;
}

.csat-grid-stars > .svg-icon > svg {
    width: 12px;
    height: 12px;
}

/* Attachment file-type icon (SVG, replaces the DevExtreme dx-icon-*file glyphs). */
.cd-attachment-icon > .svg-icon > svg {
    width: 18px;
    height: 18px;
}

/* Chevron SVGs that replaced the ‹ › text glyphs in pagers and result rows. */
.pager button .svg-icon,
.pager-btn .svg-icon,
[data-page] .svg-icon,
[data-pp-page] .svg-icon {
    display: inline-flex;
    vertical-align: middle;
}

.pager button .svg-icon > svg,
.pager-btn .svg-icon > svg,
[data-page] .svg-icon > svg,
[data-pp-page] .svg-icon > svg {
    width: 14px;
    height: 14px;
}

.cs-row-arrow > .svg-icon > svg {
    width: 18px;
    height: 18px;
}

/* Status dot inside the Active chip on Customer Details (replaces the ● character). */
.co-chip .co-chip-dot > svg {
    width: 8px;
    height: 8px;
}

/* Square status marker for the Active chip, drawn in the chip's own colour. */
.co-chip .co-chip-dot-square {
    display: inline-block;
    width: 8px;
    height: 8px;
    background: currentColor;
    flex-shrink: 0;
}

/* Azure Functions status lines: the icon box is centred on the text line, so the icon and
   its label sit on one horizontal axis (summary lines and per-function rows alike). */
.az-fn-ok-line > .svg-icon,
.az-fn-fail-line > .svg-icon,
.az-fn-row .ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    vertical-align: baseline;
}

.az-fn-ok-line > .svg-icon > svg,
.az-fn-fail-line > .svg-icon > svg,
.az-fn-row .ico .svg-icon > svg {
    width: 14px;
    height: 14px;
}

.az-fn-row .ico {
    line-height: 0;
}

/* ══════════════════════════════════════════════════════════════════
   auth.css — Login, Registration, Password Reset, Email Confirm
   Shell: sigmahq-auth.html 2-panel. Brand panel (left) always dark.
   Form panel (right) honours .dark-mode.
   ══════════════════════════════════════════════════════════════════ */

:root {
    --auth-brand-bg: #1c1c1e;
    --auth-brand-text: #ffffff;
    --auth-brand-muted: rgba(255, 255, 255, 0.48);
    --auth-brand-dim: rgba(255, 255, 255, 0.22);
    --auth-brand-feat-text: rgba(255, 255, 255, 0.62);
    --auth-field-bg: #ffffff;
    --auth-field-bg-subtle: #f8f9fa;
}

.dark-mode {
    --auth-field-bg: var(--main-background-color-subtle);
    --auth-field-bg-subtle: var(--main-background-color-subtle-highlight);
}

/* Style.css's global `body { min-height: 100vh }` doesn't know about dvh, so
   on a phone with its browser chrome visible, the body (sized to the larger
   vh) ends up taller than .auth-shell below (sized to the smaller, real dvh)
   — the gap between them is blank white body background showing past the
   shell. Matching body to the same dvh value it's missing closes that gap. */
body.p-0 {
    min-height: 100vh;
    min-height: 100dvh;
}

.auth-shell {
    display: flex;
    width: 100%;
    min-height: 100vh;
    /* dvh tracks the browser's actual visible area as mobile chrome (the URL
       bar) shows/hides; 100vh is the larger "chrome hidden" height, which is
       what let this overflow into a scroll on real phones. Falls back to the
       vh value above on browsers that don't support dvh. */
    min-height: 100dvh;
    background: var(--main-background-color);
    color: var(--main-text-color);
}

.auth-brand {
    width: 400px;
    flex-shrink: 0;
    /* Carries the same facet texture as the app's banners and dialog headers, so
       the first screen a user sees is already the material the rest of the product
       uses. --auth-brand-bg was #1c1c1e, which is exactly this tile's base colour,
       so the panel's tone is unchanged — only the texture is new. */
    background: var(--df-facet-tile);
    display: flex;
    flex-direction: column;
    padding: 48px 44px;
    position: relative;
    overflow: hidden;
}

/* The two oversized red circles (::before / ::after) and the .brand-poly /
   .brand-stripe polygon SVGs that used to decorate this panel are gone. They were
   translucent red washes over a flat fill; against the facet texture they read as
   a colour cast sitting on top of the pattern rather than as depth — the same call
   already made for the hero bands' red hexagon watermarks. The facets are the
   panel's texture now. */

/* Stacks now: the lockup is a wide 5:1 image, so the Customer Portal badge sits
   under it rather than beside it. The hexagon .brand-logo-mark — and the
   .brand-logo-text / -name that partnered it — are gone: that hexagon was a
   CSS-built stand-in, and the real lockup carries the mark and wordmark together.
   NOTE: .brand-logo is also used by the invoice page (rules in pages.css), which
   has its own .logo-light / .logo-dark children. Nothing here targets a bare
   descendant, so the two stay independent. */
.brand-logo {
    /* Single source of truth for the lockup's size: the badge indent below is
       derived from it, so the two can never drift apart. */
    --lockup-h: 40px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
    margin-bottom: auto;
    position: relative;
    z-index: 1;
    text-decoration: none;
}

.brand-logo-lockup {
    /* The 400px panel less its 44px side padding leaves 312px, so a 5:1 lockup at
       40px tall (=200px wide) sits comfortably inside it. */
    height: var(--lockup-h);
    width: auto;
    max-width: 100%;
    display: block;
}

.brand-logo-product {
    display: inline-block;
    /* Indented to sit under the "MY SIGMATEK" wordmark rather than under the
       triangle mark. The wordmark's leftmost point ("M") is at x=393.47 in the
       lockup's 303.57-tall viewBox, so its offset from the image's left edge is
       always 393.47/303.57 = 1.2961 x the rendered height, whatever that height
       is. Derived from --lockup-h so resizing the lockup re-indents the badge. */
    margin-left: calc(var(--lockup-h) * 1.2961);
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    letter-spacing: 3px;
    word-spacing: 10px;
    text-transform: uppercase;
    color: var(--main-color);
}

.brand-hero {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    position: relative;
    z-index: 1;
    padding: 40px 0;
}

.brand-visual {
    margin-bottom: 28px;
    opacity: .9;
}

.brand-headline {
    font-size: 25px;
    font-weight: var(--font-extra-bold);
    color: var(--auth-brand-text);
    line-height: 1.32;
    margin-bottom: 12px;
}

.brand-headline span { color: var(--main-color); }

.brand-body {
    font-size: 13px;
    color: var(--auth-brand-muted);
    line-height: 1.75;
    margin-bottom: 32px;
}

.brand-feats {
    display: flex;
    flex-direction: column;
    gap: 13px;
}

.brand-feat {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 13px;
    color: var(--auth-brand-feat-text);
}

.brand-feat-icon {
    width: 32px;
    height: 32px;
    background: rgba(var(--main-color-rgb), .18);
    border-radius: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    flex-shrink: 0;
}

.brand-footer {
    position: relative;
    z-index: 1;
    margin-top: 32px;
}

.brand-footer-copy {
    font-size: 11px;
    color: var(--auth-brand-dim);
}

.auth-panel {
    position: relative;
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 48px 40px;
    overflow-y: auto;
    background: var(--main-background-color);
    color: var(--main-text-color);
}

.auth-card {
    width: 100%;
    max-width: 416px;
}

.auth-screen { display: none; }
.auth-screen.active { display: block; }

/* Mobile counterpart of .brand-logo — same treatment, smaller. .form-logo-mark /
   -name retired for the same reason as the brand panel's. */
.form-logo {
    --lockup-h: 30px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    margin-bottom: 32px;
    text-decoration: none;
}

.form-logo-lockup {
    height: var(--lockup-h);
    width: auto;
    max-width: 100%;
    display: block;
}

.form-logo-product {
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    letter-spacing: 3px;
    word-spacing: 10px;
    text-transform: uppercase;
    color: var(--main-color);
    display: block;
    /* Same wordmark indent as .brand-logo-product — see the note there. */
    margin-left: calc(var(--lockup-h) * 1.2961);
}

@media (min-width: 1025px) { .form-logo { display: none; } }

.form-hdr { margin-bottom: 28px; }

/* Step bar above multi-step flows (Components/AuthSteps): flat 3px segments,
   red up to and including the current step, labels under each. */
.auth-steps {
    display: flex;
    gap: 6px;
    list-style: none;
    margin: 0 0 26px;
    padding: 0;
}

.auth-step {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.auth-step::before {
    content: "";
    height: 3px;
    background: var(--main-border-color);
}

.auth-step-done::before,
.auth-step-current::before { background: var(--main-color); }

.auth-step-label {
    font-size: 11px;
    color: var(--light-grey-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.auth-step-current .auth-step-label {
    color: var(--main-color);
    font-weight: var(--font-bold);
}

.form-title {
    font-size: 26px;
    font-weight: var(--font-extra-bold);
    color: var(--main-text-color);
    margin-bottom: 6px;
}

.form-sub {
    font-size: 14px;
    color: var(--light-grey-color);
    line-height: 1.55;
}

.field-section {
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    text-transform: uppercase;
    letter-spacing: .6px;
    color: var(--light-grey-color);
    padding-bottom: 8px;
    border-bottom: 1px solid var(--main-border-color);
    margin-bottom: 14px;
    margin-top: 20px;
}

.field-section:first-of-type { margin-top: 0; }

.auth-action-block {
    width: 300px;
    max-width: 100%;
}

.auth-status-icon {
    font-size: 4rem;
}

.field-group {
    display: flex;
    flex-direction: column;
    gap: 18px;
    margin-bottom: 6px;
}

.field {
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.field label {
    font-size: 13px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.field input[type=text],
.field input[type=email],
.field input[type=password],
.field input[type=tel] {
    padding: 13px 14px;
    border: 1.5px solid var(--main-border-color);
    border-radius: 0;
    font-size: 14px;
    font-family: inherit;
    color: var(--main-text-color);
    background: var(--auth-field-bg);
    transition: border-color .15s, box-shadow .15s;
    outline: none;
    width: 100%;
}

.field input:focus {
    border-color: var(--main-color);
    box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .1);
}

/* Same invalid edge as the global rule in Style.css; restated here because
   .field input:focus above outranks it and would swap the rail for the focus halo. */
.field input.input-validation-error {
    border-color: var(--error-color);
    box-shadow: inset 0 -2px 0 var(--error-color);
}

.field input::placeholder {
    color: var(--main-text-color-a50);
}

.field-hint {
    font-size: 11px;
    color: var(--light-grey-color);
    line-height: 1.5;
}

.field-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

/* Red asterisk marking a mandatory field. Purely visual — the input itself
   carries `required`, so the marker is aria-hidden in the markup. */
.field-required {
    color: var(--error-text);
    font-weight: var(--font-extra-bold);
    margin-left: 3px;
}

.pwd-wrap { position: relative; }
.pwd-wrap input { padding-right: 42px !important; }

/* Edge/Chromium adds its own built-in reveal-password eye on top of ours —
   hide it so only the app's .pwd-toggle icon shows. */
.pwd-wrap input::-ms-reveal,
.pwd-wrap input::-ms-clear {
    display: none;
}

.pwd-toggle {
    position: absolute;
    right: 11px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    cursor: pointer;
    font-size: 15px;
    color: var(--light-grey-color);
    padding: 2px;
    line-height: 1;
}

.pwd-toggle:hover { color: var(--main-text-color); }

.pwd-toggle:focus-visible {
    outline: none;
    color: var(--main-text-color);
    border-radius: 0;
    box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), .45);
}

.pwd-strength-bar {
    height: 3px;
    border-radius: 0;
    background: var(--main-border-color);
    margin-top: 5px;
    overflow: hidden;
}

.pwd-strength-fill {
    width: 100%;
    height: 100%;
    border-radius: 0;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform .3s, background .3s;
}

.pwd-reqs {
    background: var(--auth-field-bg-subtle);
    border: 1px solid var(--main-border-color);
    border-radius: 0;
    padding: 12px 14px;
    display: none;
    margin-top: -4px;
}

.pwd-reqs.open { display: block; }

.pwd-reqs-title {
    font-size: 10px;
    font-weight: var(--font-extra-bold);
    color: var(--light-grey-color);
    text-transform: uppercase;
    letter-spacing: .5px;
    margin-bottom: 9px;
}

.req-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.req-item {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: var(--light-grey-color);
    transition: color .2s;
}

.req-item.ok { color: var(--success-color-dark); }

.req-dot {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 1.5px solid var(--main-border-color);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 8px;
    flex-shrink: 0;
    color: transparent;
    transition: all .2s;
}

.req-item.ok .req-dot {
    background: var(--success-color-dark);
    border-color: var(--success-color-dark);
    color: var(--white-color);
}

.check-row {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    font-size: 13px;
    color: var(--main-text-color);
}

.check-row input[type=checkbox] {
    width: 18px;
    height: 18px;
    accent-color: var(--main-color);
    flex-shrink: 0;
    cursor: pointer;
    /* Optically centre the box on the label's first line: (13px * 1.5 - 18px) / 2. */
    margin-top: .75px;
}

/* Style.css has a global `label { display: block; margin-top: 1em }`. Inside a
   check-row that 1em dropped the label a full line below the checkbox, so reset
   it here. Kept as a flex item on the first line so the wrapping EULA labels in
   Registration / RequiredPasswordReset still align to their first line. */
.check-row label {
    display: inline;
    margin: 0;
    line-height: 1.5;
    cursor: pointer;
}

/* Wraps the label plus any trailing link/asterisk so they stay in one text flow
   instead of each becoming a separate flex item on the check-row. */
.check-row .check-text {
    line-height: 1.5;
}

.check-row a {
    color: var(--main-color);
    font-weight: var(--font-bold);
    text-decoration: underline;
}

.check-row a:hover { text-decoration: underline; }

.auth-card .btn-primary {
    width: 100%;
    padding: 14px 20px;
    background: var(--main-color);
    color: var(--white-color);
    border: none;
    border-radius: 0;
    font-size: 14px;
    font-weight: var(--font-extra-bold);
    font-family: inherit;
    cursor: pointer;
    letter-spacing: .2px;
    margin-top: 20px;
    transition: background .15s, transform .1s;
}

.auth-card .btn-primary:hover { background: var(--main-color-dark); }
.auth-card .btn-primary:active { transform: scale(.98); }
/* Same neutral disabled look as every other primary button (pages.css); the fill
   itself comes from that shared rule, this only keeps the text grey over the
   white set above. */
.auth-card .btn-primary:disabled { color: var(--text-sub); cursor: not-allowed; }

.auth-card .btn-primary:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px var(--white-color),
                0 0 0 3px rgba(var(--main-color-rgb), .55);
}

.auth-card .btn-secondary {
    width: 100%;
    padding: 13px 20px;
    background: var(--auth-field-bg);
    color: var(--main-text-color);
    border: 1.5px solid var(--main-border-color);
    border-radius: 0;
    font-size: 14px;
    font-weight: var(--font-bold);
    font-family: inherit;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin-top: 10px;
    text-decoration: none;
    transition: border-color .15s, background .15s;
}

.auth-card .btn-secondary:hover {
    border-color: rgba(var(--main-color-rgb), .4);
    background: var(--main-background-color-subtle);
}

.auth-card .btn-secondary:focus-visible {
    outline: none;
    border-color: var(--main-color);
    box-shadow: 0 0 0 3px rgba(var(--main-color-rgb), .35);
}

.auth-div {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 18px 0 4px;
    font-size: 12px;
    color: var(--light-grey-color);
}

.auth-div::before,
.auth-div::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--main-border-color);
}

/* Text-less divider (Login page, above Employee Sign In): drop the gap so the two
   halves join into one unbroken rule. */
.auth-div:empty { gap: 0; }

/* Login captcha (PBI 369556): Turnstile's fixed-size widget (300px "normal", or
   150px "compact" on narrow phones - see Login.cshtml) is centred under the
   full-width Sign In button instead of hanging off the left edge. Scoped to
   #screen-login so the other auth pages are unchanged. */
#screen-login .cf-turnstile {
    display: flex;
    justify-content: center;
}

.auth-footer {
    margin-top: 22px;
    text-align: center;
    font-size: 13px;
    color: var(--light-grey-color);
}

.auth-footer .link-btn {
    color: var(--main-color);
    font-weight: var(--font-bold);
    text-decoration: underline;
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
    font-size: 13px;
}

.auth-footer .link-btn:hover { text-decoration: underline; }

.auth-footer .link-btn:focus-visible {
    outline: none;
    text-decoration: underline;
    border-radius: 0;
    box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), .35);
}

/* Sits above the "Already have an account? Sign in" line (PBI 369325), sized
   and weighted as a primary footer action rather than fine print. */
.auth-footer .help-link {
    display: block;
    margin-bottom: 12px;
    font-size: 14px;
    font-weight: var(--font-bold);
    color: var(--main-text-color);
    text-decoration: underline;
}

.auth-footer .help-link:hover { color: var(--main-color); }

.auth-footer .help-link:focus-visible {
    outline: none;
    color: var(--main-color);
    border-radius: 0;
    box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), .35);
}

.inline-link {
    color: var(--main-color);
    font-weight: var(--font-bold);
    text-decoration: underline;
    font-size: 12px;
}

.inline-link:hover { text-decoration: underline; }

.inline-link:focus-visible {
    outline: none;
    text-decoration: underline;
    border-radius: 0;
    box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), .35);
}

.success-wrap {
    text-align: center;
    padding: 16px 0;
}

/* Completion pages (EmailConfirmationComplete, PasswordResetComplete): square
   success tile above the heading, left-aligned with the rest of the form. */
.success-icon {
    width: 56px;
    height: 56px;
    background: rgba(var(--success-color-rgb), .12);
    border: 1px solid rgba(var(--success-color-rgb), .40);
    border-radius: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 28px;
    margin: 0 0 20px;
    color: var(--success-color);
}

.success-icon svg {
    width: 28px;
    height: 28px;
}

/* Link styled as the primary button (a navigation, so an <a>, never a
   <button> nested inside one). */
.auth-card a.btn-primary {
    display: block;
    text-align: center;
    text-decoration: none;
}

.auth-card a.btn-primary:hover { color: var(--white-color); }

.auth-legal-sep {
    margin: 0 7px;
    opacity: .5;
}

/* ── Language picker on the (right-hand) form panel ─────────────────
   Sits in the panel's own padding gutter, top-right of the page. */
.auth-panel .auth-lang {
    position: absolute;
    top: 48px;
    right: 40px;
    z-index: 2;
}

/* Square corners match the rest of the auth surface (btn-primary,
   brand-logo-product, lang-drop-item). */
.auth-panel .lang-drop-toggle,
.auth-panel .lang-drop-menu { border-radius: 0; }

/* Transparent fill so the toggle blends into the form panel; the square
   outline alone marks it as clickable. The stacked (<1024px) layout below
   restyles it for the dark header. */
.auth-panel .lang-drop-toggle,
.auth-panel .lang-drop-toggle:focus-visible { background: transparent; }

/* Status callouts: Bootstrap's rounded, solid-tint .alert restyled to the auth
   surface — square, a light wash of the state colour with a matching hairline,
   body text kept in the normal text colour so it reads in both themes. Covers
   the 2FA "code resent" banner and the disambiguation result pages without
   touching their markup. */
.auth-card .alert {
    border-radius: 0;
    padding: 12px 14px;
    margin-bottom: 18px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--main-text-color);
    background: rgba(var(--main-color-rgb), .07);
    border: 1px solid rgba(var(--main-color-rgb), .28);
}

.auth-card .alert-success {
    background: rgba(var(--success-color-rgb), .10);
    border-color: rgba(var(--success-color-rgb), .40);
}

/* Error messages, banners and the invalid-input edge come from the shared error
   rules in Style.css. The auth card only lays a field message out as a flex row,
   so a message that wraps hangs its second line under the text, not the badge. */
.auth-card .field-validation-error:not(.field-validation-valid):not(.validation-summary-errors):not(.validation-summary-valid):not(:empty) {
    display: flex;
    align-items: flex-start;
    margin-top: 6px;
}

.auth-card .field-validation-error:not(.field-validation-valid):not(.validation-summary-errors):not(.validation-summary-valid):not(:empty)::before {
    margin-top: 2px;
}

.auth-card .validation-summary-errors,
.auth-card .error-banner {
    margin-bottom: 18px;
}

/* Desktop only (PBI 369325): the base sizes above left the form a narrow column
   in a wide, mostly empty panel, so the card and its type step up here. Kept out
   of the base rules on purpose — the stacked layout below 1025px has its own
   height-tuned scale that must not grow. */
@media (min-width: 1025px) {
    /* Long forms (Registration, RegistrationSupport, ...) scroll the whole
       page, which took the brand panel and its lockup off screen with them.
       Sticky pins the panel to the viewport while only the form moves; the
       page scroll itself (and the form panel's vertical centring) is left as
       is. align-self stops the flex stretch that would otherwise make the
       panel as tall as the form and leave it nothing to stick against. */
    .auth-brand {
        position: sticky;
        top: 0;
        align-self: flex-start;
        height: 100vh;
        height: 100dvh;
    }

    .auth-card { max-width: 580px; }

    /* Height-driven scale so Login (captcha and Sign Up included) fits the window
       with no scroll. Each clamp() runs linearly from its floor at a 650px-tall
       viewport (a 1366x768 laptop with browser chrome) to its ceiling at 1000px,
       where the sizes are the original desktop values; nothing shrinks further
       below 650px or grows past 1000px. Taller forms (Registration, ...) still
       scroll, just less. */
    .form-hdr { margin-bottom: clamp(12px, calc(-32.57px + 6.86vh), 36px); }
    .form-title { font-size: clamp(26px, calc(7.43px + 2.86vh), 36px); margin-bottom: clamp(6px, calc(-1.43px + 1.14vh), 10px); }
    .form-sub { font-size: clamp(15px, calc(9.43px + .86vh), 18px); }

    .auth-step-label { font-size: 14px; }
    .field-section { font-size: 13px; }

    .field-group { gap: clamp(8px, calc(-21.71px + 4.57vh), 24px); }
    .field { gap: clamp(6px, calc(.43px + .86vh), 9px); }
    .field label { font-size: clamp(14px, calc(10.29px + .57vh), 16px); }

    .field input[type=text],
    .field input[type=email],
    .field input[type=password],
    .field input[type=tel] {
        padding: clamp(10px, calc(-1.14px + 1.71vh), 16px) 18px;
        font-size: clamp(15px, calc(11.29px + .57vh), 17px);
    }

    .pwd-wrap input { padding-right: 48px !important; }
    .pwd-toggle { font-size: 17px; right: 14px; }
    .field-hint { font-size: 14px; }
    .pwd-reqs-title { font-size: 13px; }
    .req-item { font-size: 14px; }

    .check-row { font-size: clamp(14px, calc(10.29px + .57vh), 16px); }

    /* Centred on the label's first line (1.5 line-height). The checkbox inherits
       the row's font-size, so em tracks the scaled label. */
    .check-row input[type=checkbox] {
        width: 22px;
        height: 22px;
        margin-top: calc((1.5em - 22px) / 2);
    }

    .auth-card .btn-primary,
    .auth-card .btn-secondary {
        padding-top: clamp(11px, calc(-2px + 2vh), 18px);
        padding-bottom: clamp(11px, calc(-2px + 2vh), 18px);
        font-size: clamp(15px, calc(11.29px + .57vh), 17px);
    }

    .auth-card .btn-primary { margin-top: clamp(12px, calc(-17.71px + 4.57vh), 28px); }

    .auth-div { font-size: clamp(13px, calc(9.29px + .57vh), 15px); margin-top: clamp(8px, calc(-21.71px + 4.57vh), 24px); }
    .auth-footer,
    .auth-footer .link-btn { font-size: 16px; }
    .auth-footer .help-link { font-size: 17px; }
    .inline-link { font-size: 15px; }
    .auth-card .alert { font-size: 16px; }

    /* .auth-lang is absolutely positioned in the panel's top-right corner, so
       whenever the panel (viewport minus the 400px brand panel) is too narrow
       for the 580px card to clear it sideways — any window between ~1025px
       and ~1240px, not just standard tablet sizes — the step bar / title ran
       under the picker. The panel's top padding reserves the picker's offset
       plus its ~34px toggle (and a gap), and both scale down together on short
       windows. */
    .auth-panel .auth-lang { top: clamp(14px, calc(-49.14px + 9.71vh), 48px); }
    .auth-panel {
        padding-top: clamp(62px, calc(-8.57px + 10.86vh), 100px);
        padding-bottom: clamp(12px, calc(-54.86px + 10.29vh), 48px);
    }
}

/* Below 1024px (phones and tablets, portrait or landscape — the desktop
   2-column layout only has room to breathe above that) the two side-by-side
   panels stack: a compact branded header (logo + badge only — the hero copy,
   feature list and footer have nowhere to go and aren't the point at this
   width) sits above the form, which becomes its own bordered card instead of
   filling the page. Square corners, not the rounded card a rejected reference
   design used — matches every other card shell in the app (.modal,
   .cd-list-card, ...). */
@media (max-width: 1024px) {
    /* Pinned to exactly the visible viewport (not just "at least") and clipped
       instead of scrolling — the shell fills the screen on every phone size
       without growing past it. */
    .auth-shell {
        flex-direction: column;
        height: 100vh;
        height: 100dvh;
        overflow: hidden;
    }

    .auth-brand {
        width: 100%;
        flex-shrink: 0;
        padding: 14px 14px 16px;
    }

    .brand-hero,
    .brand-footer {
        display: none;
    }

    .brand-logo {
        /* Smaller than the desktop 40px: keeps the compact phone header from
           running the full-size lockup the full row width. */
        --lockup-h: 28px;
        margin-bottom: 0;
    }

    /* Fixed instead of following .auth-panel: keeps it pinned over the compact
       dark brand header at the top of the stacked layout, where it sat before
       the desktop-only move into the form panel. */
    .auth-panel .auth-lang {
        position: fixed;
        top: 20px;
        right: 20px;
    }

    /* Blends into the dark header here instead of the white pill the toggle
       gets on the desktop form panel — same chassis treatment the panel used
       to give it before the toggle lived there full-time. */
    .auth-panel .lang-drop-toggle {
        background: rgba(255, 255, 255, .06);
        border-color: rgba(255, 255, 255, .14);
        color: var(--auth-brand-text);
    }

    .auth-panel .lang-drop-toggle:hover {
        background: rgba(255, 255, 255, .1);
        border-color: rgba(var(--main-color-rgb), .55);
    }

    .auth-panel .lang-drop-toggle:focus-visible {
        background: rgba(255, 255, 255, .1);
        border-color: var(--main-color);
    }

    .auth-panel .lang-drop-code { color: var(--auth-brand-text); }
    .auth-panel .lang-drop-caret { color: var(--auth-brand-muted); }

    .auth-panel {
        flex: 1;
        padding: 8px;
        /* Top-aligned, not centered: the card's own height now scales with
           the screen (below) enough that centering just split the leftover
           into a matching gap above the header-adjacent card too — pinning
           it under the header instead keeps any remainder (short phones
           have none) below the footer, matching how the compact header
           reads as one continuous block with the card under it. */
        justify-content: flex-start;
        /* Scrolls, not clips: Login fits the height-driven scale below with no
           scroll, but Registration, RegistrationSupport, RequiredPasswordReset,
           PasswordResetStep2 and 2FA are taller than any phone, and a hidden
           overflow cut off their EULA / captcha / submit with no way to reach
           them. The shell stays pinned to the viewport, so only the form scrolls
           and the brand header stays put. */
        overflow-x: hidden;
        overflow-y: auto;
        overscroll-behavior: contain;
        min-height: 0;
    }

    .auth-card {
        /* Unconstrained on a phone (100% is already narrower than this), but
           caps the card on a tablet-width stacked layout instead of letting
           it stretch edge to edge — .auth-panel's align-items: center (base
           rule, untouched here) centers it once the cap kicks in. */
        max-width: 480px;
        width: 100%;
        /* Keeps its full content height inside the scrolling panel. */
        flex-shrink: 0;
        padding: 12px;
        background: var(--main-background-color);
        border: 1px solid var(--main-border-color);
    }

    /* The compact brand header above now carries the logo, so the form's own
       copy of it (originally the only logo shown at this width) is redundant. */
    .form-logo {
        display: none;
    }

    /* Scales continuously with the real visible height (dvh) instead of
       jumping between a couple of fixed breakpoints — a fixed "phone" size
       either left a big empty margin on a tall phone or (if sized for the
       tall case) overflowed a short one. Each pair is anchored at a short
       phone (667px tall, where the low end keeps the form — through
       Employee Sign In — fitting with zero scroll) and a tall phone (900px,
       where the high end grows enough that the card itself reaches close to
       the screen bottom instead of leaving it as empty space); clamp() holds
       those as hard floor/ceiling outside that range, so nothing keeps
       growing on a huge screen or shrinks past the tested minimum on a tiny
       one. The Turnstile widget itself is left at its default full-width
       "flexible" sizing (no bleed past the card padding) so it matches the
       Sign In / Submit button below it. */
    .auth-card { padding: clamp(12px, calc(-68.2px + 12.02dvh), 40px); }
    .form-hdr { margin-bottom: clamp(8px, calc(-49.3px + 8.58dvh), 28px); }
    .form-title { font-size: clamp(22px, calc(-6.6px + 4.29dvh), 32px); }
    .form-sub { font-size: clamp(14px, calc(8.3px + 0.86dvh), 16px); }
    .field-group { gap: clamp(8px, calc(-37.8px + 6.87dvh), 24px); margin-bottom: 0; }
    .auth-card .btn-primary { margin-top: clamp(12px, calc(-33.8px + 6.87dvh), 28px); }
    .auth-div { margin-top: clamp(10px, calc(-30.1px + 6.01dvh), 24px); margin-bottom: 4px; }
    .auth-card .btn-secondary { margin-top: clamp(6px, calc(-16.9px + 3.43dvh), 14px); }
    .auth-footer { margin-top: clamp(8px, calc(-26.4px + 5.15dvh), 20px); }
}

/* Tablet width, still inside the stacked (mobile-style) layout above: a
   phone-width card just floating in the extra width reads as a mistake, not
   a design, so the card and its inputs/buttons size up to actually use the
   room instead of leaving it as empty gap either side. Vertical spacing
   (padding, gaps, margins, the heading) is left to the height-driven scale
   above, which already adapts a short landscape tablet down safely — a
   second, width-only scale-up on top of that is what previously overflowed
   iPad-landscape-sized screens. */
@media (min-width: 600px) and (max-width: 1024px) {
    .auth-card { max-width: 600px; }

    .field input[type=text],
    .field input[type=email],
    .field input[type=password],
    .field input[type=tel] {
        padding: 13px 16px;
        font-size: 16px;
    }

    .auth-card .btn-primary {
        padding: 13px 20px;
        font-size: 14px;
    }

    .auth-card .btn-secondary {
        padding: 13px 20px;
        font-size: 14px;
    }

    .auth-footer { font-size: 14px; }
}

/* RegistrationDisambiguationSuccess / -Failed still use the legacy Style.css
   register shell: a fixed 600px .register-form plus a 20px right margin, and
   inputs with a 400px (260px under 900px) min-width. All of that is wider than
   the 580px desktop card and far wider than a phone, so inside the auth card it
   follows the card's width instead. */
.auth-card .register-form {
    width: 100%;
    margin-right: 0;
}

.auth-card .register-container input { min-width: 0; }
.auth-card .register-container hr { max-width: 100%; }

/* Phones (Bootstrap's <576px, matching the app-wide breakpoint contract): the
   stacked layout above steps its fixed sizes down so the form fits a 320–575px
   screen, on top of the height-driven spacing scale, which is left as is. */
@media (max-width: 575px) {
    .auth-brand { padding: 12px 12px 12px; }

    .brand-logo { --lockup-h: 24px; gap: 6px; }

    .brand-logo-product {
        font-size: 9px;
        letter-spacing: 2px;
        word-spacing: 6px;
    }

    .auth-panel .auth-lang {
        top: 12px;
        right: 12px;
    }

    .auth-steps { gap: 4px; margin-bottom: 16px; }
    .auth-step { gap: 5px; }
    .auth-step-label { font-size: 11px; }

    .field-section {
        margin-top: 14px;
        margin-bottom: 10px;
    }

    .field { gap: 5px; }
    .field label { font-size: 12px; }

    /* 16px is a floor, not a step down: iOS Safari zooms the page when a
       focused field's text is under 16px and never zooms back out. */
    .field input[type=text],
    .field input[type=email],
    .field input[type=password],
    .field input[type=tel] {
        padding: 11px 12px;
        font-size: 16px;
    }

    .field-row { gap: 10px; }
    .field-hint { font-size: 11px; }
    .pwd-reqs { padding: 10px 12px; }

    .check-row { font-size: 12px; }

    /* Re-centred on the smaller first line: (12px * 1.5 - 18px) / 2. */
    .check-row input[type=checkbox] { margin-top: 0; }

    /* 12px + 12px padding around the 15px label keeps the 44px touch target. */
    .auth-card .btn-primary,
    .auth-card .btn-secondary {
        padding: 12px 16px;
        font-size: 15px;
    }

    .auth-footer,
    .auth-footer .link-btn { font-size: 12px; }
    .auth-footer .help-link { font-size: 13px; margin-bottom: 8px; }
    .inline-link { font-size: 12px; }

    .auth-card .alert {
        padding: 10px 12px;
        font-size: 12px;
    }

    .success-icon {
        width: 48px;
        height: 48px;
        margin-bottom: 14px;
    }

    .success-icon svg {
        width: 24px;
        height: 24px;
    }

    .auth-status-icon { font-size: 3rem; }
}

/* First / last name side by side leaves each field under ~130px on the
   narrowest phones, too tight for a translated label plus its asterisk. */
@media (max-width: 400px) {
    .field-row { grid-template-columns: 1fr; }
}

/* ============================================================================
   Auth page buttons: hover and press (PBI 369556)
   Every auth page's buttons lift slightly on hover and press in on click so they
   read as clickable. The red .btn-primary darkens on hover: Style.css forces its
   background with !important, which is why the older .auth-card .btn-primary:hover
   darkening never showed, so the hover restates it with !important. Outlined
   .btn-secondary buttons sit on the nav pane's grey (#F4F5F6 light / #2A2E33 dark)
   and keep it on hover; only the edge turns red. Disabled buttons (waiting on the
   captcha or the resend timer) get no hover effect.
   ============================================================================ */
.auth-card .btn-primary,
.auth-card .btn-secondary {
    transition: background .15s, color .15s, border-color .15s, box-shadow .15s, transform .1s;
}

.auth-card .btn-primary:not(:disabled):hover {
    background-color: var(--main-color-dark) !important;
    box-shadow: 0 4px 12px rgba(var(--main-color-rgb), .30);
}

.auth-card .btn-secondary,
.auth-card .btn-secondary:hover { background: var(--sidebar-bg); }

.auth-card .btn-secondary:hover {
    border-color: var(--main-color);
    box-shadow: 0 4px 12px rgba(var(--main-color-rgb), .15);
}

@media (prefers-reduced-motion: no-preference) {
    .auth-card .btn-primary:not(:disabled):hover,
    .auth-card .btn-secondary:hover { transform: translateY(-1px); }

    .auth-card .btn-primary:not(:disabled):active,
    .auth-card .btn-secondary:active { transform: translateY(0) scale(.98); box-shadow: none; }
}

/* ============================================================================
   Auth page autofill (PBI 369556)
   Autofilled fields on every auth page look the same as typed-in ones. Chrome and
   Edge paint saved values (email, password, name) pale blue through an internal
   layer that `background` cannot reach; the large inset shadow covers it and
   -webkit-text-fill-color restores the text colour (same technique as the topbar
   search in layout.css). The focus halo and the invalid-field rail are
   box-shadows too, so they are restated alongside the cover. The long
   background-color transition stops the blue flashing before the cover paints.
   ============================================================================ */
.auth-card .field input:-webkit-autofill,
.auth-card .field input:-webkit-autofill:hover {
    -webkit-text-fill-color: var(--main-text-color);
    caret-color: var(--main-text-color);
    box-shadow: inset 0 0 0 1000px var(--auth-field-bg);
    transition: border-color .15s, box-shadow .15s, background-color 9999s ease-in-out 0s;
}

.auth-card .field input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--main-text-color);
    caret-color: var(--main-text-color);
    box-shadow: inset 0 0 0 1000px var(--auth-field-bg), 0 0 0 3px rgba(var(--main-color-rgb), .1);
}

.auth-card .field input.input-validation-error:-webkit-autofill {
    box-shadow: inset 0 -2px 0 var(--error-color), inset 0 0 0 1000px var(--auth-field-bg);
}

/* ============================================================================
   Login page (PBI 369556)
   Everything below is scoped to #screen-login (or :has(#screen-login) for the
   layout around it), so Registration, password reset and the other auth pages
   keep their own sizes and spacing.
   ============================================================================ */

/* Text sizes. Each value is the shared auth rule for the same breakpoint minus
   1px (clamp() floors, ceilings and offsets all shifted by 1px); Remember Me and
   Forgot password sit one more step below. Phone and tablet inputs stay at 16px:
   iOS Safari zooms the page when a focused field's text is under 16px. */
#screen-login .form-title { font-size: 25px; }
#screen-login .form-sub { font-size: 13px; }
#screen-login .field label { font-size: 12px; }
#screen-login .field input[type=text],
#screen-login .field input[type=email],
#screen-login .field input[type=password] { font-size: 13px; }
#screen-login .check-row { font-size: 11px; }
#screen-login .inline-link { font-size: 10px; }
#screen-login .auth-div { font-size: 11px; }
#screen-login .btn-primary,
#screen-login .btn-secondary { font-size: 13px; }

@media (min-width: 1025px) {
    #screen-login .form-title { font-size: clamp(25px, calc(6.43px + 2.86vh), 35px); }
    #screen-login .form-sub { font-size: clamp(14px, calc(8.43px + .86vh), 17px); }
    #screen-login .field label { font-size: clamp(13px, calc(9.29px + .57vh), 15px); }
    #screen-login .field input[type=text],
    #screen-login .field input[type=email],
    #screen-login .field input[type=password] { font-size: clamp(14px, calc(10.29px + .57vh), 16px); }
    #screen-login .check-row { font-size: clamp(12px, calc(8.29px + .57vh), 14px); }
    #screen-login .inline-link { font-size: 13px; }
    #screen-login .auth-div { font-size: clamp(12px, calc(8.29px + .57vh), 14px); }
    #screen-login .btn-primary,
    #screen-login .btn-secondary { font-size: clamp(14px, calc(10.29px + .57vh), 16px); }
}

@media (max-width: 1024px) {
    #screen-login .form-title { font-size: clamp(21px, calc(-7.6px + 4.29dvh), 31px); }
    #screen-login .form-sub { font-size: clamp(13px, calc(7.3px + 0.86dvh), 15px); }
}

@media (min-width: 600px) and (max-width: 1024px) {
    #screen-login .field input[type=text],
    #screen-login .field input[type=email],
    #screen-login .field input[type=password] { font-size: 16px; }
}

@media (max-width: 575px) {
    #screen-login .field label { font-size: 11px; }
    #screen-login .field input[type=text],
    #screen-login .field input[type=email],
    #screen-login .field input[type=password] { font-size: 16px; }
    #screen-login .check-row { font-size: 10px; }
    #screen-login .btn-primary,
    #screen-login .btn-secondary { font-size: 14px; }
}

/* Sign In gets the same box as the two outlined buttons: the same 1.5px edge
   (Style.css forces its colour to the brand red, so it disappears into the red
   fill), the same vertical padding and the same line height. A <button> and an
   <a> otherwise differ in line height, and a 1.5px edge renders as 1px at 100%
   zoom, so matching the box is the only way the three buttons stay exactly the
   same height at every width and zoom level. */
#screen-login .btn-primary,
#screen-login .btn-secondary { line-height: 1.5; }

#screen-login .btn-primary {
    border: 1.5px solid;
    padding-top: 13px;
    padding-bottom: 13px;
}

@media (max-width: 575px) {
    #screen-login .btn-primary {
        padding-top: 12px;
        padding-bottom: 12px;
    }
}

/* Compact desktop Login: a narrower form column, shorter inputs and buttons and
   tighter gaps, so the form sits with clear space above and below it instead of
   filling the window. Same 650px-1000px height scale as the shared desktop rules,
   just lower floors and ceilings. Phones and tablets are unchanged. */
@media (min-width: 1025px) {
    #screen-login {
        max-width: 480px;
        margin-left: auto;
        margin-right: auto;
    }

    #screen-login .field-group { gap: clamp(8px, calc(-13px + 3vh), 18px); }

    #screen-login .field input[type=text],
    #screen-login .field input[type=email],
    #screen-login .field input[type=password] { padding: clamp(8px, calc(-1.14px + 1.2vh), 12px) 16px; }

    #screen-login .btn-primary,
    #screen-login .btn-secondary { padding-top: clamp(7.5px, calc(-3.5px + 1.4vh), 11.5px); padding-bottom: clamp(7.5px, calc(-3.5px + 1.4vh), 11.5px); }

    #screen-login .btn-primary { margin-top: clamp(12px, calc(-10px + 3vh), 20px); }

    #screen-login .auth-div { margin-top: clamp(8px, calc(-8px + 2.5vh), 16px); }
}

/* 30 / 70 split: the brand panel takes 30% of the window instead of a fixed
   400px, never going below 400px, so small laptops (under ~1333px wide) keep
   today's layout. Desktop only; below 1025px the brand panel is the stacked
   header and is untouched. */
@media (min-width: 1025px) {
    .auth-shell:has(#screen-login) .auth-brand {
        width: 30%;
        min-width: 400px;
    }
}

/* Equal space above and below the form. The shared desktop rule gives .auth-panel
   extra top padding so the form clears the language picker, but the 580px card can
   only reach the picker below ~1284px of window width (400px brand panel + centred
   card + picker's 136px right offset and width; the 30% brand panel only moves
   that point lower). From 1300px up the top padding matches the bottom, so the
   vertically centred form has the same gap above and below. */
@media (min-width: 1300px) {
    .auth-panel:has(#screen-login) { padding-top: clamp(12px, calc(-54.86px + 10.29vh), 48px); }
}

/* Remember Me checkbox: 16px at every width instead of the shared 18px (22px on
   desktop), so it sits in proportion with its smaller label. Centred on the
   label's first line the same way as the shared rule: the checkbox inherits the
   row's font-size, so 1.5em is that line's height. */
#screen-login .check-row input[type=checkbox] {
    width: 16px;
    height: 16px;
    margin-top: calc((1.5em - 16px) / 2);
}

/* Remember Me label: bold, in the normal text colour (#141414 light / #C5C8CC
   dark). */
#screen-login .check-row label {
    font-weight: var(--font-bold);
    color: var(--main-text-color);
}

/* Sign Up is an outlined button (card fill, red edge, red text) that fills with
   the brand red on hover, so Sign In stays the one solid red action. It is built
   on .btn-secondary, not .btn-primary, because Style.css forces .btn-primary's
   background with !important; it overrides the nav-grey fill the other
   .btn-secondary buttons get above. Dark mode uses the lighter brand-text red for
   the label so it stays readable on the dark card. */
#screen-login a.btn-signup {
    background: var(--auth-field-bg);
    color: var(--main-color);
    border-color: var(--main-color);
}

.dark-mode #screen-login a.btn-signup {
    color: var(--brand-text);
    border-color: rgba(var(--main-color-rgb), .55);
}

#screen-login a.btn-signup:hover,
.dark-mode #screen-login a.btn-signup:hover {
    background: var(--main-color);
    border-color: var(--main-color);
    color: var(--white-color);
    box-shadow: 0 4px 12px rgba(var(--main-color-rgb), .25);
}

