/* ==========================================================================
   roth-fm.css — one stylesheet, three apps
   Drop-in replacement for:
     src/Apps/InternalAppSuite/wwwroot/css/site.css
     src/Apps/HRWeb/Client/styles/app.css   (bundled to wwwroot/dist/app.css)
   and loaded by AssetManagement's _ReskinLayout.cshtml ONLY. AssetManagement's
   legacy site.css is untouched and still serves the 59 protected views on the
   frozen _Layout.cshtml — see assetmanagement-reskin/CONTAINMENT.md.
   Plain CSS. No build step, no framework, no dependencies.

   Sections 1-17 and 21-24 are app-agnostic: tokens, shell, buttons, tabs,
   chips, panels, forms, grids, notices, dialogs, auth.
   Sections 18-20 are InternalAppSuite screens (Viper, SecurityAdmin, NewVendor).
   Sections 25-34 are HRWeb screens (timekeeping, absence, profile, technicians,
   action forms, documents, impersonation).
   Sections 35-44 are AssetManagement screens (shell additions, reactive work
   orders, threads, editable grids, the project workspace, media, the pinned
   tile board, schedule bars, and its responsive and print steps).
   No app pays for another's screen CSS beyond bytes; nothing collides.

   Round 3 changed exactly two things above section 25: .icon-btn moved from
   section 28 into section 6, and .grid--dense was added to section 11. Both are
   marked PROMOTED (round 3) at the site. Sections 1-17 are otherwise untouched.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------------- */
:root {
    /* brand */
    --roth-navy: #223085;
    --roth-navy-hover: #1a2568;
    --roth-navy-raised: #2c3ca0;      /* active rail row */
    --roth-red: #c80013;
    --roth-red-hover: #a2000f;

    /* surfaces */
    --bg: #eceff3;
    --surface: #f8f9fa;
    --surface-alt: #f2f4f6;           /* zebra row */
    --surface-sunken: #e8ecf0;        /* grid head, inert fills */
    --surface-rail: #223085;
    --overlay: rgba(22, 40, 63, .45);

    /* lines */
    --border: #d9e2ec;
    --border-strong: #c4cdd5;
    --border-hairline: #e4e9ee;

    /* text */
    --text: #1f2933;
    --text-muted: #52606d;
    --text-faint: #9aa5b1;
    --text-on-navy: #ffffff;
    --text-rail: #c9d2f0;
    --text-rail-dim: #8fa0b8;

    /* links (#337ab7 was 4.1:1 on the striped grid rows) */
    --link: #2a6496;
    --link-hover: #23527c;

    /* status chips: bg / fg / border */
    --chip-green-bg: #d9f4e6;   --chip-green-fg: #0f5132;   --chip-green-bd: #a3cfbb;
    --chip-amber-bg: #fff3cd;   --chip-amber-fg: #664d03;   --chip-amber-bd: #e0c36c;
    --chip-blue-bg:  #d7ecff;   --chip-blue-fg:  #084c7f;   --chip-blue-bd:  #9fc9ea;
    --chip-red-bg:   #f8d7da;   --chip-red-fg:   #842029;   --chip-red-bd:   #f1aeb5;
    --chip-gray-bg:  #edf2f7;   --chip-gray-fg:  #52606d;   --chip-gray-bd:  #d9e2ec;

    /* type scale (6 steps, nothing else is legal) */
    --fs-xs: 11px;
    --fs-sm: 12px;
    --fs-base: 13px;
    --fs-md: 14px;
    --fs-lg: 16px;
    --fs-xl: 20px;
    --lh-tight: 1.25;
    --lh-base: 1.45;
    --font: Arial, "Helvetica Neue", Helvetica, sans-serif;
    --font-mono: "Cascadia Mono", Consolas, "Courier New", monospace;

    /* spacing scale (4px base, 6 steps) */
    --s1: 4px;
    --s2: 8px;
    --s3: 12px;
    --s4: 16px;
    --s5: 24px;
    --s6: 32px;

    /* radii */
    --r-sm: 3px;
    --r: 4px;
    --r-lg: 6px;
    --r-pill: 999px;

    /* controls */
    --control-h: 30px;
    --control-h-lg: 34px;
    --cell-pad-y: 6px;
    --cell-pad-x: 10px;

    /* chrome metrics */
    --rail-w: 234px;
    --rail-w-collapsed: 52px;
    --topbar-h: 54px;
    --tabstrip-h: 38px;

    /* elevation */
    --shadow-panel: none;
    --shadow-pop: 0 6px 20px rgba(22, 40, 63, .16);
    --shadow-drawer: -6px 0 24px rgba(22, 40, 63, .18);

    /* focus — one ring, everywhere */
    --focus-ring: 0 0 0 2px var(--surface), 0 0 0 4px var(--roth-navy);
    --focus-ring-on-navy: 0 0 0 2px var(--roth-navy), 0 0 0 4px #ffffff;

    color-scheme: light;
}

/* --------------------------------------------------------------------------
   2. DARK THEME
   Automatic via prefers-color-scheme; [data-theme] forces either way.
   -------------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #12161c;
        --surface: #1a2029;
        --surface-alt: #1e2530;
        --surface-sunken: #232b36;
        --surface-rail: #161d33;
        --overlay: rgba(0, 0, 0, .62);

        --border: #2c3846;
        --border-strong: #3a4757;
        --border-hairline: #232b36;

        --text: #e4e9ef;
        --text-muted: #9aa5b1;
        --text-faint: #6b7683;
        --text-rail: #c9d2f0;
        --text-rail-dim: #7f8db0;

        /* a fill (buttons, rail): text in navy uses --accent-navy, which is readable on dark */
        --roth-navy: #3a4bc4;
        --roth-navy-hover: #4a5bd8;
        --roth-navy-raised: #2c3ca0;
        --roth-red: #ff4d5e;
        --roth-red-hover: #ff7481;
        --danger-fill: #d42b3c;
        --danger-fill-hover: #b82231;

        --link: #7fb3e0;
        --link-hover: #a8cdee;

        --chip-green-bg: #102f22; --chip-green-fg: #7ee2b0; --chip-green-bd: #1e5c40;
        --chip-amber-bg: #33280a; --chip-amber-fg: #f0cd6d; --chip-amber-bd: #6b551a;
        --chip-blue-bg:  #10283d; --chip-blue-fg:  #86c4f2; --chip-blue-bd:  #1e4c73;
        --chip-red-bg:   #3a1418; --chip-red-fg:   #ff9aa4; --chip-red-bd:   #7a2830;
        --chip-gray-bg:  #232b36; --chip-gray-fg:  #9aa5b1; --chip-gray-bd:  #2c3846;

        --shadow-pop: 0 6px 20px rgba(0, 0, 0, .5);
        --shadow-drawer: -6px 0 24px rgba(0, 0, 0, .5);
        --focus-ring: 0 0 0 2px var(--surface), 0 0 0 4px #7f8fee;

        color-scheme: dark;
    }
}

:root[data-theme="dark"] {
    --bg: #12161c;
    --surface: #1a2029;
    --surface-alt: #1e2530;
    --surface-sunken: #232b36;
    --surface-rail: #161d33;
    --overlay: rgba(0, 0, 0, .62);
    --border: #2c3846;
    --border-strong: #3a4757;
    --border-hairline: #232b36;
    --text: #e4e9ef;
    --text-muted: #9aa5b1;
    --text-faint: #6b7683;
    --text-rail: #c9d2f0;
    --text-rail-dim: #7f8db0;
    --roth-navy: #3a4bc4;
    --roth-navy-hover: #4a5bd8;
    --roth-navy-raised: #2c3ca0;
    --roth-red: #ff4d5e;
    --roth-red-hover: #ff7481;
    --danger-fill: #d42b3c;
    --danger-fill-hover: #b82231;
    --link: #7fb3e0;
    --link-hover: #a8cdee;
    --chip-green-bg: #102f22; --chip-green-fg: #7ee2b0; --chip-green-bd: #1e5c40;
    --chip-amber-bg: #33280a; --chip-amber-fg: #f0cd6d; --chip-amber-bd: #6b551a;
    --chip-blue-bg:  #10283d; --chip-blue-fg:  #86c4f2; --chip-blue-bd:  #1e4c73;
    --chip-red-bg:   #3a1418; --chip-red-fg:   #ff9aa4; --chip-red-bd:   #7a2830;
    --chip-gray-bg:  #232b36; --chip-gray-fg:  #9aa5b1; --chip-gray-bd:  #2c3846;
    --shadow-pop: 0 6px 20px rgba(0, 0, 0, .5);
    --shadow-drawer: -6px 0 24px rgba(0, 0, 0, .5);
    --focus-ring: 0 0 0 2px var(--surface), 0 0 0 4px #7f8fee;
    color-scheme: dark;
}

:root[data-theme="light"] {
    color-scheme: light;
}

/* --------------------------------------------------------------------------
   3. RESET / BASE
   -------------------------------------------------------------------------- */
*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    background: var(--bg);
    color: var(--text);
    font-family: var(--font);
    font-size: var(--fs-base);
    line-height: var(--lh-base);
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
}

h1, h2, h3, h4 {
    margin: 0 0 var(--s3);
    line-height: var(--lh-tight);
    color: var(--text);
}

h1 { font-size: var(--fs-xl); font-weight: 700; }
h2 { font-size: var(--fs-lg); font-weight: 700; }
h3 { font-size: var(--fs-md); font-weight: 700; }
h4 { font-size: var(--fs-base); font-weight: 700; letter-spacing: .02em; text-transform: uppercase; color: var(--text-muted); }

p { margin: 0 0 var(--s3); }

a {
    color: var(--link);
    text-decoration: none;
}

a:hover {
    color: var(--link-hover);
    text-decoration: underline;
}

hr {
    border: 0;
    border-top: 1px solid var(--border);
    margin: var(--s4) 0;
}

code, kbd, samp {
    font-family: var(--font-mono);
    font-size: var(--fs-sm);
}

/* One focus treatment for the whole app. Keyboard-only. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: var(--r);
    position: relative;
    z-index: 1;
}

.app-rail :where(a, button, [tabindex]):focus-visible {
    box-shadow: var(--focus-ring-on-navy);
}

::selection {
    background: var(--roth-navy);
    color: #fff;
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 5px; }
::-webkit-scrollbar-track { background: transparent; }

/* --------------------------------------------------------------------------
   4. APP SHELL — rail + topbar + tabstrip + workspace
   -------------------------------------------------------------------------- */
.app-shell {
    display: flex;
    height: 100vh;
    width: 100%;
    overflow: hidden;
}

/* --- left nav rail --- */
.app-rail {
    background: var(--surface-rail);
    color: var(--text-rail);
    display: flex;
    flex: 0 0 auto;
    flex-direction: column;
    overflow: hidden;
    transition: width .15s ease;
    width: var(--rail-w);
}

.app-rail[data-collapsed="true"] {
    width: var(--rail-w-collapsed);
}

.rail-brand {
    align-items: center;
    border-bottom: 1px solid rgba(255, 255, 255, .07);
    display: flex;
    flex: 0 0 auto;
    gap: var(--s2);
    height: 128px;
    padding: 0 14px;
}

/* The reversed Roth lockup (white artwork, red swoosh) sits directly on the
   navy rail — no plate. Same asset and same 128px block as InternalAppSuite,
   so the two apps read as one product. */
.rail-brand img {
    flex: 0 1 auto;
    height: 120px;
    max-width: 160px;
    min-width: 0;
    object-fit: contain;
    object-position: left center;
    width: auto;
}

.rail-toggle {
    align-items: center;
    background: none;
    border: 0;
    border-radius: var(--r);
    color: var(--text-rail-dim);
    cursor: pointer;
    display: flex;
    flex: 0 0 auto;
    font-size: var(--fs-lg);
    height: 26px;
    justify-content: center;
    line-height: 1;
    margin-left: auto;
    padding: 0;
    width: 26px;
}

.rail-toggle:hover { color: #fff; }

.rail-nav {
    flex: 1 1 auto;
    overflow-x: hidden;
    overflow-y: auto;
    padding: var(--s1) 0;
}

.rail-heading {
    color: var(--text-rail-dim);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .06em;
    padding: var(--s4) 13px var(--s1);
    text-transform: uppercase;
}

.rail-link {
    align-items: center;
    box-shadow: inset 3px 0 0 transparent;
    color: var(--text-rail);
    cursor: pointer;
    display: flex;
    font-size: var(--fs-sm);
    gap: 11px;
    height: 34px;
    padding: 0 13px;
    text-decoration: none;
    white-space: nowrap;
}

.rail-link:hover {
    background: var(--roth-navy-hover);
    color: #fff;
    text-decoration: none;
}

.rail-link.is-active {
    background: var(--roth-navy-raised);
    box-shadow: inset 3px 0 0 var(--roth-red);
    color: #fff;
    font-weight: 700;
}

.rail-link .rail-glyph {
    flex: 0 0 auto;
    font-size: var(--fs-md);
    line-height: 1;
    text-align: center;
    width: 20px;
}

.rail-link .rail-label {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rail-link .rail-count {
    background: rgba(255, 255, 255, .14);
    border-radius: var(--r-pill);
    color: #fff;
    font-size: var(--fs-xs);
    font-weight: 700;
    padding: 1px var(--s2);
}

.app-rail[data-collapsed="true"] .rail-label,
.app-rail[data-collapsed="true"] .rail-count,
.app-rail[data-collapsed="true"] .rail-heading,
.app-rail[data-collapsed="true"] .rail-brand img {
    display: none;
}

.app-rail[data-collapsed="true"] .rail-brand {
    height: var(--topbar-h);
    justify-content: center;
    padding: 0;
}

.rail-foot {
    border-top: 1px solid rgba(255, 255, 255, .07);
    color: var(--text-rail-dim);
    flex: 0 0 auto;
    font-size: var(--fs-xs);
    padding: var(--s3) 13px;
}

/* --- main column --- */
.app-main {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-width: 0;
}

/* js/soft-nav.js swaps these wrappers' contents (status chip, banners); they add no box of their own */
.shell-region { display: contents; }

.app-topbar {
    align-items: center;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    display: flex;
    flex: 0 0 auto;
    gap: var(--s4);
    height: var(--topbar-h);
    padding: 0 18px;
}

/* page trail (js/page-trail.js): the pages opened in this tab, under the title bar */
.page-trail {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    flex: 0 0 auto;
    font-size: var(--fs-sm);
    padding: 0 18px;
}

.page-trail[hidden] { display: none; }

.page-trail ol {
    align-items: center;
    display: flex;
    gap: var(--s2);
    list-style: none;
    margin: 0;
    min-height: 30px;
    overflow-x: auto;
    padding: 0;
    scrollbar-width: none;
    white-space: nowrap;
}

.page-trail li { align-items: center; display: inline-flex; flex: 0 1 auto; gap: var(--s2); min-width: 0; }
/* earlier crumbs shorten first; where you are stays readable */
.page-trail li[aria-current] { flex-shrink: 0; }
.page-trail li + li::before { color: var(--text-faint); content: "\203A"; }
.page-trail a,
.page-trail [aria-current] > span {
    max-width: 240px;
    overflow: hidden;
    text-overflow: ellipsis;
}
.page-trail a { color: var(--link); text-decoration: none; }
.page-trail a:hover { color: var(--link-hover); text-decoration: underline; }
.page-trail [aria-current] > span { color: var(--text); font-weight: 700; }
.page-trail__more { color: var(--text-faint); }

.page-trail:not([hidden]) { align-items: center; display: flex; gap: var(--s2); }
.page-trail ol { flex: 1 1 auto; min-width: 0; }

/* Hide (back on in Settings > Show page trail) */
.page-trail__hide {
    background: none;
    border: 0;
    border-radius: var(--r);
    color: var(--text-faint);
    cursor: pointer;
    flex: 0 0 auto;
    font: inherit;
    font-size: var(--fs-md);
    line-height: 1;
    padding: 2px 6px;
}

.page-trail__hide:hover,
.page-trail__hide:focus-visible { background: var(--surface-sunken); color: var(--text); }

.app-topbar .topbar-title {
    color: var(--text);
    font-size: var(--fs-md);
    font-weight: 700;
}

.app-topbar .spacer { flex: 1 1 auto; }

.app-topbar--with-scope { height: auto; min-height: var(--topbar-h); }
.app-topbar--with-scope .spacer { flex: 0 0 auto; }

.topbar-user {
    align-items: center;
    color: var(--text-muted);
    display: flex;
    font-size: var(--fs-base);
    gap: var(--s2);
    min-width: 0;
}

.topbar-user .user-email {
    color: var(--text);
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- workspace tab strip (parallel tools, closable) --- */
.tabstrip {
    align-items: stretch;
    background: var(--surface-sunken);
    border-bottom: 1px solid var(--border);
    display: flex;
    flex: 0 0 auto;
    gap: 3px;
    height: var(--tabstrip-h);
    overflow-x: auto;
    padding: 0 6px;
}

.tabstrip-tab {
    align-items: center;
    background: var(--border);
    border: 1px solid transparent;
    border-bottom: none;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    color: var(--text-muted);
    cursor: pointer;
    display: flex;
    font-size: var(--fs-sm);
    gap: 7px;
    margin-top: 5px;
    padding: 0 var(--s3);
    white-space: nowrap;
}

.tabstrip-tab:hover { color: var(--text); text-decoration: none; }

.tabstrip-tab.is-active {
    background: var(--surface);
    border-color: var(--border);
    box-shadow: inset 0 3px 0 var(--roth-navy);
    color: var(--text);
    font-weight: 700;
}

.tabstrip-close {
    background: none;
    border: 0;
    color: var(--text-faint);
    cursor: pointer;
    font-size: var(--fs-lg);
    line-height: 1;
    padding: 0 2px;
}

.tabstrip-close:hover { color: var(--roth-red); }

/* --- workspace --- */
.workspace {
    background: var(--bg);
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-height: 0;
    overflow: auto;
    padding: var(--s4);
}

.workspace--flush {
    padding: 0;
    overflow: hidden;
}

.workspace-header {
    align-items: flex-start;
    display: flex;
    flex: 0 0 auto;
    gap: var(--s4);
    justify-content: space-between;
    margin-bottom: var(--s3);
}

.eyebrow {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .06em;
    margin: 0 0 2px;
    text-transform: uppercase;
}

.workspace-header h1 { margin: 0; }

/* --------------------------------------------------------------------------
   5. SYSTEM STATUS (UnifiedInternalApi health)
   Chip lives in the top bar; degrades into a full-width strip.
   -------------------------------------------------------------------------- */
.sys-status {
    align-items: center;
    background: var(--chip-green-bg);
    border: 1px solid var(--chip-green-bd);
    border-radius: var(--r-pill);
    color: var(--chip-green-fg);
    cursor: default;
    display: inline-flex;
    font-size: var(--fs-sm);
    font-weight: 700;
    gap: 6px;
    padding: 3px var(--s3) 3px var(--s2);
}

.sys-status::before {
    background: currentColor;
    border-radius: 50%;
    content: "";
    height: 7px;
    width: 7px;
}

.sys-status[data-state="degraded"] {
    background: var(--chip-amber-bg);
    border-color: var(--chip-amber-bd);
    color: var(--chip-amber-fg);
    cursor: pointer;
}

.sys-status[data-state="down"] {
    background: var(--chip-red-bg);
    border-color: var(--chip-red-bd);
    color: var(--chip-red-fg);
    cursor: pointer;
}

.sys-status-bar {
    align-items: center;
    background: var(--chip-amber-bg);
    border-bottom: 1px solid var(--chip-amber-bd);
    color: var(--chip-amber-fg);
    display: flex;
    flex: 0 0 auto;
    font-size: var(--fs-base);
    gap: var(--s3);
    padding: var(--s2) 18px;
}

.sys-status-bar[hidden] { display: none; }

.sys-status-bar[data-state="down"] {
    background: var(--chip-red-bg);
    border-bottom-color: var(--chip-red-bd);
    color: var(--chip-red-fg);
}

.sys-status-bar .sys-status-icon {
    flex: 0 0 auto;
    font-size: var(--fs-md);
    font-weight: 700;
}

.sys-status-bar strong { font-weight: 700; }

.sys-status-bar .sys-status-detail {
    color: inherit;
    flex: 1 1 auto;
    opacity: .85;
}

.sys-status-bar .btn { margin-left: auto; }

/* --------------------------------------------------------------------------
   6. BUTTONS — one base component, explicit variants
   -------------------------------------------------------------------------- */
.btn {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text);
    cursor: pointer;
    display: inline-flex;
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: 700;
    gap: 6px;
    height: var(--control-h-lg);
    justify-content: center;
    line-height: 1;
    padding: 0 var(--s3);
    text-decoration: none;
    white-space: nowrap;
}

.btn:hover {
    background: var(--surface-sunken);
    color: var(--text);
    text-decoration: none;
}

.btn:disabled,
.btn[aria-disabled="true"] {
    cursor: not-allowed;
    opacity: .5;
}

.btn--primary {
    background: var(--roth-navy);
    border-color: var(--roth-navy);
    color: #fff;
}

.btn--primary:hover {
    background: var(--roth-navy-hover);
    border-color: var(--roth-navy-hover);
    color: #fff;
}

.btn--secondary {
    background: var(--surface);
    border-color: var(--border-strong);
    color: var(--text-muted);
}

.btn--ghost {
    background: none;
    border-color: transparent;
    color: var(--accent-navy);
}

.btn--ghost:hover {
    background: var(--surface-sunken);
}

/* destructive: red is reserved for irreversible confirms */
/* dark theme's --roth-red is a text red (too light under white text), so the fill has its own token */
.btn--danger {
    background: var(--danger-fill, var(--roth-red));
    border-color: var(--danger-fill, var(--roth-red));
    color: #fff;
}

.btn--danger:hover {
    background: var(--danger-fill-hover, var(--roth-red-hover));
    border-color: var(--danger-fill-hover, var(--roth-red-hover));
    color: #fff;
}

/* consequential but recoverable (Decline, Finish Merge) */
.btn--warning {
    background: var(--chip-amber-bg);
    border-color: var(--chip-amber-bd);
    color: var(--chip-amber-fg);
}

.btn--warning:hover {
    background: var(--chip-amber-bd);
    color: var(--chip-amber-fg);
}

.btn--sm {
    font-size: var(--fs-xs);
    height: 26px;
    padding: 0 var(--s2);
}

.btn--block {
    display: flex;
    width: 100%;
}

/* text-styled action inside grids and lists */
.btn-link {
    background: none;
    border: 0;
    color: var(--link);
    cursor: pointer;
    font: inherit;
    font-weight: 700;
    padding: 0;
    text-align: left;
}

.btn-link:hover {
    color: var(--link-hover);
    text-decoration: underline;
}

.button-row,
.toolbar {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
}

.toolbar {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    margin-bottom: var(--s3);
    padding: var(--s2) var(--s3);
}

.toolbar--bare {
    background: none;
    border: 0;
    padding: 0;
}

/* A toolbar of labelled fields (search and filter bars): the boxes and the buttons share one line.
   Each field is label above box, so the row aligns to the end, and a field's own bottom margin
   (.field) would lift the boxes 12px above the buttons. */
.toolbar:has(> .field) { align-items: end; }
.toolbar > .field { margin-bottom: 0; }

#dashboardFilterForm > .field-row {
    display: flex;
    flex: 1 1 660px;
    flex-wrap: wrap;
    margin-bottom: 0;
    max-width: 100%;
}

#dashboardFilterForm > .field-row > .field {
    flex: 1 1 200px;
    margin-bottom: 0;
    min-width: 0;
}

/* One spacer, every horizontal row component. Was .toolbar-only, which forced
   inline flex overrides inside panel feet. */
.toolbar .spacer,
.button-row .spacer,
.panel__foot .spacer,
.submit-bar .spacer { flex: 1 1 auto; }

/* PROMOTED (round 3): a quiet icon-only action. It was §28, scoped to a
   timesheet punch row; AssetManagement puts one in every grid row (delete),
   every rail (search, settings) and every tile head (drag, expand), so it
   belongs with the other buttons. Visible by default now — a delete you cannot
   see until you hover is not discoverable on a 40-row queue. Add
   .icon-btn--quiet for the old hover-reveal behaviour. */
.icon-btn {
    align-items: center;
    background: none;
    border: 0;
    border-radius: var(--r-sm);
    color: var(--text-faint);
    cursor: pointer;
    display: inline-flex;
    height: 22px;
    justify-content: center;
    padding: 0;
    width: 22px;
}

.icon-btn:hover { background: var(--surface-sunken); color: var(--text); }

.icon-btn--danger:hover { background: var(--chip-red-bg); color: var(--chip-red-fg); }

.icon-btn--quiet { opacity: 0; }

tr:hover .icon-btn--quiet,
.icon-btn--quiet:focus-visible { opacity: 1; }

.icon-btn svg {
    fill: none;
    height: 13px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 2;
    width: 13px;
}

/* PROMOTED (round 2): a button row that is itself a cell in a .field-row must
   sit on the control baseline, not centred against the label+control pair.
   Replaces the align-items:end copies in .filter-actions,
   .form-grid__actions and .impersonation-actions. */
.button-row--end { align-items: end; }

/* --------------------------------------------------------------------------
   7. TABS — one system. Replaces .admin-tabs button AND .tab-button.
   -------------------------------------------------------------------------- */
.tabs {
    border-bottom: 1px solid var(--border);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1);
    margin-bottom: var(--s4);
}

[data-utility-tabs] [hidden] { display: none; }

.tabs__tab {
    background: none;
    border: 0;
    border-bottom: 3px solid transparent;
    color: var(--text-muted);
    cursor: pointer;
    font-family: inherit;
    font-size: var(--fs-base);
    font-weight: 700;
    margin-bottom: -1px;
    padding: var(--s2) var(--s3);
    text-decoration: none;
    white-space: nowrap;
}

.tabs__tab:hover {
    color: var(--text);
    text-decoration: none;
}

.tabs__tab.is-active {
    border-bottom-color: var(--roth-navy);
    color: var(--accent-navy);
}

.tabs__tab .chip { margin-left: 6px; }

/* right-aligned escape link inside a tab bar */
.tabs__link {
    margin-left: auto;
    align-self: center;
    font-size: var(--fs-base);
    font-weight: 700;
}

.tabpane { display: none; }
.tabpane.is-active { display: block; }

.tabpane--grid.is-active {
    display: grid;
    gap: var(--s4);
}

/* --------------------------------------------------------------------------
   8. CHIPS
   -------------------------------------------------------------------------- */
.chip {
    align-items: center;
    background: var(--chip-gray-bg);
    border: 1px solid var(--chip-gray-bd);
    border-radius: var(--r-pill);
    color: var(--chip-gray-fg);
    display: inline-flex;
    font-size: var(--fs-xs);
    font-weight: 700;
    gap: 5px;
    line-height: 1.5;
    padding: 1px var(--s2);
    white-space: nowrap;
}

.chip--green { background: var(--chip-green-bg); border-color: var(--chip-green-bd); color: var(--chip-green-fg); }
.chip--amber { background: var(--chip-amber-bg); border-color: var(--chip-amber-bd); color: var(--chip-amber-fg); }
.chip--blue  { background: var(--chip-blue-bg);  border-color: var(--chip-blue-bd);  color: var(--chip-blue-fg); }
.chip--red   { background: var(--chip-red-bg);   border-color: var(--chip-red-bd);   color: var(--chip-red-fg); }
.chip--gray  { background: var(--chip-gray-bg);  border-color: var(--chip-gray-bd);  color: var(--chip-gray-fg); }

.chip--dot::before {
    background: currentColor;
    border-radius: 50%;
    content: "";
    height: 6px;
    width: 6px;
}

/* --------------------------------------------------------------------------
   9. PANELS
   -------------------------------------------------------------------------- */
.panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    min-width: 0;
}

.panel > :last-child { margin-bottom: 0; }

.panel__head {
    align-items: center;
    border-bottom: 1px solid var(--border);
    display: flex;
    gap: var(--s3);
    justify-content: space-between;
    padding: var(--s3) var(--s4);
}

.panel__head h2,
.panel__head h3 {
    font-size: var(--fs-md);
    margin: 0;
}

.panel__body {
    padding: var(--s4);
}

.panel__body--flush { padding: 0; }

.panel__foot {
    align-items: center;
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    display: flex;
    font-size: var(--fs-sm);
    gap: var(--s3);
    padding: var(--s2) var(--s4);
}

/* PROMOTED (round 2): the right-hand meta group in a panel head or foot —
   record counts, "as of" timestamps, status chips. Five HRWeb views and the
   Viper detail panel all want it, so it lives in the shared range. */
.panel__meta {
    align-items: center;
    color: var(--text-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: var(--fs-sm);
    gap: var(--s2);
    margin-left: auto;
}

.panel--split {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

/* --------------------------------------------------------------------------
   10. FORMS
   -------------------------------------------------------------------------- */
label { color: var(--text); }

.label,
.field > span:first-child,
.field > label {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .03em;
    text-transform: uppercase;
}

.req { color: var(--roth-red); }

input[type="text"],
input[type="password"],
input[type="email"],
input[type="number"],
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"],
input[type="url"],
input[type="search"],
input[type="tel"],
/* an <input> written without a type is a text box; without this it falls back to the browser default */
input:not([type]),
input[type="file"],
textarea,
select,
.input {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-base);
    min-height: var(--control-h-lg);
    padding: 0 var(--s2);
    width: 100%;
}

textarea {
    line-height: var(--lh-base);
    min-height: 78px;
    padding: 6px var(--s2);
    resize: vertical;
}

select {
    appearance: none;
    background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
                      linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
    background-position: calc(100% - 15px) 13px, calc(100% - 10px) 13px;
    background-repeat: no-repeat;
    background-size: 5px 5px, 5px 5px;
    padding-right: var(--s5);
}

select[multiple] {
    background-image: none;
    padding: var(--s1);
}

input:hover,
select:hover,
textarea:hover { border-color: var(--border-strong); }

input[readonly],
input:disabled,
textarea[readonly],
select:disabled {
    background: var(--surface-sunken);
    color: var(--text-muted);
    cursor: default;
}

input[type="checkbox"],
input[type="radio"] {
    accent-color: var(--roth-navy);
    height: 14px;
    margin: 0;
    min-height: 0;
    width: 14px;
}

input::placeholder,
textarea::placeholder { color: var(--text-faint); }

/* narrow boxes for numbers in grid cells and read-only totals (the base rule makes every box full width) */
input.input--xs { width: 4rem; }
input.input--sm { width: 6rem; }
input.input--num { text-align: right; }

.field {
    /* label and box stay together at the top: a field stretched by its grid row (a taller cell
       beside it) would otherwise share the extra height out and grow its box (40px, not 34) */
    align-content: start;
    display: grid;
    gap: var(--s1);
    margin-bottom: var(--s3);
}

.field:last-child { margin-bottom: 0; }

/* horizontal label + control (dialogs, dense editors) */
.field-inline {
    align-items: center;
    display: grid;
    gap: var(--s3);
    grid-template-columns: 160px minmax(0, 1fr);
    margin-bottom: var(--s2);
}

.field-inline > label,
.field-inline > .label {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    font-weight: 700;
    letter-spacing: 0;
    text-transform: none;
}

.field-inline[hidden] { display: none; }

/* responsive field grid for editors */
.field-row {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    margin-bottom: var(--s3);
}

.field-row > label,
.field-row > .field {
    /* label and box stay together at the top (as .field), bare labels included */
    align-content: start;
    display: grid;
    gap: var(--s1);
    /* the row spaces its fields with gap; a field's own bottom margin (all but the last) pushed the
       row's end line 12px below the controls, so a button cell aligned to the end sat low */
    margin-bottom: 0;
}

/* the display rules above outrank the hidden attribute, so a field a script hides stayed visible */
.field[hidden],
.field-row > label[hidden],
.field-row > .field[hidden] { display: none; }

/* a row whose labels may wrap to two lines: boxes line up on the row's bottom edge instead of
   dropping below their neighbours (use only where no field has text under its box) */
.field-row--end > .field,
.field-row--end > label { align-content: end; }

/* .btn's inline-flex outranks the hidden attribute too (Scheduling Report's script-hidden Refresh options) */
.btn[hidden] { display: none; }

/* .check-field's display is !important (below), so its hidden state must be too */
.check-field[hidden] { display: none !important; }

.check-field {
    align-content: end;
    align-items: center;
    display: flex !important;
    gap: var(--s2);
    font-size: var(--fs-base);
}

.check-field span,
.check-field { text-transform: none; letter-spacing: 0; }

/* PROMOTED (round 2): a multi-select used as a persistent picker rather than a
   dropdown. The height is the point — it must show a working set without
   scrolling. Replaces .technician-picker; .assignment-panel select is the
   same idea and can adopt it. */
select.listbox {
    min-height: 320px;
    width: 100%;
}

select.listbox option { padding: 3px var(--s1); }

.hint,
.guid-line {
    color: var(--text-muted);
    font-size: var(--fs-sm);
}

.guid-line code { color: var(--text); }

fieldset {
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    gap: var(--s2);
    margin: 0;
    padding: var(--s3);
}

legend {
    color: var(--accent-navy);
    font-size: var(--fs-sm);
    font-weight: 700;
    letter-spacing: .03em;
    padding: 0 var(--s1);
    text-transform: uppercase;
}

fieldset label {
    align-items: center;
    display: flex;
    font-size: var(--fs-base);
    gap: var(--s2);
}

.rule-list {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    margin: 0 0 var(--s4);
    padding-left: var(--s4);
}

.rule-list li { margin-bottom: 2px; }

/* --------------------------------------------------------------------------
   11. DATA GRIDS
   -------------------------------------------------------------------------- */
.grid-host {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: auto;
}

.grid-host--flush {
    border: 0;
    border-radius: 0;
    flex: 1 1 auto;
    min-height: 0;
}

/* Bounded grid: the host scrolls itself, so the sticky header stays in view. */
/* the 320px is a floor for the bound on short screens, not a minimum size: as min-height it held a
   one-row list (Approve Timesheets) in a 320px box of empty space */
.grid-host--scroll {
    max-height: max(320px, calc(100vh - 230px));
}

table.grid,
.grid-host table {
    background: var(--surface);
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--fs-base);
    width: 100%;
}

.grid thead th,
.grid-host thead th {
    background: var(--surface-sunken);
    border-bottom: 1px solid var(--border);
    color: var(--text-muted);
    font-size: var(--fs-sm);
    font-weight: 700;
    padding: var(--cell-pad-y) var(--cell-pad-x);
    position: sticky;
    text-align: left;
    top: 0;
    white-space: nowrap;
    z-index: 2;
}

.grid tbody td,
.grid-host tbody td {
    border-bottom: 1px solid var(--border-hairline);
    padding: var(--cell-pad-y) var(--cell-pad-x);
    text-align: left;
    vertical-align: top;
}

.grid tbody tr:nth-child(even) td,
.grid-host tbody tr:nth-child(even) td {
    background: var(--surface-alt);
}

.grid tbody tr:hover td,
.grid-host tbody tr:hover td {
    background: #e6e9f5;
}

:root[data-theme="dark"] .grid tbody tr:hover td,
:root[data-theme="dark"] .grid-host tbody tr:hover td { background: #232b4a; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .grid tbody tr:hover td,
    :root:not([data-theme="light"]) .grid-host tbody tr:hover td { background: #232b4a; }
}

.grid tbody tr.is-selected td,
.grid-host tbody tr.is-selected td {
    background: #e8ebfa;
    box-shadow: inset 3px 0 0 var(--roth-navy);
}

:root[data-theme="dark"] .grid tbody tr.is-selected td { background: #262f52; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .grid tbody tr.is-selected td { background: #262f52; }
}

.grid td.num,
.grid th.num { text-align: right; font-variant-numeric: tabular-nums; }

.grid td.nowrap,
.grid th.nowrap { white-space: nowrap; }

.grid td.shrink,
.grid th.shrink { width: 1%; white-space: nowrap; }

/* PROMOTED (round 2): a table that must not compress below legibility inside
   its .grid-host. The host already scrolls; this sets the floor it scrolls
   against. Replaces .profile-hours-table's bare min-width. */
.grid--wide { min-width: 860px; }

/* PROMOTED (round 3): rows per screen is the constraint on a production queue.
   AssetManagement's work-order queues, project worksheets, costing sheets and
   PM billing grids are tools people read forty rows of at a time, and the
   comfortable cell padding costs eight of those rows on a laptop. This is the
   one density knob; it is set on the table, never on a form.
   Replaces .compact, .pm-billing-grid, .costing-grid and .dc-project-subtable. */
.grid--dense {
    --cell-pad-y: 3px;
    --cell-pad-x: 7px;
    font-size: var(--fs-sm);
}

.grid--dense thead th { font-size: var(--fs-xs); }

/* an editable cell fills its cell and loses its own frame — the row is the frame */
.grid--dense input,
.grid--dense select,
.grid--dense textarea {
    border-color: transparent;
    background: transparent;
    min-height: 22px;
    padding: 0 3px;
}

.grid--dense input:hover,
.grid--dense select:hover,
.grid--dense textarea:hover,
.grid--dense input:focus,
.grid--dense select:focus,
.grid--dense textarea:focus {
    background: var(--surface);
    border-color: var(--border-strong);
}

/* a toolbar inside a dense cell (the DC daily report's visit date + Go) keeps full boxes beside its
   buttons: the in-cell frameless look made the date a bare 22px strip next to a 34px button */
.grid--dense .toolbar { margin-bottom: 0; }

.grid--dense .toolbar input,
.grid--dense .toolbar select {
    background: var(--surface);
    border-color: var(--border);
    min-height: var(--control-h-lg);
    padding: 0 var(--s2);
    width: auto;
}

/* sortable header: direction is rendered, not implied */
.grid__sort {
    align-items: center;
    background: none;
    border: 0;
    color: inherit;
    cursor: pointer;
    display: inline-flex;
    font: inherit;
    font-weight: 700;
    gap: 5px;
    padding: 0;
    text-transform: inherit;
}

.grid__sort:hover { color: var(--accent-navy); text-decoration: none; }

.grid__sort::after {
    color: var(--text-faint);
    content: "\2195";
    font-size: var(--fs-xs);
}

th[aria-sort="ascending"] .grid__sort { color: var(--accent-navy); }
th[aria-sort="ascending"] .grid__sort::after { content: "\25B2"; color: var(--accent-navy); font-size: 8px; }
th[aria-sort="descending"] .grid__sort { color: var(--accent-navy); }
th[aria-sort="descending"] .grid__sort::after { content: "\25BC"; color: var(--accent-navy); font-size: 8px; }

/* a header's <details> sort menu (Roth FM Work Orders) floats under the header: opened inline it
   stretched the whole header row to ~100px and pushed the other headers down. While one is open the
   grid host is tall enough to show it, so a short or empty grid does not clip it and keeps its scroll. */
.grid thead th:has(> details) { position: relative; }
.grid thead th > details > summary { cursor: pointer; }

.grid thead th > details > :not(summary) {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: var(--shadow-pop);
    left: 0;
    min-width: 140px;
    padding: var(--s2) var(--s3);
    position: absolute;
    top: calc(100% - 2px);
    z-index: 30;
}

.grid thead th:nth-last-child(-n+2) > details > :not(summary) { left: auto; right: 0; }

.grid-host:has(thead details[open]) { min-height: 150px; }

/* pager. Replaces .pager-X. */
.grid__pager {
    align-items: center;
    background: var(--surface);
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    display: flex;
    font-size: var(--fs-sm);
    gap: var(--s3);
    padding: var(--s2) var(--s3);
}

.grid__pager .spacer { flex: 1 1 auto; }

.grid__pager .btn { height: 26px; padding: 0 var(--s2); }

.grid__pager .page-current {
    color: var(--text);
    font-weight: 700;
}

/* AI review cell (Viper) — classes replace the C#-emitted inline styles */
.ai-review { display: grid; gap: 3px; min-width: 250px; }
.ai-review__note { color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.35; }
.ai-review__note--empty { color: var(--text-faint); font-style: italic; }

/* --------------------------------------------------------------------------
   12. NOTICES — state is never color-only
   -------------------------------------------------------------------------- */
.notice {
    align-items: flex-start;
    background: var(--chip-blue-bg);
    border: 1px solid var(--chip-blue-bd);
    border-radius: var(--r);
    color: var(--chip-blue-fg);
    display: flex;
    font-size: var(--fs-base);
    gap: var(--s2);
    margin: 0 0 var(--s3);
    padding: var(--s2) var(--s3);
}

.notice::before {
    content: "i";
    flex: 0 0 auto;
    font-weight: 700;
    height: 16px;
    width: 16px;
    border: 1.5px solid currentColor;
    border-radius: 50%;
    font-size: var(--fs-xs);
    line-height: 13px;
    text-align: center;
}

.notice[hidden] { display: none; }

.notice--error {
    background: var(--chip-red-bg);
    border-color: var(--chip-red-bd);
    color: var(--chip-red-fg);
}

.notice--error::before { content: "!"; }

.notice--success {
    background: var(--chip-green-bg);
    border-color: var(--chip-green-bd);
    color: var(--chip-green-fg);
}

.notice--success::before { content: "\2713"; border: 0; font-size: var(--fs-md); line-height: 16px; }

.notice--warning {
    background: var(--chip-amber-bg);
    border-color: var(--chip-amber-bd);
    color: var(--chip-amber-fg);
}

.notice--warning::before { content: "!"; }

.notice strong { font-weight: 700; }

/* inline status line (SecurityAdmin / Roles). Empty = invisible, no jump. */
.status-line {
    display: block;
    font-size: var(--fs-base);
    min-height: 22px;
}

.status-line:empty { visibility: hidden; }

.status-line.success,
.status-line.error {
    align-items: center;
    border: 1px solid;
    border-radius: var(--r);
    display: inline-flex;
    font-weight: 700;
    gap: 6px;
    padding: 2px var(--s2);
}

.status-line.success {
    background: var(--chip-green-bg);
    border-color: var(--chip-green-bd);
    color: var(--chip-green-fg);
}

.status-line.success::before { content: "\2713"; }

.status-line.error {
    background: var(--chip-red-bg);
    border-color: var(--chip-red-bd);
    color: var(--chip-red-fg);
}

.status-line.error::before { content: "!"; }

/* field-level validation text */
.field-error {
    color: var(--chip-red-fg);
    display: block;
    font-size: var(--fs-sm);
    font-weight: 700;
}

.field-error:empty { display: none; }

input[aria-invalid="true"],
select[aria-invalid="true"],
textarea[aria-invalid="true"] {
    border-color: var(--chip-red-bd);
    background: var(--chip-red-bg);
}

/* --------------------------------------------------------------------------
   13. EMPTY / LOADING
   -------------------------------------------------------------------------- */
.empty-state {
    align-items: center;
    color: var(--text-muted);
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    justify-content: center;
    padding: var(--s6) var(--s4);
    text-align: center;
}

.empty-state__glyph {
    color: var(--text-faint);
    font-size: 28px;
    line-height: 1;
}

.empty-state__title {
    color: var(--text);
    font-size: var(--fs-md);
    font-weight: 700;
}

.empty-state__body {
    font-size: var(--fs-base);
    max-width: 46ch;
}

.skeleton {
    background: linear-gradient(90deg, var(--surface-sunken) 25%, var(--border-hairline) 50%, var(--surface-sunken) 75%);
    background-size: 200% 100%;
    border-radius: var(--r-sm);
    color: transparent;
    display: block;
    height: 12px;
    animation: skeleton-sweep 1.2s linear infinite;
}

.skeleton--row { height: 18px; margin: var(--s2) 0; }

@keyframes skeleton-sweep {
    from { background-position: 200% 0; }
    to { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .skeleton { animation: none; }
    * { transition: none !important; }
}

.is-busy {
    opacity: .6;
    pointer-events: none;
}

.spinner {
    animation: spin .7s linear infinite;
    border: 2px solid var(--border);
    border-radius: 50%;
    border-top-color: var(--roth-navy);
    display: inline-block;
    height: 14px;
    width: 14px;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* --------------------------------------------------------------------------
   14. DIALOGS
   -------------------------------------------------------------------------- */
dialog.modal {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
    color: var(--text);
    max-height: 90vh;
    max-width: 720px;
    overflow: hidden;
    padding: 0;
    width: 92vw;
}

dialog.modal--wide { max-width: 1040px; }

dialog.modal::backdrop { background: var(--overlay); }

.modal__head {
    align-items: center;
    background: var(--roth-navy);
    color: #fff;
    display: flex;
    gap: var(--s3);
    justify-content: space-between;
    padding: var(--s3) var(--s4);
}

.modal__head h3 {
    color: #fff;
    font-size: var(--fs-md);
    margin: 0;
}

.modal__head .btn {
    background: rgba(255, 255, 255, .12);
    border-color: rgba(255, 255, 255, .32);
    color: #fff;
}

.modal__head .btn:hover { background: rgba(255, 255, 255, .22); }

.modal__head .btn:focus-visible { box-shadow: var(--focus-ring-on-navy); }

.modal__body {
    display: block;
    max-height: calc(90vh - 116px);
    overflow: auto;
    padding: var(--s4);
}

.modal__foot {
    border-top: 1px solid var(--border);
    display: flex;
    gap: var(--s2);
    justify-content: flex-end;
    padding: var(--s3) var(--s4);
}

.modal__section {
    border-top: 1px solid var(--border);
    margin-top: var(--s3);
    padding-top: var(--s3);
}

.modal__section-title {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .06em;
    margin-bottom: var(--s2);
    text-transform: uppercase;
}

/* two-column dense dialog body for the 30-field EDMS form */
.field-columns {
    column-gap: var(--s5);
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}

/* --------------------------------------------------------------------------
   15. DESCRIPTION LISTS
   -------------------------------------------------------------------------- */
.detail-list {
    display: grid;
    gap: var(--s1) var(--s3);
    grid-template-columns: minmax(90px, auto) 1fr;
    margin: 0;
}

.detail-list dt {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    font-weight: 700;
}

.detail-list dd {
    color: var(--text);
    margin: 0;
    overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   16. SCREEN: Auth / credential forms (SignIn, PasswordReset, ChangePassword)
   -------------------------------------------------------------------------- */
.auth-page {
    align-items: center;
    background: var(--bg);
    display: flex;
    justify-content: center;
    min-height: 100vh;
    padding: var(--s5) var(--s4);
}

/* One column: brand, card and notice share the card's width, so the page reads as a
   single unit and fits a 1024x768 screen without scrolling. */
.auth-stack {
    display: grid;
    gap: var(--s3);
    width: 100%;
    max-width: 520px;
}

/* The Roth lockup is a reversed mark: white artwork, red swoosh. On the rail it
   sits straight on navy; on the light auth page it needs the navy field it was
   drawn for. Same asset either way — never a second, differently-coloured file. */
.auth-brand {
    align-items: center;
    background: var(--roth-navy);
    border-radius: var(--r-lg);
    display: flex;
    gap: var(--s3);
    justify-content: center;
    padding: var(--s3) var(--s5);
}

.auth-brand img { height: 56px; width: auto; }

/* Every auth page has exactly one card. The old two-column grid left it pinned
   left beside an empty column. */
.auth-layout {
    align-items: start;
    display: grid;
    gap: var(--s4);
    grid-template-columns: 1fr;
}

.auth-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: var(--s5);
}

.auth-card h1 { font-size: var(--fs-xl); }
.auth-card h2 { font-size: var(--fs-lg); }

.auth-card .btn { width: 100%; height: 38px; font-size: var(--fs-base); }

/* The 440px cap suits the in-shell password pages; inside the auth card the
   fields and buttons should meet the card's own padding. */
.auth-card .credential-form,
.auth-card .auth-sso { max-width: none; }

.auth-sub {
    color: var(--text-muted);
    font-size: var(--fs-base);
    margin: -6px 0 var(--s4);
}

/* Secondary action under an auth form: "Forgot password?" / "Back to sign in". */
.auth-link {
    color: var(--link);
    font-size: var(--fs-sm);
    justify-self: center;
}

.auth-link:hover { color: var(--link-hover); }

.auth-foot {
    color: var(--text-faint);
    font-size: var(--fs-sm);
    line-height: 1.45;
    margin: 0;
    text-align: center;
    text-wrap: pretty;
}

/* the single credential-form pattern: PasswordReset + Account/ChangePassword */
.credential-form {
    display: grid;
    gap: var(--s3);
    max-width: 440px;
}

.credential-form .rule-list { margin-bottom: 0; }

/* Staff SSO entry under the credential form. The sign-in page's reachability
   probe swaps the link for the hint when the UIA authority cannot be reached
   (i.e. the visitor is off the Roth network), so without JavaScript the link
   simply stays visible. */
.auth-sso {
    border-top: 1px solid var(--border);
    display: grid;
    gap: var(--s3);
    margin-top: var(--s4);
    max-width: 440px;
    padding-top: var(--s4);
}

.auth-sso__label {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    margin: 0;
}

.auth-sso__hint {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    margin: 0;
}

/* --------------------------------------------------------------------------
   17. SCREEN: Home / My Apps
   -------------------------------------------------------------------------- */
.app-list {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}

.app-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: var(--s3) var(--s4);
}

.app-card:hover { border-color: var(--roth-navy); }

.app-card a {
    color: var(--accent-navy);
    font-size: var(--fs-md);
    font-weight: 700;
}

:root[data-theme="dark"] .app-card a { color: #9fabf0; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .app-card a { color: #9fabf0; }
}

.app-card__meta {
    align-items: center;
    display: flex;
    gap: var(--s2);
    justify-content: space-between;
}

.app-card__desc {
    color: var(--text-muted);
    font-size: var(--fs-sm);
}

/* --------------------------------------------------------------------------
   18. SCREEN: Viper workspace
   -------------------------------------------------------------------------- */
.viper-layout {
    display: grid;
    flex: 1 1 auto;
    gap: var(--s3);
    grid-template-columns: minmax(0, 1fr) minmax(340px, 420px);
    min-height: 0;
}

.viper-grid-panel,
.viper-detail-panel {
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
    overflow: hidden;
}

.viper-grid { min-width: 1080px; }
.viper-grid--processed { min-width: 1320px; }

.viper-grid td { vertical-align: top; }

.viper-detail-panel .panel__body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
}

.attachment-list {
    display: grid;
    gap: var(--s1);
    margin: var(--s3) 0 0;
}

.attachment-list a {
    align-items: center;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--r);
    display: flex;
    font-size: var(--fs-sm);
    gap: 6px;
    padding: 5px var(--s2);
}

.attachment-list a::before { content: "\1F5CE"; color: var(--text-muted); }

.attachment-tools {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1);
    margin-top: var(--s2);
}

.email-body {
    border-top: 1px solid var(--border);
    color: var(--text);
    font-size: var(--fs-base);
    margin-top: var(--s3);
    max-height: 40vh;
    overflow: auto;
    padding-top: var(--s3);
}

.email-body img { max-width: 100%; }

.invoice-values-panel {
    background: var(--surface-sunken);
    border-top: 1px solid var(--border);
    display: grid;
    gap: var(--s2);
    flex: 0 0 auto;
    padding: var(--s3) var(--s4);
}

.merge-item {
    align-items: center;
    border-bottom: 1px solid var(--border);
    display: grid;
    gap: var(--s3);
    grid-template-columns: auto 1fr 90px;
    padding: var(--s2) 0;
}

.merge-item:last-child { border-bottom: 0; }

.merge-item label {
    align-items: center;
    display: flex;
    gap: 6px;
}

.truck-row {
    border-bottom: 1px solid var(--border);
    padding: var(--s2) 0;
}

/* Add Utility / Truck form */
.utility-form {
    display: grid;
    gap: var(--s2);
    max-width: 620px;
}

/* --------------------------------------------------------------------------
   19. SCREEN: SecurityAdmin
   -------------------------------------------------------------------------- */
.admin-shell {
    display: grid;
    gap: var(--s3);
}

.admin-title {
    align-items: center;
    display: flex;
    gap: var(--s4);
    justify-content: space-between;
}

.admin-title h1 { margin: 0; }

.admin-editor {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    gap: var(--s3);
    padding: var(--s4);
}

.admin-editor h2 {
    border-bottom: 1px solid var(--border);
    font-size: var(--fs-md);
    margin: 0;
    padding-bottom: var(--s2);
}

.role-group-grid {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}

.role-group {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    gap: 3px;
    max-height: 300px;
    overflow: auto;
    padding: var(--s3);
}

.role-group h2,
.role-group h3 {
    background: var(--surface);
    border-bottom: 1px solid var(--border-hairline);
    color: var(--text-muted);
    font-size: var(--fs-xs);
    letter-spacing: .06em;
    margin: 0 0 var(--s1);
    padding-bottom: var(--s1);
    position: sticky;
    text-transform: uppercase;
    top: calc(var(--s3) * -1);
}

.role-group label {
    align-items: center;
    border-radius: var(--r-sm);
    display: flex;
    font-size: var(--fs-base);
    gap: var(--s2);
    padding: 2px var(--s1);
}

.role-group label:hover { background: var(--surface-sunken); }

/* dual multi-select assignment */
.role-admin-layout {
    display: grid;
    gap: var(--s4);
    grid-template-columns: minmax(230px, 320px) 1fr;
}

.role-picker {
    display: grid;
    gap: var(--s3);
}

.assignment-panel {
    display: grid;
    gap: var(--s3);
    grid-template-columns: minmax(180px, 1fr) auto minmax(180px, 1fr);
}

.assignment-panel h2 {
    font-size: var(--fs-xs);
    letter-spacing: .06em;
    margin: 0 0 var(--s1);
    text-transform: uppercase;
    color: var(--text-muted);
}

.assignment-panel select {
    min-height: 340px;
    width: 100%;
}

.assignment-panel option { padding: 3px var(--s1); }

.assignment-actions {
    align-content: center;
    display: grid;
    gap: var(--s2);
}

/* --------------------------------------------------------------------------
   20. SCREEN: NewVendor
   -------------------------------------------------------------------------- */
.vendor-shell { display: grid; gap: var(--s3); }

.vendor-option-grid,
.vendor-upload-grid {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

.vendor-upload-grid label {
    display: grid;
    gap: var(--s1);
}

.vendor-upload-grid input[type="file"] {
    font-size: var(--fs-sm);
    padding: 5px var(--s2);
}

/* --------------------------------------------------------------------------
   21. SCREEN: Error / SignOut
   -------------------------------------------------------------------------- */
.bare-page {
    align-items: center;
    display: flex;
    justify-content: center;
    min-height: 70vh;
    padding: var(--s5);
}

.bare-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    gap: var(--s3);
    justify-items: center;
    max-width: 460px;
    padding: var(--s6) var(--s5);
    text-align: center;
}

.bare-card .request-id {
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--fs-sm);
    overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   22. UTILITIES
   -------------------------------------------------------------------------- */
.muted { color: var(--text-muted); }
.faint { color: var(--text-faint); }
.nowrap { white-space: nowrap; }
.mono { font-family: var(--font-mono); }
.stack { display: grid; gap: var(--s3); }
.stack--tight { display: grid; gap: var(--s2); }
/* Grid items default to min-width:auto, so one wide table in a .stack pushes
   the whole column past its container instead of scrolling inside .grid-host. */
.stack > *,
.stack--tight > * { min-width: 0; }
.row { align-items: center; display: flex; gap: var(--s2); }
.spacer { flex: 1 1 auto; }
.sr-only {
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}


/* ==========================================================================
   HRWeb SECTIONS (25-34)
   ========================================================================== */

/* --------------------------------------------------------------------------
   25. IMPERSONATION — deliberately loud. Full-width, above everything.
   Unlike API health (a chip that expands), impersonation is a standing state
   with real consequences: it never collapses.
   -------------------------------------------------------------------------- */
.impersonation-bar {
    align-items: center;
    background: var(--chip-amber-bg);
    border-bottom: 2px solid var(--chip-amber-bd);
    color: var(--chip-amber-fg);
    display: flex;
    flex: 0 0 auto;
    font-size: var(--fs-base);
    gap: var(--s3);
    padding: var(--s2) 18px;
}

.impersonation-bar::before {
    content: "\26A0";
    font-size: var(--fs-lg);
    line-height: 1;
}

.impersonation-bar strong { font-weight: 700; }

.impersonation-bar .impersonation-meta {
    color: inherit;
    flex: 1 1 auto;
    opacity: .8;
}

.impersonation-bar form { margin: 0; }

.impersonation-persona-grid {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

.impersonation-persona {
    align-items: start;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    gap: var(--s2);
    grid-template-columns: auto 1fr;
    padding: var(--s3);
}

.impersonation-persona:hover { border-color: var(--roth-navy); }

.impersonation-persona small {
    color: var(--text-muted);
    display: block;
    font-size: var(--fs-sm);
    margin-top: 2px;
}

/* --------------------------------------------------------------------------
   26. FORM DENSITY — HR forms are comfortable, HR grids stay dense.
   .form-comfortable raises control height and label size inside a region.
   Applied to every HRWeb data-entry form; never to a grid.
   -------------------------------------------------------------------------- */
.form-comfortable {
    --control-h-lg: 38px;
    font-size: var(--fs-md);
}

.form-comfortable .field > label,
.form-comfortable .field > span:first-child,
.form-comfortable label > span:first-child {
    font-size: var(--fs-sm);
    letter-spacing: .02em;
    text-transform: none;
}

.form-comfortable input,
.form-comfortable select,
.form-comfortable textarea { font-size: var(--fs-md); }

.form-comfortable .field { gap: var(--s2); margin-bottom: var(--s4); }

.form-comfortable .btn { height: 38px; font-size: var(--fs-base); }

/* responsive column sets — the old .two-up / .three-up, tokenized */
.cols-2,
.cols-3,
.cols-4 {
    display: grid;
    gap: var(--s3);
}

.cols-2 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.cols-3 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.cols-4 { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }

.form-comfortable .cols-2,
.form-comfortable .cols-3,
.form-comfortable .cols-4 { gap: var(--s4); }

.col-span-all { grid-column: 1 / -1; }

/* --------------------------------------------------------------------------
   27. BALANCE + STAT CARDS
   Balance = an entitlement the employee spends (vacation, sick, personal).
   Stat    = a computed total for a period. Different jobs, different shapes.
   -------------------------------------------------------------------------- */
.balance-grid {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}

.balance-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--roth-navy);
    border-radius: var(--r-lg);
    padding: var(--s3) var(--s4);
}

.balance-card__label {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.balance-card__hours {
    color: var(--text);
    font-size: 28px;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    line-height: 1.1;
    margin: 2px 0 var(--s1);
}

.balance-card--negative { border-left-color: var(--roth-red); }
.balance-card--negative .balance-card__hours { color: var(--chip-red-fg); }

.balance-card__split {
    color: var(--text-muted);
    display: flex;
    font-size: var(--fs-sm);
    gap: var(--s3);
}

.balance-card__split b {
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

.stat-strip {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
    overflow: hidden;
}

.stat-cell {
    border-right: 1px solid var(--border-hairline);
    padding: var(--s2) var(--s3);
}

.stat-cell:last-child { border-right: 0; }

.stat-cell__label {
    color: var(--text-muted);
    display: block;
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.stat-cell__value {
    color: var(--text);
    display: block;
    font-size: var(--fs-xl);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    line-height: 1.2;
}

.stat-cell--accent .stat-cell__value { color: var(--accent-navy); }
.stat-cell--zero .stat-cell__value { color: var(--text-faint); }

/* --------------------------------------------------------------------------
   28. SCREEN: Timekeeping Entry — the week board
   Replaces .timekeeping-top-row / -left-stack / -day-grid / -summary-row and
   the ~200 lines of hard-coded flex widths they needed. Three regions:
   week bar, week board, totals. No panel is sized against another.
   -------------------------------------------------------------------------- */
.week-bar {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s3);
    margin-bottom: var(--s3);
    padding: var(--s2) var(--s3);
}

.week-bar__range {
    color: var(--text);
    font-size: var(--fs-md);
    font-weight: 700;
    white-space: nowrap;
}

.week-bar__sub {
    color: var(--text-muted);
    font-size: var(--fs-sm);
}

.week-bar form {
    align-items: end;
    display: flex;
    gap: var(--s2);
    margin: 0;
}

.week-bar label {
    display: grid;
    gap: 2px;
}

.week-bar label > span {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.week-bar select { min-width: 96px; width: auto; }

.week-bar .spacer { flex: 1 1 auto; }

/* the board: seven equal day columns, one scroll context, no nested tables */
.week-board {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    margin-bottom: var(--s3);
    overflow: hidden;
}

.day-col {
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.day-col:last-child { border-right: 0; }

.day-col--today { background: var(--surface-alt); }

.day-col__head {
    align-items: baseline;
    background: var(--surface-sunken);
    border-bottom: 1px solid var(--border);
    display: flex;
    gap: var(--s1);
    justify-content: space-between;
    padding: var(--s2) var(--s2);
}

.day-col__name {
    color: var(--text);
    font-size: var(--fs-sm);
    font-weight: 700;
    white-space: nowrap;
}

.day-col__date {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    white-space: nowrap;
}

.day-col__total {
    color: var(--text);
    flex: 0 0 auto;
    font-size: var(--fs-sm);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    white-space: nowrap;
}

.day-col__total--zero { color: var(--text-faint); font-weight: 400; }

.day-col__body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    gap: 1px;
    min-height: 108px;
    padding: var(--s1);
}

.day-col__empty {
    color: var(--text-faint);
    font-size: var(--fs-xs);
    padding: var(--s2);
    text-align: center;
}

/* one punch or one paid-hours block */
/* Label / value / delete. The label track is minmax(0,1fr) and the value is
   content-sized, so at narrow column widths the label would floor near zero and
   ellipsis away to nothing. Below 1180px the row stacks instead (see §23
   responsive) rather than shrinking — a punch you cannot read is not a punch. */
.punch {
    align-items: baseline;
    border-radius: var(--r-sm);
    display: grid;
    gap: var(--s1);
    grid-template-columns: minmax(3.5em, 1fr) auto auto;
    padding: 3px var(--s1);
}

.punch:hover { background: var(--surface-sunken); }

.punch__label {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.punch__value {
    color: var(--text);
    font-size: var(--fs-xs);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    white-space: nowrap;
}

.punch--suspect .punch__value {
    background: var(--chip-red-bg);
    border-radius: var(--r-sm);
    color: var(--chip-red-fg);
    padding: 0 3px;
}

.punch__comment {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-style: italic;
    grid-column: 1 / -1;
    overflow-wrap: anywhere;
    padding: 0 var(--s1) 3px;
}

/* .icon-btn PROMOTED (round 3) into section 6 — AssetManagement uses it in
   every grid row, every rail and every tile head, not just on a punch. The
   hover-reveal that was hard-coded to .punch is now opt-in via .icon-btn--quiet. */
.punch .icon-btn { opacity: 0; }
.punch:hover .icon-btn,
.punch .icon-btn:focus-visible { opacity: 1; }

/* add-entry docked to the board, not floating in a separate column */
.entry-dock {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    margin-bottom: var(--s3);
    padding: var(--s3);
}

/* auto-fit rather than five fixed tracks: the five controls together need
   ~750px, and the dock is only that wide when the rail is open on a >=1440px
   screen. Fixed tracks overflowed the panel at every laptop width. */
.entry-dock__row {
    align-items: end;
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}

.entry-dock__row > .btn { align-self: end; }

.entry-dock label { display: grid; gap: var(--s1); }

.entry-dock label > span:first-child,
.entry-dock .time-field > span:first-child {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.entry-dock [hidden] { display: none !important; }

.time-field { display: grid; gap: var(--s1); }

/* Every part must be able to shrink. A fixed-width select plus a content-sized
   segmented control gave the picker a 232px min-content floor, which overflowed
   any dock track narrower than that — i.e. every laptop. */
.time-parts {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1);
    min-width: 0;
}

.time-parts select {
    flex: 1 1 62px;
    min-width: 0;
    padding-right: var(--s4);
    width: auto;
}

.time-parts .segmented { flex: 0 1 auto; min-width: 0; }

.segmented button { min-width: 0; padding: 0 var(--s2); }

.segmented {
    border: 1px solid var(--border);
    border-radius: var(--r);
    display: inline-flex;
    overflow: hidden;
}

.segmented button {
    background: var(--surface);
    border: 0;
    color: var(--text-muted);
    cursor: pointer;
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: 700;
    height: var(--control-h-lg);
    padding: 0 var(--s3);
}

.segmented button + button { border-left: 1px solid var(--border); }

.segmented button.is-selected {
    background: var(--roth-navy);
    color: #fff;
}

.quick-picks {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1);
}

.quick-picks button {
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    color: var(--text-muted);
    cursor: pointer;
    font-family: inherit;
    font-size: var(--fs-xs);
    font-weight: 700;
    padding: 2px var(--s2);
}

.quick-picks button:hover {
    background: var(--surface);
    border-color: var(--roth-navy);
    color: var(--accent-navy);
}

/* submit state, always visible, never a separate panel */
.submit-bar {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s3);
    padding: var(--s2) var(--s3);
}

.submit-bar .spacer { flex: 1 1 auto; }

.submit-bar form { margin: 0; }

/* --------------------------------------------------------------------------
   29. SCREEN: Absence calendar
   -------------------------------------------------------------------------- */
.month-grid {
    background: var(--border);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    gap: 1px;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    overflow: hidden;
}

.month-dow {
    background: var(--surface-sunken);
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .06em;
    padding: var(--s1) var(--s2);
    text-align: center;
    text-transform: uppercase;
}

.month-cell {
    background: var(--surface);
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-height: 92px;
    min-width: 0;
    padding: var(--s1) 5px 5px;
}

.month-cell--pad { background: var(--surface-sunken); }
.month-cell--weekend { background: var(--surface-alt); }

.month-cell__num {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    text-align: right;
}

.month-cell--today .month-cell__num {
    background: var(--roth-navy);
    border-radius: var(--r-pill);
    color: #fff;
    margin-left: auto;
    padding: 0 5px;
}

.absence-pill {
    background: var(--chip-blue-bg);
    border: 1px solid var(--chip-blue-bd);
    border-radius: var(--r-sm);
    color: var(--chip-blue-fg);
    font-size: var(--fs-xs);
    overflow: hidden;
    padding: 0 4px;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.absence-pill--vacation { background: var(--chip-green-bg); border-color: var(--chip-green-bd); color: var(--chip-green-fg); }
.absence-pill--sick     { background: var(--chip-amber-bg); border-color: var(--chip-amber-bd); color: var(--chip-amber-fg); }
.absence-pill--more     { background: none; border: 0; color: var(--text-muted); font-weight: 700; }

/* --------------------------------------------------------------------------
   30. SCREEN: Employee profile
   -------------------------------------------------------------------------- */
.profile-head {
    display: grid;
    gap: var(--s4);
    grid-template-columns: minmax(260px, 1fr) minmax(0, 1.5fr);
    margin-bottom: var(--s4);
}

.profile-id {
    display: grid;
    gap: var(--s1);
}

.profile-id h1 { margin: 0; }

.profile-facts {
    display: grid;
    gap: 2px var(--s3);
    grid-template-columns: auto 1fr;
    margin: var(--s2) 0 var(--s3);
}

.profile-facts dt {
    color: var(--text-muted);
    font-size: var(--fs-sm);
}

.profile-facts dd {
    color: var(--text);
    font-size: var(--fs-sm);
    font-weight: 700;
    margin: 0;
}

/* progressive disclosure: the six toggle links become real sections */
.disclosure {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    margin-bottom: var(--s2);
}

.disclosure > summary {
    align-items: center;
    cursor: pointer;
    display: flex;
    font-size: var(--fs-md);
    font-weight: 700;
    gap: var(--s2);
    list-style: none;
    padding: var(--s3) var(--s4);
}

.disclosure > summary::-webkit-details-marker { display: none; }

.disclosure > summary::before {
    color: var(--text-muted);
    content: "\25B8";
    font-size: var(--fs-sm);
    transition: transform .12s ease;
}

.disclosure[open] > summary::before { transform: rotate(90deg); }

.disclosure[open] > summary { border-bottom: 1px solid var(--border); }

.disclosure > summary .chip { margin-left: auto; }

.disclosure__body { padding: var(--s4); }

.disclosure__body--flush { padding: 0; }

/* --------------------------------------------------------------------------
   31. SCREEN: Technician timesheets — the widest grid in either app
   -------------------------------------------------------------------------- */
.tech-grid { min-width: 1500px; }

.tech-grid th,
.tech-grid td { white-space: nowrap; }

.tech-grid td:first-child,
.tech-grid td:nth-child(2) { white-space: normal; min-width: 150px; }

/* day pairs read as pairs: a hairline between days, none within one */
.tech-grid th.day-start,
.tech-grid td.day-start { border-left: 1px solid var(--border); }

.tech-grid thead th .dow {
    color: var(--text);
    display: block;
    font-size: var(--fs-sm);
}

.tech-grid thead th .kind {
    color: var(--accent-navy);
    display: block;
    font-size: var(--fs-xs);
}

.tech-grid thead th .date {
    color: var(--text-faint);
    display: block;
    font-size: var(--fs-xs);
    font-weight: 400;
}

.tech-grid tfoot td {
    background: var(--surface-sunken);
    border-top: 1px solid var(--border-strong);
    font-weight: 700;
    position: sticky;
    bottom: 0;
}

.tech-service-grid { min-width: 1800px; }

.tech-service-grid input,
.tech-service-grid select {
    min-height: 26px;
    min-width: 74px;
    padding: 0 5px;
}

/* --------------------------------------------------------------------------
   32. SCREEN: Employee Action Forms
   The legacy 4-column label/field grid, renamed .eaf-row. The old .action-row
   collided with a global .action-row used as a flex button row elsewhere —
   that collision is what forced the .timekeeping-submit-row override. Gone.
   -------------------------------------------------------------------------- */
.eaf-shell {
    display: grid;
    gap: var(--s3);
    max-width: 1000px;
}

.eaf-intro {
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text-muted);
    font-size: var(--fs-base);
    padding: var(--s2) var(--s3);
}

.eaf-section {
    background: var(--surface-sunken);
    border-radius: var(--r);
    color: var(--accent-navy);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .06em;
    margin: var(--s4) 0 var(--s2);
    padding: var(--s1) var(--s3);
    text-transform: uppercase;
}

.eaf-section:first-child { margin-top: 0; }

.eaf-row {
    align-items: center;
    border-radius: var(--r-sm);
    display: grid;
    gap: var(--s2) var(--s3);
    grid-template-columns: 200px minmax(0, 1fr) 200px minmax(0, 1fr);
    padding: 5px var(--s3);
}

.eaf-row--alt { background: var(--surface-alt); }

.eaf-row--full { grid-template-columns: 200px minmax(0, 1fr); align-items: start; }

.eaf-row > label {
    color: var(--text-muted);
    font-size: var(--fs-base);
    margin: 0;
}

.eaf-row input[type="text"],
.eaf-row input:not([type]),
.eaf-row select { max-width: 280px; }

.eaf-row input[type="checkbox"] { width: 14px; }

.eaf-foot {
    border-top: 1px solid var(--border);
    display: flex;
    gap: var(--s2);
    margin-top: var(--s4);
    padding-top: var(--s3);
}

/* --------------------------------------------------------------------------
   33. SCREEN: Documents
   -------------------------------------------------------------------------- */
.doc-list {
    display: grid;
    gap: var(--s2);
    list-style: none;
    margin: 0;
    padding: 0;
}

.doc-item {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: flex;
    gap: var(--s3);
    padding: var(--s2) var(--s3);
}

.doc-item:hover { border-color: var(--roth-navy); }

.doc-item__glyph {
    color: var(--text-faint);
    font-size: var(--fs-lg);
    flex: 0 0 auto;
}

.doc-item__name {
    color: var(--accent-navy);
    flex: 1 1 auto;
    font-size: var(--fs-md);
    font-weight: 700;
    min-width: 0;
    overflow-wrap: anywhere;
}

:root[data-theme="dark"] .doc-item__name { color: #9fabf0; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .doc-item__name { color: #9fabf0; }
}

.doc-item__meta {
    color: var(--text-muted);
    flex: 0 0 auto;
    font-size: var(--fs-sm);
}

/* --------------------------------------------------------------------------
   34. SCREEN: Service reports
   -------------------------------------------------------------------------- */

/* A standing technician picker beside its results — the one HRWeb screen that
   is not toolbar-over-grid. Both tracks must be able to shrink: a bare 1fr
   floors at min-content, so the 8-column results table would hold the column
   open at its .grid--wide floor instead of scrolling inside .grid-host.
   Collapses at 1180px alongside the other §23 steps. */
.sr-layout {
    align-items: start;
    display: grid;
    gap: var(--s4);
    grid-template-columns: minmax(260px, 340px) minmax(0, 1fr);
}

@media (max-width: 1180px) {
    .sr-layout { grid-template-columns: minmax(0, 1fr); }
}

.legend {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s4);
}

.legend__item {
    align-items: center;
    color: var(--text-muted);
    display: flex;
    font-size: var(--fs-sm);
    gap: var(--s2);
}

.legend__swatch {
    border-radius: var(--r-sm);
    display: inline-block;
    height: 12px;
    width: 12px;
}


/* ==========================================================================
   ASSETMANAGEMENT SECTIONS (35-44)
   Loaded only by _ReskinLayout.cshtml. The 59 protected views render on the
   frozen _Layout.cshtml with site.css and never see any of this.
   ========================================================================== */

/* --------------------------------------------------------------------------
   35. SHELL ADDITIONS — scope picker, footer, session timer, combo box
   AssetManagement is the only app with a standing data scope (a customer and a
   location) that every screen reads. It is not a filter on one grid; it is the
   question "which building am I looking at", and it belongs in the top bar
   beside the user, not inside each page.
   -------------------------------------------------------------------------- */
.scope-picker {
    align-items: center;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--r);
    cursor: pointer;
    display: flex;
    flex: 1 1 420px;
    flex-wrap: wrap;
    gap: var(--s3);
    min-width: 0;
    padding: 3px var(--s3);
}

.scope-picker:hover { border-color: var(--roth-navy); }

.scope-picker__field {
    align-items: center;
    display: flex;
    flex: 1 1 220px;
    gap: var(--s3);
    min-width: 0;
}

.scope-picker__label {
    color: var(--text-muted);
    flex: 0 0 auto;
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

/* The value is a readonly input today so the picker can focus it. It must not
   look like a field — it is a label that opens a dialog. */
.scope-picker__value {
    background: none;
    border: 0;
    color: var(--text);
    flex: 1 1 0;
    font-size: var(--fs-base);
    font-weight: 700;
    min-height: 0;
    min-width: 0;
    overflow: hidden;
    padding: 0;
    text-overflow: ellipsis;
    white-space: nowrap;
    width: 0;
}

.scope-picker__value:hover { border-color: transparent; }

/* empty scope is a state, not a blank */
.scope-picker__value:placeholder-shown { color: var(--text-faint); font-weight: 400; }

.app-foot {
    align-items: center;
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    display: flex;
    flex: 0 0 auto;
    flex-wrap: wrap;
    font-size: var(--fs-sm);
    gap: var(--s3);
    padding: var(--s2) 18px;
}

.app-foot p { margin: 0; }

.session-timer {
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: var(--fs-sm);
    margin-left: auto;
    white-space: nowrap;
}

/* under ten minutes the timer stops being furniture */
.session-timer[data-state="expiring"] {
    background: var(--chip-amber-bg);
    border-radius: var(--r);
    color: var(--chip-amber-fg);
    font-weight: 700;
    padding: 0 var(--s2);
}

/* type-ahead over a server lookup: customer, project, subcontractor */
.combo { position: relative; }

.combo__list {
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    box-shadow: var(--shadow-pop);
    left: 0;
    list-style: none;
    margin: 2px 0 0;
    max-height: 260px;
    overflow: auto;
    padding: var(--s1);
    position: absolute;
    right: 0;
    top: 100%;
    z-index: 20;
}

.combo__list:empty { display: none; }

.combo__list li {
    border-radius: var(--r-sm);
    cursor: pointer;
    padding: 4px var(--s2);
}

.combo__list li:hover,
.combo__list li.is-active { background: var(--surface-sunken); }

/* legacy free text arrives with newlines that carry meaning */
.pre-line { white-space: pre-line; overflow-wrap: anywhere; }

/* --------------------------------------------------------------------------
   36. SCREEN: Reactive work-order detail
   The app's largest view. A standing location rail, a status header, a two-level
   tab strip and a main column. The rail is reference material the technician
   reads while editing the main column, so it never collapses above 1180px and
   never scrolls independently — losing the address to a nested scrollbar is the
   complaint the current screen generates.
   -------------------------------------------------------------------------- */
.wo-layout {
    align-items: start;
    display: grid;
    gap: var(--s4);
    grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);
}

/* Both tracks minmax(0, ...): a bare 1fr floors at min-content, which would hold
   the main column open at the costing sheet's width instead of letting it
   scroll inside .grid-host. Same reason as .sr-layout in §34. */
.wo-main { min-width: 0; }

.wo-rail {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    gap: var(--s2);
    padding: var(--s3);
    position: relative;
}

.wo-rail__ticket {
    color: var(--text);
    font-family: var(--font-mono);
    font-size: var(--fs-lg);
    font-weight: 700;
}

.wo-rail__title {
    color: var(--text);
    font-size: var(--fs-md);
    font-weight: 700;
    overflow-wrap: anywhere;
}

.wo-rail__section {
    border-top: 1px solid var(--border-hairline);
    color: var(--text-muted);
    display: grid;
    font-size: var(--fs-sm);
    font-style: normal;
    gap: 2px;
    padding-top: var(--s2);
}

.wo-rail__section:first-of-type { border-top: 0; padding-top: 0; }

.wo-rail__section strong { color: var(--text); }

.wo-rail__section > h2 {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .06em;
    margin: 0;
    text-transform: uppercase;
}

/* The two vertical banners on the current screen are the only way a field user
   finds "roof status" and "uploads" without scrolling the rail. Kept, as a
   sticky section marker rather than absolutely-positioned artwork. */
.wo-side-tab {
    background: var(--surface-sunken);
    border-radius: var(--r-sm);
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .08em;
    padding: 2px var(--s2);
    text-transform: uppercase;
    width: fit-content;
}

.wo-status {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s3);
    margin-bottom: var(--s3);
    padding: var(--s2) var(--s3);
}

.wo-status__text {
    color: var(--text);
    flex: 1 1 auto;
    font-size: var(--fs-md);
    font-weight: 700;
    min-width: 0;
}

.wo-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
    margin-left: auto;
}

/* second-level tabs: same geometry, subordinate weight, no second rule line */
.tabs--sub {
    border-bottom: 0;
    margin-bottom: var(--s3);
}

.tabs--sub .tabs__tab {
    border-bottom-width: 2px;
    font-size: var(--fs-sm);
    padding: var(--s1) var(--s2);
}

/* --------------------------------------------------------------------------
   37. THREADS — work-order comments and project notes
   Two features, one component. Both are an append-only list of authored text
   with a timestamp, a scope flag and two row actions.
   -------------------------------------------------------------------------- */
.thread {
    display: grid;
    gap: var(--s2);
    list-style: none;
    margin: 0;
    padding: 0;
}

.thread__item {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong);
    border-radius: var(--r);
    display: grid;
    gap: var(--s1);
    padding: var(--s2) var(--s3);
}

.thread__item--flag { border-left-color: var(--roth-red); }

.thread__meta {
    align-items: center;
    color: var(--text-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: var(--fs-sm);
    gap: var(--s2);
}

.thread__meta strong { color: var(--text); }

.thread__meta .button-row { margin-left: auto; }

/* Reactive comments (owner 2026-10-08): system entries are a muted, read-only audit trail; editable rows select for the row menu. */
.comments-toolbar { align-items: center; margin-bottom: var(--s2); }
.thread__item--system { background: transparent; border-left-color: var(--border); }
.thread__item--system .thread__body { color: var(--text-muted); }
.thread__item[data-row] { cursor: default; }
.thread__item[data-row].is-selected { background: #e8ebfa; border-left-color: var(--roth-navy); }
:root[data-theme="dark"] .thread__item[data-row].is-selected { background: #262f52; border-left-color: #7f8fee; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .thread__item[data-row].is-selected { background: #262f52; border-left-color: #7f8fee; }
}

.thread__body {
    color: var(--text);
    overflow-wrap: anywhere;
    white-space: pre-line;
}

/* --------------------------------------------------------------------------
   38. EDITABLE GRIDS — costing, PM billing, project worksheets
   The grid itself is §11 plus .grid--dense. What is left is the three things a
   spreadsheet needs that a read-only table does not: a totals row that stays
   put, a per-row save state, and an add-row affordance that is part of the
   table rather than floating under it.
   -------------------------------------------------------------------------- */
.grid tfoot td {
    background: var(--surface-sunken);
    border-top: 1px solid var(--border-strong);
    bottom: 0;
    font-weight: 700;
    position: sticky;
    z-index: 1;
}

.grid tbody tr[aria-invalid="true"] td {
    background: var(--chip-red-bg);
    box-shadow: inset 3px 0 0 var(--chip-red-bd);
}

/* the add row is the last row, not a button below the table */
.grid tbody tr.row-add td {
    background: var(--surface-sunken);
    border-bottom: 0;
}

/* per-row save state, in the row, never a toast over the row being edited */
.row-state {
    color: var(--text-muted);
    display: inline-block;
    font-size: var(--fs-xs);
    min-width: 4em;
}

.row-state[data-state="saving"] { color: var(--chip-amber-fg); }
.row-state[data-state="saved"]  { color: var(--chip-green-fg); }
.row-state[data-state="error"],
.row-state[data-state="conflict"] { color: var(--chip-red-fg); font-weight: 700; }

/* --------------------------------------------------------------------------
   39. SCREEN: DailyConstruction project workspace
   Three regions: a fixed action rail, a section nav, and the section itself.
   The current screen sizes all three against each other with hard-coded
   percentages, which is why the nav collapses to unreadable at 1366px. Here the
   two rails are content-sized and only the main column flexes.
   -------------------------------------------------------------------------- */
.dcp-layout {
    align-items: start;
    display: grid;
    gap: var(--s3);
    grid-template-columns: minmax(180px, 220px) minmax(0, 1fr) minmax(150px, 190px);
}

.dcp-nav {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
    position: sticky;
    top: var(--s4);
}

.dcp-nav__list {
    display: grid;
    list-style: none;
    margin: 0;
    padding: var(--s1) 0;
}

.dcp-nav__list a {
    box-shadow: inset 3px 0 0 transparent;
    color: var(--text);
    display: block;
    font-size: var(--fs-sm);
    padding: 5px var(--s3);
}

.dcp-nav__list a:hover { background: var(--surface-sunken); text-decoration: none; }

/* --accent-navy, not --roth-navy: in dark mode --roth-navy is a button fill (#3a4bc4, 2:1 as text) */
.dcp-nav__list a.is-active {
    background: var(--surface-sunken);
    box-shadow: inset 3px 0 0 var(--accent-navy);
    color: var(--accent-navy);
    font-weight: 700;
}

.dcp-nav .rail-heading { color: var(--text-muted); padding: var(--s3) var(--s3) var(--s1); }

.dcp-main { min-width: 0; }

/* the action rail is a stack of full-width buttons, third column so it does not
   push the section nav off a laptop */
.dcp-actions {
    display: grid;
    gap: var(--s1);
    position: sticky;
    top: var(--s4);
}

.dcp-actions .btn { justify-content: flex-start; }

/* --------------------------------------------------------------------------
   40. DOCUMENTS, MEDIA AND WORK AREAS
   -------------------------------------------------------------------------- */
.media-grid {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
}

.media-thumb {
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--r);
    display: block;
    height: auto;
    max-width: 100%;
    object-fit: cover;
}

.media-grid figure {
    display: grid;
    gap: var(--s1);
    margin: 0;
    min-width: 0;
}

.media-grid figcaption {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    overflow-wrap: anywhere;
}

/* the work-area list: a checkbox, a title, a detail line and row actions */
.worksheet {
    display: grid;
    gap: var(--s1);
    list-style: none;
    margin: 0;
    padding: 0;
}

.worksheet__row {
    align-items: center;
    border: 1px solid var(--border);
    border-radius: var(--r);
    display: grid;
    gap: var(--s2) var(--s3);
    grid-template-columns: auto minmax(0, 1fr) auto;
    padding: var(--s1) var(--s2);
}

.worksheet__row:hover { border-color: var(--border-strong); }

.worksheet__title { font-weight: 700; overflow-wrap: anywhere; }

.worksheet__detail {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    grid-column: 2 / -1;
}

/* --------------------------------------------------------------------------
   41. SCREEN: DailyConstruction pinned tile board
   The one component in this app that neither sibling has: user-arranged cards,
   draggable, resizable in 1/2/4 column spans, expandable to a dialog. Its
   1,004-line stylesheet carried a private token set; that set is deleted and
   every value here comes from §1.
   -------------------------------------------------------------------------- */
.tile-grid {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

.tile {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow: hidden;
}

.tile--span-1 { grid-column: span 1; }
.tile--span-2 { grid-column: span 2; }
.tile--span-4 { grid-column: span 4; }

.tile__head {
    align-items: flex-start;
    background: var(--surface-sunken);
    border-bottom: 1px solid var(--border);
    display: flex;
    gap: var(--s2);
    padding: var(--s2) var(--s3);
}

.tile__title {
    color: var(--text);
    flex: 1 1 auto;
    font-size: var(--fs-base);
    font-weight: 700;
    margin: 0;
    min-width: 0;
    overflow-wrap: anywhere;
}

.tile__meta {
    color: var(--text-muted);
    display: block;
    font-size: var(--fs-xs);
    font-weight: 400;
}

.tile__drag { cursor: grab; }

.tile__body { flex: 1 1 auto; min-height: 0; padding: var(--s3); }

.tile__foot {
    border-top: 1px solid var(--border-hairline);
    color: var(--text-muted);
    font-size: var(--fs-sm);
    padding: var(--s2) var(--s3);
}

/* drag states: the moving tile fades, the target shows where it will land */
.tile.is-dragging { opacity: .4; }

.tile.is-drop-target { border-color: var(--roth-navy); box-shadow: inset 0 0 0 1px var(--roth-navy); }

/* expanded is a dialog, not a fixed-position div with a hand-rolled backdrop */
.tile--expanded {
    display: flex;
    flex-direction: column;
    max-height: 88vh;
}

.tile--expanded .tile__body { overflow: auto; }

.tile--expanded .field-row { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

/* the grid host owns incremental row loading; keep it scrollable so its
   scroll event can request the next page in every tile mode */
.tile .grid-host { max-height: 360px; }
.tile--expanded .grid-host { max-height: calc(88vh - 220px); }

dialog.tile--fullscreen {
    border: 0;
    border-radius: 0;
    height: 100vh;
    max-height: 100vh;
    max-width: none;
    width: 100vw;
}

dialog.tile--fullscreen .grid-host { max-height: calc(100vh - 190px); }

/* body flags while a dialog owns the screen — stops the board scrolling behind it */
.is-modal-open,
.is-tile-open { overflow: hidden; }

/* --------------------------------------------------------------------------
   42. SCHEDULE BARS
   The project schedule is a table with a bar per row, not a chart library.
   -------------------------------------------------------------------------- */
.gantt {
    border-collapse: separate;
    border-spacing: 0;
    table-layout: fixed;
    width: 100%;
}

.gantt th {
    background: var(--surface-sunken);
    border-bottom: 1px solid var(--border);
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    padding: var(--cell-pad-y) 4px;
    text-align: center;
    white-space: nowrap;
}

.gantt__label {
    background: var(--surface);
    border-right: 1px solid var(--border);
    font-size: var(--fs-sm);
    left: 0;
    overflow: hidden;
    padding: var(--cell-pad-y) var(--cell-pad-x);
    position: sticky;
    text-align: left;
    text-overflow: ellipsis;
    white-space: nowrap;
    z-index: 1;
}

.gantt__cell {
    border-bottom: 1px solid var(--border-hairline);
    border-right: 1px solid var(--border-hairline);
    height: 26px;
    padding: 3px 0;
}

.gantt__bar {
    background: var(--roth-navy);
    border-radius: var(--r-pill);
    display: block;
    height: 10px;
}

.gantt__bar--planned { background: var(--border-strong); }
.gantt__bar--late { background: var(--roth-red); }

.gantt__foot td {
    border-top: 1px solid var(--border-strong);
    color: var(--text-muted);
    font-size: var(--fs-xs);
    padding: var(--s1) 4px;
    text-align: center;
}

/* --------------------------------------------------------------------------
   23. RESPONSIVE — four considered steps, not one patch
   -------------------------------------------------------------------------- */

/* 1440: the Viper detail panel stops stealing grid width */
@media (max-width: 1440px) {
    .viper-layout { grid-template-columns: minmax(0, 1fr) minmax(320px, 360px); }
}

/* 1180: Viper stacks — grid on top, detail below, both scroll independently */
@media (max-width: 1180px) {
    .viper-layout {
        grid-template-columns: 1fr;
        grid-template-rows: minmax(240px, 45vh) minmax(0, 1fr);
    }

    .role-admin-layout { grid-template-columns: 1fr; }
    .field-columns { grid-template-columns: 1fr; }
}

/* 900: rail collapses to icons; tab strip and editors go single column */
@media (max-width: 900px) {
    .app-rail { width: var(--rail-w-collapsed); }
    .app-rail .rail-label,
    .app-rail .rail-count,
    .app-rail .rail-heading,
    .app-rail .rail-brand img { display: none; }
    .rail-brand { height: 54px; justify-content: center; padding: 0; }

    .auth-layout { grid-template-columns: 1fr; }
    .assignment-panel { grid-template-columns: 1fr; }
    .assignment-panel select { min-height: 200px; }
    .assignment-actions { grid-auto-flow: column; justify-content: start; }
    .field-inline { grid-template-columns: 1fr; gap: var(--s1); }
}

/* 640: the rail becomes a horizontal scroller above the content */
@media (max-width: 640px) {
    .app-shell { flex-direction: column; height: auto; min-height: 100vh; }
    .app-rail { flex-direction: row; overflow-x: auto; width: 100%; }
    .app-rail .rail-brand { border-bottom: 0; border-right: 1px solid rgba(255,255,255,.07); }
    .rail-nav { display: flex; padding: 0; }
    .rail-link { box-shadow: inset 0 3px 0 transparent; height: 44px; }
    .rail-link.is-active { box-shadow: inset 0 3px 0 var(--roth-red); }
    .app-topbar { flex-wrap: wrap; gap: var(--s2); height: auto; padding: var(--s2) var(--s3); }
    .workspace { padding: var(--s3); }
    .app-list { grid-template-columns: 1fr; }
    dialog.modal { max-height: 100vh; width: 100vw; border-radius: 0; }
    .modal__body { max-height: calc(100vh - 116px); }
}

/* --- HRWeb responsive additions ------------------------------------------ */

/* 1180: the entry dock wraps before it squeezes; profile head stacks */
@media (max-width: 1180px) {
    .profile-head { grid-template-columns: 1fr; }

    /* Seven columns across a laptop leaves ~120px per day. Label beside value
       does not fit; label above value does. */
    .punch { grid-template-columns: minmax(0, 1fr) auto; row-gap: 0; }
    .punch__value { grid-column: 1 / -1; }
    .punch__label { white-space: normal; }
}

/* 900: the week board turns from seven columns into seven stacked days.
   This is the phone/tablet timesheet: one day per row, entries beneath it,
   totals inline. No horizontal scroll, no 44px-minimum violations. */
@media (max-width: 900px) {
    .week-board { grid-template-columns: 1fr; }
    .day-col { border-bottom: 1px solid var(--border); border-right: 0; }
    .day-col:last-child { border-bottom: 0; }
    .day-col__body { min-height: 0; }
    .day-col__head { position: sticky; top: 0; z-index: 1; }
    .punch { grid-template-columns: 1fr auto auto; min-height: 34px; }
    .icon-btn { height: 34px; opacity: 1; width: 34px; }
    .icon-btn svg { height: 16px; width: 16px; }
    .month-grid { grid-template-columns: 1fr; }
    .month-dow { display: none; }
    .month-cell { flex-direction: row; flex-wrap: wrap; min-height: 0; }
    .month-cell--pad { display: none; }
    .month-cell__num { text-align: left; width: 100%; }
    .eaf-row,
    .eaf-row--full { grid-template-columns: 1fr; }
    .stat-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .stat-cell { border-bottom: 1px solid var(--border-hairline); }
}

/* 640: touch targets everywhere; the week bar becomes a stack */
@media (max-width: 640px) {
    .week-bar { align-items: stretch; flex-direction: column; }
    .week-bar form { flex-wrap: wrap; }
    .week-bar select { flex: 1 1 100px; min-width: 0; width: 100%; }
    .entry-dock__row { grid-template-columns: 1fr; }
    .entry-dock .btn,
    .submit-bar .btn,
    .week-bar .btn { min-height: 44px; width: 100%; }
    .segmented button { height: 44px; flex: 1 1 auto; }
    .quick-picks button { min-height: 32px; padding: 0 var(--s3); }
    .balance-grid { grid-template-columns: 1fr; }
    .stat-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .impersonation-bar { align-items: flex-start; flex-direction: column; gap: var(--s2); }
    .doc-item { flex-wrap: wrap; }
}



/* --- AssetManagement responsive additions -------------------------------- */

@media (max-width: 1100px) {
    .app-topbar--with-scope { flex-wrap: wrap; padding-block: var(--s2); }
    .app-topbar--with-scope .spacer { flex: 1 1 auto; }
    .app-topbar--with-scope .scope-picker { flex-basis: 100%; order: 1; }
}

/* 1440: the project workspace drops its action rail into the section foot
   rather than squeezing three columns */
@media (max-width: 1440px) {
    .dcp-layout { grid-template-columns: minmax(170px, 200px) minmax(0, 1fr); }
    .dcp-actions {
        grid-auto-flow: column;
        grid-column: 1 / -1;
        justify-content: start;
        position: static;
    }
    .tile-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .tile--span-4 { grid-column: span 2; }
}

/* 1180: the work-order rail goes above the main column. It stays a rail, not a
   collapsed drawer — the address and ticket number are what the technician is
   reading while they type. */
@media (max-width: 1180px) {
    .wo-layout { grid-template-columns: minmax(0, 1fr); }
    .wo-rail { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
    .wo-rail__section:first-of-type { border-top: 0; }
    .dcp-nav { position: static; }
}

/* 900: single column everywhere; the worksheet row stacks rather than shrinking */
@media (max-width: 900px) {
    .dcp-layout { grid-template-columns: minmax(0, 1fr); }
    .tile-grid { grid-template-columns: minmax(0, 1fr); }
    .tile--span-2,
    .tile--span-4 { grid-column: span 1; }
    .worksheet__row { grid-template-columns: auto minmax(0, 1fr); }
    .media-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }
    .gantt__label { position: static; }
}

/* 640: the scope picker is the header's most important element on a phone,
   so it takes the full row rather than truncating to nothing */
@media (max-width: 640px) {
    .scope-picker { flex: 1 1 100%; }
    .app-foot { flex-direction: column; align-items: flex-start; }
    .session-timer { margin-left: 0; }
    .wo-status { align-items: stretch; flex-direction: column; }
    .wo-actions { margin-left: 0; }
    .wo-actions .btn { flex: 1 1 auto; min-height: 44px; }
    .icon-btn { height: 34px; width: 34px; }
    .icon-btn--quiet { opacity: 1; }
    .thread__meta .button-row { margin-left: 0; }
    .tile--expanded .grid-host,
    dialog.tile--fullscreen .grid-host { max-height: calc(100vh - 230px); }
}

/* --------------------------------------------------------------------------
   24. PRINT
   -------------------------------------------------------------------------- */
@media print {
    .app-rail,
    .app-topbar,
    .tabstrip,
    .toolbar,
    .btn { display: none !important; }

    .app-shell, .workspace { display: block; height: auto; overflow: visible; }
    .panel { border-color: #999; break-inside: avoid; }

    /* HRWeb: timesheets and technician grids are printed and signed */
    .impersonation-bar { display: none !important; }
    .icon-btn, .quick-picks, .entry-dock, .segmented { display: none !important; }
    .week-board { border-color: #999; break-inside: avoid; }
    .day-col { border-right-color: #999; }
    .tech-grid { min-width: 0; font-size: 9px; }
    .tech-grid th, .tech-grid td { padding: 2px 3px; }
    .grid-host { border-color: #999; overflow: visible; }
    .disclosure { border-color: #999; break-inside: avoid; }
    .disclosure > summary::before { display: none; }

    /* AssetManagement: daily reports and costing sheets are printed and filed */
    .app-foot,
    .scope-picker,
    .dcp-actions,
    .dcp-nav,
    .wo-actions,
    .icon-btn,
    .tabs { display: none !important; }

    .dcp-layout,
    .wo-layout { display: block; }

    .wo-rail { border-color: #999; break-inside: avoid; margin-bottom: 12px; }
    .tile-grid { display: block; }
    .tile { border-color: #999; break-inside: avoid; margin-bottom: 8px; }
    .grid--dense { font-size: 9px; }
    .grid--dense th,
    .grid--dense td { padding: 2px 3px; }
    .grid tfoot td { position: static; }
    .gantt__label { position: static; }
    .thread__item { border-color: #999; break-inside: avoid; }
}

/* ==========================================================================
   35. TEMPORARY: legacy Timekeeping Entry / Review / UnsubmittedReview
   --------------------------------------------------------------------------
   Timekeeping/Entry, Review and UnsubmittedReview still carry their original
   markup. DESIGN-GAP.md section 4 is explicit that if they ship unrebuilt the
   OLD css ships with them, unchanged -- their layout is what is broken, not
   their colours, so restyling this markup on Roth tokens would fix nothing and
   would make the rebuild harder to review.

   Lifted verbatim from the pre-reskin app.css, including its three custom
   properties scoped locally so nothing leaks into the shared range above.

   DELETE THIS WHOLE SECTION when DESIGN.md section 3 (.week-bar / .week-board /
   .entry-dock / .stat-strip / .submit-bar) lands. Nothing else references it.
   ========================================================================== */

.timekeeping-top-row,
.timekeeping-entry-grid,
.timekeeping-left-stack,
.timekeeping-daily-panel,
.timekeeping-day-grid,
.timekeeping-day-card,
.timekeeping-comment-row,
.timekeeping-summary-panel,
.timekeeping-summary-row,
.timekeeping-submit-panel,
.timekeeping-submit-row,
.timekeeping-time-field,
.timekeeping-entry-trail,
.timekeeping-add-panel,
.timekeeping-filter-panel,
.timesheet-status {
    --brand-strong: #083e46;
    --line: #d6cec0;
    --muted: #54636c;
}

.timekeeping-day-card .badge {
    --brand-strong: #083e46;
}
.timekeeping-top-row {
    /* Flex with `flex: 0 0 calc(50% - half-gap)` on each child forces every
       panel to be exactly half the row width — same as the Weekly Summary
       panel below, which is also constrained to max-width: 1100px. CSS Grid
       with `minmax(0, 1fr)` *should* do this too, but the Add Entry form's
       time picker (3.4rem + 3.4rem + AM/PM segmented control = ~10.5rem
       min-content) was still bullying the right column wider than its share
       in some browser/zoom combos, clipping the warning banner. Hard widths
       remove the ambiguity. */
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    max-width: 1100px;
    margin: 0 auto 1rem;
    align-items: flex-start;
}
.timekeeping-top-row > * {
    /* Half the row, minus half the 0.75rem gap, so the two panels plus the
       gap add up to exactly 100%. flex-basis is a hard cap (flex-grow: 0,
       flex-shrink: 0), so content can't bully the column wider. min-width: 0
       defangs the flex min-content default. */
    flex: 0 0 calc(50% - 0.375rem);
    min-width: 0;
}
.timekeeping-left-stack {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
.timekeeping-top-row .panel {
    max-width: none;
    margin: 0;
    padding: 0.85rem;
    border-radius: 0.75rem;
    box-shadow: 0 10px 24px rgba(29, 42, 47, 0.07);
}
.timekeeping-top-row .panel__header {
    margin-bottom: 0.6rem;
}
.timekeeping-top-row .panel__header h1 {
    font-size: 1.35rem;
}
.timekeeping-top-row .panel__header h2 {
    font-size: 1.2rem;
}
.timekeeping-top-row .muted {
    font-size: 0.9rem;
}
.timekeeping-top-row .status-banner {
    margin-bottom: 0.65rem;
    padding: 0.55rem 0.7rem;
    border-radius: 0.5rem;
    /* Long warning text ("...delete your previous weeks timesheet...") must
       wrap inside the column. break-word only kicks in for words too long to
       fit on a line; normal word-spacing wraps are preferred. */
    overflow-wrap: break-word;
}
.timekeeping-top-row .form-grid {
    gap: 0.45rem;
    grid-template-columns: repeat(4, minmax(0, 1fr));
}
.timekeeping-top-row .form-grid label,
.timekeeping-time-field {
    gap: 0.2rem;
    font-size: 0.8rem;
}
.timekeeping-top-row .form-grid input,
.timekeeping-top-row .form-grid select {
    min-width: 0;
    width: 100%;
    min-height: 2rem;
    padding: 0.28rem 0.4rem;
    border-radius: 0.35rem;
    font-size: 0.82rem;
}
.timekeeping-entry-grid {
    align-items: start;
}
.timekeeping-time-field {
    display: grid;
    /* The inner .time-parts row (hour + minute + AM/PM) needs ~10.5rem to render
       without clipping the AM/PM segmented control. A single 1/4-width column
       of the surrounding form-grid is too narrow, so reserve two columns. */
    grid-column: span 2;
}
.timekeeping-top-row .filter-actions,
.timekeeping-top-row .action-row {
    gap: 0.35rem;
}
.timekeeping-top-row .button {
    min-height: 2rem;
    padding: 0.28rem 0.55rem;
    border-radius: 0.4rem;
    font-size: 0.82rem;
}
.timekeeping-summary-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0.75rem;
    max-width: 1100px;
    margin: 0 auto 1rem;
    align-items: start;
}
.timekeeping-daily-panel,
.timekeeping-summary-panel {
    max-width: 1100px;
    margin: 0 auto 1rem;
    padding: 0.85rem;
}
.timekeeping-daily-panel .panel__header,
.timekeeping-summary-panel .panel__header {
    margin-bottom: 0.6rem;
}
.timekeeping-daily-panel .panel__header h2,
.timekeeping-summary-panel .panel__header h2 {
    font-size: 1.15rem;
}
.timekeeping-daily-panel .muted,
.timekeeping-summary-panel .muted {
    font-size: 0.82rem;
}
.timekeeping-day-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 0.5rem;
}
.timekeeping-day-card {
    min-width: 0;
    padding: 0.55rem;
    border: 1px solid var(--line);
    border-radius: 0.55rem;
    background: rgba(255, 255, 255, 0.74);
    font-size: 0.75rem;
}
.timekeeping-day-card .panel__header {
    margin-bottom: 0.45rem;
    gap: 0.35rem;
}
.timekeeping-day-card .panel__meta {
    display: grid;
    justify-items: end;
    gap: 0.25rem;
}
.timekeeping-day-card h3 {
    margin: 0;
    font-size: 0.85rem;
}
.timekeeping-day-card .badge {
    padding: 0.2rem 0.4rem;
    font-size: 0.62rem;
}
.timekeeping-day-card .table-scroll {
    width: 100%;
    max-width: 100%;
    overflow-x: auto;
}
.timekeeping-day-card .data-table {
    min-width: 0;
    table-layout: fixed;
    font-size: 0.59rem;
}
.timekeeping-day-card .data-table th,
.timekeeping-day-card .data-table td {
    padding: 0.35rem 0.4rem;
    overflow-wrap: anywhere;
}
.timekeeping-day-card .data-table th:first-child,
.timekeeping-day-card .data-table td:first-child {
    width: 55%;
    font-weight: 700;
    color: var(--brand-strong);
}
.timekeeping-comment-row td {
    color: var(--muted);
    font-size: 0.62rem;
}
.timekeeping-day-card .button {
    padding: 0.25rem 0.45rem;
    border-radius: 0.35rem;
    font-size: 0.78rem;
}
.timekeeping-submit-row {
    /* The base .action-row class is redefined later in this stylesheet as a
       4-column grid (11rem 1fr 11rem 1fr) for the Action Forms layout. That
       crushes the timesheet status badge into a ~70px column and forces the
       label to wrap onto four lines. Override back to flex here so the button
       and badge can flow naturally with the row's available width. */
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    grid-template-columns: none;
}
.timekeeping-submit-row .timesheet-status {
    /* Once flex layout is restored, let the badge expand into the row instead
       of collapsing to its (now inline-flex) content width. */
    flex: 1 1 auto;
    white-space: nowrap;
}
/* `.timekeeping-submit-panel` previously had `align-self: end` from when
   .timekeeping-left-stack was a CSS grid (and it was a no-op because each
   row was content-sized). The left-stack is now a flex column, where the
   same rule would right-align the panel and shrink it to content width —
   so the rule was removed. The submit panel now stretches to the full
   half-row width, matching the filter panel above it. */
.timekeeping-submit-panel .timekeeping-submit-row {
    gap: 0.45rem;
}
.timekeeping-submit-panel .button {
    min-height: 1.8rem;
    padding: 0.25rem 0.55rem;
    border-radius: 0.4rem;
    font-size: 0.72rem;
}
.timesheet-status {
    display: inline-flex;
    align-items: center;
    min-height: 2rem;
    padding: 0 0.65rem;
    border: 1px solid var(--line);
    border-radius: 0.45rem;
    background: var(--surface);
    color: var(--brand-strong);
    font-size: 0.78rem;
    font-weight: 700;
}
.timekeeping-summary-panel .stats-grid {
    gap: 0.4rem;
}
.timekeeping-summary-panel .stat-card {
    min-width: 0;
    padding: 0.45rem 0.5rem;
    border-radius: 0.5rem;
}
.timekeeping-summary-panel .stat-card span {
    margin-bottom: 0.1rem;
    font-size: 0.75rem;
    line-height: 1.15;
    overflow-wrap: anywhere;
}
.timekeeping-summary-panel .stat-card small {
    display: block;
    color: var(--muted);
    font-size: 0.68rem;
    line-height: 1.1;
}
.timekeeping-summary-panel .stat-card strong {
    display: block;
    margin-top: 0.15rem;
    font-size: 1rem;
}
.timekeeping-entry-trail {
    margin: 0;
    display: grid;
    gap: 0.45rem;
}
.timekeeping-entry-trail__item {
    display: grid;
    grid-template-columns: minmax(5.5rem, auto) 1fr;
    gap: 0.6rem;
    align-items: baseline;
}
.timekeeping-entry-trail dt {
    margin: 0;
    font-weight: 700;
    color: var(--brand-strong);
}
.timekeeping-entry-trail dd {
    margin: 0;
}
    .timekeeping-top-row > * {
        /* At narrow widths the 50/50 flex split is too tight for the time
           picker + form-grid in Add Entry. Stack panels full-width. */
        flex: 0 0 100%;
    }
    .timekeeping-summary-row {
        grid-template-columns: 1fr;
    }
    .timekeeping-day-grid {
        grid-template-columns: 1fr;
    }

/* Bare .badge, still used by the three legacy timekeeping views only. */
.badge {
    align-items: center;
    background: #edf4f5;
    border-radius: 999px;
    color: #083e46;
    display: inline-flex;
    font-weight: 700;
    padding: 0.35rem 0.7rem;
}
/* ===== end of TEMPORARY section 35 ===== */

/* --------------------------------------------------------------------------
   45. ASSETMANAGEMENT GROUPED RAIL
   AssetManagement's _ReskinLayout only: navigation groups collapse behind a
   .rail-group-head row with an inline-SVG glyph and a chevron; the group
   holding the current page opens on load. In icon mode (the rail toggle, or
   the 900px step below) a group's sub-list becomes a flyout beside the rail,
   so the collapsed rail stays usable instead of a bare glyph column.
   IAS/HRWeb rails keep the flat heading list; sections 1-17 are untouched.
   The brand row uses the shared full-height lockup, matching IAS/HRWeb.
   -------------------------------------------------------------------------- */
.rail-group {
    position: relative;
}

.rail-group-head {
    background: none;
    border: 0;
    font-family: inherit;
    text-align: left;
    width: 100%;
}

.rail-glyph svg {
    display: block;
    margin: 0 auto;
}

.rail-chevron {
    color: var(--text-rail-dim);
    display: flex;
    flex: 0 0 auto;
    margin-left: auto;
    transition: transform .12s ease;
}

.rail-group[data-open="true"] > .rail-group-head .rail-chevron {
    transform: rotate(90deg);
}

.rail-sub {
    display: none;
    flex-direction: column;
}

.rail-group[data-open="true"] > .rail-sub {
    display: flex;
}

.rail-sub .rail-link {
    height: 30px;
    padding-left: 44px;
}

.rail-sub form {
    display: contents;
}

/* A closed group that holds the current page shows it on its head row. */
.rail-group--has-active[data-open="false"] > .rail-group-head {
    background: var(--roth-navy-raised);
    box-shadow: inset 3px 0 0 var(--roth-red);
    color: #fff;
    font-weight: 700;
}

/* --- icon mode via the rail toggle --- */
/* The rail (and its scroller) must not clip the flyout; the group icons fit
   without scrolling, so visible overflow costs nothing while collapsed. */
.app-rail[data-collapsed="true"] {
    overflow: visible;
    position: relative;
    z-index: 30;
}

.app-rail[data-collapsed="true"] .rail-nav {
    overflow: visible;
}

.app-rail[data-collapsed="true"] .rail-chevron {
    display: none;
}

.app-rail[data-collapsed="true"] .rail-group--has-active > .rail-group-head {
    background: var(--roth-navy-raised);
    box-shadow: inset 3px 0 0 var(--roth-red);
    color: #fff;
}

.app-rail[data-collapsed="true"] .rail-group[data-open="true"] > .rail-sub {
    background: var(--roth-navy-hover);
    border-radius: 0 var(--r-lg) var(--r-lg) 0;
    box-shadow: var(--shadow-pop);
    left: 100%;
    min-width: 220px;
    padding: var(--s1) 0;
    position: absolute;
    top: 0;
    z-index: 40;
}

.app-rail[data-collapsed="true"] .rail-sub .rail-link {
    height: 32px;
    padding: 0 14px;
}

.app-rail[data-collapsed="true"] .rail-sub .rail-label {
    display: block;
}

/* --- icon mode forced by width (mirrors the 900px step above) --- */
@media (max-width: 900px) {
    .app-rail {
        overflow: visible;
        position: relative;
        z-index: 30;
    }

    .app-rail .rail-nav {
        overflow: visible;
    }

    .app-rail .rail-chevron {
        display: none;
    }

    .app-rail .rail-group--has-active > .rail-group-head {
        background: var(--roth-navy-raised);
        box-shadow: inset 3px 0 0 var(--roth-red);
        color: #fff;
    }

    .app-rail .rail-group[data-open="true"] > .rail-sub {
        background: var(--roth-navy-hover);
        border-radius: 0 var(--r-lg) var(--r-lg) 0;
        box-shadow: var(--shadow-pop);
        left: 100%;
        min-width: 220px;
        padding: var(--s1) 0;
        position: absolute;
        top: 0;
        z-index: 40;
    }

    .app-rail .rail-sub .rail-link {
        height: 32px;
        padding: 0 14px;
    }

    .app-rail .rail-sub .rail-label {
        display: block;
    }
}

/* --- 640: the rail is a horizontal strip; flyouts drop below their icon --- */
@media (max-width: 640px) {
    .app-rail {
        flex-wrap: wrap;
        overflow: visible;
    }

    .rail-nav {
        flex-wrap: wrap;
    }

    .app-rail .rail-group[data-open="true"] > .rail-sub {
        border-radius: 0 0 var(--r-lg) var(--r-lg);
        left: 0;
        top: 100%;
    }
}

/* --------------------------------------------------------------------------
   46. SCREEN: Location Information reports
   Fifteen report pages behind one location. Each opens with the same location
   strip (.loc-facts), then panels built from sections 9, 11 and 15. What is
   left here is the three shapes those sections do not cover: the fact strip,
   a card grid for warranty-like records and the leak-history card.
   -------------------------------------------------------------------------- */
.loc-facts {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    margin: 0;
    overflow: hidden;
}

.loc-facts > div {
    border-right: 1px solid var(--border-hairline);
    min-width: 0;
    padding: var(--s2) var(--s3);
}

.loc-facts > div:last-child { border-right: 0; }

.loc-facts dt,
.loc-leak__facts dt {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.loc-facts dd,
.loc-leak__facts dd {
    color: var(--text);
    font-weight: 600;
    margin: 2px 0 0;
    overflow-wrap: anywhere;
}

/* a label + control that sits inside a .panel__head toolbar */
.loc-inline-field {
    align-items: center;
    color: var(--text-muted);
    display: inline-flex;
    font-size: var(--fs-sm);
    gap: var(--s2);
}

.loc-inline-field input { width: auto; }

.loc-card-grid {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

.loc-card {
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-left: 3px solid var(--roth-navy);
    border-radius: var(--r);
    display: grid;
    gap: var(--s2);
    min-width: 0;
    padding: var(--s3);
}

.loc-card h3 {
    font-size: var(--fs-md);
    margin: 0;
}

.loc-leak-list {
    display: grid;
    gap: var(--s3);
}

.loc-leak {
    border: 1px solid var(--border);
    border-radius: var(--r);
    cursor: pointer;
    display: grid;
    gap: var(--s3);
    padding: var(--s3);
}

.loc-leak:hover { border-color: var(--border-strong); background: var(--surface-alt); }

.loc-leak__facts {
    display: grid;
    gap: var(--s2) var(--s3);
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    margin: 0;
}

.loc-card__head {
    align-items: flex-start;
    display: flex;
    gap: var(--s2);
    justify-content: space-between;
}

.loc-card__head p { font-size: var(--fs-sm); margin: 2px 0 0; }

.loc-card > p { margin: 0; }

/* a stat strip that sits flush inside a panel, between its head and a grid */
.loc-stat-strip {
    border: 0;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
}

/* status chips emitted by the page models */
.warranty-status-active,
.condition-rating-green { background: var(--chip-green-bg); border-color: var(--chip-green-bd); color: var(--chip-green-fg); }
.warranty-status-expiring,
.condition-rating-amber { background: var(--chip-amber-bg); border-color: var(--chip-amber-bd); color: var(--chip-amber-fg); }
.warranty-status-expired,
.condition-rating-red { background: var(--chip-red-bg); border-color: var(--chip-red-bd); color: var(--chip-red-fg); }

/* file facts above a drawing or download: label over value, one row */
.loc-file-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s5);
    margin: 0;
}

.loc-file-meta dt {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.loc-file-meta dd { margin: 2px 0 0; overflow-wrap: anywhere; }

.loc-drawing {
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--r);
    display: block;
    height: auto;
    max-width: 100%;
}

.loc-pager {
    justify-content: flex-end;
    padding: var(--s2) var(--s4);
}

/* photo galleries: a thumbnail that opens the viewer, caption, select box */
.loc-photo-meta {
    color: var(--text-muted);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1) var(--s4);
    margin: 0;
}

.loc-photo-grid {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

.loc-photo {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    display: grid;
    margin: 0;
    min-width: 0;
    overflow: hidden;
}

.loc-photo__image {
    background: var(--surface-sunken);
    border: 0;
    cursor: zoom-in;
    display: block;
    padding: 0;
    width: 100%;
}

.loc-photo__image img {
    aspect-ratio: 4 / 3;
    display: block;
    object-fit: cover;
    width: 100%;
}

.loc-photo__image:focus-visible { box-shadow: var(--focus-ring); outline: 0; }

.loc-photo figcaption {
    display: grid;
    font-size: var(--fs-sm);
    gap: var(--s1);
    overflow-wrap: anywhere;
    padding: var(--s2);
}

.loc-photo .btn-link { justify-self: start; }

.loc-photo-detail {
    display: grid;
    gap: var(--s4);
    grid-template-columns: minmax(0, 2fr) minmax(220px, 1fr);
}

.loc-photo-detail img {
    border-radius: var(--r);
    display: block;
    height: auto;
    max-width: 100%;
}

@media (max-width: 720px) {
    .loc-photo-detail { grid-template-columns: 1fr; }
}

@media print {
    .no-print,
    .loc-photo .btn-link { display: none !important; }

    .loc-photo,
    .loc-card,
    .loc-leak { border-color: #999; break-inside: avoid; }
}

/* the in-page index of a long report (AIM, Facility Condition) */
.loc-index {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s4);
    padding: var(--s2) var(--s4);
}

.loc-index h2 { margin: 0; }

.loc-index ol {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1) var(--s4);
    list-style: none;
    margin: 0;
    padding: 0;
}

@media print {
    .loc-index { display: none !important; }
}

/* .empty-state is display:flex, which would otherwise beat the hidden attribute */
.empty-state[hidden] { display: none; }

.loc-upload {
    border-top: 1px solid var(--border);
    display: block;
    padding: var(--s3) var(--s4);
}

.loc-file-preview {
    display: block;
    margin-top: var(--s1);
    max-height: 120px;
    width: auto;
}

.loc-link-list {
    display: grid;
    gap: var(--s2);
    list-style: none;
    margin: 0;
}

/* --------------------------------------------------------------------------
   47. ROW EDITORS - one small form per record (Inspection Approval checklist,
   materials, labor and photo rows). Each row is a wrapping field grid.
   -------------------------------------------------------------------------- */
.edit-list {
    display: grid;
    gap: var(--s2);
}

.edit-row {
    align-items: end;
    border: 1px solid var(--border);
    border-radius: var(--r);
    display: grid;
    gap: var(--s2) var(--s3);
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    padding: var(--s3);
}

.edit-row.new-row {
    background: var(--surface-alt);
    border-style: dashed;
}

.edit-row .wide { grid-column: span 2; }

.field.compact { margin-bottom: 0; }

.checkbox-field {
    align-items: center;
    display: flex;
    flex-direction: row;
    gap: var(--s2);
    min-height: 30px;
}

@media (max-width: 640px) {
    .edit-row .wide { grid-column: 1 / -1; }
}

/* --------------------------------------------------------------------------
   48. SCREEN: RMS inspections and Roth AiR
   The field-tech pages keep their rms-* and reactive-* component classes:
   page scripts and the offline capture client select and toggle them (tabs
   flip .active, cards and canvases are found by id). This section restyles
   those components from section-1 tokens so they sit in the shell.
   -------------------------------------------------------------------------- */
.rms-mode-bar {
    align-items: center;
    background: var(--surface-alt);
    border-bottom: 1px solid var(--border);
    color: var(--text-muted);
    display: flex;
    flex: 0 0 auto;
    font-size: var(--fs-xs);
    font-weight: 700;
    gap: var(--s2);
    justify-content: flex-end;
    letter-spacing: .04em;
    padding: var(--s1) var(--s4);
    text-transform: uppercase;
}

.rms-mobile-shell .workspace {
    padding-left: calc(var(--s4) + env(safe-area-inset-left, 0px));
    padding-right: calc(var(--s4) + env(safe-area-inset-right, 0px));
}

td.empty-state,
th.empty-state { display: table-cell; padding: var(--s5) var(--s4); }

/* dashboards */
.rms-dashboard-shell { display: grid; gap: var(--s4); }

.rms-kpi-grid {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.rms-kpi-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    gap: var(--s2);
    padding: var(--s3) var(--s4);
}

.rms-kpi-topline {
    align-items: center;
    display: grid;
    gap: var(--s3);
    grid-template-columns: 40px 1fr;
}

.rms-kpi-topline p,
.rms-kpi-progress-text {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .04em;
    margin: 0;
    text-transform: uppercase;
}

.rms-kpi-topline strong {
    color: var(--text);
    display: block;
    font-size: 26px;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
    margin-top: 2px;
}

.rms-kpi-icon {
    align-items: center;
    border-radius: var(--r-lg);
    color: #fff;
    display: inline-flex;
    font-size: 20px;
    font-weight: 700;
    height: 40px;
    justify-content: center;
    width: 40px;
}

.rms-kpi-icon-completed { background: #2e7d32; }
.rms-kpi-icon-submitted { background: var(--roth-navy); }
.rms-kpi-icon-rejected { background: var(--roth-red); }

.rms-kpi-progress {
    background: var(--surface-sunken);
    border-radius: var(--r-pill);
    height: 6px;
    overflow: hidden;
}

.rms-kpi-progress span { background: var(--roth-navy); display: block; height: 100%; }

.rms-home-panel { padding: 0; }

.rms-home-heading {
    border-bottom: 1px solid var(--border);
    padding: var(--s3) var(--s4);
}

.rms-home-heading h1 { font-size: var(--fs-lg); margin: 0; }

.rms-home-heading-blue {
    background: var(--roth-navy);
    border-bottom: 0;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
}

.rms-home-heading-blue h1 { color: #fff; }

.rms-table-controls {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s4);
    justify-content: space-between;
    padding: var(--s3) var(--s4);
}

.rms-table-controls label {
    align-items: center;
    color: var(--text-muted);
    display: inline-flex;
    font-size: var(--fs-sm);
    gap: var(--s2);
}

.rms-table-controls input[type="text"] { min-width: 220px; width: auto; }

.rms-table-wrap {
    border-top: 1px solid var(--border);
    overflow-x: auto;
}

.rms-dashboard-table,
.rms-table,
.rms-table-wrap table {
    background: var(--surface);
    width: 100%;
}

.rms-dashboard-table { min-width: 980px; }

.rms-dashboard-table th a { color: var(--text); font-weight: 700; }

.rms-dashboard-row { cursor: pointer; }

.rms-dashboard-row:hover,
.rms-dashboard-row:focus { background: var(--surface-alt); outline: none; }

.rms-row-link { color: var(--link); }

.rms-row-actions { display: flex; flex-wrap: wrap; gap: var(--s1); white-space: nowrap; }

.rms-inline-form { display: inline; margin: 0; }

.rms-status-pill,
.status-pill {
    background: var(--chip-gray-bg);
    border: 1px solid var(--chip-gray-bd);
    border-radius: var(--r-pill);
    color: var(--chip-gray-fg);
    display: inline-block;
    font-size: var(--fs-xs);
    font-weight: 700;
    padding: 2px 8px;
    white-space: nowrap;
}

.rms-status-assigned { background: var(--chip-blue-bg); border-color: var(--chip-blue-bd); color: var(--chip-blue-fg); }
.rms-status-inprogress { background: var(--chip-amber-bg); border-color: var(--chip-amber-bd); color: var(--chip-amber-fg); }
.rms-status-rejected { background: var(--chip-red-bg); border-color: var(--chip-red-bd); color: var(--chip-red-fg); }
.rms-status-completed { background: var(--chip-green-bg); border-color: var(--chip-green-bd); color: var(--chip-green-fg); }

.rms-table-summary {
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    font-size: var(--fs-sm);
    margin: 0;
    padding: var(--s2) var(--s4);
}

.rms-roth-filter {
    align-items: end;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s3);
    margin-bottom: var(--s3);
    padding: var(--s3);
}

.rms-roth-filter-mode {
    border: 0;
    display: flex;
    flex: 0 1 auto;
    flex-wrap: wrap;
    gap: var(--s2) var(--s3);
    padding: 0;
}

.rms-roth-filter > label {
    color: var(--text-muted);
    display: grid;
    flex: 1 1 240px;
    font-size: var(--fs-xs);
    font-weight: 700;
    gap: var(--s1);
    letter-spacing: .03em;
    min-width: 0;
    text-transform: uppercase;
}

.rms-roth-filter > button { flex: 0 0 auto; }

/* the legacy detail layout: main column + rail, tabs, status header */
.reactive-detail-layout {
    align-items: flex-start;
    display: grid;
    gap: var(--s4);
    grid-template-columns: minmax(0, 1fr) 220px;
}

.rms-detail .reactive-detail-layout { grid-template-columns: 280px minmax(0, 1fr); }

.reactive-detail-main { min-width: 0; }

.reactive-detail-rail,
.rms-detail-rail {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    color: var(--text);
    font-size: var(--fs-base);
    min-width: 0;
    overflow: hidden;
    position: sticky;
    top: 0;
}

.reactive-rail-badge {
    background: var(--roth-navy);
    color: #fff;
    font-weight: 700;
    padding: var(--s2) var(--s3);
}

.reactive-rail-location {
    background: var(--roth-red);
    color: #fff;
    font-weight: 700;
    overflow-wrap: anywhere;
    padding: var(--s2) var(--s3);
}

.reactive-rail-section {
    border-top: 1px solid var(--border);
    padding: var(--s2) var(--s3);
}

.reactive-rail-address { display: grid; font-style: normal; gap: 2px; margin: 0; }

.rms-rail-list,
.rms-detail-grid {
    display: grid;
    gap: var(--s2) var(--s3);
    margin: 0;
}

.rms-rail-list { padding: var(--s3); }

.rms-detail-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* a grid of fields (Roth AiR detail) spaces them with gap, like .field-row: a field's own bottom
   margin pushed an end-aligned button cell 12px below the boxes; the grid keeps the space below it */
.rms-detail-grid:has(> .field) { margin-bottom: var(--s3); }
.rms-detail-grid > .field { margin-bottom: 0; }

.rms-rail-list div,
.rms-detail-grid div { min-width: 0; }

.rms-rail-list dt,
.rms-detail-grid dt,
.rms-detail-header-grid span,
.rms-area-block header span {
    color: var(--text-muted);
    display: block;
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.rms-rail-list dd,
.rms-detail-grid dd { margin: 2px 0 0; overflow-wrap: anywhere; }

.rms-detail-header-grid {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(3, minmax(0, 1fr));
    margin-bottom: var(--s3);
    padding: var(--s3) var(--s4);
}

.rms-detail-header-grid strong { display: block; margin-top: 2px; overflow-wrap: anywhere; }

.rms-action-panel {
    border-top: 1px solid var(--border);
    display: grid;
    gap: var(--s2);
    padding: var(--s3);
}

.rms-action-panel h2 { font-size: var(--fs-md); margin: 0; }
.rms-action-panel form { display: grid; gap: var(--s2); margin: 0; }
.rms-action-panel .btn { width: 100%; }

.rms-detail-actions,
.reactive-action-row {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
    margin: var(--s2) 0;
}

.rms-detail-form,
.reactive-detail-form { display: grid; gap: var(--s3); }

.reactive-detail .field input,
.reactive-detail .field select,
.reactive-detail .field textarea { max-width: 100%; min-width: 0; width: 100%; }

.reactive-status-header {
    align-items: center;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    color: var(--text);
    display: flex;
    gap: var(--s3);
    justify-content: space-between;
    margin-bottom: var(--s3);
    padding: var(--s2) var(--s4);
}

.reactive-status-header-id { font-size: var(--fs-lg); font-weight: 700; margin-right: var(--s3); }
.reactive-status-header-state { font-size: var(--fs-md); font-weight: 700; letter-spacing: .04em; }

.reactive-tabs,
.reactive-settings-tabs {
    border-bottom: 1px solid var(--border);
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    list-style: none;
    margin: 0;
    padding: 0;
}

.reactive-settings-tabs { margin-bottom: var(--s3); }

.reactive-tab,
.reactive-settings-tab {
    background: transparent;
    border: 0;
    border-bottom: 2px solid transparent;
    color: var(--text-muted);
    cursor: pointer;
    display: block;
    font: inherit;
    font-weight: 700;
    margin-bottom: -1px;
    padding: var(--s2) var(--s3);
}

.reactive-tab:hover,
.reactive-settings-tab:hover { color: var(--text); }

.reactive-tab.active,
.reactive-settings-tab.active {
    border-bottom-color: var(--roth-red);
    color: var(--text);
}

.reactive-tab:disabled { cursor: not-allowed; opacity: .55; }

.reactive-tab-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 0 0 var(--r-lg) var(--r-lg);
    border-top: 0;
    padding: var(--s4);
}

.reactive-tab-panel[hidden] { display: none; }

.rms-detail-nav { margin-bottom: var(--s3); }
.rms-detail-nav .reactive-tab { text-decoration: none; }
.rms-detail-nav .reactive-tab:hover { text-decoration: none; }

.rms-detail > .reactive-tab-panel {
    border-radius: var(--r-lg);
    border-top: 1px solid var(--border);
    margin-bottom: var(--s3);
}

.rms-detail > #rmsOfflineLeakWorkspace { padding: 0; }

#rmsOfflineLeakWorkspace > summary {
    cursor: pointer;
    font-weight: 700;
    padding: var(--s3) var(--s4);
}

#rmsOfflineLeakWorkspace > summary:hover { background: var(--surface-sunken); }

.rms-offline-body {
    display: grid;
    gap: var(--s2);
    padding: 0 var(--s4) var(--s4);
}

.rms-offline-body > .rms-offline-actions { margin: 0; }

/* roof areas, checklists, photos */
.rms-area-list { display: grid; gap: var(--s3); }

.rms-area-block {
    border: 1px solid var(--border);
    border-radius: var(--r);
    padding: var(--s3);
}

.rms-area-block header {
    align-items: center;
    display: flex;
    gap: var(--s3);
    justify-content: space-between;
    margin-bottom: var(--s2);
}

.rms-area-block h2 { font-size: var(--fs-md); margin: 0; }

.rms-checkbox { align-items: center; display: flex; gap: var(--s2); }
.rms-checkbox input { width: auto; }

.rms-photo-strip,
.rms-photo-grid {
    display: grid;
    gap: var(--s3);
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    margin-top: var(--s3);
}

.rms-photo-strip figure,
.rms-photo-grid figure {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    margin: 0;
    min-width: 0;
    overflow: hidden;
}

.rms-photo-strip img,
.rms-photo-grid img {
    aspect-ratio: 4 / 3;
    display: block;
    object-fit: cover;
    width: 100%;
}

.rms-photo-strip figcaption,
.rms-photo-grid figcaption {
    display: grid;
    font-size: var(--fs-sm);
    gap: 2px;
    padding: var(--s2);
}

.rms-photo-grid figcaption span { color: var(--text-muted); }

.rms-reject-form textarea { width: 100%; }

.costing-section h2 { font-size: var(--fs-md); margin: 0 0 var(--s2); }
.costing-total { font-variant-numeric: tabular-nums; font-weight: 700; text-align: right; white-space: nowrap; }

/* legacy overlay modals kept by their page scripts (hidden-attribute driven) */
.rms-modal,
.modal-backdrop {
    background: var(--overlay);
    inset: 0;
    position: fixed;
    z-index: 1000;
}

.rms-modal { align-items: center; display: flex; justify-content: center; }

.rms-modal[hidden],
.modal-backdrop[hidden],
.modal-roofing[hidden] { display: none; }

.rms-modal-content,
.modal-roofing .modal-dialog {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
    color: var(--text);
    max-height: 90vh;
    max-width: 560px;
    overflow: auto;
    width: 92vw;
}

.rms-modal-content { padding: var(--s4); }
.rms-modal-content h3 { margin-top: 0; }

.modal-roofing {
    align-items: center;
    display: flex;
    inset: 0;
    justify-content: center;
    pointer-events: none;
    position: fixed;
    z-index: 1001;
}

.modal-roofing .modal-dialog { pointer-events: auto; }
.modal-roofing.modal-lg .modal-dialog,
.modal-roofing.reactive-modal-wide .modal-dialog { max-width: 1040px; }

.modal-roofing .modal-header {
    align-items: center;
    background: var(--roth-navy);
    color: #fff;
    display: flex;
    flex-direction: row-reverse;
    justify-content: space-between;
    padding: var(--s3) var(--s4);
}

.modal-roofing .modal-header h3 { color: #fff; font-size: var(--fs-md); margin: 0; }

.modal-roofing .modal-close {
    background: transparent;
    border: 0;
    color: #fff;
    cursor: pointer;
    font-size: 20px;
    line-height: 1;
}

.modal-roofing .modal-body { padding: var(--s4); }

.modal-roofing .modal-footer {
    border-top: 1px solid var(--border);
    display: flex;
    gap: var(--s2);
    justify-content: flex-end;
    padding: var(--s3) var(--s4);
}

@media (max-width: 900px) {
    .rms-kpi-grid,
    .rms-detail .reactive-detail-layout,
    .reactive-detail-layout,
    .rms-detail-header-grid,
    .rms-detail-grid { grid-template-columns: 1fr; }

    .rms-table-controls { align-items: flex-start; flex-direction: column; }

    .reactive-detail-rail,
    .rms-detail-rail { position: static; }
}

@media (max-width: 992px) {
    .rms-mobile-shell .btn { min-height: var(--rms-field-action-height, 2.75rem); }
}

/* RMS developer inventory pages (Index, DetailParity, WriteWorkflows) */
.rms-inventory { display: grid; gap: var(--s3); padding: var(--s4); }
.rms-inventory h1, .rms-inventory h2, .rms-inventory p { margin: 0; }
.rms-inventory-nav a { border: 1px solid var(--border); border-radius: var(--r); padding: var(--s1) var(--s3); }

.rms-detail-checklist { display: grid; gap: var(--s1); margin: 0; padding-left: var(--s4); }
.proposal-readonly { border: 1px solid var(--border); border-radius: var(--r); display: grid; gap: var(--s2); padding: var(--s3); }

/* --------------------------------------------------------------------------
   49. SCREEN: PM Dashboard (RoofingApproval)
   The approved RoofingApproval dashboard design: task groups as panels of
   icon rows with a trailing chevron. Tasks with no web page yet stay listed,
   disabled, with a "Not yet available" chip.
   -------------------------------------------------------------------------- */
.pm-dashboard {
    display: grid;
    gap: var(--s4);
    max-width: 760px;
}

.pm-dashboard .panel__head .eyebrow { margin: 0; }

.pm-task-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.pm-task-list li + li { border-top: 1px solid var(--border-hairline); }

.pm-task {
    align-items: center;
    color: var(--text);
    display: flex;
    gap: var(--s3);
    min-height: 42px;
    padding: var(--s2) var(--s4);
    text-decoration: none;
}

a.pm-task:hover { background: var(--surface-alt); color: var(--text); }

a.pm-task:focus-visible { box-shadow: inset var(--focus-ring); outline: 0; }

.pm-task__label { flex: 1 1 auto; font-weight: 700; min-width: 0; }

.pm-task__chevron { color: var(--text-faint); font-size: var(--fs-lg); }

.pm-task--disabled { color: var(--text-muted); cursor: default; }
.pm-task--disabled .pm-task__label { font-weight: 400; }
.pm-task--disabled .pm-task__icon { opacity: .55; }

.pm-task__icon {
    align-items: center;
    border: 1px solid;
    border-radius: var(--r);
    display: inline-flex;
    flex: 0 0 28px;
    height: 28px;
    justify-content: center;
}

.pm-task__icon--red { background: var(--chip-red-bg); border-color: var(--chip-red-bd); color: var(--chip-red-fg); }
.pm-task__icon--green { background: var(--chip-green-bg); border-color: var(--chip-green-bd); color: var(--chip-green-fg); }
.pm-task__icon--amber { background: var(--chip-amber-bg); border-color: var(--chip-amber-bd); color: var(--chip-amber-fg); }
.pm-task__icon--blue { background: var(--chip-blue-bg); border-color: var(--chip-blue-bd); color: var(--chip-blue-fg); }
.pm-task__icon--navy { background: var(--roth-navy); border-color: var(--roth-navy); color: #fff; }

.eyebrow a { color: inherit; }
.eyebrow a:hover { color: var(--link); }

/* disabled design tabs (listed, not yet built) and stacked tab panes */
.tabs__tab.is-disabled { cursor: default; opacity: .45; }
.tabs__tab.is-disabled:hover { color: var(--text-muted); }
.tabpane.stack.is-active { display: grid; }
/* .stack's display:grid comes after .tabpane's display:none, so an inactive stacked pane stayed on
   screen (Subcontractor Rates showed under every later tab) */
.tabpane.stack:not(.is-active) { display: none; }


/* --------------------------------------------------------------------------
   PM Dashboard Utilities (/PmDashboard/Utilities/*)
   The design's Utilities drawer and its modals, laid out as pages: a catalog,
   list + detail editors, check-box grids, merge diffs and a done state.
   -------------------------------------------------------------------------- */
.utility-catalog .pm-task__label { display: grid; gap: 2px; }
.utility-catalog__name { font-weight: 700; }
.utility-catalog__desc { color: var(--text-muted); font-size: var(--fs-sm); font-weight: 400; }
.pm-task--disabled .utility-catalog__name { font-weight: 400; }

/* list on the left, detail on the right; stacks at 900 */
.utility-split {
    align-items: start;
    display: grid;
    gap: var(--s4);
    grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
}

.utility-list {
    border: 1px solid var(--border);
    border-radius: var(--r);
    list-style: none;
    margin: 0;
    max-height: 520px;
    overflow: auto;
    padding: 0;
}

.utility-list li + li { border-top: 1px solid var(--border-hairline); }

.utility-list a,
.utility-list button {
    background: none;
    border: 0;
    color: var(--text);
    display: block;
    font: inherit;
    padding: var(--s2) var(--s3);
    text-align: left;
    text-decoration: none;
    width: 100%;
}

.utility-list a:hover,
.utility-list button:hover { background: var(--surface-alt); }

.utility-list .is-selected,
.utility-list [aria-current="true"] { background: var(--surface-sunken); font-weight: 700; }

/* check-box grids (checklist solutions, report options, field picks) */
.utility-checks {
    display: grid;
    gap: var(--s2) var(--s4);
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}

.utility-checks label {
    align-items: center;
    display: flex;
    gap: var(--s2);
}

/* side-by-side merge preview: identical fields are muted, differing ones marked */
.utility-diff td.utility-diff__field { font-weight: 700; white-space: nowrap; }
.utility-diff tr.utility-diff__same td { color: var(--text-muted); }
.utility-diff tr.utility-diff__changed td { background: var(--chip-amber-bg); }
.utility-diff label { align-items: flex-start; display: flex; gap: var(--s2); }

/* compact inputs inside a grid row */
.grid .utility-cell-input { max-width: 9rem; min-width: 5rem; }
.grid .utility-cell-input--wide { max-width: 18rem; min-width: 10rem; }

/* key facts above a form: label over value, wrapping */
.utility-facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s5);
    margin: 0;
}

.utility-facts div { display: grid; gap: 2px; }
.utility-facts dt { color: var(--text-muted); font-size: var(--fs-sm); }
.utility-facts dd { font-weight: 700; margin: 0; }

@media (max-width: 900px) {
    .utility-split { grid-template-columns: minmax(0, 1fr); }
    .utility-list { max-height: 280px; }
}
/* --------------------------------------------------------------------------
   50. SCREEN: PM Dashboard timesheets (RoofingApproval Approve Timesheets,
       View Approved Timesheets and the Timesheet editor). Page-specific only;
       everything else is the shared panel / grid / stat-strip / modal kit.
   -------------------------------------------------------------------------- */
.ts-toolbar__tech { flex: 1 1 280px; max-width: 380px; }

/* the columns need ~640px; 780 made a sideways scrollbar in a laptop-width card */
.ts-list { min-width: 640px; }
.ts-list tbody tr[data-ts-open] { cursor: pointer; }

.ts-header {
    background: var(--bg);
    flex-wrap: wrap;
    padding: var(--s2) 0;
    position: sticky;
    top: 0;
    z-index: 5;
}

.ts-header h1 { align-items: center; display: flex; flex-wrap: wrap; gap: var(--s2); }
.ts-header__tech { font-weight: 400; }

.ts-stamp {
    border: 2px solid var(--chip-green-bd);
    border-radius: var(--r);
    color: var(--chip-green-fg);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .12em;
    padding: 2px var(--s2);
    text-transform: uppercase;
    transform: rotate(-4deg);
}

.ts-summary { max-width: 760px; }
.ts-over-max .stat-cell__value { color: var(--chip-red-fg); }

.ts-grid { min-width: 1150px; }
.ts-grid td { vertical-align: middle; }
.ts-grid .ts-day { width: 56px; }

/* The day columns (the hours) sit to the right of six reference columns, past the edge of a laptop
   card; the job name stays pinned while the grid scrolls to them, and is wide enough not to wrap to
   four lines. Striped and hovered rows keep their colour (those rules out-rank this background). */
.ts-grid th:first-child,
.ts-grid td:first-child {
    background: var(--surface);
    left: 0;
    min-width: 240px;
    position: sticky;
    z-index: 1;
}

.ts-grid thead th:first-child { z-index: 3; }
.ts-grid td:first-child { box-shadow: 1px 0 0 var(--border); }
.ts-grid tfoot td { border-top: 1px solid var(--border); padding: var(--cell-pad-y) var(--cell-pad-x); }

.ts-input { min-height: var(--control-h); padding: 0 6px; }
.ts-input--name { min-width: 180px; }
.ts-input--type { min-width: 150px; }
.ts-input--money { text-align: right; width: 96px; }
.ts-input--code { width: 104px; }
.ts-input--hour { padding: 0 4px; text-align: center; width: 48px; }
.ts-input--invalid { border-color: var(--chip-red-bd); box-shadow: 0 0 0 1px var(--chip-red-bd); }

.grid tbody tr.ts-row--error td { background: var(--chip-red-bg); }
.grid tbody tr.ts-row--warn td { background: var(--chip-amber-bg); }

.ts-picker-cell { align-items: center; display: inline-flex; gap: var(--s1); }
.ts-picker-cell--unreconciled .btn { border-color: var(--chip-amber-bd); background: var(--chip-amber-bg); }

.ts-add { display: block; }
.ts-add .toolbar { align-items: flex-end; margin-bottom: var(--s2); }
.ts-add__name { flex: 1 1 200px; max-width: 280px; }
.ts-add__money,
.ts-add__code { width: 130px; }

.ts-expenses {
    align-items: start;
    display: grid;
    gap: var(--s4);
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}

.ts-expenses .panel__head { align-items: baseline; display: flex; flex-wrap: wrap; gap: var(--s2); }
.ts-expense-list { display: grid; gap: var(--s3); }

.ts-expense-row {
    align-items: center;
    display: flex;
    gap: var(--s2);
    justify-content: space-between;
}

.ts-expense-row > span:first-child { color: var(--text-muted); font-weight: 700; }
.ts-expense-row > input { text-align: right; width: 110px; }
.ts-expense-row--wrap { flex-wrap: wrap; }
.ts-inline { align-items: center; display: inline-flex; flex-wrap: wrap; gap: var(--s1); }
.ts-inline input { text-align: right; width: 72px; }
.ts-expense-total { border-top: 1px solid var(--border-hairline); padding-top: var(--s2); }
.ts-expense-total strong { font-size: var(--fs-lg); }
/* --------------------------------------------------------------------------
   PM Dashboard - Add/Edit Subcontractors (/PmDashboard/Subcontractors)
   The legacy RoofingWizard.frmSubcontractor tabs on the shell's tabs, panels,
   field rows and grids. Only the shapes those do not cover live here.
   -------------------------------------------------------------------------- */
.sub-fieldset { border: 0; margin: 0; min-width: 0; padding: 0; }

.sub-columns {
    display: grid;
    gap: var(--s4);
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

.sub-group {
    border: 1px solid var(--border);
    border-radius: var(--r);
    margin: 0 0 var(--s3);
    min-width: 0;
    padding: var(--s2) var(--s3) var(--s3);
}

.sub-group > legend {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    font-weight: 700;
    padding: 0 var(--s1);
}

.sub-choices {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s4);
    margin-bottom: var(--s3);
}

.sub-checklist {
    display: grid;
    gap: var(--s2) var(--s4);
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}

.sub-state-grid { max-height: 60vh; }

.sub-rate-grid input { max-width: 110px; text-align: right; }

.sub-zip-list { font-family: var(--font-mono, ui-monospace, monospace); max-width: 320px; }

.sub-logo { display: grid; gap: var(--s2); justify-items: start; }

.sub-logo__image {
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--r);
    max-height: 120px;
    max-width: 240px;
    object-fit: contain;
    padding: var(--s1);
}

.sub-note { white-space: pre-wrap; }

.sub-login-edit form { min-width: 320px; padding-top: var(--s2); }

.sub-add .field-row { align-items: end; margin-bottom: 0; }

/* --------------------------------------------------------------------------
   Work Orders search - /WorkOrders/Reactive, /ReactiveUsps, /Proactive,
   /SuperSearch (legacy ServiceWeb WorkOrderSelection.ascx). Filters use the
   shell toolbar/field/disclosure pieces; only the stacked result cells the
   legacy grid showed need their own shapes.
   -------------------------------------------------------------------------- */
.wo-search__filters { align-items: end; flex-wrap: wrap; }

.wo-search__customer { min-width: 280px; }

.wo-search__more .field-row,
.wo-search__sort .field-row { margin-bottom: 0; }

.wo-search__more .field-row + .field-row { margin-top: var(--s3); }

.wo-search-grid td { vertical-align: top; }

.wo-search-grid__work { min-width: 280px; }

.wo-search-grid__title { display: inline-block; font-weight: 700; margin-bottom: var(--s1); }

.wo-search-grid__note {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    margin: var(--s1) 0 0;
}

.wo-search-grid__status { min-width: 180px; }

.wo-search-grid__status p,
.wo-search-grid__description p { margin: var(--s1) 0 0; }

.wo-search-grid__description { min-width: 240px; white-space: pre-line; }

.wo-search-grid__secondary-id {
    color: var(--text-muted);
    display: block;
    margin-top: var(--s2);
}

.wo-search-facts {
    column-gap: var(--s2);
    display: grid;
    font-size: var(--fs-sm);
    grid-template-columns: max-content minmax(0, 1fr);
    margin: var(--s1) 0 0;
    row-gap: 2px;
}

.wo-search-facts dt { color: var(--text-muted); font-weight: 400; }

.wo-search-facts dd { margin: 0; overflow-wrap: anywhere; }

.wo-search-facts__gap { padding-top: var(--s2); }
   Proactive work order detail (/WorkOrders/Proactive/{id}) - the USPS e-work
   order / PM Upgrades screen (legacy UserRoth/RoofingWorkOrder/WorkOrderDetail).
   Reuses the reactive detail's rail, status header, tabs, grids and dialogs;
   only the shapes those do not cover live here.
   -------------------------------------------------------------------------- */
.proactive-wo { display: grid; gap: var(--s3); }

.proactive-wo__id { color: var(--text-muted); font-family: var(--font-mono); }

.proactive-wo__rail .btn--sm { justify-self: start; margin-top: var(--s1); }

.proactive-wo .wo-main { display: grid; gap: var(--s3); align-content: start; }

.proactive-wo__summary,
.proactive-wo__workflow { padding: var(--s3) var(--s4); }

.proactive-wo__facts { display: grid; gap: var(--s2); margin: 0; }
.proactive-wo__facts dt,
.proactive-wo__label {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    font-weight: 700;
    margin: 0;
}
.proactive-wo__facts dd { margin: 2px 0 0; }

.proactive-wo__text { overflow-wrap: anywhere; white-space: pre-line; }

.proactive-wo__status-strip {
    display: grid;
    gap: var(--s2);
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    margin-top: var(--s2);
}

.proactive-wo__status-strip > div {
    border-left: 3px solid var(--border);
    display: grid;
    gap: 2px;
    padding-left: var(--s2);
}

.proactive-wo__status-strip span { color: var(--text-muted); font-size: var(--fs-xs); text-transform: uppercase; }

.proactive-wo .tabs { margin-bottom: 0; }
.proactive-wo .tabpane.is-active { padding-top: var(--s2); }

.proactive-wo__dates {
    display: grid;
    gap: var(--s4);
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    max-width: 720px;
}

.proactive-wo__date-col .field { gap: 2px; }
.proactive-wo__date-col input[type="date"] { max-width: 180px; }

.proactive-wo__bid-actions { margin-bottom: var(--s2); }
.proactive-wo__bid-file { margin-top: var(--s1); }
.proactive-wo__bid-file input[type="file"] { max-width: 100%; }
.proactive-wo__amount { max-width: 120px; text-align: right; }

.proactive-wo__subhead { font-size: var(--fs-md); margin: var(--s4) 0 var(--s2); }

.proactive-wo__nearby { max-height: 320px; overflow: auto; }

.proactive-wo__empty { color: var(--text-muted); padding: var(--s4); text-align: center; }

.proactive-wo__upload { align-items: center; flex-wrap: wrap; gap: var(--s2); margin-bottom: var(--s3); }

.proactive-wo__muted { color: var(--text-muted); font-size: var(--fs-sm); }

.proactive-wo dialog .field textarea,
.proactive-wo dialog .field input,
.proactive-wo dialog .field select { width: 100%; }
   Uncoded e-work orders
   /WorkOrders/Uncoded - USPS eFMS review list and its coding panel.
   -------------------------------------------------------------------------- */
.uncoded-wizard { margin-bottom: var(--s3); }

.uncoded-wizard .panel__body { display: grid; gap: var(--s3); padding: var(--s3) var(--s4); }

.uncoded-steps {
    display: grid;
    gap: var(--s3);
    list-style: none;
    margin: 0;
    padding: 0;
}

.uncoded-step {
    border-left: 3px solid var(--border);
    display: grid;
    gap: var(--s2);
    padding-left: var(--s3);
}

.uncoded-step .field select,
.uncoded-step .field input[type="text"] { max-width: 420px; }

.uncoded-areas { border: 0; margin: 0; min-width: 0; padding: 0; }

.uncoded-areas legend { margin-bottom: var(--s2); padding: 0; }

.uncoded-grid td { vertical-align: top; }

.uncoded-grid td:first-child { min-width: 320px; }

.uncoded-contact { margin-bottom: var(--s2); }

.uncoded-problem {
    border-top: 1px solid var(--border);
    margin: 0;
    padding-top: var(--s2);
    white-space: pre-wrap;
}

.uncoded-begin { display: grid; gap: var(--s2); justify-items: start; min-width: 220px; }

.uncoded-pager { justify-content: flex-end; padding: var(--s2) var(--s3); }

.uncoded-sr {
    clip: rect(0 0 0 0);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}

/* --------------------------------------------------------------------------
   Work order admin and invoices (/WorkOrders/Admin WO Team Admin,
   /WorkOrders/Invoices submitted invoices, /WorkOrders/InvoiceDisputes).
   Page-specific shapes only; tabs, grids, dialogs and notices are the kit's.
   -------------------------------------------------------------------------- */
.wo-admin-grid td { vertical-align: top; }
.wo-admin-grid .btn-link { font-weight: 700; text-align: left; }

.wo-admin-customers {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    list-style: none;
    margin: var(--s1) 0 0;
    padding: 0;
}

.wo-admin-fieldset { border: 0; margin: 0 0 var(--s3); min-width: 0; padding: 0; }

.wo-admin-choices {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s4);
}

.wo-admin-columns {
    display: grid;
    gap: var(--s4);
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

.wo-admin-columns select[multiple] { min-height: 220px; width: 100%; }

.wo-admin-help { margin-top: var(--s3); }
.wo-admin-help ol { margin: var(--s1) 0; padding-left: var(--s5); }
.wo-admin-hint { font-size: var(--fs-sm); margin: var(--s2) 0 0; }

[data-wo-primary][hidden] { display: none; }

.wo-invoice-grid { min-width: 1100px; }
.wo-invoice-comments { max-width: 280px; white-space: pre-wrap; }

.wo-pager {
    align-items: center;
    display: flex;
    gap: var(--s3);
    justify-content: flex-end;
}

.wo-dispute-list { list-style: none; margin: 0; padding: 0; }

.wo-dispute {
    border-bottom: 1px solid var(--border);
    display: grid;
    gap: var(--s3) var(--s5);
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    padding: var(--s3) var(--s4);
}

.wo-dispute:last-child { border-bottom: 0; }
.wo-dispute__main strong { display: inline-block; margin-left: var(--s1); }
.wo-dispute__detail { margin: var(--s1) 0; padding: var(--s1); white-space: pre-wrap; }

.wo-dispute__location {
    border-left: 2px solid var(--border);
    margin-bottom: var(--s1);
    padding-left: var(--s2);
}

@media (max-width: 640px) {
    .wo-dispute { grid-template-columns: minmax(0, 1fr); padding: var(--s3); }
}

/* --------------------------------------------------------------------------
   Work-type tabs and accents. A page that holds more than one kind of work
   (inspections and roof leaks, timesheets and work orders ...) gives each kind
   a tab (Pages/Shared/_WorkTypeTabs.cshtml, js/work-type-tabs.js) and an
   accent that tints its tab, KPI cards, progress bars and panel heading. The
   same kind of work keeps the same accent everywhere (WorkTypeAccents). The
   .tabs bar uses the same active treatment, navy unless a tab or an
   enclosing pane sets an accent.
   -------------------------------------------------------------------------- */
:root {
    --accent-navy: #223085;  --accent-navy-soft: rgba(34, 48, 133, .10);
    --accent-teal: #0b7a75;  --accent-teal-soft: rgba(11, 122, 117, .12);
    --accent-amber: #9a5b00; --accent-amber-soft: rgba(196, 122, 0, .13);
    --accent-violet: #6b3fa0; --accent-violet-soft: rgba(107, 63, 160, .11);
    --accent-green: #2e7d32; --accent-green-soft: rgba(46, 125, 50, .11);
    --accent-rose: #b0305c;  --accent-rose-soft: rgba(176, 48, 92, .10);
    --accent-slate: #4a5568; --accent-slate-soft: rgba(74, 85, 104, .10);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --accent-navy: #8c9bf2;  --accent-navy-soft: rgba(140, 155, 242, .16);
        --accent-teal: #3cc4bd;  --accent-teal-soft: rgba(60, 196, 189, .16);
        --accent-amber: #f0b44c; --accent-amber-soft: rgba(240, 180, 76, .16);
        --accent-violet: #b99af0; --accent-violet-soft: rgba(185, 154, 240, .16);
        --accent-green: #6fcf76; --accent-green-soft: rgba(111, 207, 118, .15);
        --accent-rose: #f07fa6;  --accent-rose-soft: rgba(240, 127, 166, .15);
        --accent-slate: #a7b1c2; --accent-slate-soft: rgba(167, 177, 194, .14);
        --on-accent: #10151c;
    }
}

:root[data-theme="dark"] {
    --accent-navy: #8c9bf2;  --accent-navy-soft: rgba(140, 155, 242, .16);
    --accent-teal: #3cc4bd;  --accent-teal-soft: rgba(60, 196, 189, .16);
    --accent-amber: #f0b44c; --accent-amber-soft: rgba(240, 180, 76, .16);
    --accent-violet: #b99af0; --accent-violet-soft: rgba(185, 154, 240, .16);
    --accent-green: #6fcf76; --accent-green-soft: rgba(111, 207, 118, .15);
    --accent-rose: #f07fa6;  --accent-rose-soft: rgba(240, 127, 166, .15);
    --accent-slate: #a7b1c2; --accent-slate-soft: rgba(167, 177, 194, .14);
    --on-accent: #10151c;
}

:root { --wt: var(--accent-navy); --wt-soft: var(--accent-navy-soft); }

.accent--navy { --wt: var(--accent-navy); --wt-soft: var(--accent-navy-soft); }
.accent--teal { --wt: var(--accent-teal); --wt-soft: var(--accent-teal-soft); }
.accent--amber { --wt: var(--accent-amber); --wt-soft: var(--accent-amber-soft); }
.accent--violet { --wt: var(--accent-violet); --wt-soft: var(--accent-violet-soft); }
.accent--green { --wt: var(--accent-green); --wt-soft: var(--accent-green-soft); }
.accent--rose { --wt: var(--accent-rose); --wt-soft: var(--accent-rose-soft); }
.accent--slate { --wt: var(--accent-slate); --wt-soft: var(--accent-slate-soft); }

.wt-tabs {
    border-bottom: 1px solid var(--border);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
}

.wt-tab {
    align-items: center;
    background: none;
    border: 0;
    border-bottom: 3px solid transparent;
    color: var(--text-muted);
    cursor: pointer;
    display: inline-flex;
    font-family: inherit;
    font-size: var(--fs-base);
    font-weight: 700;
    gap: var(--s2);
    margin-bottom: -1px;
    padding: var(--s2) var(--s4);
}

.wt-tab:hover { color: var(--text); }

.wt-tab__glyph { display: inline-flex; }

.wt-tab__count {
    background: var(--surface-sunken);
    border-radius: var(--r-pill);
    color: var(--text);
    font-size: var(--fs-xs);
    font-variant-numeric: tabular-nums;
    padding: 1px 8px;
}

.wt-tab.is-active,
.tabs__tab.is-active,
.reactive-tab.active,
.reactive-settings-tab.active {
    background: var(--wt-soft);
    border-bottom-color: var(--wt);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    color: var(--wt);
}

.wt-tab.is-active .wt-tab__count,
.tabs__tab.is-active .chip { background: var(--wt); border-color: transparent; color: var(--on-accent, #fff); }

.wt-pane { display: grid; gap: var(--s4); min-width: 0; }
.wt-pane > * { min-width: 0; }
.wt-pane[hidden] { display: none; }

/* accent details inside a pane */
.wt-pane .rms-kpi-card { border-top: 3px solid var(--wt); }
.wt-pane .rms-kpi-progress span { background: var(--wt); }
.wt-pane > .panel { border-left: 4px solid var(--wt); }
/* panels ported without head/body wrappers get the body padding inside a pane */
.wt-pane > .panel:not(:has(> .panel__head, > .panel__body, > .wt-heading, > .rms-home-heading, > .grid-host:first-child)) { padding: var(--s4); }
.wt-pane > .panel > h2:first-child { margin-top: 0; }

/* asp-validation-summary renders an empty box when the model is valid */
.validation-summary-valid { display: none; }
.wt-pane > .panel > h2:first-child,
.wt-pane > .panel > .panel__head h2,
.wt-pane > h2:first-child { color: var(--wt); }
.wt-pane > .grid-host { border-top: 3px solid var(--wt); }

.wt-heading {
    align-items: center;
    background: var(--wt-soft);
    border-bottom: 1px solid var(--border);
    display: flex;
    gap: var(--s2);
    padding: var(--s3) var(--s4);
}

.wt-heading h1,
.wt-heading h2 { color: var(--wt); font-size: var(--fs-lg); margin: 0; }

.wt-heading__glyph { color: var(--wt); display: inline-flex; }

.wt-heading__note {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .04em;
    margin-left: auto;
    text-transform: uppercase;
}

@media print {
    .wt-tabs { display: none !important; }
    .wt-pane[hidden] { display: grid !important; }
}

/* --------------------------------------------------------------------------
   Visual review: Roofing Dashboard (/Home reactive queues).
   The queue grid is three or four columns, so it keeps a phone-width floor
   rather than .grid--wide's 860px, which pushed Status out of view on a
   laptop. The dates cell lines its labels up instead of ragged bold runs.
   The footer keeps clear of the fixed Software Request button.
   -------------------------------------------------------------------------- */
.grid--queue { min-width: 600px; }

.date-stack {
    column-gap: var(--s2);
    display: grid;
    grid-template-columns: max-content max-content;
    margin: 0;
}

.date-stack dt { color: var(--text-muted); font-weight: 700; }
.date-stack dd { font-variant-numeric: tabular-nums; margin: 0; }

body:has(#sr-launch) .app-foot { padding-right: 170px; }

/* on a phone the footer stacks, so it clears the button from below instead */
@media (max-width: 640px) {
    body:has(#sr-launch) .app-foot { padding-bottom: 64px; padding-right: 18px; }
}

/* --------------------------------------------------------------------------
   Visual review: Timesheets / Work Orders (/WorkOrders).
   Mapping rows edit in place, so the row actions sit side by side in their
   shrink cell instead of wrapping into a column; the add form keeps clear of
   the grid above it.
   -------------------------------------------------------------------------- */
.grid td.shrink > .button-row { flex-wrap: nowrap; }

/* below this the in-place inputs truncate their values; the host scrolls */
.wo-number-grid { min-width: 520px; }

.wo-number-add { margin-top: var(--s3); }

/* --------------------------------------------------------------------------
   Visual review: Reactive Repairs (RLSA) and the other work-order searches
   (/WorkOrders/Reactive, /ReactiveUsps, /Proactive, /SuperSearch).
   The filter fields grow to fill each row instead of sitting at their
   natural widths. The Dates column is nowrap, and a minmax(0, 1fr) value
   track gave it no width of its own, so the dates were clipped under
   Status; there both tracks size to their content.
   -------------------------------------------------------------------------- */
.wo-search__filters > .field { flex: 1 1 200px; margin-bottom: 0; }
.wo-search__filters > .wo-search__customer { flex: 2 1 280px; }

.wo-search-grid td.nowrap .wo-search-facts { grid-template-columns: max-content max-content; }

/* the 280px floor left the ID column clipped on a ~950px window */
.wo-search-grid__work { min-width: 240px; }

/* one label width on every row, so the values line up down the column */
.wo-search-grid__work .wo-search-facts { grid-template-columns: minmax(6.5rem, max-content) minmax(0, 1fr); }

/* --------------------------------------------------------------------------
   Visual review: Reactive work order detail (/WorkOrders/Reactive/{id}).
   The status header and the rail are cards, but the main column sat bare in
   an unpadded outer panel, so the tabs and fields touched its border. The
   outer wrapper is now plain and the main column is the third card. In the
   stacked rail (<=1180px) grid rows stretch each section, which pushed a
   label and its value apart; sections pack to the top, and the work-type
   chip keeps its own width instead of filling its cell.
   -------------------------------------------------------------------------- */
.panel.reactive-work-order { background: none; border: 0; }

.reactive-work-order .wo-main {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: var(--s3) var(--s4) var(--s4);
}

.wo-rail > * { align-self: start; }
.wo-rail > .chip { justify-self: start; }

.wo-rail__section { align-content: start; }

/* Servicing > Find: a union TPRC row carries .merge-item, whose grid display
   (from the RLSA merge list) took the row out of the table and shifted its
   cells under the wrong headings. The row stays a table row and gets the
   highlight the legend above the grid promises. */
.grid tr.merge-item { display: table-row; }
.grid tbody tr.merge-item td { background: var(--accent-amber-soft); }

/* Billing actions: eight buttons in one row ran past the card edge */
.reactive-work-order .panel__foot { flex-wrap: wrap; }

/* Colour and dividers. The page takes the roof-leak accent (teal) from
   .accent--teal on the section; each main tab carries the accent of the work
   it holds (servicing violet, costing amber, billing rose, comments slate,
   USPS navy) and the main card's top edge follows the active tab. Every
   colour here is --wt / --wt-soft, so dark mode comes from the accent tokens. */
.reactive-work-order { --wt-line: color-mix(in srgb, var(--wt) 38%, transparent); }

.reactive-work-order .wo-status {
    background: linear-gradient(90deg, var(--wt-soft), transparent 75%), var(--surface);
    border-left: 5px solid var(--wt);
}

.reactive-work-order .wo-status__text h1 { color: var(--wt); }

.reactive-work-order .wo-rail { border-top: 4px solid var(--wt); }
.reactive-work-order .wo-rail__ticket { color: var(--wt); }
.reactive-work-order .wo-rail__section { border-top-color: var(--wt-line); }

.reactive-work-order .wo-rail__section > h2 {
    align-items: center;
    color: var(--wt);
    display: flex;
    gap: var(--s2);
}

.reactive-work-order .wo-rail__section > h2::before,
.wo-section::before {
    background: var(--wt);
    border-radius: 2px;
    content: "";
    flex: 0 0 auto;
    height: 12px;
    width: 4px;
}

.reactive-work-order .wo-main {
    --wt-line: color-mix(in srgb, var(--wt) 38%, transparent);
    border-top: 4px solid var(--wt);
}

.reactive-work-order .wo-main > .tabs .tabs__tab {
    align-items: center;
    display: inline-flex;
    gap: var(--s2);
}

.reactive-work-order .wo-main > .tabs .tabs__tab::before {
    background: var(--wt);
    border-radius: 50%;
    content: "";
    height: 8px;
    width: 8px;
}

.reactive-work-order .wo-main h2 { color: var(--wt); }

/* a labelled divider between groups of fields */
.wo-section {
    align-items: center;
    color: var(--wt);
    display: flex;
    font-size: var(--fs-xs);
    font-weight: 700;
    gap: var(--s2);
    letter-spacing: .06em;
    margin: var(--s3) 0 var(--s2);
    text-transform: uppercase;
}

/* the reported pane is a grid whose gap already spaces its children */
.reactive-work-order #reported-panel > .wo-section { margin: 0; }

.wo-section::after {
    background: var(--wt-line, var(--border));
    content: "";
    flex: 1 1 auto;
    height: 1px;
}

.tabpane > .wo-section:first-child,
.tabpane > .validation-summary-valid + .wo-section { margin-top: 0; }

.reactive-work-order .wo-flags fieldset {
    background: var(--wt-soft);
    border-color: var(--wt-line);
}

.reactive-work-order .wo-flags legend {
    color: var(--wt);
    float: left;
    margin: 0 0 var(--s1);
    padding: 0;
    width: 100%;
}

.reactive-work-order .wo-rail > .chip {
    background: var(--wt-soft);
    border-color: var(--wt-line);
    color: var(--wt);
}

.reactive-work-order .wo-record {
    background: var(--surface-sunken);
    border-left: 3px solid var(--wt);
    border-radius: var(--r);
    padding: var(--s2) var(--s3);
}

/* Servicing > Find: the dispatch buttons and legend sat on the panel edge,
   and the subcontractor list ran the full length of the page; the panel is
   padded and the list scrolls inside its host with a sticky header. */
.reactive-work-order #tabs-tprcList {
    display: grid;
    gap: var(--s3);
    padding: var(--s3) var(--s4) var(--s4);
}

.reactive-work-order #tabs-tprcList .legend { margin: 0; }

/* in-place tab switch (js/partial-tabs.js) in flight */
[data-partial-tabs][aria-busy="true"] .wo-main {
    opacity: .6;
    pointer-events: none;
    transition: opacity .15s;
}

/* --------------------------------------------------------------------------
   Visual review: shared page skin (.wt-page).
   The reactive work order detail look for any page: wrap the page body in
   <div class="wt-page accent--x"> (the accent of the page's kind of work,
   WorkTypeAccents). The header becomes a tinted band with an accent edge and
   an accent title, top-level panels become cards with an accent top edge
   (padded when they have no head/body of their own), and panel headings get
   the accent bar. Every colour is --wt / --wt-soft / --wt-line, so dark mode
   comes from the accent tokens with no new colours.
   -------------------------------------------------------------------------- */
.wt-page {
    --wt-line: color-mix(in srgb, var(--wt) 38%, transparent);
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    min-width: 0;
}

.wt-page > * { min-width: 0; }

.wt-page > .workspace-header,
.wt-page > .stack > .workspace-header {
    background: linear-gradient(90deg, var(--wt-soft), transparent 75%), var(--surface);
    border: 1px solid var(--border);
    border-left: 5px solid var(--wt);
    border-radius: var(--r-lg);
    flex-wrap: wrap;
    margin: 0;
    padding: var(--s3) var(--s4);
}

.wt-page > .workspace-header h1,
.wt-page > .stack > .workspace-header h1 { color: var(--wt); }
.wt-page > .workspace-header > :only-child { flex: 1 1 auto; }

.wt-page > .panel,
.wt-page > .stack > .panel,
.wt-page .wt-card { border-top: 4px solid var(--wt); }

/* panels ported without head/body wrappers keep their content off the border */
.wt-page > .panel:not(:has(> .panel__head, > .panel__body, > .grid-host:first-child, > .tabs:first-child)),
.wt-page > .stack > .panel:not(:has(> .panel__head, > .panel__body, > .grid-host:first-child, > .tabs:first-child)) { padding: var(--s4); }

.wt-page .panel__head h2,
.wt-page .panel__head h3,
.wt-page > .panel > h2:first-child,
.wt-page > .stack > .panel > h2:first-child {
    align-items: center;
    color: var(--wt);
    display: flex;
    gap: var(--s2);
}

.wt-page .panel__head h2::before,
.wt-page .panel__head h3::before,
.wt-page > .panel > h2:first-child::before,
.wt-page > .stack > .panel > h2:first-child::before {
    background: var(--wt);
    border-radius: 2px;
    content: "";
    flex: 0 0 auto;
    height: 12px;
    width: 4px;
}

.wt-page > .panel > h2:first-child,
.wt-page > .stack > .panel > h2:first-child { margin-top: 0; }

.wt-page .wo-flags fieldset { background: var(--wt-soft); border-color: var(--wt-line); }
.wt-page .wo-flags legend { color: var(--wt); }

.wt-page .wo-record {
    background: var(--surface-sunken);
    border-left: 3px solid var(--wt);
    border-radius: var(--r);
    padding: var(--s2) var(--s3);
}

/* in-place tab switch (js/partial-tabs.js) in flight on any page */
.wt-page[data-partial-tabs][aria-busy="true"] {
    opacity: .6;
    pointer-events: none;
    transition: opacity .15s;
}

/* --------------------------------------------------------------------------
   Visual review: work-order searches (/WorkOrders/ReactiveUsps, /Proactive,
   /SuperSearch; same partial as /WorkOrders/Reactive). The search card and
   results card sit in the page skin; the search button row and the pager
   wrap on a phone instead of running past the card edge.
   -------------------------------------------------------------------------- */
.wo-search .button-row { flex-wrap: wrap; }
.wo-search .grid__pager { flex-wrap: wrap; }

/* a long unbroken token in a description (e.g. **ROTH**P3**) widened the column
   to ~460px and pushed the grid past the card at 1280px */
.wo-search-grid__description { overflow-wrap: anywhere; }

/* --------------------------------------------------------------------------
   Visual review: Proactive work order detail (/WorkOrders/Proactive/{id}).
   Same treatment as the reactive detail: the page takes the work-order
   accent (roof-leak groups the leak accent) from the section; the status
   header is a tinted band, the rail and the main cards get accent edges and
   the rail headings the accent bar. The tabs and their panes sat bare on the
   page background; they are now one padded card. The workflow strip is a
   sunken record strip. Every colour is --wt / --wt-soft / --wt-line.
   -------------------------------------------------------------------------- */
.proactive-wo { --wt-line: color-mix(in srgb, var(--wt) 38%, transparent); }

.proactive-wo .wo-status {
    background: linear-gradient(90deg, var(--wt-soft), transparent 75%), var(--surface);
    border-left: 5px solid var(--wt);
}

.proactive-wo .wo-status__text h1 { color: var(--wt); }

.proactive-wo .wo-rail { border-top: 4px solid var(--wt); }
.proactive-wo .wo-rail__section { border-top-color: var(--wt-line); }

.proactive-wo .wo-rail > .chip {
    background: var(--wt-soft);
    border-color: var(--wt-line);
    color: var(--wt);
}

.proactive-wo .wo-rail__section > h2 {
    align-items: center;
    color: var(--wt);
    display: flex;
    gap: var(--s2);
}

.proactive-wo .wo-rail__section > h2::before {
    background: var(--wt);
    border-radius: 2px;
    content: "";
    flex: 0 0 auto;
    height: 12px;
    width: 4px;
}

.proactive-wo__summary,
.proactive-wo__card { border-top: 4px solid var(--wt); }

.proactive-wo__card { min-width: 0; padding: var(--s3) var(--s4) var(--s4); }

.proactive-wo__workflow > .proactive-wo__label { color: var(--wt); }

.proactive-wo__status-strip > div {
    background: var(--surface-sunken);
    border-left-color: var(--wt);
    border-radius: var(--r);
    padding: var(--s2) var(--s3);
}

.proactive-wo__subhead.wo-section:first-child { margin-top: 0; }

/* on a phone the main column's implicit track grew to the two date columns
   (2 x 240px), so the card scrolled sideways and the tabs were clipped */
.proactive-wo .wo-main { grid-template-columns: minmax(0, 1fr); }

/* --------------------------------------------------------------------------
   Visual review: Uncoded e-work orders (/WorkOrders/Uncoded). The FSSP
   problem text took every spare pixel, so the Location column shrank to
   ~110px and its FIN, PO and call numbers broke one digit group per line.
   Location keeps a readable floor; the incoming-data column takes the rest.
   -------------------------------------------------------------------------- */
.uncoded-grid td:nth-child(2) { min-width: 220px; }

/* the header search: .field-inline's 160px label column left "Find" stranded
   mid-header, well away from its box; the label now sits beside the box */
.uncoded-find {
    grid-template-columns: auto minmax(240px, 1fr);
    gap: var(--s2);
    margin-bottom: 0;
}

/* on a phone the search takes its own full-width row under the heading */
@media (max-width: 640px) {
    .panel__head:has(.uncoded-find) { flex-wrap: wrap; }
    .panel__head:has(.uncoded-find) > form { flex: 1 1 100%; flex-wrap: nowrap; }
    .uncoded-find { flex: 1 1 auto; grid-template-columns: auto minmax(0, 1fr); }
}

/* --------------------------------------------------------------------------
   Visual review: PM Dashboard lists and utilities (/PmDashboard/Lists/*,
   /PmDashboard/Utilities/*, /PmDashboard/Scheduler). These compact pages
   had a bare h1, labels with no span, bare status lines and unstyled
   tables. They now use the page skin (header band, cards); their tables
   are .grid; a toolbar's fields and buttons line up on the input baseline;
   a table in a card scrolls inside the card on a phone.
   -------------------------------------------------------------------------- */
.wt-page > .toolbar { align-items: end; margin: 0; }
.wt-page > .grid-host { border-top: 4px solid var(--wt); }
.wt-page .panel:has(> table.grid) { overflow-x: auto; }
.wt-page .panel > table.grid { border: 1px solid var(--border-hairline); border-radius: var(--r); }
.wt-page .panel > h2 + table.grid { margin-top: var(--s2); }
.pm-dashboard > .panel { border-top: 4px solid var(--wt); }
.pm-dashboard .panel__head h2.eyebrow { color: var(--wt); }

/* Inspection Approval: nine columns with nowrap headings ran 13px past the card at 1280px */
#inspectionApprovalRoot .grid thead th { white-space: normal; }

/* Subcontractors: nowrap phones and headings and unbroken contact notes held the list at
   1124px in a 992px card at 1280px */
.sub-list-grid thead th,
.sub-list-grid td.nowrap,
.sub-list-grid td.shrink { white-space: normal; }
/* the name and contact break between words only and keep a readable width: 'anywhere' let them
   shrink to ~50px and wrap a word every few letters, rows hundreds of px tall; a narrow card scrolls */
.sub-list-grid td:nth-child(2) { min-width: 180px; overflow-wrap: break-word; }
.sub-list-grid td:nth-child(9) { min-width: 160px; overflow-wrap: break-word; }
/* long contact notes ('Used for Warranty leaks when...') show two lines; the full text is the tooltip */
.sub-contact { -webkit-box-orient: vertical; display: -webkit-box; -webkit-line-clamp: 2; overflow: hidden; }
/* the search is the main filter: it takes the spare width (it was the narrowest box, its hint cut off) */
.toolbar > .sub-search { flex: 1 1 280px; }
.sub-list-grid td.num,
.sub-list-grid td:nth-child(4) { white-space: nowrap; }
.sub-list-grid td.nowrap { min-width: 7.5rem; }

/* --------------------------------------------------------------------------
   Visual review: RMS (/RmsInspections, lists, assignments, timesheet and
   the migration inventory pages). Page skin with the inspection accent; the
   RMS dashboard and bucket lists gain the header band. The inventory tables
   list routes and stored-procedure names with no spaces, which held them at
   up to 1653px in a 958px card; those cells break anywhere so the tables
   read in the card instead of scrolling sideways.
   -------------------------------------------------------------------------- */
.wt-page.rms-dashboard-shell > .workspace-header { margin: 0; }
.rms-inventory .grid td { overflow-wrap: anywhere; }
.rms-inventory .grid td:first-child { min-width: 8rem; }

/* --------------------------------------------------------------------------
   Visual review: Access denied inside the signed-in shell (/Auth/AccessDenied).
   The auth layout is built for the signed-out full screen: min-height 100vh
   inside the workspace centred the card below a screen of blank space on a
   phone, and repeated the Roth lockup the rail already shows. In the shell
   the page sits at the top and the lockup is dropped.
   -------------------------------------------------------------------------- */
.workspace .auth-page { align-items: flex-start; min-height: 0; padding: 0; }
.workspace .auth-page .auth-brand { display: none; }

/* --------------------------------------------------------------------------
   Autosave status line (js/form-autosave.js). A small pill beside the Save
   button: muted while pending/saving, green when saved, red on an error.
   Chip tokens carry the dark-theme colours.
   -------------------------------------------------------------------------- */
.autosave-status {
    align-self: center;
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    color: var(--text-muted);
    font-size: var(--fs-sm);
    padding: 2px var(--s2);
}

.autosave-status[hidden] { display: none; }

.autosave-status[data-state="saved"] {
    background: var(--chip-green-bg);
    border-color: var(--chip-green-bd);
    color: var(--chip-green-fg);
}

.autosave-status[data-state="error"],
.autosave-status[data-state="retrying"] {
    background: var(--chip-red-bg);
    border-color: var(--chip-red-bd);
    color: var(--chip-red-fg);
}

/* --------------------------------------------------------------------------
   Visual review: the legacy detail layout (Roth AiR work order, RMS
   inspection / leak / preliminary / envelope detail). Pages opt in with
   .wt-detail and an accent class on the .reactive-detail section, and get
   the reactive work order detail look: the outer wrapper is plain so the
   status header, rail and tab panels read as cards; the status header is a
   tinted band with an accent edge; the rail has an accent top edge and the
   badge and location carry the accent instead of the navy/red bars; the
   active tab fills with the accent. Every colour is --wt / --wt-soft.
   -------------------------------------------------------------------------- */
.reactive-detail.wt-detail { --wt-line: color-mix(in srgb, var(--wt) 38%, transparent); }
.panel.reactive-detail.wt-detail { background: none; border: 0; padding: 0; }
.panel.reactive-detail.wt-detail > p:first-child { margin: 0 0 var(--s2); }

.wt-detail .reactive-status-header {
    background: linear-gradient(90deg, var(--wt-soft), transparent 75%), var(--surface);
    border-left: 5px solid var(--wt);
}

.wt-detail .reactive-status-header-text,
.wt-detail .reactive-status-header-id { color: var(--wt); }

.wt-detail .reactive-detail-rail { border-top: 4px solid var(--wt); }

.wt-detail .reactive-rail-badge {
    background: var(--wt-soft);
    color: var(--wt);
    font-size: var(--fs-xs);
    letter-spacing: .06em;
    text-transform: uppercase;
}

.wt-detail .reactive-rail-location {
    background: none;
    border-bottom: 1px solid var(--wt-line);
    color: var(--text);
    font-size: var(--fs-md);
}

.wt-detail .reactive-rail-section { border-top-color: var(--wt-line); }

.wt-detail .rms-detail-header-grid { border-left: 3px solid var(--wt); }

.wt-detail .reactive-tab.active {
    background: var(--wt-soft);
    border-bottom-color: var(--wt);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    color: var(--wt);
}

.wt-detail .reactive-tab-panel { border-top: 3px solid var(--wt); }

/* Visual review: the location strip that opens every Location Information report reads as a
   record strip with the report's accent edge. */
.wt-page .loc-facts { border-left: 3px solid var(--wt); }

/* --------------------------------------------------------------------------
   Visual review: PM list editors (/PmDashboard/Lists/*). A list of 100+
   records ran the page long and pushed the record editor, which opens below
   it, out of sight; the list scrolls inside its own card (sticky header) so
   the editor sits in view under it. Short lists keep their natural height.
   The USPS price editor's billing total and area amounts are a card.
   -------------------------------------------------------------------------- */
.grid-host.list-scroll { max-height: min(55vh, 480px); }

/* --------------------------------------------------------------------------
   Confirmation checkbox ahead of a command button (PM utilities: price
   editor, merges, imports). The bare label sat inline against the button;
   it gets its own line with the box beside its text.
   -------------------------------------------------------------------------- */
.wt-page form > label:has(> input[type="checkbox"][required]) {
    align-items: center;
    display: flex;
    gap: var(--s2);
    margin: var(--s3) 0 var(--s2);
}

/* --------------------------------------------------------------------------
   RMS pages in the app shell. RmsOffline.css pads the body 48px to clear the
   legacy layout's fixed footer; the app shell is full height with its footer
   in flow, so the padding only added a second, document-level scroll.
   -------------------------------------------------------------------------- */
body.rms-mobile-shell:has(> .app-shell) { padding-bottom: 0; }

/* RMS fact grids: the 900px rule above drops them to one column, which on a
   tablet or a narrow window stacks five short facts down the page. Two
   columns until phone width. */
@media (max-width: 900px) {
    .rms-detail-header-grid,
    .rms-detail-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
    .rms-detail-header-grid,
    .rms-detail-grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Daily Construction project workspace below desktop width.
   - The action buttons became one non-wrapping column-flow row below 1440px
     and pushed the page sideways (~200px at a 1024px window); they wrap now.
   - Below 900px the section nav stacked every link above the content; it
     flows into columns until phone width.
   - The project name under "Daily Construction" had no inline padding.
   - Section tabs ([data-dc-project-tabs]) are plain links inside .tabs and
     rendered as a bare link row; they take the .tabs__tab look.
   -------------------------------------------------------------------------- */
@media (max-width: 1440px) {
    .dcp-actions { display: flex; flex-wrap: wrap; }
    .dcp-actions .btn--block { width: auto; }
}

@media (max-width: 900px) and (min-width: 561px) {
    .dcp-nav > nav { column-gap: var(--s3); columns: 3 170px; }
    .dcp-nav__list { break-inside: avoid; }
    .dcp-nav__list:first-child { column-span: all; }
}

.dcp-nav__list > li.is-active:not(:has(a)) { padding: 0 var(--s3) var(--s1); }

.tabs[data-dc-project-tabs] > a {
    border-bottom: 3px solid transparent;
    color: var(--text-muted);
    font-size: var(--fs-base);
    font-weight: 700;
    margin-bottom: -1px;
    padding: var(--s2) var(--s3);
    text-decoration: none;
    white-space: nowrap;
}

.tabs[data-dc-project-tabs] > a:hover { color: var(--text); text-decoration: none; }

.tabs[data-dc-project-tabs] > a.is-active {
    border-bottom-color: var(--accent-navy);
    color: var(--accent-navy);
}

/* --------------------------------------------------------------------------
   Visual review: warranty information (/Warranties/{locationId}) - the
   reactive work order's legacy "Warranty Information" modal as a page: roof
   area warranties and contractor warranties. Below the floor the grids
   scroll in their cards rather than crushing the dates and phones.
   -------------------------------------------------------------------------- */
.warranty-grid { min-width: 720px; }
.warranty-grid td { vertical-align: top; }
.warranty-grid td .muted { font-size: var(--fs-sm); }

/* --------------------------------------------------------------------------
   Visual review: PM Dashboard (/PmDashboard). The task cards sat in one
   760px column, leaving a third of a laptop screen empty; they now pair up
   two per row while there is room for two readable cards.
   -------------------------------------------------------------------------- */
.pm-dashboard {
    align-items: start;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
    max-width: 1200px;
}

/* --------------------------------------------------------------------------
   Visual review: Inspection Approval detail (/InspectionApproval/Inspections/{id}).
   Rebuilt 2026-09-30 around how an approver works: a sticky action bar (Save /
   Approve / Reject), a "Needs your attention" strip whose items link to the
   section that fixes them, then one review section per legacy tab down a single
   page with a section rail (a sticky list beside the sections at desktop width,
   a scrolling chip row on a phone). Colours are the accent and chip tokens only.
   -------------------------------------------------------------------------- */
.ia-review { --ia-sticky: 0px; }

.ia-actionbar {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 5px solid var(--wt);
    border-radius: var(--r-lg);
    box-shadow: 0 6px 12px -10px rgba(0, 0, 0, .45);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s3);
    justify-content: space-between;
    padding: var(--s2) var(--s3);
    position: sticky;
    top: 0;
    z-index: 5;
}

.ia-actionbar__state {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s3);
    min-width: 0;
}

.ia-actionbar .check-field { margin: 0; }

.ia-meta {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1) var(--s3);
    margin: var(--s1) 0 0;
}

/* Reject opens its reason under the button instead of a separate form lower down. */
.ia-reject { position: relative; }
.ia-reject > summary { list-style: none; }
.ia-reject > summary::-webkit-details-marker { display: none; }

.ia-reject__panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-top: 4px solid var(--chip-red-bd);
    border-radius: var(--r-lg);
    box-shadow: 0 10px 24px -12px rgba(0, 0, 0, .5);
    display: grid;
    gap: var(--s2);
    margin-top: var(--s1);
    padding: var(--s3);
    position: absolute;
    right: 0;
    width: min(420px, calc(100vw - 32px));
    z-index: 6;
}

.ia-attention .panel__body { display: grid; gap: var(--s1); }
.ia-attention .wo-section:first-child { margin-top: 0; }

.ia-attention__list {
    display: grid;
    gap: var(--s1);
    list-style: none;
    margin: 0;
    padding: 0;
}

.ia-attention__list li {
    border-left: 3px solid var(--chip-gray-bd);
    border-radius: var(--r);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1) var(--s2);
    padding: var(--s1) var(--s2);
}

.ia-attention__list a { font-weight: 700; white-space: nowrap; }
.ia-attention__list--block li { background: var(--chip-red-bg); border-left-color: var(--chip-red-bd); color: var(--chip-red-fg); }
.ia-attention__list--confirm li { background: var(--chip-amber-bg); border-left-color: var(--chip-amber-bd); color: var(--chip-amber-fg); }
.ia-attention__list--block a,
.ia-attention__list--confirm a { color: inherit; text-decoration: underline; }

.ia-layout {
    align-items: start;
    display: grid;
    gap: var(--s3);
    grid-template-columns: minmax(180px, 220px) minmax(0, 1fr);
}

.ia-main { display: grid; gap: var(--s3); min-width: 0; }

.ia-rail {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    max-height: calc(100vh - 96px);
    overflow-y: auto;
    padding: var(--s2);
    position: sticky;
    top: 72px;
}

.ia-rail ol { display: grid; gap: 2px; list-style: none; margin: 0; padding: 0; }

.ia-rail__link {
    align-items: center;
    border-radius: var(--r);
    color: var(--text);
    display: flex;
    font-size: var(--fs-sm);
    gap: var(--s2);
    padding: var(--s1) var(--s2);
    text-decoration: none;
}

.ia-rail__link:hover { background: var(--wt-soft); }
.ia-rail__label { flex: 1 1 auto; min-width: 0; }

.ia-rail__dot {
    border: 2px solid var(--chip-gray-bd);
    border-radius: 50%;
    flex: 0 0 auto;
    height: 10px;
    width: 10px;
}

.ia-rail__link--viewed .ia-rail__dot { background: var(--chip-green-fg); border-color: var(--chip-green-fg); }
.ia-rail__link--notviewed .ia-rail__dot { border-color: var(--chip-amber-fg); }
.ia-rail__link--notrequired,
.ia-rail__link--irrelevant { color: var(--text-muted); }

.ia-rail__count {
    background: var(--chip-red-bg);
    border: 1px solid var(--chip-red-bd);
    border-radius: 999px;
    color: var(--chip-red-fg);
    font-size: var(--fs-xs);
    font-weight: 700;
    padding: 0 6px;
}

/* anchors land below the sticky action bar */
.ia-section,
.ia-attention { scroll-margin-top: 84px; }

.ia-section > .panel__head,
.ia-history > summary { flex-wrap: wrap; gap: var(--s2); }

.ia-mark { margin-left: auto; }

.ia-history > summary { cursor: pointer; list-style: none; }
.ia-history > summary::-webkit-details-marker { display: none; }
.ia-history > summary h2::after { content: " +"; }
.ia-history[open] > summary h2::after { content: " -"; }

/* tablet value differs from the location's current value */
.ia-review tr.ia-diff > td,
.ia-review .edit-row.ia-diff { background: var(--chip-amber-bg); }
.ia-review tr.ia-diff > td { color: var(--chip-amber-fg); }

.ia-compare th[scope="row"] { font-weight: 600; white-space: nowrap; }
.ia-longtext dd { white-space: pre-line; }

.ia-ratings select { min-width: 5.5em; }
.ia-ratings tfoot th,
.ia-ratings tfoot td { border-top: 2px solid var(--wt-line); font-weight: 700; }

.ia-repair-row .field.compact.wide { flex: 2 1 240px; }

.ia-current > summary { color: var(--wt); cursor: pointer; font-size: var(--fs-sm); font-weight: 700; }

.ia-drawing {
    border: 1px solid var(--border);
    border-radius: var(--r);
    max-height: 480px;
    max-width: 100%;
    object-fit: contain;
}

.ia-initials {
    background: var(--surface-sunken);
    border-radius: var(--r);
    fill: none;
    height: 44px;
    stroke: var(--text);
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 3;
    width: 132px;
}

@media (max-width: 900px) {
    .ia-layout { grid-template-columns: minmax(0, 1fr); }

    /* the rail becomes a row of section chips that scrolls sideways under the action bar */
    .ia-rail {
        max-height: none;
        overflow-x: auto;
        overflow-y: hidden;
        padding: var(--s1);
        /* relative, not static: the rail's absolutely positioned .sr-only state labels must stay
           inside its sideways scroll or they widen the whole page */
        position: relative;
    }

    .ia-rail ol { display: flex; gap: var(--s1); width: max-content; }
    .ia-rail__link { white-space: nowrap; }
    .ia-actionbar { justify-content: flex-start; }
    .ia-reject__panel { left: 0; right: auto; }
}

/* Below 640px the shell stops scrolling its workspace and the window scrolls instead, so a
   sticky bar has nothing to stick to; the actions ride along the bottom of the screen. */
@media (max-width: 640px) {
    .ia-review { padding-bottom: 200px; }

    /* sits above the shared Software Request button, which is fixed to the bottom-right corner */
    .ia-actionbar {
        bottom: 64px;
        left: var(--s2);
        position: fixed;
        right: var(--s2);
        top: auto;
    }

    .ia-reject__panel { bottom: calc(100% + var(--s1)); margin-top: 0; }
    .ia-section,
    .ia-attention { scroll-margin-top: var(--s2); }
}

/* --------------------------------------------------------------------------
   Visual review: Inspection Approval detail follow-ups (2026-09-30 review).
   Between phone width and 1180px the action bar wraps to two rows (~81px), so
   a section jumped to from the rail or the attention strip landed with its
   heading 4px under the bar; the offset now clears two rows. On a phone the
   fixed bar took ~107px of the screen: it is tighter, with smaller controls.
   -------------------------------------------------------------------------- */
@media (min-width: 641px) and (max-width: 1180px) {
    .ia-section,
    .ia-attention { scroll-margin-top: 112px; }
}

@media (max-width: 640px) {
    .ia-actionbar { gap: var(--s1) var(--s2); padding: var(--s1) var(--s2); }
    .ia-actionbar .check-field { font-size: var(--fs-sm); }
    .ia-actionbar .btn { font-size: var(--fs-sm); min-height: 30px; padding: 0 var(--s3); }
    .ia-actionbar .button-row { gap: var(--s1); }
    .ia-review { padding-bottom: 150px; }
}

.ia-review .autosave-status { color: var(--text-muted); font-size: var(--fs-sm); }
.ia-review .autosave-status[data-state="error"],
.ia-review .autosave-status[data-state="retrying"] { color: var(--chip-red-fg); }

/* --------------------------------------------------------------------------
   Visual review: Inspection Approval detail as tabs (owner, 2026-09-30).
   js/inspection-approval-tabs.js adds .ia-layout--tabs: the section rail
   becomes a tab bar above one visible section, in legacy tab order, each tab
   keeping its review dot and attention count. "Needs your attention" is a
   collapsed <details>; its summary keeps the blocking / to-confirm chip.
   -------------------------------------------------------------------------- */
.ia-layout--tabs { grid-template-columns: minmax(0, 1fr); }

.ia-layout--tabs .ia-rail {
    background: none;
    border: 0;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
    max-height: none;
    overflow: visible;
    padding: 0;
    position: static;
    scroll-margin-top: 84px;
    /* the old sticky rail's offset would push a relatively positioned rail onto the section */
    top: auto;
}

.ia-layout--tabs .ia-rail ol { display: flex; flex-wrap: wrap; gap: 2px var(--s1); }

.ia-layout--tabs .ia-rail__link {
    border-bottom: 3px solid transparent;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    color: var(--text-muted);
    font-weight: 700;
    margin-bottom: -1px;
    padding: var(--s2) var(--s3);
    white-space: nowrap;
}

.ia-layout--tabs .ia-rail__link:hover { color: var(--text); }

.ia-layout--tabs .ia-rail__link.is-active {
    background: var(--wt-soft);
    border-bottom-color: var(--wt);
    color: var(--wt);
}

.ia-layout--tabs .ia-rail__link:focus-visible { box-shadow: var(--focus-ring); outline: 0; }

@media (min-width: 641px) and (max-width: 1180px) {
    .ia-layout--tabs .ia-rail { scroll-margin-top: 112px; }
}

/* narrow screens: one sideways-scrolling row of tabs */
@media (max-width: 900px) {
    .ia-layout--tabs .ia-rail { overflow-x: auto; overflow-y: hidden; position: relative; }
    .ia-layout--tabs .ia-rail ol { flex-wrap: nowrap; width: max-content; }
}

@media (max-width: 640px) {
    .ia-layout--tabs .ia-rail { scroll-margin-top: var(--s2); }
}

/* collapsed "Needs your attention" */
details.ia-attention > summary {
    cursor: pointer;
    list-style: none;
}

details.ia-attention > summary::-webkit-details-marker { display: none; }

details.ia-attention > summary::after {
    border-bottom: 2px solid var(--text-muted);
    border-right: 2px solid var(--text-muted);
    content: "";
    height: 8px;
    margin-left: var(--s2);
    transform: rotate(45deg);
    transition: transform .15s;
    width: 8px;
}

details.ia-attention[open] > summary::after { transform: rotate(225deg); }
details.ia-attention:not([open]) > summary { border-bottom: 0; }

@media print {
    .ia-layout--tabs .ia-rail { display: none; }
    .ia-section[hidden] { display: block !important; }
    details.ia-attention > .panel__body { display: grid !important; }
}

/* --------------------------------------------------------------------------
   Visual review: Scheduling (page 18). The Technician Schedule and
   Check-in / Check-out tabs each get their own picker row: one labelled
   control plus its button, left-aligned instead of spread across the grid.
   -------------------------------------------------------------------------- */
.sched-pick {
    align-items: end;
    grid-template-columns: minmax(0, 320px) auto;
    justify-content: start;
    margin-bottom: 0;
}

@media (max-width: 640px) {
    .sched-pick { grid-template-columns: minmax(0, 1fr); }
}

/* --------------------------------------------------------------------------
   Visual review: Scheduling rebuild (page 18). The legacy frmSchedule flow:
   criteria, a colour-coded backlog with a count and legend, a selection
   panel with each inspection's site details, then assign / Open Scheduler.
   Legend colours map to the chip and accent tokens so dark mode follows.
   -------------------------------------------------------------------------- */
.sched-criteria { display: grid; gap: var(--s3); }

.sched-criteria__group {
    border: 0;
    margin: 0;
    min-width: 0;
    padding: 0;
}

.sched-criteria__group > legend {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    font-weight: 700;
    margin-bottom: var(--s2);
    padding: 0;
}

.sched-criteria .field-row { margin-bottom: var(--s2); }

.sched-criteria__checks {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s4);
}

/* legend colour per status: edge = strong tone, soft = chip fill */
.sched-u--soon    { --sched-u: var(--chip-green-fg); --sched-u-bg: var(--chip-green-bg); --sched-u-bd: var(--chip-green-bd); }
.sched-u--passed  { --sched-u: var(--chip-amber-fg); --sched-u-bg: var(--chip-amber-bg); --sched-u-bd: var(--chip-amber-bd); }
.sched-u--pastdue { --sched-u: var(--chip-red-fg);   --sched-u-bg: var(--chip-red-bg);   --sched-u-bd: var(--chip-red-bd); }
.sched-u--weather { --sched-u: var(--chip-blue-fg);  --sched-u-bg: var(--chip-blue-bg);  --sched-u-bd: var(--chip-blue-bd); }
.sched-u--prelim  { --sched-u: var(--accent-violet); --sched-u-bg: var(--accent-violet-soft); --sched-u-bd: var(--accent-violet); }
.sched-u--future,
.sched-u--none    { --sched-u: var(--chip-gray-bd);  --sched-u-bg: var(--chip-gray-bg);  --sched-u-bd: var(--chip-gray-bd); }

.sched-chip--soon    { background: var(--chip-green-bg); border-color: var(--chip-green-bd); color: var(--chip-green-fg); }
.sched-chip--passed  { background: var(--chip-amber-bg); border-color: var(--chip-amber-bd); color: var(--chip-amber-fg); }
.sched-chip--pastdue { background: var(--chip-red-bg);   border-color: var(--chip-red-bd);   color: var(--chip-red-fg); }
.sched-chip--weather { background: var(--chip-blue-bg);  border-color: var(--chip-blue-bd);  color: var(--chip-blue-fg); }
.sched-chip--prelim  { background: var(--accent-violet-soft); border-color: var(--accent-violet); color: var(--accent-violet); }
.sched-chip--future  { background: var(--chip-gray-bg);  border-color: var(--chip-gray-bd);  color: var(--chip-gray-fg); }

.sched-board { display: grid; gap: var(--s3); margin-top: var(--s3); }

.sched-summary {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s4);
    justify-content: space-between;
    padding: var(--s2) var(--s3);
}

.sched-summary__count {
    display: grid;
    gap: 2px;
    min-width: 0;
}

.sched-summary__count strong { font-size: var(--fs-lg); }
.sched-summary__count .muted { font-size: var(--fs-sm); overflow-wrap: anywhere; }

.sched-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1);
}

.sched-legend__item {
    align-items: center;
    border: 1px solid transparent;
    border-radius: var(--r-pill);
    color: var(--text);
    display: inline-flex;
    font-size: var(--fs-sm);
    gap: var(--s1);
    padding: 2px var(--s2);
    text-decoration: none;
}

.sched-legend__item:hover { border-color: var(--border-strong); text-decoration: none; }

.sched-legend__item.is-active {
    background: var(--wt-soft);
    border-color: var(--wt);
    font-weight: 700;
}

.sched-legend__count { color: var(--text-muted); font-variant-numeric: tabular-nums; }

.sched-layout {
    align-items: start;
    display: grid;
    gap: var(--s3);
    grid-template-columns: minmax(0, 1fr);
}

.sched-layout__grid { display: grid; gap: var(--s2); min-width: 0; }

.sched-grid td:nth-child(2) { min-width: 200px; }
.sched-grid td:nth-child(3),
.sched-grid td:nth-child(5) { white-space: nowrap; }

.sched-grid tbody tr.sched-row td:first-child { box-shadow: inset 4px 0 0 var(--sched-u); }
.sched-grid .chip { margin-top: 2px; }
.sched-grid th a { color: inherit; }
.sched-grid th[aria-sort="ascending"] a::after { content: " \25B2"; font-size: var(--fs-xs); }
.sched-grid th[aria-sort="descending"] a::after { content: " \25BC"; font-size: var(--fs-xs); }
.sched-ids { font-size: var(--fs-xs); }

.sched-pager {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s3);
    justify-content: space-between;
}

.sched-pager__page { color: var(--text-muted); font-size: var(--fs-sm); }

.sched-detail {
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-left: 4px solid var(--sched-u);
    border-radius: var(--r-lg);
    display: grid;
    gap: var(--s2);
    padding: var(--s2) var(--s3);
}

.sched-detail__head {
    align-items: start;
    display: flex;
    gap: var(--s2);
    justify-content: space-between;
}

.sched-detail__head > div { display: grid; gap: 2px; min-width: 0; }
.sched-detail__head .muted { font-size: var(--fs-sm); }

.sched-detail__facts {
    display: grid;
    font-size: var(--fs-sm);
    gap: 2px var(--s2);
    grid-template-columns: minmax(84px, auto) minmax(0, 1fr);
    margin: 0;
}

.sched-detail__facts dt { color: var(--text-muted); font-weight: 700; }
.sched-detail__facts dd { margin: 0; overflow-wrap: anywhere; }
.sched-detail__text { white-space: pre-line; }

.sched-map__figure { margin: 0; }

.sched-map__figure img {
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    display: block;
    height: auto;
    max-width: 100%;
}

.sched-map__legend {
    columns: 2 280px;
    font-size: var(--fs-sm);
    margin: 0;
    padding-left: var(--s4);
}

.sched-edit > summary { list-style: none; }
.sched-edit > summary::-webkit-details-marker { display: none; }

.sched-edit__form {
    align-items: center;
    display: grid;
    gap: var(--s2);
    grid-template-columns: auto minmax(0, 1fr);
    margin-top: var(--s2);
    min-width: 260px;
}

.sched-edit__form > .btn { grid-column: 1 / -1; justify-self: start; }

.is-scripted [data-sched-nojs] { display: none; }

@media (max-width: 640px) {
    .sched-summary { align-items: stretch; flex-direction: column; }
    .sched-edit__form { min-width: 0; }
}

/* --------------------------------------------------------------------------
   Visual review: Scheduling assign-to-schedule (owner, 2026-10-01). Legacy
   dragged backlog rows onto a technician's or subcontractor's scheduler and
   saved later; here Assign to... opens a dialog, the rows land on the
   Schedule tab marked "Not saved", and Save schedule writes them.
   -------------------------------------------------------------------------- */
.sched-assign {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
    color: var(--text);
    max-width: min(480px, calc(100vw - 32px));
    padding: var(--s4);
    width: 100%;
}

.sched-assign::backdrop { background: rgb(0 0 0 / .45); }
.sched-assign > h2 { font-size: var(--fs-lg); margin: 0 0 var(--s2); }
.sched-assign .field { margin: var(--s3) 0; }

/* without script the picker shows inline under the button row */
.sched-board:not(.is-scripted) .sched-assign {
    display: block;
    margin: var(--s3) 0 0;
    max-width: none;
    position: static;
}


.sched-schedule__totals {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
    margin: 0;
}

.sched-schedule tr.is-pending > td { background: var(--chip-amber-bg); }
.sched-schedule tr.is-pending > td:first-child { box-shadow: inset 3px 0 0 var(--chip-amber-fg); }

/* --------------------------------------------------------------------------
   Soft navigation (js/soft-nav.js, owner 2026-10-01): pages on the
   AssetManagementSoftNavigation list swap the workspace in place instead of
   reloading. A thin bar along the top shows a page is loading.
   -------------------------------------------------------------------------- */
.soft-nav-progress {
    background: var(--link);
    height: 3px;
    left: 0;
    opacity: 0;
    pointer-events: none;
    position: fixed;
    top: 0;
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform .2s ease-out, opacity .3s ease .1s;
    width: 100%;
    z-index: 1000;
}

.is-soft-loading .soft-nav-progress {
    animation: soft-nav-progress 8s cubic-bezier(.1, .7, .2, 1) forwards;
    opacity: 1;
    transition: none;
}

@keyframes soft-nav-progress {
    from { transform: scaleX(.05); }
    to { transform: scaleX(.92); }
}

.is-soft-loading main.workspace[aria-busy="true"] { cursor: progress; }

@media (prefers-reduced-motion: reduce) {
    .is-soft-loading .soft-nav-progress { animation: none; transform: scaleX(.6); }
}

/* --------------------------------------------------------------------------
   Visual review: Scheduling backlog row selection (owner, 2026-10-01). Rows
   select like the legacy grid (click, Ctrl+click, Shift+click, keyboard); the
   selection panel is gone and a right-click menu (or Actions) holds Assign
   to..., Show on map and Site details. The checkbox column is what the form
   posts and is shown only without script.
   -------------------------------------------------------------------------- */
.sched-board.is-scripted .sched-pick-col { display: none; }
.sched-board:not(.is-scripted) [data-sched-jsonly] { display: none; }

.sched-board.is-scripted .sched-grid tbody tr.sched-row { cursor: default; }
.sched-board.is-scripted .sched-grid tbody tr.sched-row:hover td { background: var(--surface-alt); }
.sched-board.is-scripted .sched-grid tbody tr.sched-row.is-selected:hover td { background: var(--wt-soft); }
.sched-board.is-scripted .sched-grid tbody tr.sched-row:focus { outline: none; }
.sched-board.is-scripted .sched-grid tbody tr.sched-row:focus-visible td {
    box-shadow: inset 0 2px 0 var(--link), inset 0 -2px 0 var(--link);
}

/* the urgency edge moves to the first visible cell */
.sched-board.is-scripted .sched-grid tbody tr.sched-row td:nth-child(2) { box-shadow: inset 4px 0 0 var(--sched-u); }
.sched-board.is-scripted .sched-grid tbody tr.sched-row:focus-visible td:nth-child(2) {
    box-shadow: inset 4px 0 0 var(--sched-u), inset 0 2px 0 var(--link), inset 0 -2px 0 var(--link);
}

.sched-summary__actions { align-items: center; flex-wrap: wrap; }
.sched-selection { font-size: var(--fs-sm); white-space: nowrap; }
.sched-pending { margin: 0; }
.sched-hint { font-size: var(--fs-sm); margin: 0; }

/* only ever shown as the right-click / Actions menu, never as buttons under the grid */
.sched-menu {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
    display: none;
    flex-direction: column;
    gap: 0;
    min-width: 200px;
    padding: var(--s1) 0;
    position: fixed;
    z-index: 60;
}

.sched-menu.is-open { display: flex; }

.sched-board.is-scripted .sched-menu__item {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    color: var(--text);
    justify-content: flex-start;
    padding: var(--s2) var(--s3);
    text-align: left;
    width: 100%;
}

.sched-board.is-scripted .sched-menu__item:hover,
.sched-board.is-scripted .sched-menu__item:focus-visible {
    background: var(--wt-soft);
    outline: none;
}

.sched-board.is-scripted .sched-menu__item:disabled { background: none; color: var(--text-muted); cursor: default; }

.sched-details-dialog { max-width: min(760px, calc(100vw - 32px)); }
.sched-details {
    display: grid;
    gap: var(--s2);
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    max-height: 60vh;
    overflow: auto;
}

/* without script the details show inline only after Show details */
.sched-board:not(.is-scripted) .sched-details-dialog:not([data-posted]) { display: none; }

/* States multi-select (Pages/Scheduling/_StatePicker.cshtml): the Scheduling search and the
   Scheduler's find form; the legacy scheduler let several states be ticked */
.sched-multi:not(.is-scripted) [data-state-picker-jsonly] { display: none; }

.sched-multi { position: relative; }

.sched-multi__summary {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text);
    cursor: pointer;
    font-size: var(--fs-base);
    line-height: calc(var(--control-h-lg) - 2px);
    list-style: none;
    min-height: var(--control-h-lg);
    overflow: hidden;
    padding: 0 30px 0 var(--s2);
    position: relative;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sched-multi__summary::-webkit-details-marker { display: none; }

.sched-multi__summary::after {
    border-bottom: 2px solid var(--text-muted);
    border-right: 2px solid var(--text-muted);
    content: "";
    height: 7px;
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-70%) rotate(45deg);
    width: 7px;
}

.sched-multi[open] > .sched-multi__summary { border-color: var(--link); }
.sched-multi__summary:focus-visible { box-shadow: var(--focus-ring); outline: 0; }

.sched-multi__panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
    display: grid;
    gap: var(--s2);
    left: 0;
    margin-top: 4px;
    min-width: 100%;
    padding: var(--s2);
    position: absolute;
    top: 100%;
    width: max(100%, 320px);
    z-index: 40;
}

.sched-multi__filter { width: 100%; }

.sched-multi__options {
    display: grid;
    gap: 2px var(--s2);
    grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
    max-height: 240px;
    overflow: auto;
}

.sched-multi__option {
    align-items: center;
    border-radius: var(--r);
    cursor: pointer;
    display: flex;
    gap: 6px;
    padding: 3px 4px;
}

.sched-multi__option:hover { background: var(--surface-alt); }
.sched-multi__option[hidden] { display: none; }
.sched-multi__foot { display: flex; justify-content: flex-end; }

@media (max-width: 640px) {
    .sched-multi__panel { width: 100%; }
}

/* the Scheduler's find toolbar: the states dropdown sits beside its button */
#find-inspections .toolbar .sched-multi { min-width: 220px; }

/* Scheduling backlog: Inspections and Map are two tabs (owner, 2026-10-01); Show on map switches to
   Map and draws the selection in place */
.sched-views { margin-bottom: 0; }
.sched-map { display: grid; gap: var(--s3); }
.sched-map[hidden],
.sched-layout[hidden] { display: none; }
.sched-map [data-sched-map-status] { margin: 0; }
.sched-map [data-sched-map-status]:empty { display: none; }
.sched-map [data-sched-map-body][aria-busy="true"] { opacity: .5; }

/* Interactive Scheduling maps (js/scheduling-map.js): numbered pins, the location name on hover */
.sched-livemap {
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    height: min(560px, 70vh);
    overflow: hidden;
    width: 100%;
}

.sched-livemap[hidden] { display: none; }

.sched-pin {
    align-items: center;
    background: var(--roth-navy);
    border: 2px solid #fff;
    border-radius: 50%;
    box-shadow: 0 1px 4px rgb(0 0 0 / .35);
    color: #fff;
    cursor: pointer;
    display: inline-flex;
    font-size: 12px;
    font-weight: 700;
    height: 26px;
    justify-content: center;
    min-width: 26px;
    padding: 0 4px;
}

.sched-pin:hover,
.sched-pin:focus-visible { background: var(--link); outline: none; }

.sched-pin-popup .mapboxgl-popup-content {
    color: #1b2433;
    font-size: var(--fs-sm);
    font-weight: 700;
    padding: 6px 10px;
}

/* the Schedule tab's picker: kind of assignee, then the assignee, then its buttons */
.sched-pick--assignee { grid-template-columns: minmax(0, 200px) minmax(0, 320px) auto; }

@media (max-width: 640px) {
    .sched-pick--assignee { grid-template-columns: minmax(0, 1fr); }
}

/* Visual review: Scheduler (page 19). One assignee picker panel; compact reorder buttons; route map */
.sched-planner-pick .toolbar { align-items: end; flex-wrap: wrap; }
.sched-planner-pick .field { min-width: 200px; }
.sched-move { display: inline-flex; gap: 4px; }
.sched-move .btn { min-width: 32px; padding-left: 0; padding-right: 0; }

/* Visual review: Scheduling Report (page 20). Regions as the chosen customer's checklist, inline checks */
.sched-report__regions { grid-column: 1 / -1; }
.sched-report__regions > p { margin: 0; }
.sched-report__region-list { display: grid; gap: 2px var(--s3); grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
.sched-report__checks,
.sched-report__actions,
.sched-report__error,
.sched-report__result { grid-column: 1 / -1; }
.sched-report__actions { justify-content: flex-end; }

/* Visual review: Scheduling Status (page 21). Find panel, matches as a grid, one column per value */
.sched-status__find { grid-template-columns: minmax(0, 420px) auto; justify-content: start; margin-bottom: 0; }
.sched-status__text { font-size: var(--fs-sm); margin-bottom: 4px; min-width: 220px; }
.sched-status__chips { display: flex; flex-wrap: wrap; gap: 4px; }

@media (max-width: 640px) {
    .sched-status__find { grid-template-columns: minmax(0, 1fr); }
}

/* Scheduling backlog menu: Select all sits below the actions on the selection */
.sched-menu__divider { border-top: 1px solid var(--border); margin: var(--s1) 0; }

/* Stops | Map tabs over a schedule (the Schedule tab, the Scheduler) */
[data-route-tabs] { display: grid; gap: var(--s2); }
[data-route-tabs] [data-route-pane][hidden] { display: none; }
/* the static map button stands down when the Map tab is there (.btn would otherwise outrank [hidden]) */
[data-route-static-map][hidden] { display: none; }
.sched-workdays { margin: var(--s3) 0; }
/* Schedule Contract Expire: one compact line per contract (was ~300px per row: bare checkboxes stacked
   over lowercase labels), a filter bar and a pager */
.contract-filter .field:has(select) { min-width: 240px; }
.contract-filter .field:has(input[type="search"]) { flex: 1 1 260px; }
.contract-hint { margin: 0; padding: var(--s2) var(--s4); }
.contract-expire td { vertical-align: top; }
.contract-expire input[type="date"] { min-width: 140px; }
.contract-expire textarea { min-height: 52px; min-width: 200px; }
.contract-expire .check-field { font-size: var(--fs-sm); margin-bottom: var(--s1); }

/* location utilities (Inspection Date, Location Name, Uploads): customer and location share the bar so Load stays on the same line (the customer
   select took the width and left the others ~90px) */
.utility-filters > .field { flex: 1 1 180px; }
.utility-hint { margin: 0; }
/* the Scheduler's confirm + Save row sat flush against the stops table */
.sched-save-confirm { margin: var(--s4) 0 var(--s2); }

/* Row menus (js/row-menu.js): an interactable list's actions live in a right-click menu on the row
   (owner 2026-10-06), also opened by a long press, the context-menu key / Shift+F10, or the list's
   Actions button. Rows select on click. */
[data-row-menu] [data-row] { cursor: default; user-select: none; }
[data-row-menu] [data-row]:focus { outline: none; }
[data-row-menu] [data-row]:focus-visible td,
[data-row-menu] li[data-row]:focus-visible { box-shadow: inset 0 0 0 2px var(--roth-navy); }
[data-row-menu] [data-row].is-selected:focus-visible td { box-shadow: inset 3px 0 0 var(--roth-navy), inset 0 0 0 2px var(--roth-navy); }
.row-menu {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
    display: none;
    flex-direction: column;
    min-width: 200px;
    padding: var(--s1) 0;
    position: fixed;
    z-index: 60;
}
.row-menu.is-open { display: flex; }
.row-menu[hidden]:not(.is-open) { display: none; }
.row-menu > form { display: flex; flex-direction: column; margin: 0; }
.row-menu .row-menu__item {
    align-items: center;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    color: var(--text);
    cursor: pointer;
    display: flex;
    font: inherit;
    justify-content: flex-start;
    min-height: 34px;
    padding: var(--s2) var(--s3);
    text-align: left;
    text-decoration: none;
    width: 100%;
}
.row-menu .row-menu__item:hover,
.row-menu .row-menu__item:focus-visible { background: var(--wt-soft, var(--surface-alt)); outline: none; }
.row-menu .row-menu__item:disabled,
.row-menu .row-menu__item[aria-disabled="true"] { background: none; color: var(--text-muted); cursor: default; }
.row-menu .row-menu__item--danger { color: var(--danger-fill, var(--roth-red)); }
.row-menu__divider { border-top: 1px solid var(--border); margin: var(--s1) 0; }
.row-menu__hint { color: var(--text-muted); font-size: var(--fs-sm); margin: 0; }

/* Toasts (js/toast.js): status messages over the page, top centre; autosave reports here, not in a
   label beside the form (owner 2026-10-06) */
.toast-region {
    align-items: center;
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    left: 50%;
    max-width: 520px;
    pointer-events: none;
    position: fixed;
    /* under the title bar, not over its title and status chip */
    top: calc(var(--topbar-h) + var(--s2));
    transform: translateX(-50%);
    width: calc(100% - 32px);
    z-index: 10050;
}
.toast {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--roth-navy);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
    color: var(--text);
    display: flex;
    gap: var(--s2);
    padding: var(--s2) var(--s3);
    pointer-events: auto;
}
.toast--saved,
.toast--success {
    /* owner 2026-10-06: save toasts are green; same tokens as .notice--success, so dark mode follows */
    background: var(--chip-green-bg);
    border-color: var(--chip-green-bd);
    border-left-color: var(--chip-green-fg);
    color: var(--chip-green-fg);
}
.toast--warning { border-left-color: var(--chip-amber-fg); }
.toast--error { border-left-color: var(--danger-fill, var(--roth-red)); }
.toast__text { flex: 1 1 auto; }
.toast .toast__close {
    background: none;
    border: 0;
    color: var(--text-muted);
    cursor: pointer;
    font-size: var(--fs-md);
    line-height: 1;
    min-height: 0;
    padding: 0 var(--s1);
}
/* a new-record form lifted into a modal (js/modal-forms.js) */
dialog.modal--form .modal__body > form { margin: 0; }
dialog.modal--form .modal__body textarea { min-width: min(420px, 100%); width: 100%; }
/* on/off switch: a bool checkbox (role="switch") drawn as a toggle; .switch-row lays several on one line */
.switch-row { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); margin: var(--s1) 0 var(--s3); }
.switch-field { align-items: center; cursor: pointer; display: inline-flex; gap: var(--s2); font-size: var(--fs-base); text-transform: none; letter-spacing: 0; }
.switch-field input[type="checkbox"] { appearance: none; -webkit-appearance: none; background: var(--border-strong); border-radius: 999px; cursor: pointer; flex: none; height: 20px; margin: 0; position: relative; transition: background .15s; width: 36px; }
.switch-field input[type="checkbox"]::before { background: #fff; border-radius: 50%; box-shadow: 0 1px 2px rgba(0, 0, 0, .3); content: ""; height: 16px; left: 2px; position: absolute; top: 2px; transition: transform .15s; width: 16px; }
.switch-field input[type="checkbox"]:checked { background: var(--wt, var(--brand, #0d6e7a)); }
.switch-field input[type="checkbox"]:checked::before { transform: translateX(16px); }
.switch-field input[type="checkbox"]:focus-visible { outline: 2px solid var(--wt, var(--brand, #0d6e7a)); outline-offset: 2px; }
.switch-field input[type="checkbox"]:disabled { cursor: not-allowed; opacity: .55; }
/* fields that only apply while a switch is on (recall date / amount); .field-row's display must not show them */
[data-recall-fields][hidden] { display: none !important; }
/* reactive Paperwork: an empty document list is a single line; the head carries the count and Upload files... */
.doc-group .panel__head { align-items: center; display: flex; flex-wrap: wrap; gap: var(--s2); justify-content: flex-start; }
.doc-group .panel__head h4 { margin: 0; }
.doc-group .panel__head .btn { margin-left: auto; }
.doc-group--empty { padding-bottom: 0; }
.doc-group--empty .panel__head { border-bottom: 0; }
label.btn.is-disabled { opacity: .55; pointer-events: none; }
/* one muted line at the top of a panel saying what it edits */
.panel-note { color: var(--muted, #6b7785); font-size: var(--fs-sm, 12px); margin: 0 0 var(--s3); }
/* reactive Costing: room for the whole total; unused price-list rows hidden until "Show all" */
.stat-strip--costing { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.stat-strip--costing .stat-cell__value { white-space: nowrap; }
/* Billing tab: names and PO numbers wrap; action groups each carry a small label. */
.stat-strip--billing .stat-cell__value { white-space: normal; overflow-wrap: anywhere; }
.field__link { font-size: 0.8125rem; margin-top: 0.25rem; }
.billing-actions { display: flex; flex-direction: column; gap: 0.75rem; }
.billing-actions__group { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.billing-actions__label { flex: 0 0 9rem; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted, inherit); }
@media (max-width: 640px) { .billing-actions__label { flex-basis: 100%; } .billing-actions__group .btn { flex: 1 1 auto; min-height: 44px; } }
.costing-materials:not(.show-all) tr[data-unused="true"] { display: none; }
.costing-materials .panel__head { align-items: center; display: flex; gap: var(--s2); justify-content: space-between; }
/* reactive Servicing > Rates: padded like the other panels, truck charges as a small tier table */
.rates-panel { padding: var(--s3) var(--s4); }
.rates-panel h4 { margin: 0 0 var(--s2); }
.rates-panel__tiers { margin-top: var(--s3); max-width: 560px; }
/* a note box that starts at three lines and grows with its text (browsers without field-sizing keep rows="3") */
textarea.textarea--grow { field-sizing: content; max-height: 28em; min-height: 4.6em; }
/* reactive detail header: page tools (new ticket, search, settings) under the work order actions */
.wo-status .wo-tools { display: flex; flex-basis: 100%; flex-wrap: wrap; gap: var(--s1); }
/* a form that autosaves hides its Save (js/form-autosave.js); .btn's display must not bring it back */
[data-autosave-save][hidden] { display: none !important; }
/* toasts while a modal is open sit on its title bar (js/toast.js sets top/left from the bar) */
.toast-region.toast-region--modal { transform: translate(-50%, -50%); width: auto; }
/* long record forms (location master lists) read two fields to a line instead of one tall column */
dialog.modal--form .form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 0 var(--s3); }
dialog.modal--form .form-grid > .field { margin: 0 0 var(--s2); }

/* in-page confirm (js/dialogs.js): the site never uses the browser's own confirm / alert pop-ups */
dialog.modal--confirm { max-width: 460px; }
dialog.modal--confirm .modal__message { margin: 0; }

/* Checklist Solutions (owner 2026-10-08): headings in the item picker, one self-saving solution list. */
option.option--heading { font-weight: 600; color: var(--text); }
.solution-picker__tools { align-items: end; flex-wrap: wrap; }
.solution-picker__tools .field { flex: 1 1 18rem; }
.solution-picker__list { columns: 2 22rem; column-gap: var(--s4); list-style: none; margin: 0; padding: 0; }
.solution-picker__list li { break-inside: avoid; padding: 0.3rem 0; border-bottom: 1px solid var(--border); }
.solution-picker__list li[hidden] { display: none; }

/* Location picker on the location utilities (owner 2026-10-06): a search box with a list of matches. */
.location-picker { position: relative; flex: 2 1 320px; }
.location-picker__list { background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--r); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18); left: 0; list-style: none; margin: 4px 0 0; max-height: 320px; overflow-y: auto; padding: 4px 0; position: absolute; right: 0; top: 100%; z-index: 20; }
.location-picker__list[hidden] { display: none; }
.location-picker__list [role="option"] { cursor: pointer; padding: 6px 10px; }
.location-picker__list [role="option"].is-active,
.location-picker__list [role="option"]:hover { background: #e8ebfa; }
:root[data-theme="dark"] .location-picker__list [role="option"].is-active,
:root[data-theme="dark"] .location-picker__list [role="option"]:hover { background: #262f52; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .location-picker__list [role="option"].is-active,
    :root:not([data-theme="light"]) .location-picker__list [role="option"]:hover { background: #262f52; }
}
.location-picker__note { color: var(--text-muted); font-size: var(--fs-sm); padding: 6px 10px; }
