/* ============================================================================
   Cascadia Code — SHIPPED webfont (2026-06-11, AppDevGuidance §19.2/§49).
   Previously the font stack relied on locally-installed Cascadia; machines
   without it fell back to Courier New — the "oldschool" look. Variable font
   covers weights 200-700 in one file; cached offline by the service worker.
   ========================================================================== */
@font-face {
    font-family: 'Cascadia Code';
    src: url('fonts/CascadiaCode.woff2') format('woff2');
    font-weight: 200 700;
    font-style: normal;
    font-display: swap;
}

/* ============================================================================
   NM design tokens (2026-06-11, AppDevGuidance §49). Components consume these
   variables — never hex/px literals. Hard floors: no text below 0.7rem
   (--nm-font-small), no interactive target below 24px (--nm-control-h).
   ========================================================================== */
:root {
    /* palette */
    --nm-red: #ED1C24;          /* NACOS red — errors/flags */
    --nm-amber: #f59e0b;        /* soft attention — "did you forget?", non-blocking (vs red errors) */
    --nm-blue: #0d4f8a;         /* primary action blue */
    /* Stage 5 (2026-06-11): grid header + line colors moved from the Excel
       blue-grey pastiche to the Tailwind kit's zinc neutrals so the grids
       match the chrome. ONE-LINE REVERT if the blue is missed:
       --nm-hdr-bg: #D6E4F0;  --nm-border: #b4c6d8; */
    --nm-hdr-bg: #f4f4f5;       /* grid header (was Excel-blue #D6E4F0) */
    --nm-calc-bg: #fafafa;      /* calculated/read-only cells — zinc-50
                                   (tan #EEECE1 retired; revert by setting
                                   #EEECE1 here) */
    --nm-border: #d4d4d8;       /* grid lines, zinc-300 (was blue-grey #b4c6d8) */
    /* typography
       Data surfaces use the kit's sans too — same
       family everywhere; digit alignment is preserved because every data
       surface declares font-variant-numeric: tabular-nums (equal-width
       digits), which Segoe UI supports. The shipped Cascadia Code stays
       available via --nm-font-truemono if real monospace is ever wanted.
       ONE-LINE REVERT: --nm-font-mono: var(--nm-font-truemono); */
    --nm-font-truemono: 'Cascadia Code', 'Consolas', 'Courier New', monospace;
    --nm-font-mono: 'Segoe UI Variable Text', 'Segoe UI', system-ui, sans-serif;
    --nm-font-grid: 0.8rem;     /* grid body text */
    --nm-font-ui: 0.85rem;      /* chrome: buttons, inputs, pickers */
    --nm-font-small: 0.7rem;    /* FLOOR — badges, sub-labels; nothing smaller */
    /* metrics */
    --nm-control-h: 24px;       /* FLOOR — min height of any interactive target */
    --nm-radius: 4px;
    --nm-sp-1: 2px;
    --nm-sp-2: 4px;
    --nm-sp-3: 8px;

    /* ── Chrome layer (Stage 2, 2026-06-11) — extracted from the licensed
       Tailwind Plus Catalyst kit: zinc neutral scale, hairline rings,
       8/12px radii, soft shadows, sans chrome around the mono grid. */
    --nm-z50: #fafafa;  --nm-z100: #f4f4f5; --nm-z200: #e4e4e7; --nm-z300: #d4d4d8;
    --nm-z400: #a1a1aa; --nm-z500: #71717a; --nm-z600: #52525b; --nm-z700: #3f3f46;
    --nm-z900: #18181b; --nm-z950: #09090b;
    --nm-font-sans: 'Segoe UI Variable Text', 'Segoe UI', system-ui, sans-serif;
    --nm-ring: rgba(9, 9, 11, 0.10);          /* Catalyst border-zinc-950/10 */
    --nm-ring-strong: rgba(9, 9, 11, 0.15);
    --nm-radius-lg: 8px;
    --nm-radius-xl: 12px;
    --nm-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    --nm-shadow-card: 0 1px 3px rgba(0, 0, 0, 0.07), 0 1px 2px rgba(0, 0, 0, 0.05);
}

/* Soft attention pill — a working weekday (Mon–Fri, non-holiday) with zero
   hours entered. Amber, not --nm-red: "did you forget?", not an error, never
   blocks Submit. Used in the per-day date cell of the time-entry forms only
   (autnav/hourly/exempt-times); grid templates legitimately charge zero-work
   days to PTO/holiday columns so they don't carry this. */
.nm-daywarn {
    display: inline-block;
    margin-left: 6px;
    padding: 0 5px;
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: #1a1a1a;
    background: var(--nm-amber);
    border-radius: 999px;
    vertical-align: middle;
    white-space: nowrap;
}

/* Small muted footnote — the single-timezone-per-day assumption, shown above the
   clock-time grids. Quieter than the interim banner. */
.nm-tz-note {
    margin: 0 4px 4px;
    font-size: var(--nm-font-small);
    color: var(--nm-z500);
    font-style: italic;
}

/* Interim banner above the time-entry area (SAP-retirement prefill gap). Amber
   attention, not a red error — informational. */
.nm-interim-note {
    margin: 4px 4px 6px 4px;
    padding: 6px 10px;
    font-size: 0.78rem;
    font-weight: 500;
    line-height: 1.35;
    color: #1a1a1a;
    background: #fff7ed;            /* amber-50 */
    border: 1px solid var(--nm-amber);
    border-left-width: 4px;
    border-radius: var(--nm-radius);
}

html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    background: var(--nm-z100);
}

/* Rounded table card (Stage 4, 2026-06-11): HTML tables can't round their own
   corners under border-collapse, so each grid is wrapped in this clipping
   shell — inline-block shrink-wraps to the table's content width (the
   pan-don't-squish philosophy stays), overflow:hidden rounds the corners of
   the table's own outer borders, shadow-sm gives the card depth. */
.nm-tablecard {
    display: inline-block;
    border: 1px solid var(--nm-z200);  /* anchors header to body on white pages */
    border-radius: var(--nm-radius-lg);
    overflow: hidden;
    box-shadow: var(--nm-shadow-sm);
    vertical-align: top;
    /* No max-width: the wrapper shrink-wraps and may exceed the viewport —
       the user pans the page (the documented content-width philosophy).
       ⚠ DESKTOP ONLY since 2026-07-25: at ≤640px the phone layout layer (end of
       this file) inverts this — cards clamp to the viewport and wide tables
       scroll INSIDE, because shrink-wrapping past the viewport is what made
       iOS scale the whole page down to unreadable. */
}
.nm-tablecard > table { margin: 0 !important; }

/* Entry-grid variant: overflow stays VISIBLE because OrderAutocomplete's
   suggestion dropdown is an absolutely-positioned descendant — overflow:hidden
   would clip it at the card edge (z-index cannot escape clipping). Corners are
   rounded on the corner CELLS instead; bottom corners stay square (white rows,
   not noticeable) to avoid per-section :last-child artifacts. */
.nm-tablecard-open { overflow: visible; }
.nm-tablecard-open > table > thead > tr:first-child > *:first-child,
.nm-tablecard-open > table > tbody:first-child > tr:first-child > *:first-child { border-top-left-radius: var(--nm-radius-lg); }
.nm-tablecard-open > table > thead > tr:first-child > *:last-child,
.nm-tablecard-open > table > tbody:first-child > tr:first-child > *:last-child { border-top-right-radius: var(--nm-radius-lg); }

/* Centered card-header band for display tables ("Time Entries" etc.) —
   lives INSIDE the .nm-tablecard so the title is attached to (and exactly
   as wide as) its table. */
.nm-tablehdr {
    display: block;
    text-align: center;
    background: var(--nm-z50);
    padding: 4px 10px;
    font-family: var(--nm-font-sans);
    font-size: 13px;
    font-weight: 600;
    color: var(--nm-z950);
    border-bottom: 1px solid var(--nm-z300);
}

/* Circular soft icon buttons (Stage 5, 2026-06-11) — the kit's
   circular-button pattern in its soft tints, sized for dense grid rows.
   Used for the per-day add (+) and remove (x) row actions. */
.nm-iconbtn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* relative so the drawn +/x bars (absolute, below) anchor to this circle. */
    position: relative;
    width: 20px;
    height: 20px;
    border: none;
    border-radius: 9999px;
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
    /* Keep the inline-flex button centered in its table cell regardless of that row's
       height (rowspan day cell, .ts-err rows) — default baseline alignment shifted it. */
    vertical-align: middle;
    cursor: pointer;
    padding: 0;
    font-family: var(--nm-font-sans);
}
.nm-iconbtn:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }
.nm-iconbtn-add { background: rgba(59, 130, 246, 0.15); color: #1d4ed8; }
.nm-iconbtn-add:hover { background: rgba(59, 130, 246, 0.25); }
.nm-iconbtn-del { background: rgba(239, 68, 68, 0.12); color: #b91c1c; }
.nm-iconbtn-del:hover { background: rgba(239, 68, 68, 0.22); }

/* Draw the + and × as CSS shapes instead of the ✚/✕ dingbat glyphs — the glyphs sat
   off-center in the circles, worse on some rows. Font glyphs are placed by the font's
   baseline/ascent metrics, which no amount of line-height fully centers and which the
   fallback symbol font renders inconsistently. Two absolutely-positioned bars,
   translate(-50%,-50%)-centered, are font-independent and pixel-perfect on every row.
   The text glyph is hidden via font-size:0 on add/del; the swap button keeps its glyph. */
.nm-iconbtn-add, .nm-iconbtn-del { font-size: 0; }
.nm-iconbtn-add::before, .nm-iconbtn-add::after,
.nm-iconbtn-del::before, .nm-iconbtn-del::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    background: currentColor;
    border-radius: 1px;
}
.nm-iconbtn-add::before { width: 10px; height: 2px;  transform: translate(-50%, -50%); }
.nm-iconbtn-add::after  { width: 2px;  height: 10px; transform: translate(-50%, -50%); }
.nm-iconbtn-del::before { width: 11px; height: 2px;  transform: translate(-50%, -50%) rotate(45deg); }
.nm-iconbtn-del::after  { width: 11px; height: 2px;  transform: translate(-50%, -50%) rotate(-45deg); }
/* Mode-swap (hours ⇄ times) — soft indigo so it reads as "transform", not add/delete. */
.nm-iconbtn-swap { background: rgba(99, 102, 241, 0.15); color: #4338ca; }
.nm-iconbtn-swap:hover { background: rgba(99, 102, 241, 0.25); }

/* Page surface: zinc background with the app content on a white card
   (MainLayout's <main>/<article>). min-width:fit-content lets the dense
   content-width grids widen the card instead of overflowing it. */
main.nm-main { padding: 10px 12px 28px; }
article.nm-article {
    background: #fff;
    border: 1px solid var(--nm-ring);
    border-radius: var(--nm-radius-xl);
    box-shadow: var(--nm-shadow-card);
    padding: 0 10px 14px;
    min-width: fit-content;
}

a, .btn-link {
    color: #006bb7;
}

/* Primary action — Catalyst solid dark/zinc button (Stage 2, 2026-06-11):
   zinc-900 body, near-black border, inner top highlight, 8px radius. */
.btn-primary {
    color: #fff;
    background-color: var(--nm-z900);
    border-color: rgba(9, 9, 11, 0.9);
    border-radius: var(--nm-radius-lg);
    font-family: var(--nm-font-sans);
    font-weight: 600;
    box-shadow: inset 0 1px rgba(255, 255, 255, 0.15), var(--nm-shadow-sm);
}

.btn-primary:hover:not(:disabled) {
    background-color: #2c2c30; /* zinc-900 + white/10 overlay */
    border-color: rgba(9, 9, 11, 0.9);
}

.btn-primary:disabled {
    /* Catalyst disabled = 50% opacity of the solid button — visually distinct
       from active without inventing a new hue. */
    background-color: var(--nm-z900);
    border-color: rgba(9, 9, 11, 0.9);
    color: #fff;
    opacity: 0.5;
    box-shadow: none;
}

/* Secondary action — the per-form Save button (only consumer of .btn-outline-primary
   in this app). SAME zinc palette as the solid .btn-primary above, just outlined:
   white body, zinc-900 text + border. Submit (solid) and Save (outline) now read as
   one color family — primary↔secondary — instead of grey-vs-blue. Bootstrap's default
   .btn-outline-primary is blue, which is why Save clashed before this override. */
.btn-outline-primary {
    color: var(--nm-z900);
    background-color: #fff;
    border-color: var(--nm-z900);
    border-radius: var(--nm-radius-lg);
    font-family: var(--nm-font-sans);
    font-weight: 600;
    box-shadow: var(--nm-shadow-sm);
}

.btn-outline-primary:hover:not(:disabled) {
    color: #fff;
    background-color: var(--nm-z900);
    border-color: var(--nm-z900);
}

.btn-outline-primary:disabled {
    color: var(--nm-z900);
    background-color: #fff;
    border-color: var(--nm-z900);
    opacity: 0.5;
    box-shadow: none;
}

/* ── Shared chrome classes (Catalyst, 2026-06-17) — promoted to the global
   stylesheet so they're available on EVERY page, not just Home. Home.razor
   declares an identical set in its inline <style> for the timesheet page; the
   Employee Admin page and the Create-timesheet modal (which reuses .hdr-btn /
   .hdr-input) live outside Home, so without these here their chrome would fall
   back to the un-themed browser default. Same selectors + declarations as
   Home's copy → harmless when both are in the DOM, correct when Home isn't.
   Scoped under .nm-mono to match how Home declares them (the wrapper marks the
   surface as "app chrome"). */
.nm-mono .hdr-input, .nm-mono select.hdr-input {
    font-family: var(--nm-font-sans); font-size: 13px; color: var(--nm-z950); background: #fff;
    border: 1px solid var(--nm-ring-strong); border-radius: var(--nm-radius-lg);
    height: 28px; padding: 0 8px; box-shadow: var(--nm-shadow-sm); outline: none;
}
.nm-mono .hdr-input:focus { outline: 2px solid #3b82f6; outline-offset: -1px; }
/* Header day-start / end HHMM inputs (mirror of Home's inline copy). An auto/intrinsic width renders far
   too wide (a number input falls back to its ~20-char default); a too-small fixed width clips the 4th
   digit. An explicit box-sizing-border width fits the 4 tabular HHMM digits + reduced padding, flex locked
   so the controls row can't stretch it. */
.nm-mono .hdr-time-input {
    box-sizing: border-box; width: 4em; flex: 0 0 auto; padding: 0 6px; text-align: center;
    font-variant-numeric: tabular-nums;
}
.nm-mono .hdr-time-input::-webkit-outer-spin-button,
.nm-mono .hdr-time-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* Approvals table — the Date + numeric columns (.hdr-mono cells) render in TRUE monospace
   (Cascadia Code, falling back to Consolas/Courier), NOT the app's sans figure font. tabular-nums
   aligns digits but not the proportional letters in a date like "Mon Jun 15", so the date column
   needs a real monospace face to read as a fixed-width column. Scoped to this table only (0,2,0 beats
   the global .hdr-mono); the app-wide --nm-font-mono sans+tabular doctrine is unchanged elsewhere. */
.nm-approval-table .hdr-mono { font-family: var(--nm-font-truemono); }
/* Approvals table Action / Cancel cells — the checkbox is laid out as a CENTRED BLOCK, not as an
   inline box on the text baseline. Measured cause (Erik 2026-07-25, "the checkboxes are off
   centre"): a bare <input type=checkbox> sits ON the baseline, so its 13px box occupies the TOP of
   the cell's line box and leaves the font's descender space below it. In a row the 20px status pill
   makes taller than the checkbox's own line box, vertical-align:middle then centres that line box —
   putting the checkbox ~1px ABOVE the row's centre while the "—" placeholder beside it lands dead
   on, which is exactly what reads as crooked. As the cell's only block the checkbox IS the content
   box, so the same vertical-align:middle centres the box itself (measured 0.00px off), and
   margin:0 auto removes the UA's asymmetric 4px/3px side margins. Header cells keep the inline
   checkbox — it sits on its own line under the label there and was never off. */
.nm-approval-table td.nm-check-cell input[type=checkbox] { display: block; margin: 0 auto; }
/* The engineer grids' per-row Cancel checkbox (nm-cancel-check, hourly-times-orders/-all) has
   the IDENTICAL baseline problem in its .ts-table cells (Erik 2026-07-25, second report: "the
   cancel checkbox still is off center… slightly higher") — same block fix. The header select-all
   sits on its own line under the "Cancel" label and was never off. */
.ts-table td input.nm-cancel-check { display: block; margin: 0 auto; }
.nm-mono .hdr-btn {
    font-family: var(--nm-font-sans); font-size: 13px; font-weight: 600; color: var(--nm-z950);
    background: #fff; border: 1px solid var(--nm-ring); border-radius: var(--nm-radius-lg);
    height: 28px; padding: 0 10px; display: inline-flex; align-items: center; gap: 4px;
    box-shadow: var(--nm-shadow-sm); cursor: pointer;
    /* At TRUE phone width (nothing wide stretching the layout — e.g. the offline Approve tab)
       the header's flex rows used to squeeze these until labels broke mid-word; whole buttons
       wrap to the next row instead (Erik 2026-07-16). Mirrors Home.razor's inline rule. */
    white-space: nowrap; flex: 0 0 auto;
}
.nm-mono .hdr-btn:hover:not(:disabled) { background: rgba(9,9,11,.03); border-color: var(--nm-ring-strong); }
.nm-mono .hdr-btn:disabled { opacity: .5; cursor: default; box-shadow: none; }
.nm-mono .hdr-btn:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}


/* ============================================================================
   Timesheet form grids — relocated from the per-form inline <style> blocks so
   they load from the <head> instead of from an interactive component body. A
   <style> inside an interactive component does not reliably apply on the first
   prerender->hydration render, so the grids showed unstyled until a manual
   reload; head-loaded rules are render-mode-independent. The three clock-time
   variants share the .ts-table namespace but diverge in spots, so each is scoped
   under a per-form class (.tsf-orders / .tsf-all / .tsf-exempt) carried on the
   form's root element — selectors and values are otherwise unchanged. The
   .de-table / .eh-table / .dp-table variants already use unique namespaces and
   relocate as-is. Each main entry grid is wrapped in a .ts-scroll overflow box
   so a narrow viewport scrolls the grid within its own width instead of widening
   the card (and the week-notes box) past the window.
   ========================================================================== */

/* Horizontal scroll box around each timesheet entry grid. width/max-width pin it
   to the surrounding card so the grid's min-width columns scroll inside this box
   on a narrow viewport rather than stretching the card past the window edge. */
.ts-scroll { width: 100%; max-width: 100%; overflow-x: auto; }

/* ── hourly-times-orders grid (.tsf-orders) ── */
/* Full content width — the table stretches to fill its container (matching the
   Approval Dashboard panel). The time-entry columns keep their fixed widths; the
   Optional Note column carries no width so it flexes to consume the remaining width
   and the table reaches the full page width. A narrow screen pans/zooms the fixed
   columns instead of squishing the Order text. */
.tsf-orders .ts-table { font-size: var(--nm-font-grid); border-collapse: collapse; width: 100%;
            font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }
.tsf-orders .ts-table th, .tsf-orders .ts-table td { border: 1px solid var(--nm-border); padding: var(--nm-sp-1) var(--nm-sp-2); vertical-align: middle; line-height: 1.3; }

/* Excel light-blue header */
.tsf-orders .ts-hdr { background-color: var(--nm-hdr-bg); color: #1a1a1a; font-weight: normal; text-align: center; font-size: var(--nm-font-grid); }
.tsf-orders .ts-hdr-bold { background-color: var(--nm-hdr-bg); color: #1a1a1a; font-weight: bold; text-align: center; font-size: var(--nm-font-grid); }

/* Excel light-tan calculated cells */
.tsf-orders .ts-calc { background-color: var(--nm-calc-bg); text-align: center; font-size: var(--nm-font-grid); }
/* th defaults to bold — Site/Prep/Wait/Trvl headers read lighter at normal weight. */
.tsf-orders thead th.ts-calc { font-weight: normal; }

/* Acted-on per-segment rows (#128): the per-status row tint (set inline) carries through the
   Site/Prep/Wait/Trvl cells (no tan override), and the disabled time boxes read in solid black —
   not the browser's faded disabled grey — so the whole row is legible with only the Status chip
   carrying colour. */
.tsf-orders .ts-segrow td.ts-calc,
.tsf-all    .ts-segrow td.ts-calc { background: transparent; color: #1a1a1a; }
.tsf-orders .ts-segrow input.ts-time:disabled,
.tsf-all    .ts-segrow input.ts-time:disabled {
    color: #1a1a1a; -webkit-text-fill-color: #1a1a1a; opacity: 1;
    /* The bubble keeps its outline but its fill is the row tint (transparent → the tinted <tr>
       shows through), so an acted-on box reads as part of its coloured row, not a white inset. */
    background: transparent;
}
/* Conflict outline — ONLY the specific time box(es) whose span overlaps other time on the same day:
   the editable row's two span-defining boxes (first + last entered time) and the acted-on segment's
   boxes. Outline only — the box interior keeps its normal fill — so a clash points at the exact cells,
   not the whole row. */
/* The error ring (Erik 2026-07-24, v2): a 2px RED outer border with a 1px BLACK inner ring drawn as
   an inset box-shadow flush against it. box-shadow (not outline) so it isn't clipped by the card's
   overflow:hidden and follows the border radius. This is the one error language for every input
   bubble (time boxes here; OrderAutocomplete / EmployeeAutocomplete IsError and the Mehrarbeit
   .mn-invalid boxes carry the same ring). */
.tsf-orders input.ts-time.ts-conflict,
.tsf-all    input.ts-time.ts-conflict {
    border: 2px solid var(--nm-red) !important;
    box-shadow: inset 0 0 0 1px #000 !important;
}

/* FYI (Returned / Cancelled) rows ghost ONLY the time-data columns — the S/P/W word, the time inputs and
   the Site/Prep/Wait/Trvl calc cells (the data that doesn't drive payroll). The Date, Order and the status
   badge stay legible. !important beats the disabled-input colour, the segment-row calc colour and the
   inline S/P/W word colour. -webkit-text-fill-color is ESSENTIAL: the acted-on rule above forces the
   disabled time boxes to solid black via -webkit-text-fill-color, and in WebKit/Chromium that property
   wins over `color` for the actual glyph fill — so ghosting `color` alone left the times black in Chrome.
   Overriding -webkit-text-fill-color too is what actually greys the time values. */
.ts-table tr.ts-fyi-ghost td.ts-calc,
.ts-table tr.ts-fyi-ghost input.ts-time,
.ts-table tr.ts-fyi-ghost .ts-pw-word {
    color: #a7b0bb !important;
    -webkit-text-fill-color: #a7b0bb !important;
}

/* Small in-flight spinner (approve / cancel busy indicator). The element supplies its own
   border/size inline; this just spins it. */
@keyframes nm-spin { to { transform: rotate(360deg); } }
.nm-spin { animation: nm-spin 0.7s linear infinite; }

/* Status pill (Submitted / Approved / Returned / Cancelled / Saved / Draft) — ONE fixed height so
   every badge lines up uniformly (#131), regardless of text or border style. Colours + border-style
   come inline per status. */
.nm-status-pill {
  display: inline-flex; align-items: center; box-sizing: border-box; height: 20px;
  padding: 0 8px; border-radius: 10px; font-size: 0.72rem; font-weight: 600; line-height: 1;
  border: 1px solid transparent; white-space: nowrap; vertical-align: middle;
}

/* Day label — calculated metadata (not user-entered), so it shares the
   tan .ts-calc tint. Same treatment as the right-hand Site/Prep/Wait/Trvl
   columns so "brown = system-generated" reads consistently across the row.
   The side borders are suppressed (no left/right rules) but the bottom gets
   a 2px black line directly — the ts-day cell is a rowspan cell anchored
   in the first row of its day group, so `.ts-day-last .ts-day` never matches
   it and the standard ts-day-last td rule wouldn't draw the divider under
   the day label. Declaring border-bottom here instead fires at the bottom
   of the spanned range, so every day group closes with a black line across
   the whole row. (Exception: SUNDAY's label — tagged nm-corner-bl — gets
   border-bottom:0 in the .nm-entrygrid block below, because the table's
   bottom edge is now the wrapper's rounded border.) */
.tsf-orders .ts-day { font-weight: bold; font-size: var(--nm-font-grid); border: none; border-bottom: 2px solid #000; background-color: var(--nm-calc-bg);
          font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }
.tsf-orders .ts-day-weekend { font-weight: bold; font-size: var(--nm-font-grid); border: none; border-bottom: 2px solid #000; background-color: var(--nm-calc-bg);
                  font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }

/* Inputs — Excel-tight */
.tsf-orders .ts-table input[type=text], .tsf-orders .ts-table select {
    border: 1px solid var(--nm-z300); border-radius: var(--nm-radius); padding: 0 var(--nm-sp-1);
    font-size: var(--nm-font-grid); background: #fff; height: var(--nm-control-h);
}
.tsf-orders .ts-table input.ts-time { width: 52px; box-sizing: border-box; text-align: center; font-family: var(--nm-font-mono); }
/* Non-time text inputs (Order, Optional Note) fill their column with
   left-aligned text. Time inputs (.ts-time) stay fixed + centered. */
.tsf-orders .ts-table input.ts-order { width: 100%; text-align: left; box-sizing: border-box; }
/* Center the time-input cells so the 46px input sits in the middle of
   the wider (48px+) column rather than left-glued. */
.tsf-orders .ts-table td:has(> input.ts-time) { text-align: center; }

/* S/P/W toggle buttons — compact.
   Each of the three states has a matching color that also tints the middle two time
   cells on the same row so the interval shape is visually tagged:
     S (On-Site) → #00E8C3 teal
     P (Prep)    → #F5FB3C yellow
     W (Wait)    → #FBDA00 gold */
.tsf-orders .ts-pw-group { display: inline-flex; gap: 0; border: 1px solid var(--nm-border); border-radius: var(--nm-radius); overflow: hidden; }
.tsf-orders .ts-pw-btn { border: none; background: #fff; padding: 0 var(--nm-sp-2); font-size: var(--nm-font-grid); cursor: pointer;
             color: #999; line-height: 1.3; border-right: 1px solid #d0d0d0; height: var(--nm-control-h); }
.tsf-orders .ts-pw-btn:last-child { border-right: none; }
.tsf-orders .ts-pw-btn:hover { background: #e0e0e0; }
/* Neutral active (used for S when the middle time cells aren't both filled yet — keeps
   the old pre-color-change look so the teal doesn't appear until both From-pair
   times are present). */
.tsf-orders .ts-pw-btn.active     { background: #d0d0d0; color: #1a1a1a; font-weight: bold; }
.tsf-orders .ts-pw-btn.active-s   { background: #00E8C3; color: #1a1a1a; font-weight: bold; }
.tsf-orders .ts-pw-btn.active-p   { background: #F5FB3C; color: #1a1a1a; font-weight: bold; }
.tsf-orders .ts-pw-btn.active-w   { background: #FBDA00; color: #1a1a1a; font-weight: bold; }

/* Middle time cells get the same color as the active S/P/W button. */
.tsf-orders .ts-cell-site { background: #00E8C3; }
.tsf-orders .ts-cell-prep { background: #F5FB3C; }
.tsf-orders .ts-cell-wait { background: #FBDA00; }

/* Remove button — visuals now come from the global .nm-iconbtn/.nm-iconbtn-del
   helpers (app.css); the old .ts-rm rule carried no layout, so it's gone. */

/* No-entry row */
.tsf-orders .ts-empty td { text-align: center; color: #aaa; font-size: var(--nm-font-small); opacity: 0.5; }

/* Day group border — black, and continues across the day-label column so the
   divider reads as a single horizontal line from edge to edge. Single-side
   border model (see the .nm-entrygrid block at the end of this style block):
   each divider is drawn ONLY as the closing row's border-BOTTOM. The old
   matching border-TOP rules on .ts-day-first are gone — under
   border-collapse:separate adjacent borders abut instead of merging, so
   keeping both sides would stack into a 4px line. The boundary above Monday
   is covered by the thead's own 2px black border-bottom. !important: the
   .nm-entrygrid single-side rule below has higher specificity (0,2,1) and
   would otherwise repaint these bottoms as the 1px gray grid line.
   (The .ts-day-first class stays on the rows — harmless, and it keeps the
   markup self-describing.) */
.tsf-orders .ts-day-last td { border-bottom: 2px solid #000 !important; }
.tsf-orders .ts-day-last .ts-day { border-bottom: 2px solid #000 !important; }
/* The old "tbody tr:last-child always closes with black" belt+suspenders rules
   are gone: the table's bottom edge is now the .nm-tablecard wrapper's own
   border, and the .nm-entrygrid last-row rules below zero the cell bottoms
   there so the frame isn't doubled. */

/* Force the day-label cell's bottom border to black on EVERY day group.
   The `.ts-day` rule above sets `border-bottom: 2px solid #000`, but the base
   `.ts-table td { border: 1px solid #b4c6d8 }` at the top has higher specificity
   (0,1,1 vs 0,1,0) and overrides it on all four sides. When Sunday rowspans down
   and the `.ts-day-last td` !important rule targets a *different* row (the last
   entry row, which has no `.ts-day` cell), the rowspanned day-label keeps the
   light blue border and the black divider below Sunday disappears. This selector
   (specificity 0,2,1) wins over the base and locks the black bottom in place. */
.tsf-orders .ts-table td.ts-day { border-bottom: 2px solid #000 !important; }

/* Strong black rule under the column-header row too. */
.tsf-orders .ts-table thead th { border-bottom: 2px solid #000; }

/* Invalid / conflicting row — the offending TIME boxes get a red outline per-box via .ts-conflict, so
   only the span-defining cells light up. The Order box is NOT reddened by a time conflict — it only
   reds via OrderAutocomplete's own validation (missing / invalid cost object). No full-row fill, so the
   Status chip and Optional Note read uncoloured. */

/* CA OT / CA DT headers used to drop to a proportional font so the space was
   narrower — that made them read thinner than the surrounding monospace headers.
   Keep them in the shared monospace stack so all column titles match visually. */

/* 1px black vertical separators between columns of the Weekly Payroll Summary
   + SAP Entries tables. Overrides the default .ts-table light-blue border on
   the right edge of every cell so column breaks read clearly. */
.tsf-orders .summary-v-sep th, .tsf-orders .summary-v-sep td { border-right: 1px solid #000 !important; }

/* Every row of the SAP Entries preview is app-generated — no user input touches
   these cells directly — so the whole table reads as "calculated" via the same
   tan background the Site/Prep/Wait/Trvl columns use. */
.tsf-orders .ts-sap-entries tbody td { background-color: var(--nm-calc-bg); }

/* ─── Tailwind Plus "condensed content" treatment for the read-only display
   tables (Week Payroll Summary + Time Entries preview), tagged nm-displaytable.
   Horizontal hairlines only — no vertical separators — white surface, gray data
   text with a dark first column. !important is required on the border channels:
   the .summary-v-sep border-right rule above is itself !important.
   Backgrounds win on plain specificity
   ((0,2,1)+ beats .ts-calc/.ts-hdr/.ts-sap-entries), so the tan calc tint and
   the blue header go white here without touching the entry grid. Font stays
   the .ts-table mono stack — it's data. */
.tsf-orders .ts-table.nm-displaytable th,
.tsf-orders .ts-table.nm-displaytable td {
    border: 0 !important;
    border-bottom: 1px solid var(--nm-z200) !important;
    padding: 3px 10px;
    background: #fff;
}
.tsf-orders .ts-table.nm-displaytable thead th {
    /* z50, not white — a white header band reads as detached from its
       (colorless) table. The faint gray ties the header to the data rows. */
    background: var(--nm-z50);
    font-weight: 600;
    color: var(--nm-z950);
    border-bottom: 1px solid var(--nm-z300) !important;
}
.tsf-orders .ts-table.nm-displaytable tbody td { color: var(--nm-z500); }
.tsf-orders .ts-table.nm-displaytable tbody td:first-child { color: var(--nm-z950); }
/* Last body row: no bottom hairline — the rounded .nm-tablecard edge closes
   the table. */
.tsf-orders .ts-table.nm-displaytable tbody tr:last-child td { border-bottom: 0 !important; }
/* Total row (payroll summary) */
.tsf-orders .ts-table.nm-displaytable tr.nm-total-row td {
    font-weight: 600;
    color: var(--nm-z950);
    border-top: 1px solid var(--nm-z300) !important;
    border-bottom: 0 !important;
}
/* ─── Rounded main entry grid (Tailwind-kit rollout) ───
   border-collapse:collapse IGNORES border-radius, so the grid's square
   corners poke past the .nm-tablecard wrapper's rounded edge. The main
   entry grid switches to border-collapse:separate with a SINGLE-SIDE
   border model (scoped to .nm-entrygrid — the display tables share
   .ts-table and keep their own hairline model):
     interior verticals   → each cell's border-RIGHT only
     interior horizontals → each cell's border-BOTTOM only
     outer frame          → the wrapper's own 1px var(--nm-z200) border;
                            cells on the table's outer edges carry no
                            border there, so the frame is never doubled.
   Corner radii are calc(var(--nm-radius-lg) - 1px) so the cell corners
   nest just inside the wrapper's 1px border. Keep this block LAST in the
   style element: the single-side rule relies on source order to beat the
   equal-or-lower-specificity legacy border rules above. */
.tsf-orders .ts-table.nm-entrygrid { border-collapse: separate; border-spacing: 0;
                         border-radius: calc(var(--nm-radius-lg) - 1px); }
.tsf-orders .ts-table.nm-entrygrid th, .tsf-orders .ts-table.nm-entrygrid td {
    border: 0;
    border-right: 1px solid var(--nm-border);
    border-bottom: 1px solid var(--nm-border);
}
/* The single-side rule above is (0,2,1) and outguns the (0,1,2)
   `.ts-table thead th` divider — restate the 2px black header underline. */
.tsf-orders .ts-table.nm-entrygrid thead th { border-bottom: 2px solid #000; }
/* Outer right + bottom edges: the wrapper closes the frame. !important on
   the bottom rules so the 2px-black `.ts-day-last td` / `.ts-table td.ts-day`
   !important dividers can't repaint a second line just inside the wrapper
   border on the table's last row. */
.tsf-orders .ts-table.nm-entrygrid tr > *:last-child { border-right: 0; }
.tsf-orders .ts-table.nm-entrygrid tbody tr:last-child td { border-bottom: 0 !important; }
/* Corner CELLS round their own backgrounds (hdr-bg / tan ts-day) so they
   don't overshoot the wrapper's rounded edge. */
.tsf-orders .ts-table.nm-entrygrid thead tr:first-child th:first-child { border-top-left-radius: calc(var(--nm-radius-lg) - 1px); }
.tsf-orders .ts-table.nm-entrygrid thead tr:first-child th:last-child { border-top-right-radius: calc(var(--nm-radius-lg) - 1px); }
.tsf-orders .ts-table.nm-entrygrid tbody tr:last-child > *:last-child { border-bottom-right-radius: calc(var(--nm-radius-lg) - 1px); }
/* Bottom-LEFT corner is NOT tr:last-child's first cell: Sunday's day-label
   is a rowspan cell anchored rows above, and its box reaches the table's
   bottom edge. Markup tags it nm-corner-bl (dayIndex == 6 on all three
   .ts-day render sites). (0,3,1) + !important beats the (0,2,2) !important
   black-bottom rule on `.ts-table td.ts-day`. */
.tsf-orders .ts-table.nm-entrygrid td.nm-corner-bl { border-bottom: 0 !important;
                                         border-bottom-left-radius: calc(var(--nm-radius-lg) - 1px); }

/* Date labels are LETTER grids ("MON JUN 8" / "WED JUN 10") — proportional
   sans staggers them (tabular-nums only fixes digits). True mono for the
   day-label column + the display tables' date column. */
.tsf-orders .ts-table .ts-day, .tsf-orders .ts-table .ts-day-weekend { font-family: var(--nm-font-truemono); }
.tsf-orders .ts-table.nm-displaytable tbody td:first-child { font-family: var(--nm-font-truemono); }    


/* ── hourly-times-all grid (.tsf-all) ── */
/* Full content width — the table stretches to fill its container (matching the
   Approval Dashboard panel). The time-entry columns keep their fixed widths; the
   Optional Note column carries no width so it flexes to consume the remaining width
   and the table reaches the full page width. On a narrow screen the fixed columns
   pan/zoom instead of being squished (which clipped the Order text). */
.tsf-all .ts-table { font-size: var(--nm-font-grid); border-collapse: collapse; width: 100%;
            font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }
.tsf-all .ts-table th, .tsf-all .ts-table td { border: 1px solid var(--nm-border); padding: var(--nm-sp-1) var(--nm-sp-2); vertical-align: middle; line-height: 1.3; }

/* Excel light-blue header */
.tsf-all .ts-hdr { background-color: var(--nm-hdr-bg); color: #1a1a1a; font-weight: normal; text-align: center; font-size: var(--nm-font-grid); }
.tsf-all .ts-hdr-bold { background-color: var(--nm-hdr-bg); color: #1a1a1a; font-weight: bold; text-align: center; font-size: var(--nm-font-grid); }

/* Excel light-tan calculated cells */
.tsf-all .ts-calc { background-color: var(--nm-calc-bg); text-align: center; font-size: var(--nm-font-grid); }
/* th defaults to bold — Site/Prep/Wait/Trvl headers read lighter with normal weight. */
.tsf-all thead th.ts-calc { font-weight: normal; }

/* Day label — calculated metadata (not user-entered), so it shares the
   tan .ts-calc tint. Same treatment as the right-hand Site/Prep/Wait/Trvl
   columns so "brown = system-generated" reads consistently across the row.
   The side borders are suppressed (no left/right rules) but the bottom gets
   a 2px black line directly — the ts-day cell is a rowspan cell anchored
   in the first row of its day group, so `.ts-day-last .ts-day` never matches
   it and the standard ts-day-last td rule wouldn't draw the divider under
   the day label. Declaring border-bottom here instead fires at the bottom
   of the spanned range, so every day group (including Sunday) closes with
   a black line across the whole row. */
.tsf-all .ts-day { font-weight: bold; font-size: var(--nm-font-grid); border: none; border-bottom: 2px solid #000; background-color: var(--nm-calc-bg);
          font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }
.tsf-all .ts-day-weekend { font-weight: bold; font-size: var(--nm-font-grid); border: none; border-bottom: 2px solid #000; background-color: var(--nm-calc-bg);
                  font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }

/* Inputs — Excel-tight */
.tsf-all .ts-table input[type=text], .tsf-all .ts-table select {
    border: 1px solid var(--nm-z300); border-radius: var(--nm-radius); padding: 0 var(--nm-sp-1);
    font-size: var(--nm-font-grid); background: #fff; height: var(--nm-control-h);
}
.tsf-all .ts-table input.ts-time { width: 52px; box-sizing: border-box; text-align: center; font-family: var(--nm-font-mono); }
/* Non-time text inputs (Order, Optional Note) fill their column with
   left-aligned text. Time inputs (.ts-time) stay fixed + centered. */
.tsf-all .ts-table input.ts-order { width: 100%; text-align: left; box-sizing: border-box; }
/* Center the time-input cells so the 46px input sits in the middle of
   the wider (48px+) column rather than left-glued. */
.tsf-all .ts-table td:has(> input.ts-time) { text-align: center; }

/* S/P/W toggle buttons — compact.
   Each of the three states has a matching color that also tints the middle two time
   cells on the same row so the interval shape is visually tagged:
     S (On-Site) → #00E8C3 teal
     P (Prep)    → #F5FB3C yellow
     W (Wait)    → #FBDA00 gold */
.tsf-all .ts-pw-group { display: inline-flex; gap: 0; border: 1px solid var(--nm-border); border-radius: 2px; overflow: hidden; }
.tsf-all .ts-pw-btn { border: none; background: #fff; padding: 0 var(--nm-sp-2); font-size: var(--nm-font-grid); cursor: pointer;
             color: #999; line-height: 1.3; border-right: 1px solid #d0d0d0; height: var(--nm-control-h); }
.tsf-all .ts-pw-btn:last-child { border-right: none; }
.tsf-all .ts-pw-btn:hover { background: #e0e0e0; }
/* Neutral active (used for S when the middle time cells aren't both filled yet — keeps
   the old pre-color-change look so the teal doesn't appear until both From-pair
   times are present). */
.tsf-all .ts-pw-btn.active     { background: #d0d0d0; color: #1a1a1a; font-weight: bold; }
.tsf-all .ts-pw-btn.active-s   { background: #00E8C3; color: #1a1a1a; font-weight: bold; }
.tsf-all .ts-pw-btn.active-p   { background: #F5FB3C; color: #1a1a1a; font-weight: bold; }
.tsf-all .ts-pw-btn.active-w   { background: #FBDA00; color: #1a1a1a; font-weight: bold; }

/* Middle time cells get the same color as the active S/P/W button. */
.tsf-all .ts-cell-site { background: #00E8C3; }
.tsf-all .ts-cell-prep { background: #F5FB3C; }
.tsf-all .ts-cell-wait { background: #FBDA00; }

/* Remove button — visual styling now comes from the global
   .nm-iconbtn / .nm-iconbtn-del helpers in app.css; the old .ts-rm
   rule carried no layout properties, so it was deleted outright. */

/* No-entry row */
.tsf-all .ts-empty td { text-align: center; color: #aaa; font-size: var(--nm-font-small); opacity: 0.5; }

/* Day group border — black, and continues across the day-label column so the
   divider reads as a single horizontal line from edge to edge. !important on
   every rule: the cell border rules (1px light blue) sit in the same cascade
   bucket and were visibly winning on the last Sunday row. Forcing it here is
   the cheapest way to guarantee the day-group divider renders under every
   row that ends a day.
   Single-side border model (see the .nm-entrygrid block at the bottom of this
   style element): day dividers are BOTTOM-driven only. The old
   `.ts-day-first { border-top: 2px }` twins were deleted — under
   border-collapse:separate a row's top border no longer merges with the
   previous row's bottom border; the pair would stack into a 4px line.
   Monday's top divider comes from the header row's 2px black border-bottom. */
.tsf-all .ts-day-last td { border-bottom: 2px solid #000 !important; }
.tsf-all .ts-day-last .ts-day { border-bottom: 2px solid #000 !important; }
/* Belt + suspenders — the very last row in the body always closes with black, so
   adding a row to Sunday can't leave it without a bottom divider. (For the main
   entry grid the .nm-entrygrid rules below override this back to 0 at the
   table's bottom edge — the wrapper's own border closes the frame there.) */
.tsf-all .ts-table tbody tr:last-child td { border-bottom: 2px solid #000 !important; }
.tsf-all .ts-table tbody tr:last-child .ts-day { border-bottom: 2px solid #000 !important; }

/* Force the day-label cell's bottom border to black on EVERY day group.
   The `.ts-day` rule above sets `border-bottom: 2px solid #000`, but the base
   `.ts-table td { border: 1px solid var(--nm-border) }` at the top has higher specificity
   (0,1,1 vs 0,1,0) and overrides it on all four sides. When Sunday rowspans down
   and the `.ts-day-last td` !important rule targets a *different* row (the last
   entry row, which has no `.ts-day` cell), the rowspanned day-label keeps the
   light blue border and the black divider below Sunday disappears. This selector
   (specificity 0,2,1) wins over the base and locks the black bottom in place. */
.tsf-all .ts-table td.ts-day { border-bottom: 2px solid #000 !important; }

/* Strong black rule under the column-header row too. */
.tsf-all .ts-table thead th { border-bottom: 2px solid #000; }

/* Invalid / conflicting row — the offending TIME boxes get a red outline per-box via .ts-conflict, so
   only the span-defining cells light up. The Order box is NOT reddened by a time conflict — it only
   reds via OrderAutocomplete's own validation (missing / invalid cost object). No full-row fill, so the
   Status chip and Optional Note read uncoloured. */

/* CA OT / CA DT headers used to drop to a proportional font so the space was
   narrower — that made them read thinner than the surrounding monospace headers.
   Keep them in the shared monospace stack so all column titles match visually. */

/* 1px black vertical separators between columns of the Weekly Payroll Summary
   + SAP Entries tables. Overrides the default .ts-table light-blue border on
   the right edge of every cell so column breaks read clearly. */
.tsf-all .summary-v-sep th, .tsf-all .summary-v-sep td { border-right: 1px solid #000 !important; }

/* Every row of the SAP Entries preview is app-generated — no user input touches
   these cells directly — so the whole table reads as "calculated" via the same
   tan background the Site/Prep/Wait/Trvl columns use. */
.tsf-all .ts-sap-entries tbody td { background-color: var(--nm-calc-bg); }

/* ── Tailwind Plus "condensed content" treatment for the read-only DISPLAY
   tables (Week Payroll Summary + Time Entries preview). Scoped under
   .ts-table.nm-displaytable so these rules outrank the base grid rules
   above while leaving the main entry grid's Excel gridlines untouched.
   Horizontal hairlines only: border-right needs !important to counter
   .summary-v-sep's !important vertical separators, and the last-row
   border-bottom needs !important to counter the entry grid's
   "tbody tr:last-child → 2px black" day-divider rule. Backgrounds go
   white — everything in these tables is calculated, so the tan
   "calculated" tint carries no distinction here. ── */
.tsf-all .ts-table.nm-displaytable th,
.tsf-all .ts-table.nm-displaytable td {
    border: 0;
    border-right: 0 !important;
    border-bottom: 1px solid var(--nm-z200);
    padding: 3px 10px;
}
.tsf-all .ts-table.nm-displaytable thead th {
    /* z50 band instead of plain white so the header reads visually attached
       to its table rather than floating detached against a colorless background. */
    background: var(--nm-z50);
    font-weight: 600;
    color: var(--nm-z950);
    border-bottom: 1px solid var(--nm-z300);
}
.tsf-all .ts-table.nm-displaytable tbody td { background: #fff; color: var(--nm-z500); }
.tsf-all .ts-table.nm-displaytable tbody td:first-child { color: var(--nm-z950); }
.tsf-all .ts-table.nm-displaytable tbody tr:last-child td { border-bottom: 1px solid var(--nm-z200) !important; }
/* Total row (payroll summary only — the SAP preview has no totals row). */
.tsf-all .ts-table.nm-displaytable:not(.ts-sap-entries) tbody tr:last-child td {
    font-weight: 600;
    color: var(--nm-z950);
    border-top: 1px solid var(--nm-z300);
    border-bottom: 0 !important;
}

/* ── Separate-border model for the MAIN ENTRY GRID only (.nm-entrygrid) ──
   border-collapse:collapse ignores border-radius entirely, so the square
   table corners poked past the rounded .nm-tablecard wrapper shadow. The
   main grid switches to separate borders with a SINGLE-SIDE model: every
   cell draws only its RIGHT + BOTTOM edge; the wrapper's own 1px
   var(--nm-z200) border supplies the outer frame (top/left always, and
   right/bottom where the last column / last row zero theirs below).
   Interior lines stay single-width because no cell ever draws left/top.
   Scoped to .nm-entrygrid so the read-only display tables — which share
   .ts-table — keep border-collapse:collapse and their own rules.
   This block sits LAST in the style element on purpose: the single-side
   cell rule ties the base `.ts-table th/td` rule on specificity (0,1,1)
   and must win on source order. */
.tsf-all table.nm-entrygrid {
    border-collapse: separate;
    border-spacing: 0;
    /* -1px nests the table's rounding inside the wrapper's 1px border. */
    border-radius: calc(var(--nm-radius-lg) - 1px);
}
.tsf-all .nm-entrygrid th, .tsf-all .nm-entrygrid td {
    border: 0;
    border-right: 1px solid var(--nm-border);
    border-bottom: 1px solid var(--nm-border);
}
/* Outer right edge comes from the wrapper, not the last column. */
.tsf-all .nm-entrygrid tr > *:last-child { border-right: 0; }
/* Outer bottom edge comes from the wrapper too — this also neutralizes the
   "tbody tr:last-child → 2px black" belt-+-suspenders day-divider rule above
   (same !important bucket; this selector is more specific and later). */
.tsf-all .ts-table.nm-entrygrid tbody tr:last-child td { border-bottom: 0 !important; }
/* Corner CELLS round their own backgrounds so the header-blue / calc-tan
   fills don't overshoot the table's radius (with separate borders, the
   table's border-radius does not clip cell backgrounds). The bottom-left
   corner is Sunday's day-label cell, which is usually a rowspan cell
   anchored rows ABOVE tr:last-child — no pure structural selector can
   reach it, hence the dedicated .ts-day-bl class stamped on it in markup. */
.tsf-all .nm-entrygrid thead tr:first-child th:first-child { border-top-left-radius: calc(var(--nm-radius-lg) - 1px); }
.tsf-all .nm-entrygrid thead tr:first-child th:last-child { border-top-right-radius: calc(var(--nm-radius-lg) - 1px); }
.tsf-all .nm-entrygrid tbody tr:last-child td:last-child { border-bottom-right-radius: calc(var(--nm-radius-lg) - 1px); }
.tsf-all .ts-table.nm-entrygrid tbody td.ts-day.ts-day-bl {
    /* Wrapper closes the frame under the (possibly rowspanned) Sunday
       label — beats the 2px-black `.ts-table td.ts-day` divider rule. */
    border-bottom: 0 !important;
    border-bottom-left-radius: calc(var(--nm-radius-lg) - 1px);
}

/* Date labels are LETTER grids ("MON JUN 8" / "WED JUN 10") — proportional
   sans staggers them (tabular-nums only fixes digits). True mono is applied to
   the day-label column and the display tables' date column for even alignment. */
.tsf-all .ts-table .ts-day, .tsf-all .ts-table .ts-day-weekend { font-family: var(--nm-font-truemono); }
.tsf-all .ts-table.nm-displaytable tbody td:first-child { font-family: var(--nm-font-truemono); }    


/* ── exempt-times-all grid (.tsf-exempt) ── */
/* Full content width — the table stretches to fill its container (matching the
   Approval Dashboard panel). The time-entry columns keep their fixed widths; the
   Optional Note column carries no width so it flexes to consume the remaining width
   and the table reaches the full page width. On a narrow screen the fixed columns
   pan/zoom instead of being squished (which clipped the Order text). */
.tsf-exempt .ts-table { font-size: var(--nm-font-grid); border-collapse: collapse; width: 100%;
            font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }
.tsf-exempt .ts-table th, .tsf-exempt .ts-table td { border: 1px solid var(--nm-border); padding: var(--nm-sp-1) var(--nm-sp-2); vertical-align: middle; line-height: 1.3; }

/* Excel light-blue header */
.tsf-exempt .ts-hdr { background-color: var(--nm-hdr-bg); color: #1a1a1a; font-weight: normal; text-align: center; font-size: var(--nm-font-grid); }
.tsf-exempt .ts-hdr-bold { background-color: var(--nm-hdr-bg); color: #1a1a1a; font-weight: bold; text-align: center; font-size: var(--nm-font-grid); }

/* Excel light-tan calculated cells */
.tsf-exempt .ts-calc { background-color: var(--nm-calc-bg); text-align: center; font-size: var(--nm-font-grid); }
/* th defaults to bold — Site/Prep/Wait/Trvl headers render lighter (font-weight:normal). */
.tsf-exempt thead th.ts-calc { font-weight: normal; }

/* Day label — calculated metadata (not user-entered), so it shares the
   tan .ts-calc tint. Same treatment as the right-hand Site/Prep/Wait/Trvl
   columns so "brown = system-generated" reads consistently across the row.
   The side borders are suppressed (no left/right rules) but the bottom gets
   a 2px black line directly — the ts-day cell is a rowspan cell anchored
   in the first row of its day group, so `.ts-day-last .ts-day` never matches
   it and the standard ts-day-last td rule wouldn't draw the divider under
   the day label. Declaring border-bottom here instead fires at the bottom
   of the spanned range, so every day group (including Sunday) closes with
   a black line across the whole row. */
.tsf-exempt .ts-day { font-weight: bold; font-size: var(--nm-font-grid); border: none; border-bottom: 2px solid #000; background-color: var(--nm-calc-bg);
          font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }
.tsf-exempt .ts-day-weekend { font-weight: bold; font-size: var(--nm-font-grid); border: none; border-bottom: 2px solid #000; background-color: var(--nm-calc-bg);
                  font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }

/* Inputs — Excel-tight */
.tsf-exempt .ts-table input[type=text], .tsf-exempt .ts-table select {
    border: 1px solid var(--nm-z300); border-radius: var(--nm-radius); padding: 0 2px;
    font-size: var(--nm-font-grid); background: #fff; height: var(--nm-control-h);
}
.tsf-exempt .ts-table input.ts-time { width: 52px; box-sizing: border-box; text-align: center; font-family: var(--nm-font-mono); }
/* Non-time text inputs (Order, Optional Note) fill their column with
   left-aligned text. Time inputs (.ts-time) stay fixed + centered. */
.tsf-exempt .ts-table input.ts-order { width: 100%; text-align: left; box-sizing: border-box; }
/* Center the time-input cells so the 46px input sits in the middle of
   the wider (48px+) column rather than left-glued. */
.tsf-exempt .ts-table td:has(> input.ts-time) { text-align: center; }

/* Mobile clock-time fit. At small viewports the day Start / End inputs (.ts-time,
   "HH:MM" = 5 glyphs) clipped the last digit: the 52px border-box width left a
   46px content area that the centered text overflowed once the cell's own
   horizontal padding was added. Widen the input and trim the cell padding so the
   full HH:MM is always visible (the grid stays horizontally scrollable via
   .ts-scroll, so the extra width never breaks the layout). Covers all three
   clock-time grids (.tsf-orders / .tsf-all / .tsf-exempt). */
@media (max-width: 480px) {
    .tsf-orders .ts-table input.ts-time,
    .tsf-all .ts-table input.ts-time,
    .tsf-exempt .ts-table input.ts-time { width: 58px; }
    .tsf-orders .ts-table td:has(> input.ts-time),
    .tsf-all .ts-table td:has(> input.ts-time),
    .tsf-exempt .ts-table td:has(> input.ts-time) { padding-left: 1px; padding-right: 1px; }
}

/* S/P/W toggle buttons — compact.
   Each of the three states has a matching color that also tints the middle two time
   cells on the same row so the interval shape is visually tagged:
     S (On-Site) → #00E8C3 teal
     P (Prep)    → #F5FB3C yellow
     W (Wait)    → #FBDA00 gold */
.tsf-exempt .ts-pw-group { display: inline-flex; gap: 0; border: 1px solid var(--nm-border); border-radius: 2px; overflow: hidden; }
.tsf-exempt .ts-pw-btn { border: none; background: #fff; padding: 0 4px; font-size: var(--nm-font-grid); cursor: pointer;
             color: #999; line-height: 1.3; border-right: 1px solid #d0d0d0; height: var(--nm-control-h); }
.tsf-exempt .ts-pw-btn:last-child { border-right: none; }
.tsf-exempt .ts-pw-btn:hover { background: #e0e0e0; }
/* Neutral active (used for S when the middle time cells aren't both filled yet — keeps
   the old pre-color-change look so the teal doesn't appear until both From-pair
   times are present). */
.tsf-exempt .ts-pw-btn.active     { background: #d0d0d0; color: #1a1a1a; font-weight: bold; }
.tsf-exempt .ts-pw-btn.active-s   { background: #00E8C3; color: #1a1a1a; font-weight: bold; }
.tsf-exempt .ts-pw-btn.active-p   { background: #F5FB3C; color: #1a1a1a; font-weight: bold; }
.tsf-exempt .ts-pw-btn.active-w   { background: #FBDA00; color: #1a1a1a; font-weight: bold; }

/* Middle time cells get the same color as the active S/P/W button. */
.tsf-exempt .ts-cell-site { background: #00E8C3; }
.tsf-exempt .ts-cell-prep { background: #F5FB3C; }
.tsf-exempt .ts-cell-wait { background: #FBDA00; }

/* Remove button — now the global .nm-iconbtn-del circle (app.css). */

/* No-entry row */
.tsf-exempt .ts-empty td { text-align: center; color: #aaa; font-size: var(--nm-font-small); opacity: 0.5; }

/* Day group border — black, and continues across the day-label column so the
   divider reads as a single horizontal line from edge to edge. !important on
   every rule: the base .ts-table td border shorthand (1px light blue on all
   sides) has equal specificity and was visibly winning on the last Sunday
   row because "border-bottom" overrides via `border:` shorthand come from
   the same bucket. Forcing it here is the cheapest way to guarantee the
   day-group divider renders under every row that ends a day.
   BOTTOM-ONLY under the rounded-corner model: the entry grid now runs
   border-collapse:separate (see the .nm-entrygrid block below), where
   adjacent borders abut instead of collapsing — the old .ts-day-first
   border-TOP rules would stack onto the preceding day's 2px bottom and
   render a 4px divider, so they're gone. Each day's divider is drawn
   solely by the row that ENDS the day (plus td.ts-day below for the
   rowspanned label cell). The old "very last row closes with black"
   belt-and-suspenders rules are retired too: the rounded .nm-tablecard
   wrapper border now frames the table bottom, and a black 2px line
   there would double up against it (zeroed in the .nm-entrygrid block). */
.tsf-exempt .ts-day-last td { border-bottom: 2px solid #000 !important; }
.tsf-exempt .ts-day-last .ts-day { border-bottom: 2px solid #000 !important; }

/* Force the day-label cell's bottom border to black on EVERY day group.
   The `.ts-day` rule above sets `border-bottom: 2px solid #000`, but the base
   `.ts-table td { border: 1px solid var(--nm-border) }` at the top has higher specificity
   (0,1,1 vs 0,1,0) and overrides it on all four sides. When Sunday rowspans down
   and the `.ts-day-last td` !important rule targets a *different* row (the last
   entry row, which has no `.ts-day` cell), the rowspanned day-label keeps the
   light blue border and the black divider below Sunday disappears. This selector
   (specificity 0,2,1) wins over the base and locks the black bottom in place. */
.tsf-exempt .ts-table td.ts-day { border-bottom: 2px solid #000 !important; }

/* Strong black rule under the column-header row too. */
.tsf-exempt .ts-table thead th { border-bottom: 2px solid #000; }

/* Error row — NACOS red wins over the informational P/W colors on the same row.
   Text stays dark so any content inside an invalid row remains readable.
   The note/comment cell is explicitly exempted: a comment never causes the
   validation error, so tinting it red would point the user at the wrong field. */
.tsf-exempt .ts-err { background-color: var(--nm-red); color: #1a1a1a; }
.tsf-exempt .ts-err td.ts-cell-site,
.tsf-exempt .ts-err td.ts-cell-prep,
.tsf-exempt .ts-err td.ts-cell-wait { background: var(--nm-red); color: #1a1a1a; }
.tsf-exempt .ts-err input { background: var(--nm-red); color: #1a1a1a; }
.tsf-exempt .ts-err td.ts-note-cell { background: #ffffff; color: #1a1a1a; }
.tsf-exempt .ts-err td.ts-note-cell input { background: #ffffff; color: #1a1a1a; }
/* The + / x action-button cell can never itself contain bad data — tinting
   it red just visually noisy-s the row. Keep it white so the buttons stay
   legible even when the entry's times are invalid. */
.tsf-exempt .ts-err td.ts-action-cell { background: #ffffff; }

/* CA OT / CA DT headers used to drop to a proportional font so the space was
   narrower — that made them read thinner than the surrounding monospace headers.
   Keep them in the shared monospace stack so all column titles match visually. */

/* 1px black vertical separators between columns of the Weekly Payroll Summary
   + SAP Entries tables. Overrides the default .ts-table light-blue border on
   the right edge of every cell so column breaks read clearly. */
.tsf-exempt .summary-v-sep th, .tsf-exempt .summary-v-sep td { border-right: 1px solid #000 !important; }

/* Tailwind Plus condensed display-table treatment — read-only tables only
   (Time Entries / SAP-entries preview). No vertical cell borders, light z200
   row dividers, white surface everywhere (every cell here is app-generated,
   so the tan "calculated" tint carries no signal and is dropped). !important
   on the border declarations is required to out-rank the .summary-v-sep
   border-right, which is itself !important. (The entry grid's 2px-black
   day-divider rules key off .ts-day-last/.ts-day classes that never appear
   in display-table rows.) Mono font + font-size inherit from .ts-table. */
.tsf-exempt .ts-table.nm-displaytable th,
.tsf-exempt .ts-table.nm-displaytable td {
    border: 0 !important;
    border-bottom: 1px solid var(--nm-z200) !important;
    padding: 3px 10px;
}
.tsf-exempt .ts-table.nm-displaytable thead th {
    /* z50, not white — a white header band reads as detached from the table
       body. The faint fill ties it to its rows. */
    background: var(--nm-z50);
    font-weight: 600;
    color: var(--nm-z950);
    border-bottom: 1px solid var(--nm-z300) !important;
}
.tsf-exempt .ts-table.nm-displaytable tbody td { background: #fff; color: var(--nm-z500); }
.tsf-exempt .ts-table.nm-displaytable tbody td:first-child { color: var(--nm-z950); }
/* Last display row keeps the standard z200 divider — kept explicit as a
   guard so no future last-row override in the entry-grid rules can leak
   a heavier border into the read-only tables. */
.tsf-exempt .ts-table.nm-displaytable tbody tr:last-child td { border-bottom: 1px solid var(--nm-z200) !important; }
.tsf-exempt .ts-table.nm-displaytable tfoot td {
    font-weight: 600;
    color: var(--nm-z950);
    border-top: 1px solid var(--nm-z300) !important;
    border-bottom: 0 !important;
}

/* ── Rounded-corner border model — MAIN ENTRY GRID ONLY (.nm-entrygrid) ──
   border-collapse:collapse ignores border-radius entirely, so the square
   table corners poked past the rounded .nm-tablecard wrapper shadow.
   The grid switches to separate + a single-side
   border model: every cell draws only its RIGHT and BOTTOM edge, and the
   wrapper's own 1px z200 border (global .nm-tablecard) supplies the
   OUTER frame on all four sides — last column drops border-right, last
   row drops border-bottom so nothing doubles up against the wrapper.
   Reasoned cell-by-cell: interior vertical line = one right border,
   interior horizontal line = one bottom border, outer frame = wrapper.
   Scoped to .nm-entrygrid so the read-only .nm-displaytable tables keep
   their own border model untouched. */
.tsf-exempt .ts-table.nm-entrygrid {
    border-collapse: separate;
    border-spacing: 0;
    /* -1px nests the table's radius just inside the wrapper's 1px border */
    border-radius: calc(var(--nm-radius-lg) - 1px);
}
.tsf-exempt .ts-table.nm-entrygrid th,
.tsf-exempt .ts-table.nm-entrygrid td {
    border: 0;
    border-right: 1px solid var(--nm-border);
    border-bottom: 1px solid var(--nm-border);
}
/* Restate the strong black header underline — the single-side rule above
   out-specifies the earlier `.ts-table thead th` declaration. */
.tsf-exempt .ts-table.nm-entrygrid thead th { border-bottom: 2px solid #000; }
/* Outer right edge comes from the wrapper. */
.tsf-exempt .ts-table.nm-entrygrid tr > *:last-child { border-right: 0; }
/* Outer bottom edge comes from the wrapper — !important to out-rank the
   2px-black day-divider rules above (themselves !important), which would
   otherwise stack a black line directly on top of the wrapper border. */
.tsf-exempt .ts-table.nm-entrygrid tbody:last-child tr:last-child > th,
.tsf-exempt .ts-table.nm-entrygrid tbody:last-child tr:last-child > td { border-bottom: 0 !important; }
/* Sunday's rowspanned day-label cell bottoms out at the table's last row
   but is anchored in an earlier tr, so tr:last-child can't reach it —
   the markup stamps ts-corner-bl on the day cell whenever dayIndex == 6. */
.tsf-exempt .ts-table.nm-entrygrid td.ts-day.ts-corner-bl { border-bottom: 0 !important; }
/* Corner CELLS round their backgrounds (header blue / calc tan) so the
   fills don't overshoot the wrapper's rounded corners. Same -1px radius
   as the table so the curves nest inside the wrapper's 1px border. */
.tsf-exempt .ts-table.nm-entrygrid thead tr:first-child > th:first-child { border-top-left-radius: calc(var(--nm-radius-lg) - 1px); }
.tsf-exempt .ts-table.nm-entrygrid thead tr:first-child > th:last-child { border-top-right-radius: calc(var(--nm-radius-lg) - 1px); }
.tsf-exempt .ts-table.nm-entrygrid td.ts-corner-bl { border-bottom-left-radius: calc(var(--nm-radius-lg) - 1px); }
.tsf-exempt .ts-table.nm-entrygrid tbody:last-child tr:last-child > td:last-child { border-bottom-right-radius: calc(var(--nm-radius-lg) - 1px); }

/* Date labels are LETTER grids ("MON JUN 8" / "WED JUN 10") — proportional
   sans staggers them (tabular-nums only fixes digits). True mono is used for the
   day-label column + the display tables' date column. */
.tsf-exempt .ts-table .ts-day, .tsf-exempt .ts-table .ts-day-weekend { font-family: var(--nm-font-truemono); }
.tsf-exempt .ts-table.nm-displaytable tbody td:first-child { font-family: var(--nm-font-truemono); }    


/* ── exempt-de grid (.de-table) ── */
/* Content-width, not stretched — table only as wide as its columns;
   no max-width cap so a narrow screen overflows (pan/zoom) instead
   of squishing the cost-object column. */
.de-table { font-size: var(--nm-font-grid); border-collapse: collapse; width: 100%;
            font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }
.de-table th, .de-table td { border: 1px solid var(--nm-border); padding: 3px 1px; vertical-align: middle; line-height: 1.3; }
.de-table thead th { border-bottom: 2px solid #000; }
.de-table tbody tr td { border-bottom: 2px solid #000; }
.de-table tfoot td { border-top: 2px solid #000; }
.de-vdiv { border-left: 2px solid #000 !important; }
.de-hdr { background-color: var(--nm-hdr-bg); color: #1a1a1a; font-weight: normal; text-align: center; font-size: var(--nm-font-small); white-space: nowrap; }
.de-hdr-wknd { background-color: var(--nm-hdr-bg); /* weekend tint dropped — SAT/SUN text suffices */ color: #1a1a1a; font-weight: normal; text-align: center; font-size: var(--nm-font-small); white-space: nowrap; }
.de-date-hdr { padding: 3px 8px; }
.de-date-hdr > div { line-height: 1.15; }
.de-calc { background-color: var(--nm-calc-bg); text-align: center; font-size: var(--nm-font-grid); }
.de-table tbody tr:nth-child(even) > td:not(.de-calc) { background-color: var(--nm-z100); /* kit zebra (was lavender #EFEBFF) */ }
.de-table tbody tr:nth-child(odd)  > td:not(.de-calc) { background-color: #fff; /* kit zebra (was grey #F0F0F0) */ }
.de-table input[type=text] { border: 1px solid var(--nm-z300); border-radius: var(--nm-radius); padding: 0 2px;
    font-size: var(--nm-font-grid); background: #fff; height: var(--nm-control-h); width: 100%; min-width: 80px; }
.de-table input[type=number] { border: 1px solid var(--nm-z300); border-radius: var(--nm-radius); padding: 0;
    font-size: var(--nm-font-grid); background: #fff; height: var(--nm-control-h); width: 44px; text-align: center;
    -moz-appearance: textfield; }
.de-table input[type=number]::-webkit-outer-spin-button,
.de-table input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.de-rm { margin-left:2px; }
/* >24h day-total → red: an impossible day. */
.de-day-over24 { background-color: var(--nm-red) !important; color: #fff; }
.de-day-over24 input { background-color: var(--nm-red) !important; color: #fff; }
/* Split-mismatch cue: if sum(Hours per day) != dayTotal[d] (e.g. user typed
   overrides totalling more than dayTotal, or excluded all rows leaving
   remainder unallocated), tint the day-total cell amber so it's visible. */
.de-split-mismatch { background-color: #FFE082 !important; }
/* Adicom-prefill cell border: thin amber bar on the LEFT to signal "this
   value came from Adicom, you can edit it." Disappears once the engineer
   types a different value. */
.de-adicom-prefill { box-shadow: inset 3px 0 0 #FFA000; }
/* Day-Total row label cell — slightly darker than .de-hdr to set it apart
   from the date-row beneath it. */
.de-daytotal-label { background-color: #B7CCE0; font-weight: bold; }
/* Cell layout: checkbox on left, number input on right, vertically centered. */
.de-cell { display: flex; align-items: center; gap: 2px; padding: 0 2px; justify-content: center; }
.de-cell input[type=number] { width: 38px; }
.de-cell input[type=checkbox] { margin: 0; width: 16px; height: 16px; }
/* Override-marker: when the engineer typed a number directly into a cell
   (`_isOverride[r][d] == true`), the checkbox shows black so the engineer
   can distinguish "auto-split share" from "override pinned-value" while
   toggling row inclusion. Uses accent-color which is honored across
   Chrome/Edge/Firefox/Safari for native checkbox tinting. */
.de-cell input[type=checkbox].de-cb-override { accent-color: #000; }
/* Include-unchecked styling — cell looks "off" so the engineer sees the
   row was excluded for that day. */
.de-excluded { background-color: #d8d8d8 !important; opacity: 0.65; }
/* Holiday tint — NACOS-deep-red column + italic header label. */
.de-hol-hdr { font-size: var(--nm-font-small); color: #880015; font-weight: bold; font-style: italic; display: block; margin-top: 1px; line-height: 1.1; }
.de-hol-cell { background-color: #880015 !important; }
.de-hol-hdr-weekend { font-size: var(--nm-font-small); color: #888; font-weight: normal; font-style: italic; display: block; margin-top: 1px; line-height: 1.1; }
/* Per-day "CHECK" badge in the date header. Fires when Adicom-recorded
   day-total disagrees with the sum of SAP-approved Hours for that day —
   the engineer either over- or under-allocated against their actual
   clock-in/clock-out time. Workdays only (weekends + company holidays
   never fire this — SAP doesn't expect entries on those). */
.de-day-check { display: inline-block; font-size: var(--nm-font-small); color: #fff;
    background-color: var(--nm-red); border-radius: 2px; padding: 0 3px;
    margin-top: 2px; font-weight: bold; line-height: 1.2; cursor: help; }
/* Display-table treatment (Tailwind Plus condensed) — read-only
   Time Entries mirror only; the entry grid above keeps its Excel
   gridlines. No vertical cell borders, white body (everything here
   is calculated, the tan calc distinction is meaningless). */
.de-table.nm-displaytable th,
.de-table.nm-displaytable td { border: 0; border-bottom: 1px solid var(--nm-z200); padding: 3px 10px; }
.de-table.nm-displaytable .de-vdiv { border-left: 0 !important; }
.de-table.nm-displaytable thead th { background: var(--nm-z50); font-weight: 600; color: var(--nm-z950);
    border-bottom: 1px solid var(--nm-z300); }
/* nth-child arms tie the base striping rules' specificity and win by
   source order; they also override the .de-calc tan background. */
.de-table.nm-displaytable tbody tr:nth-child(odd) > td,
.de-table.nm-displaytable tbody tr:nth-child(even) > td { background-color: #fff; }
.de-table.nm-displaytable tbody td { color: var(--nm-z500); }
.de-table.nm-displaytable tbody td:first-child { color: var(--nm-z950); }
.de-table.nm-displaytable tfoot td { font-weight: 600; color: var(--nm-z950);
    border-top: 1px solid var(--nm-z300); border-bottom: 0; }
/* ── Entry-grid border-model conversion (2026-06-11) ──
   border-collapse:collapse ignores border-radius, so the grid's square
   corners poked past the .nm-tablecard wrapper's rounded frame. Switch
   the MAIN grid (scoped via .nm-entrygrid — the display table above
   keeps collapse) to separate + a single-side model: each cell paints
   only its RIGHT + BOTTOM edge; the wrapper's own 1px var(--nm-z200)
   border supplies the outer frame (top/left edges + closing the last
   column/row). In THIS grid every interior line was already the 2px
   black Excel divider (.de-vdiv on every column after the first, plus
   the thead/tbody 2px bottoms), so the single-side borders carry
   2px #000 directly — no 1px interior lines existed to preserve. */
.de-table.nm-entrygrid { border-collapse: separate; border-spacing: 0;
    border-radius: calc(var(--nm-radius-lg) - 1px); }
.de-table.nm-entrygrid th, .de-table.nm-entrygrid td {
    border: 0; border-right: 2px solid #000; border-bottom: 2px solid #000; }
/* .de-vdiv painted border-LEFT; under collapse it merged with (and won
   over) the neighbor's right border, but under separate the two ABUT →
   doubled line. The divider now lives on the preceding cell's
   border-right (every interior vertical here is a divider), so zero
   the left arm. !important to outgun .de-vdiv's own !important. */
.de-table.nm-entrygrid .de-vdiv { border-left: 0 !important; }
/* tfoot's 2px top line now comes from the tbody last row's bottom
   border — an own border-top would abut it into a 4px band. */
.de-table.nm-entrygrid tfoot td { border-top: 0; }
/* Outer edge: last column / visually-last row paint nothing; the
   wrapper border closes the frame. (tbody:last-child arm is moot
   while the tfoot exists — kept in case the tfoot ever goes away.) */
.de-table.nm-entrygrid tr > *:last-child { border-right: 0; }
.de-table.nm-entrygrid tbody:last-child tr:last-child > th,
.de-table.nm-entrygrid tbody:last-child tr:last-child > td,
.de-table.nm-entrygrid tfoot tr:last-child > td { border-bottom: 0; }
/* Corner CELLS round to match the wrapper (-1px nests inside its 1px
   border) so their backgrounds don't overshoot the rounded frame.
   Higher specificity than the global .nm-tablecard-open top-corner
   rules, which target the same cells with the un-inset radius. */
.de-table.nm-entrygrid thead tr:first-child > th:first-child { border-top-left-radius: calc(var(--nm-radius-lg) - 1px); }
.de-table.nm-entrygrid thead tr:first-child > th:last-child { border-top-right-radius: calc(var(--nm-radius-lg) - 1px); }
.de-table.nm-entrygrid tfoot tr:last-child > td:first-child { border-bottom-left-radius: calc(var(--nm-radius-lg) - 1px); }
.de-table.nm-entrygrid tfoot tr:last-child > td:last-child { border-bottom-right-radius: calc(var(--nm-radius-lg) - 1px); }


/* ── exempt-hours-all grid (.eh-table) ── */
/* Content-width layout, not stretched. Each column is only as wide
   as its content and the table only as wide as the column sum. On a narrow
   phone the table overflows and the user pans/zooms — it never squishes the
   columns to fit the viewport (which clipped the Cost Object text). The
   earlier max-width:100% cap was dropped for that reason. */
.eh-table { font-size: var(--nm-font-grid); border-collapse: collapse; width: 100%;
            font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }
/* Row height bumped from 1px to 3px vertical padding so the zebra stripe is
   visible under the number inputs. There is no checkbox column that exposes the
   row's td bg, so the extra height + transparent number-input bg is needed to
   show the stripe at all. */
.eh-table th, .eh-table td { border: 1px solid var(--nm-border); padding: 3px 1px; vertical-align: middle; line-height: 1.3; }
.eh-table thead th { border-bottom: 2px solid #000; }
.eh-table tbody tr td { border-bottom: 2px solid #000; }
.eh-table tfoot td { border-top: 2px solid #000; }
.eh-vdiv { border-left: 2px solid #000 !important; }
.eh-hdr { background-color: var(--nm-hdr-bg); color: #1a1a1a; font-weight: normal; text-align: center; font-size: var(--nm-font-small); white-space: nowrap; }
.eh-hdr-wknd { background-color: var(--nm-hdr-bg); /* weekend tint dropped — SAT/SUN text suffices */ color: #1a1a1a; font-weight: normal; text-align: center; font-size: var(--nm-font-small); white-space: nowrap; }
/* Two-line date header: DOW on top, date below. Line-height tight so the two
   lines stack without eating vertical space. */
.eh-date-hdr { padding: 3px 8px; }
.eh-date-hdr > div { line-height: 1.15; }
.eh-calc { background-color: var(--nm-calc-bg); text-align: center; font-size: var(--nm-font-grid); }
.eh-table tbody tr:nth-child(even) > td:not(.eh-calc) { background-color: var(--nm-z100); /* kit zebra (was lavender #EFEBFF) */ }
.eh-table tbody tr:nth-child(odd)  > td:not(.eh-calc) { background-color: #fff; /* kit zebra (was grey #F0F0F0) */ }
.eh-table input[type=text] { border: 1px solid var(--nm-z300); border-radius: var(--nm-radius); padding: 0 var(--nm-sp-1);
    font-size: var(--nm-font-grid); background: #fff; height: var(--nm-control-h); width: 100%; min-width: 80px; }
/* Inputs stay white so typed values read crisply. The zebra shows through
   the 3px vertical TD padding above/below each input (see .eh-table td
   rule) — that's the visible alternating band between rows. */
.eh-table input[type=number] { border: 1px solid var(--nm-z300); border-radius: var(--nm-radius); padding: 0;
    font-size: var(--nm-font-grid); background: #fff; height: var(--nm-control-h); width: 44px; text-align: center;
    -moz-appearance: textfield; }
.eh-table input[type=number]::-webkit-outer-spin-button,
.eh-table input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* Visual props live on .nm-iconbtn / .nm-iconbtn-del (app.css); only layout remains. */
.eh-rm { margin-left:2px; }

/* Red background on day cells whose total Hrs across rows exceeds 24h —
   an impossible-day value. Submit is also gated via HasValidationError. */
.eh-day-over24 { background-color: var(--nm-red) !important; color: #fff; }
.eh-day-over24 input { background-color: var(--nm-red) !important; color: #fff; }

/* Company holiday — per-country paid-off day. NACOS-deep-red column tint
   + italic red holiday name below the date header. Engineers on this template
   can still record hours against holidays (PTO/actual work) so inputs stay
   editable. */
.eh-hol-hdr { font-size: var(--nm-font-small); color: #880015; font-weight: bold; font-style: italic; display: block; margin-top: 1px; line-height: 1.1; }
.eh-hol-cell { background-color: #880015 !important; }

/* Weekend-falling holidays — soft gray-italic FYI label, no cell tint. The
   day is already non-work; deep-red would over-claim. */
.eh-hol-hdr-weekend { font-size: var(--nm-font-small); color: #888; font-weight: normal; font-style: italic; display: block; margin-top: 1px; line-height: 1.1; }

/* ── Tailwind Plus condensed display-table treatment (2026-06-11) — read-only
   Time Entries mirror only. Horizontal hairlines on a white surface; the
   entry grid above keeps its Excel gridlines. Selectors ride the .eh-table
   prefix so they outrank the base grid rules. ── */
.eh-table.nm-displaytable th,
.eh-table.nm-displaytable td { border: 0; border-bottom: 1px solid var(--nm-z200); padding: 3px 10px; }
/* .eh-vdiv carries an !important border-left — neutralize it in display tables. */
.eh-table.nm-displaytable th.eh-vdiv,
.eh-table.nm-displaytable td.eh-vdiv { border-left: 0 !important; }
.eh-table.nm-displaytable thead th { background: var(--nm-z50); font-weight: 600; color: var(--nm-z950); border-bottom: 1px solid var(--nm-z300); }
.eh-table.nm-displaytable tbody td { color: var(--nm-z500); }
.eh-table.nm-displaytable tbody td:first-child { color: var(--nm-z950); }
/* Mirrors the base zebra selectors (their :not(.eh-calc) lifts specificity) —
   display-table body is plain white, no stripes. */
.eh-table.nm-displaytable tbody tr:nth-child(even) > td:not(.eh-calc),
.eh-table.nm-displaytable tbody tr:nth-child(odd)  > td:not(.eh-calc) { background-color: #fff; }
/* Everything in a display table is calculated — the tan calc tint is meaningless here. */
.eh-table.nm-displaytable td.eh-calc { background-color: #fff; }
.eh-table.nm-displaytable tfoot td { font-weight: 600; color: var(--nm-z950); border-top: 1px solid var(--nm-z300); border-bottom: 0; }

/* ── Entry-grid border-model conversion (2026-06-11) — rounded corners.
   border-collapse:collapse ignores border-radius entirely, so the square
   table corners poked past the .nm-tablecard wrapper's rounded shadow.
   The main grid (.nm-entrygrid) switches to separate borders with a
   single-side model: interior verticals = RIGHT borders only, interior
   horizontals = BOTTOM borders only, and the table's OUTER frame comes
   from the wrapper's own 1px var(--nm-z200) border. The display table
   below keeps collapse — every rule here is scoped to .nm-entrygrid. ── */
.eh-table.nm-entrygrid { border-collapse: separate; border-spacing: 0;
    border-radius: calc(var(--nm-radius-lg) - 1px); } /* -1px nests inside the wrapper's 1px border */
/* Single-side base. (0,2,1) outranks the all-sides 1px base rule above
   AND the unscoped 2px divider rules — the dividers are re-declared at
   entry-grid specificity right below. */
.eh-table.nm-entrygrid th,
.eh-table.nm-entrygrid td { border: 0; border-right: 1px solid var(--nm-border); border-bottom: 1px solid var(--nm-border); }
/* Semantic 2px black row dividers, kept as bottom borders. The old
   `.eh-table tfoot td { border-top: 2px }` is intentionally NOT re-added:
   under separate borders it would stack on the tbody last row's 2px
   bottom (4px line) — the tbody bottom alone draws the total-bar divider. */
.eh-table.nm-entrygrid thead th { border-bottom: 2px solid #000; }
.eh-table.nm-entrygrid tbody tr td { border-bottom: 2px solid #000; }
/* .eh-vdiv drew the 2px vertical gridlines as border-LEFT — under separate
   borders that abuts the neighbor's 1px right border (3px line). Express
   the same divider as a 2px RIGHT border on the PRECEDING cell instead,
   and zero the vdiv cell's own left edge (must out-!important the base
   .eh-vdiv rule). Every non-first column carries .eh-vdiv, so this
   reproduces the original all-black vertical gridlines exactly. */
.eh-table.nm-entrygrid .eh-vdiv { border-left: 0 !important; }
.eh-table.nm-entrygrid tr > *:has(+ .eh-vdiv) { border-right: 2px solid #000; }
/* The wrapper closes the frame: no right border on the last column, no
   bottom border on the visually-last row (tfoot here; the tbody rule is
   dead today but covers a future tfoot removal). */
.eh-table.nm-entrygrid tr > *:last-child { border-right: 0; }
.eh-table.nm-entrygrid tfoot tr:last-child > * { border-bottom: 0; }
.eh-table.nm-entrygrid tbody:last-child tr:last-child > * { border-bottom: 0; }
/* Round the corner CELLS too — the -open wrapper keeps overflow:visible
   for the autocomplete dropdown, so corner-cell backgrounds (hdr-bg /
   calc tan) would otherwise overshoot the radius. (0,4,2) outranks the
   global .nm-tablecard-open corner rules (0,3,3), so the -1px nesting
   value wins. */
.eh-table.nm-entrygrid thead tr:first-child > *:first-child { border-top-left-radius: calc(var(--nm-radius-lg) - 1px); }
.eh-table.nm-entrygrid thead tr:first-child > *:last-child { border-top-right-radius: calc(var(--nm-radius-lg) - 1px); }
.eh-table.nm-entrygrid tfoot tr:last-child > *:first-child { border-bottom-left-radius: calc(var(--nm-radius-lg) - 1px); }
.eh-table.nm-entrygrid tfoot tr:last-child > *:last-child { border-bottom-right-radius: calc(var(--nm-radius-lg) - 1px); }


/* ── dp grid (.dp-table) ── */
/* Full content width — the table stretches to fill its container (matching the
   Approval Dashboard panel). The day/total columns keep their fixed widths; the
   Notes column carries no fixed width so it flexes to consume the remaining width
   and the table reaches the full page width. On a narrow screen the fixed columns
   overflow and the user pans/zooms — they are never squished to fit the viewport
   (which clipped the cost-object text). */
.dp-table { font-size: var(--nm-font-grid); border-collapse: collapse; width: 100%;
            font-family: var(--nm-font-mono); font-variant-numeric: tabular-nums; }
.dp-table th, .dp-table td { border: 1px solid var(--nm-border); padding: 1px 1px; vertical-align: middle; line-height: 1.3; }
/* Minimum row height so a row with no cost object / no times doesn't
   collapse to input height and make the grid look cut off. Row `height`
   is unreliable in tables; bump the tbody cells' vertical padding from
   1px to 3px instead — same trick the eh-table uses. */
.dp-table tbody td { padding: 3px 1px !important; }
/* ─── Entry-grid border model (2026-06-11): border-collapse:separate so the
   table can round its own corners — `collapse` ignores border-radius entirely
   and the square table corners poked past the rounded .nm-tablecard wrapper.
   Single-side model: every cell draws only its RIGHT + BOTTOM edge; the
   wrapper's own 1px var(--nm-z200) border supplies the outer frame, so the
   last column's right border and the visually-last row's bottom border are
   zeroed. Scoped to .nm-entrygrid — the read-only Time Entries preview shares
   .dp-table and keeps `collapse` (its .nm-tablecard clips via overflow:hidden). */
.dp-table.nm-entrygrid { border-collapse: separate; border-spacing: 0; border-radius: calc(var(--nm-radius-lg) - 1px); }
.dp-table.nm-entrygrid th, .dp-table.nm-entrygrid td {
    border: 0;
    border-right: 1px solid var(--nm-border);
    border-bottom: 1px solid var(--nm-border);
}
.dp-table.nm-entrygrid tr > *:last-child { border-right: 0; }
/* Visually-last row = the single tfoot totals row (2026-06-12 restructure).
   The wrapper border closes the frame below it. */
.dp-table.nm-entrygrid tfoot tr:last-child td { border-bottom: 0; }
/* Under `separate`, abutting borders stack instead of merging — a .dp-vdiv
   cell's 2px black left edge would sit beside its neighbor's 1px right edge
   (3px total). Zero the right border of any cell directly before a divider. */
.dp-table.nm-entrygrid th:has(+ .dp-vdiv),
.dp-table.nm-entrygrid td:has(+ .dp-vdiv) { border-right: 0; }
/* Round the corner CELLS too, so their header/total backgrounds don't
   overshoot the table radius. 1px less than the wrapper's --nm-radius-lg
   so the rounding nests flush inside the wrapper's 1px border (these
   out-specific the global .nm-tablecard-open corner rules in app.css).
   Bottom corners are tfoot row 1's rowspan=2 end cells — they are what
   visually sits in the table's bottom corners. */
.dp-table.nm-entrygrid thead tr:first-child > *:first-child { border-top-left-radius: calc(var(--nm-radius-lg) - 1px); }
.dp-table.nm-entrygrid thead tr:first-child > *:last-child  { border-top-right-radius: calc(var(--nm-radius-lg) - 1px); }
.dp-table.nm-entrygrid tfoot tr:first-child > *:first-child { border-bottom-left-radius: calc(var(--nm-radius-lg) - 1px); }
.dp-table.nm-entrygrid tfoot tr:first-child > *:last-child  { border-bottom-right-radius: calc(var(--nm-radius-lg) - 1px); }

/* Solid horizontal separator between data rows + under the header block. The rule
   deliberately targets only the BOTTOM of the header region — the second header row
   (Hrs/OPTO/OFS) and any rowspan=2 cells from the first row (Order / Total / Notes) —
   so the date row (MON APR 30…) keeps its thin internal light border against
   the Hrs/OPTO/OFS sub-header directly below it. */
.dp-table.nm-entrygrid thead tr:last-child th { border-bottom: 2px solid #000; }
.dp-table.nm-entrygrid thead tr:first-child th[rowspan] { border-bottom: 2px solid #000; }
.dp-table.nm-entrygrid tbody tr td { border-bottom: 2px solid #000; }
/* Totals bar: the 2px separator above it is the LAST TBODY ROW's bottom
   border (single-side model — a tfoot border-top would stack on it under
   `separate`). The bar is ONE row (2026-06-12) of self-labeled stat
   cells — label / hairline / value stacked per stat, NO vertical
   sub-dividers, so nothing misaligns with the body's 3 columns. */
.dp-totstats { display: flex; align-items: stretch; min-height: 32px; }
/* justify-content:flex-start (NOT center) — stats with a value and
   stats with an empty value must share the same label baseline, or
   the underlines stagger. */
.dp-totstat { flex: 1; display: flex; flex-direction: column; align-items: stretch;
              justify-content: flex-start; padding: 2px 3px; }
/* Vertical sub-divider between stats. */
.dp-totstat + .dp-totstat { border-left: 1px solid var(--nm-border); }
/* Label, underline and value all BLACK (z950) — the grey/z400 pass read
   too faint. Inter-stat border-left above stays
   var(--nm-border) — only the in-stat text/underline darkened. */
.dp-totstat-lbl { font-size: var(--nm-font-small); font-weight: 500; color: var(--nm-z950);
                  text-align: center; line-height: 1.1;
                  border-bottom: 1px solid var(--nm-z950);
                  padding-bottom: 2px; margin-bottom: 2px; }
/* flex:1 + centered so the value floats mid-space below the label
   regardless of bar height; empty values keep the box via min-height. */
.dp-totstat-val { font-weight: 600; text-align: center; line-height: 1.1; min-height: 12px;
                  color: var(--nm-z950);
                  flex: 1; display: flex; align-items: center; justify-content: center; }
/* Vertical divider between day groups (and at the Total boundary). Applied to the
   first cell of each day block in every row — colspan=3 date headers, Hrs sub-header,
   Hrs data cell — plus the rowspan=2 Total header and the per-row Total cell.
   Single-side model: the preceding cell's border-right is zeroed via the
   :has rule above, so the divider renders at exactly 2px. */
.dp-vdiv { border-left: 2px solid #000 !important; }
/* Thin divider for lighter internal column boundaries. Normalized to the
   1px zinc base border 2026-06-11 so every sub-column split (header,
   body, tfoot) is the same color and the verticals read as one line —
   only the 2px black .dp-vdiv day-group boundaries stay black.
   (Currently unused in markup — the ST/PTO/OPTO/OFS totals splits are
   inline border-rights on the cells; kept for parity with .dp-vdiv.) */
.dp-vdiv-thin { border-left: 1px solid var(--nm-border) !important; }

/* 1px black vertical separators between columns of the Weekly Summary + SAP Entries
   tables below the main grid. Overrides the default .dp-table light-blue 1px border
   on the right edge of every cell so column breaks are easier to follow. */
.summary-v-sep th, .summary-v-sep td { border-right: 1px solid #000 !important; }

/* "✕" remove button — visuals come from the global .nm-iconbtn /
   .nm-iconbtn-del helpers (app.css); only the layout offset stays here. */
.dp-rm { margin-left:2px; }

/* Excel light-blue header */
.dp-hdr { background-color: var(--nm-hdr-bg); color: #1a1a1a; font-weight: normal; text-align: center; font-size: var(--nm-font-small); white-space: nowrap; }
.dp-hdr-wknd { background-color: var(--nm-hdr-bg); /* weekend tint dropped 2026-06-11 — the SAT/SUN text suffices (was var(--nm-z200), before that blue-grey #c8d8e8) */ color: #1a1a1a; font-weight: normal; text-align: center; font-size: var(--nm-font-small); white-space: nowrap; }

/* Calculated cells */
.dp-calc { background-color: var(--nm-calc-bg); text-align: center; font-size: var(--nm-font-grid); }

/* Zebra-stripe every other data row, excluding .dp-calc cells so the Total column
   keeps its tan tint. Mirrors the alternating-row helper in the ANCS00023 template
   so the eye can track a single row across 22+ narrow columns without losing place. */
.dp-table tbody tr:nth-child(even) > td:not(.dp-calc) { background-color: var(--nm-z100); /* kit zebra (was lavender #EFEBFF) */ }
.dp-table tbody tr:nth-child(odd)  > td:not(.dp-calc) { background-color: #fff; /* kit zebra (was grey #F0F0F0) */ }

/* Inputs */
.dp-table input[type=text] {
    border: 1px solid var(--nm-z300); border-radius: var(--nm-radius); padding: 0 2px;
    font-size: var(--nm-font-grid); background: #fff; height: var(--nm-control-h); width: 100%; min-width: 80px;
}
.dp-table input[type=number] {
    border: 1px solid var(--nm-z300); border-radius: var(--nm-radius); padding: 0;
    font-size: var(--nm-font-grid); background: #fff; height: var(--nm-control-h); width: 34px; text-align: center;
    -moz-appearance: textfield;
}
.dp-table input[type=number]::-webkit-outer-spin-button,
.dp-table input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.dp-table input[type=checkbox] { width: 16px; height: 16px; margin: 0; cursor: pointer; }

/* Summary + SAP Entries layout. Both tables are content-width — each column
   only as wide as its content, the table only as wide as the column sum, and
   the flex row wraps them onto separate lines when there's no room. A wide
   table overflows and the user pans/zooms rather than stretching to fill the
   viewport (tables should only be as wide as their contents, not spread
   across the whole interface). */
.dp-summary-sap-row { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; margin-top: 6px; }

/* Weekly Summary: content-sized column, with a bit of horizontal cell padding
   so headers aren't cramped against the column borders when there's space. */
.dp-summary-col--weekly { flex: 0 0 auto; min-width: 0; }
.dp-summary-col--weekly .dp-table th,
.dp-summary-col--weekly .dp-table td { padding-left: 6px; padding-right: 6px; }

/* SAP Entries: also content-sized — no stretch-to-fill, which previously
   ballooned the auto-width Description column across the whole row. */
.dp-summary-col--sap { flex: 0 0 auto; min-width: 0; }

/* Company holiday — per-country / (US) per-state paid-off day. The day's
   column gets a NACOS-deep-red tint so it reads as "special"; the holiday
   name below the date header matches that tint so the label visually
   "belongs to" the column. Inputs stay white and editable — DP engineers
   record hours against holidays themselves (OPTO / actual work as
   applicable). The darker colour won the legibility test over the earlier
   tan tint — a DP engineer reviewing last year in one glance needs the
   holiday columns to pop without squinting. */
.dp-hol-hdr { font-size: var(--nm-font-small); color: #880015; font-weight: bold; font-style: italic; display: block; margin-top: 1px; }
.dp-hol-cell { background-color: #880015 !important; }

/* Weekend-falling holidays — soft gray-italic FYI label, no cell tint. The
   day is already non-work; the deep-red would over-claim. */
.dp-hol-hdr-weekend { font-size: var(--nm-font-small); color: #888; font-weight: normal; font-style: italic; display: block; margin-top: 1px; }

/* Over-24h-per-day column highlight + invalidation. Sum of Hrs across all rows
   for one day exceeds 24 → column gets the NACOS red tint and Submit is
   blocked. Applied per-cell since the day spans multiple rows; the red wins
   over dp-hol-cell's tan when both apply. */
.dp-day-over24 { background-color: var(--nm-red) !important; color: #fff; }
.dp-day-over24 input { background-color: var(--nm-red) !important; color: #fff; }

/* ─── Display-table treatment (Tailwind Plus "condensed content" look) ───
   Read-only Time Entries preview only — the main entry grid keeps its Excel
   gridlines. !important is required on borders/backgrounds to out-rank
   .summary-v-sep's !important border-right, the inline border-left on the
   Total column, and the zebra-stripe / dp-calc backgrounds above. Font
   family (mono) and size are inherited unchanged — it's data. */
.dp-table.nm-displaytable th,
.dp-table.nm-displaytable td {
    border: 0 !important;
    border-bottom: 1px solid var(--nm-z200) !important;
    padding: 3px 10px !important;
}
.dp-table.nm-displaytable thead th {
    background: var(--nm-z50) !important;
    font-weight: 600;
    color: var(--nm-z950);
    border-bottom: 1px solid var(--nm-z300) !important;
}
/* White body — every cell here is calculated, so the tan dp-calc tint
   (and the entry grid's zebra striping) carries no meaning. */
.dp-table.nm-displaytable tbody td {
    background: #fff !important;
    color: var(--nm-z500);
}
.dp-table.nm-displaytable tbody td:first-child { color: var(--nm-z950); }
.dp-table.nm-displaytable tfoot td {
    font-weight: 600;
    color: var(--nm-z950);
    border-top: 1px solid var(--nm-z300) !important;
    border-bottom: 0 !important;
}


/* ── AutoGrowTextArea base (Erik 2026-07-25) ─────────────────────────────────────────────
   EVERY multi-line box starts at the SWR reading size (the --srm-area-font convention) and
   shrinks only past ~15 lines (nm002m-ui.js fitTextarea). The min-height floors the box at
   least as tall as a standard single-line input, so a pre-layout fit can never leave it
   squashed (the 3px Week-notes bug). CSS min-height beats the JS's inline height, so the
   floor holds even against a stale measurement.
   Start size: 18px. It opened at 18pt (= 24px) when v1.3.304 unified the boxes; Erik read
   that as too big on a real report and asked for 18 (2026-07-25). ONE value — the shrink
   logic reads whatever the CSS says. */
textarea.nm-autogrow {
    font-size: var(--nm-area-font, 18px);
    min-height: 2.4rem;
    line-height: 1.35;
}


/* ── Note inputs on touch devices (Erik 2026-07-25/26) ───────────────────────────────────
   v1.3.321 lifted the FOCUSED note input to a fixed bar at the top of the viewport; Erik
   (IMG_1609): it teleported over the environment bar and out of context — "instead, move
   the viewport over the input box selected, at the left side of the screen." That scroll
   behavior lives in nm002m-ui.js (focusin → scroll the .ts-scroll box so the focused
   cell lands at its left edge). Here only the iOS zoom guard remains: 16px on the input
   at REST — Safari makes the zoom decision from the size at focus time, so a :focus-only
   bump is too late. */
@media (pointer: coarse) {
    input.nm-note-input { font-size: 16px; }
}

/* ── All-week tile entrance (Erik 2026-07-29: tiles "should fade in if they have data,
   not fly out and disappear in a rush") — runs once when the tile element enters the DOM,
   i.e. when its lazy read lands non-empty. Gentle and short; no exit animation (a tile
   that leaves does so because the data says it should — see the known-empty skip). */
@keyframes nmTileFadeIn {
    from { opacity: 0; transform: translateY(3px); }
    to   { opacity: 1; transform: none; }
}
.nm-tile-fadein { animation: nmTileFadeIn 0.3s ease-out; }

/* ── Optional Note touch focus widen-in-place (Erik 2026-07-29: "I wanted it to widen to
   the width of the page in place" — v1.3.362's fixed-top overlay hid under the
   environment banner). On touch devices the FOCUSED note input grows LEFTWARD from its
   own cell to (almost) the viewport width, staying on its row: absolute within the cell,
   anchored to the cell's right edge, vertically centered on the row. No fixed
   positioning, so nothing overlays it and it scrolls with its row. CSS-only: the §54
   freeze, the events and the DOM shape are untouched. 16px font so iOS doesn't zoom.
   PHONE-WIDTH ONLY (Erik: "this only needs to apply for the phone width view") — the same
   ≤640px breakpoint as the phone layout layer, so tablets/desktops keep the plain cell. */
@media (max-width: 640px) {
    td:has(input.nm-note-input:focus) {
        position: relative;
        min-width: 90px;   /* hold the cell's footprint while the input floats */
    }
    input.nm-note-input:focus {
        position: absolute;
        right: 2px;
        top: 50%;
        transform: translateY(-50%);
        /* !important on width + visuals: every clock form carries its own higher-specificity
           input rules — e.g. `.tsf-all .ts-table input.ts-order` sets width:100% at (0,3,1),
           beating this rule's (0,2,1), and 100% of the ANCHOR CELL is ~90px, so the input
           floated but never widened (Erik 2026-07-29: "completely unreadable… it should
           take up the width of the window but it isn't"). Same war over border / padding /
           font-size (the 16px is the iOS zoom guard — 15.2px re-zooms). Verified in a live
           harness: without !important the focused input is ~160px, with it ~92vw. */
        width: min(92vw, 640px) !important;
        z-index: 30;
        font-size: 16px !important;
        padding: 10px 12px !important;
        border: 2px solid #2e7d32 !important;
        border-radius: 8px !important;
        background: #fff;
        color: #1b2b38;
        box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
    }
}


/* ── Phone layout layer (Erik 2026-07-25: "only when it is viewed at a small phone width
   the interface would be adapted") ─────────────────────────────────────────────────────
   ≤640px ONLY — desktop is byte-identical. Two jobs:

   1. KILL THE ZOOM-OUT — BY REMOVING THE WIDENERS, NEVER BY CLIPPING. The desktop
      "content-width philosophy" (article.nm-article min-width:fit-content + .nm-tablecard
      shrink-wrap; the user pans) makes iOS Safari scale the WHOLE page down to the widest
      element, so everything renders tiny all the time (Erik's screenshots). At phone width
      the philosophy inverts: the card stops growing (min-width:0 below), so .ts-scroll
      finally clips the entry grids at the viewport and they scroll INSIDE their boxes;
      the preview/summary .nm-tablecards do the same.
      ⚠ Deliberately NO html/body overflow-x clamp: v1.3.322 shipped one and it made any
      surface this layer hadn't boxed yet UNREACHABLE — clipped with no pan, no zoom
      ("basically non-functional on a phone", Erik 2026-07-26). A residual wide surface
      must fall back to the old pan/pinch-zoom behavior, not get amputated.

   2. BIGGER TYPE + TAP TARGETS. The size variables are the one lever the whole app reads —
      bumping them here scales grids, chrome and badges together. hdr-* chrome overrides
      live in Home.razor's own style block (its rules render later in the document, so the
      override must sit with them). */
@media (max-width: 640px) {
    :root {
        --nm-font-grid: 0.95rem;    /* grid body text (0.8rem on desktop) */
        --nm-font-ui: 1rem;         /* chrome: buttons, inputs, pickers (0.85rem) */
        --nm-font-small: 0.78rem;   /* badges, sub-labels (0.7rem) */
        --nm-control-h: 32px;       /* interactive-target floor (24px) */
    }
    /* THE widener: the white app card grows to its densest grid on desktop. At phone
       width it clamps to the viewport, which is what lets every .ts-scroll box inside
       actually scroll instead of inflating the card (and with it the whole page). */
    article.nm-article { min-width: 0; max-width: 100%; }
    /* The title/offline-chip bar wraps instead of forcing ~520px of page width. */
    .nm-appbar { flex-wrap: wrap; }
    /* Preview / summary cards: stop shrink-wrapping past the viewport — wide tables
       scroll inside the card. The entry-grid variant (-open) is excluded: it needs
       overflow visible for the autocomplete dropdown and already sits in .ts-scroll. */
    .nm-tablecard:not(.nm-tablecard-open) {
        display: block;
        max-width: 100%;
        overflow-x: auto;
    }
    /* Calculated columns yield to INPUT columns (Erik IMG_1610: "Site prep wait trvl and
       status columns can all be width 0 when there is no space as they're just
       calculated — you need to be able to see the input fields primarily"). Entry grids
       only — the read-only preview tables keep their calculated cells. Covers the clock
       templates' Site/Prep/Wait/Trvl/Holiday/ST-split cells (.ts-calc) and the Status
       chip column (.ts-status-col). DP's per-row totals use dp-calc and stay. */
    .nm-entrygrid .ts-calc,
    .nm-entrygrid .ts-status-col { display: none; }
}
