/* Minimal starter styling - swap for whatever design system/branding you want.
   Kept deliberately plain so it doesn't fight a future design pass. */

:root {
    --border: #d9dee3;
    --text-muted: #5f6b76;
    /* NEW (2026-09-20, shift-patterns round 10) - Karl: "Please make the
       hh:mm text lighter grey... make the same lighter grey" (the
       "mins"/"count" placeholder text too). --text-muted above is this
       app's one existing muted-grey token, used everywhere for genuine
       body/label text that still needs to be read as text - a
       placeholder is deliberately meant to recede further than that (it
       disappears the moment a value is typed), so this is a second,
       lighter step rather than reusing --text-muted at a lower opacity
       (which would also dim the border/background together in any rule
       that reused the same custom property, not just the text colour). */
    --text-muted-light: #a3adb5;
    /* NEW (2026-09-20, shift-patterns round 12) - Karl: "Please make the
       'hh:mm' and 'mins' default text to an even lighter grey." A third,
       paler step again - --text-muted-light above (round 10) is kept as
       the "count" placeholder's own colour, this one is specifically for
       hh:mm/mins (see .shift-day-table--wide's own placeholder rules
       further down for exactly which placeholder text uses which token). */
    --text-muted-lighter: #c1cbd3;
    /* NEW (2026-09-20, shift-patterns round 14) - Karl: "Please make hh:mm
       and mins default text lighter grey still." A fourth, paler step
       again - --text-muted-lighter above (round 12) is left in place but
       no longer used by this table's own placeholder rule, which now
       points at this token instead (see .shift-day-table--wide's own
       placeholder rules further down). */
    --text-muted-lightest: #d5dfe7;
    --primary: #1f6feb;
    --danger: #b3261e;
    --success: #1a7f37;
    --surface: #ffffff;
    --surface-alt: #f5f7f9;
    /* NEW (2026-09-11) - factored out of .calendar-day--selected's own
       background (below) so CalendarDayCell.razor's half-day selection
       highlight (isHalfSelection, its own remarks) can paint exactly the
       same blue tint over just one half of a cell, via this same variable,
       rather than duplicating the color-mix() expression in two places. */
    --calendar-selected-bg: color-mix(in srgb, var(--primary) 20%, var(--surface));
    /* NEW (2026-09-11, still later) - Karl's hover-preview outline for an
       existing absence on BookAbsence.razor (CalendarDayCell.
       IsPreviewHighlighted) - a distinct amber, deliberately not --primary
       (already used for .calendar-day--popup-highlight, the day-details
       popup's own ring) or --danger/--success, so the two never read as
       the same thing even if both applied to one cell at once. */
    --hover-preview-outline: #b45f06;
    /* NEW (2026-09-18, Karl's message 3) - the "yellow"/"purple" pill
       colours for AbsenceTypesTable.razor's new pro-rata allowance
       rounding pill (see that file's own remarks). Deliberately new,
       distinct tokens rather than reusing --hover-preview-outline (already
       "amber", a different context/table) - this app had no purple
       anywhere before this. Both used the same way --danger/--success/
       --hover-preview-outline already are: solid for a pill's own
       border/text, a light color-mix() tint (15%) for its background. */
    --rounding-fixed: #8a6d00;
    --rounding-half-day: #6f42c1;
    /* NEW (2026-09-20) - Karl's shift-end "wraps past midnight" highlight
       on ShiftPatternEditor.razor's day table (see .shift-end-wraps
       further down): "highlight ... in light yellow background." This
       app's first genuine general-purpose highlight/warning colour -
       --rounding-fixed above is a similar hue but scoped to one specific
       control's own Fixed state, not meant to be reused for an unrelated
       "pay attention here" cue, so this is its own token. */
    --highlight-wrap-bg: #fff3c4;
    /* NEW (2026-09-25) - the "Custom allowance overrides" section's always-
       visible warning paragraph (EmployeeDetail.razor/CompanyGroupEditor.razor -
       Karl: "There should be a message/subtitle at the top of this override
       to state this is not designed to be used as a one-off change"). Text
       colour paired with --highlight-wrap-bg above (this app's existing
       general-purpose warning background) rather than reusing --danger
       (that's this app's "something's wrong" red, not "please read this
       carefully" amber) or --rounding-fixed (already scoped to one specific
       control's own state, per that token's own remarks just above). */
    --warning-text: #7a5d00;
}

/* The small fixed colour allowlist CompanyLabelMarkup.Render recognises in
   company_labels.Subtitle text - see that class's own remarks. Reuses the
   same design tokens as everywhere else rather than introducing new
   colours just for this. */
.label-danger { color: var(--danger); }
.label-success { color: var(--success); }
.label-primary { color: var(--primary); }
.label-muted { color: var(--text-muted); }

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
    color: #1a1f24;
    background: var(--surface-alt);
}

/* Fixed viewport height (not min-height) + overflow: hidden here is what
   stops the whole page scrolling as one block - it forces .nav-menu and
   main's .content (the two children that need to move independently) to
   each manage their own scrolling instead, rather than the browser just
   growing the page underneath everything. */
.app-shell {
    display: flex;
    height: 100vh;
    overflow: hidden;
}

.nav-menu {
    width: 250px;   /* CHANGED (2026-10-12) - was 220px (Karl: "slightly wider") */
    flex-shrink: 0;
    height: 100vh;
    overflow-y: auto;
    background: #10151a;
    color: #cfd8e0;
    padding: 1rem;
}

.nav-menu .nav-header {
    font-weight: 600;
    font-size: 1.1rem;
    color: #fff;
    margin-bottom: 1rem;
}

.nav-menu nav {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin-top: 1rem;
}

.nav-menu nav a {
    color: #cfd8e0;
    text-decoration: none;
    padding: 0.5rem 0.6rem;
    border-radius: 6px;
}

.nav-menu nav a:hover,
.nav-menu nav a.active {
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
}

/* Expandable nav groups (Company settings, Devices, and any future
   top-level page with children - see AppPageRegistry/NavMenu.razor).
   .nav-group-link reuses the same "nav-menu nav a" rule above for colour/
   hover/active - this just adds the flex row so the chevron toggle sits
   beside it instead of stretching the whole row into one giant link. */
.nav-group-header {
    display: flex;
    align-items: stretch;
    gap: 0.15rem;
}

.nav-group-header .nav-group-link {
    flex: 1 1 auto;
    min-width: 0;
}

.nav-expand-toggle {
    flex: 0 0 auto;
    width: 1.75rem;
    background: none;
    border: none;
    color: #9aa7b2;
    cursor: pointer;
    border-radius: 6px;
    font-size: 0.7rem;
    transition: transform 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.nav-expand-toggle:hover {
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
}

.nav-expand-toggle.expanded {
    transform: rotate(90deg);
}

.nav-children {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin: 0.25rem 0 0.25rem 0.75rem;
    padding-left: 0.5rem;
    border-left: 1px solid rgba(255, 255, 255, 0.12);
}

.nav-children a {
    font-size: 0.92rem;
}

.tenant-switcher {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    margin-bottom: 0.5rem;
}

.tenant-switcher select {
    width: 100%;
}

/* main is the right-hand column - a flex column itself, so its two
   children (.top-row, .content) can be told apart: the header stays a
   fixed size, and only .content is allowed to grow/scroll (see below). */
main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
}

.top-row {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 1rem;
    padding: 0.75rem 1.5rem;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    /* Never shrunk or scrolled - stays pinned at the top of main while
       .content scrolls underneath it. */
    flex-shrink: 0;
}

.tenant-badge {
    font-size: 0.85rem;
    color: var(--text-muted);
}

.role-chip {
    margin-left: 0.4rem;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    font-size: 0.75rem;
}

/* NEW (2026-09-12) - the portal-wide "Acting as" switcher, in the header's
   .top-row (a flex row, see above) alongside .tenant-badge/.logout-form -
   an inline label+select, not stacked like .tenant-switcher above (that
   one lives in the sidebar's own column-flex .nav-menu, a different
   layout context). */
.acting-as-switcher {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.acting-as-switcher select {
    font: inherit;
    color: inherit;
    padding: 0.2rem 0.4rem;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--surface);
}

.acting-as-blocked {
    color: var(--danger);
}

.logout-form { margin: 0; }

.logout-button, button {
    font: inherit;
    padding: 0.4rem 0.9rem;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--surface);
    cursor: pointer;
}

button.primary {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}

/* The only element that actually scrolls now - nav-menu and .top-row are
   both pinned (see .app-shell/main above), so this is where page content
   grows past the viewport and gets its own scrollbar instead of the whole
   page moving. */
.content {
    padding: 1.5rem;
    flex: 1 1 auto;
    overflow-y: auto;
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--surface);
}

.data-table th, .data-table td {
    text-align: left;
    padding: 0.6rem 0.8rem;
    border-bottom: 1px solid var(--border);
}

.clickable-row { cursor: pointer; }
.clickable-row:hover { background: var(--surface-alt); }

/* Narrow leading column holding the ▸/▾ expand indicator on the Employee
   list's rows (2026-09-06) - fixed-width and muted so it reads as a UI
   affordance rather than a data column. Widened 1.5rem -> 28px (2026-09-07)
   to match the inline width now set on this column's own <th> in
   EmployeeList.razor (that inline width is what actually governs the
   rendered column under table-layout:fixed - this class's own width is
   mostly cosmetic/informational at this point, but kept in step with it so
   the two never quietly disagree again). See .employee-list-table's own
   min-width below for the real fix to what Karl reported ("the drop down
   arrow column is compressed and displays the icon and '...'" on smaller
   screens) - this rename-in-step alone wouldn't have fixed that on its
   own. */
.expand-indicator {
    width: 28px;
    text-align: center;
    color: var(--text-muted);
}

/* NEW (2026-09-18, next round) - Karl: "The employee_id column is adding
   three dots to the ▾ icon's cell on lower resolution displays as if there
   is not enough room to display the entire cell's contents." Different root
   cause from the earlier "drop down arrow column is compressed" report
   above - the min-width fix below still holds (the 933px sum this column's
   own 28px feeds into hasn't changed), so the table itself isn't being
   squeezed. This is plain arithmetic instead: this app's own global
   `* { box-sizing: border-box; }` (top of this file) means a cell's
   declared width already INCLUDES its padding, and .data-table th/td's
   shared 0.6rem 0.8rem horizontal padding (1.6rem, ~25.6px at the browser's
   default 16px root font-size) alone eats nearly this whole 28px column,
   leaving only a couple of pixels for the actual ▸/▾ glyph - marginal
   enough that it renders fine under some font-rendering/zoom/DPI
   combinations and clips into "..." under others, which matches "lower
   resolution displays" rather than one specific breakpoint (this isn't a
   @media-query-shaped problem at all). Two classes
   (.employee-list-table .expand-indicator) needed here to out-specificity
   .data-table th/.data-table td's own class+element selector (0,1,1) - a
   bare .expand-indicator override (0,1,0) would lose to it regardless of
   source order. Only the horizontal padding is reduced - the shared
   vertical 0.6rem is left alone, so row height doesn't change.

   WIDENED SLIGHTLY (2026-09-18, next round again) - Karl: "Is it possible
   to increase the padding slightly please - the padding is now very
   small." 0.2rem -> 0.4rem each side (3.2px -> 6.4px, 6.4px -> 12.8px
   combined) - still comfortably clear of the ~25.6px that caused the
   original overflow (28px column, content width now 15.2px), just less
   cramped-looking around the glyph than the first pass. */
.employee-list-table .expand-indicator {
    padding-left: 0.4rem;
    padding-right: 0.4rem;
}

/* Statuses.razor's leading column, same idea as .expand-indicator above -
   NEW (2026-09-06, later same day), replacing what used to be a numeric
   "ID" column. Karl: "The lock icon is fine to indicate the first 6
   values are hardcoded/locked, but people do not need to be aware of the
   ID, nor need to enter the ID of a new entry." Holds only the 🔒 icon
   (built-in rows) or nothing at all (the up-to-two custom rows) - no
   header text either, same "icon-only column, no label" precedent as
   .expand-indicator. Centred like that column too; a little wider (2.25rem
   vs. 1.5rem) since an emoji glyph needs more breathing room than a plain
   ▸/▾ character. */
.locked-indicator {
    width: 2.25rem;
    text-align: center;
    color: var(--text-muted);
}

/* Employee list only (2026-09-06, later same day) - Karl: "please make the
   titles and columns a fixed width as they are jumping around when I click
   the sort button and the sort icon appears." table-layout:fixed makes the
   browser honour each <th>'s own width (set inline in EmployeeList.razor)
   regardless of a cell's content width, so the sort arrow appearing/
   disappearing in .sort-indicator can no longer nudge every column over -
   scoped to this one table rather than the shared .data-table rule, since
   other data-table pages (e.g. the settings tables) rely on auto layout
   for columns that don't specify a width at all. */
.employee-list-table {
    table-layout: fixed;

    /* NEW (2026-09-07) - fixes Karl's report: "On smaller screens, the drop
       down arrow column is compressed and displays the icon and '...'."
       Root cause: this table is width:100% + table-layout:fixed, sitting
       inside .table-scroll (overflow-x:auto) - with no min-width of its
       own, a width:100% table can never actually grow wider than its
       container, so on a narrow viewport the browser proportionally
       shrinks every declared column width (including the expand-indicator
       column's own 28px, the narrowest of them all) to fit, rather than
       .table-scroll's horizontal scrollbar ever kicking in the way it's
       meant to for every other data table on this portal. This min-width
       is the sum of every <th>'s own declared width in EmployeeList.razor
       (28+65+70+140+140+100+100+105+105+80 = 933px, rounded up a little
       for cell borders) - once the table can't shrink below that, columns
       stop being squeezed at all on a narrow screen and .table-scroll
       scrolls horizontally instead, exactly like the rest of this portal's
       tables already do.

       UPDATED (2026-09-26) - the 28px expand column is gone (accordion
       replaced by row icons) and the old 80px Edit column is now a 200px
       icon column: 65+70+140+140+100+100+105+105+200 = 1025px, rounded up
       the same way.

       UPDATED AGAIN (same day) - Status/Presence columns 100 -> 126px to
       hold Karl's 100px pills: 65+70+140+140+126+126+105+105+200 = 1077px.

       UPDATED AGAIN (2026-09-26, later) - icon column 200 -> 230px for the
       new Badges icon: 1107px. */
    min-width: 1120px;
}

/* NEW (2026-09-26) - Karl: "limit the Status and Presence pill width on
   hr/employees to around 100px in total. I think the status-pill text
   needs to be limited to about 80px." Scoped to the employee list only -
   .status-pill's base rule (block, full cell width) is shared by the
   Statuses settings page and others. 100px wide, 10px side padding + 1px
   border each side leaves 78px for the text, which ellipsises ("…") beyond
   that - the full name is in the pill's tooltip. */
.employee-list-table .status-pill {
    width: 100px;
    max-width: 100%;
    padding-left: 10px;
    padding-right: 10px;
}

.employee-list-table th,
.employee-list-table td {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* NEW (2026-09-26) - hr/employees' row action icons (Info, View, Calendar,
   Allowances, Edit, Set as leaver), replacing the old text Edit button.
   Same look for <button> and <a> (Calendar/Allowances are links so they
   can open in a new tab). Muted by default, primary blue on hover/focus, a
   faint disabled state for "Set as leaver" (not built yet). */
.employee-row-actions {
    white-space: nowrap;
}

.icon-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    margin-right: 2px;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    vertical-align: middle;
    text-decoration: none;
}

.icon-button:hover,
.icon-button:focus-visible {
    color: var(--primary);
    border-color: var(--border);
    background: var(--surface);
}

.icon-button:disabled,
.icon-button:disabled:hover {
    color: var(--text-muted);
    opacity: 0.35;
    border-color: transparent;
    background: transparent;
    cursor: not-allowed;
}

/* Screen-reader-only text (e.g. the icon column's "Actions" header) -
   standard visually-hidden pattern: present in the accessibility tree,
   takes no space on screen. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* NEW (2026-09-26) - hr/employees' Info popup. Same centred, dimmed native
   popover treatment as .allowance-forecast-popover, narrower since it's
   just the seven detail items. */
.employee-info-popover {
    padding: 1.1rem 1.35rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
    width: min(40rem, 90vw);
    max-height: 85vh;
    overflow-y: auto;
}

/* NEW (2026-09-26) - hr/employees' Allowances popup (read-only absence
   and allowance summary). Same look as .employee-info-popover, but wide
   enough for the twelve-column summary table (about 66rem on its own);
   on a narrower screen the table scrolls sideways inside the popup rather
   than squashing its columns. */
.employee-allowances-popover {
    padding: 1.1rem 1.35rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
    width: min(72rem, 95vw);
    max-height: 90vh;
    overflow-y: auto;
}

.employee-allowances-popover::backdrop {
    background: rgba(15, 23, 32, 0.55);
}

.employee-allowances-popover h2 {
    margin: 0 0 0.25rem;
    font-size: 1.15rem;
}

.employee-allowances-popover .form-actions {
    align-items: center;
    gap: 1rem;
}

.employee-absence-summary-panel__table {
    overflow-x: auto;
}

.employee-info-popover::backdrop {
    background: rgba(15, 23, 32, 0.55);
}

.employee-info-popover h2 {
    margin: 0 0 0.25rem;
    font-size: 1.15rem;
}

.employee-info-popover .subtitle {
    margin: 0 0 1rem;
}

/* white-space:nowrap above is inherited by default - reset it back to
   normal wrapping inside the expand panel (which sits in a colspan'd <td>
   of this same table), since that panel's content isn't a fixed-width
   column and should wrap normally rather than clip/ellipsis. */
.employee-list-table .inline-editor-panel {
    white-space: normal;
}

/* --- Inline expand-to-edit editor (2026-09-05, Company groups, renamed
   2026-09-06 from Employee groups - Karl's request: edit a group's
   customisations "on the same page ... open in section underneath when a
   box is ticked like the employee edit page", rather than navigating to a
   separate detail page per group). The editor's own <tr> spans every
   column via colspan, so it needs its own background/border framing to
   read as "attached to the row above it" rather than just another data
   row - .inline-editor-row turns off the normal cell padding/border (the
   inner .inline-editor-panel supplies its own instead) so the
   CompanyGroupEditor's EditForm isn't cramped inside table-cell spacing.
   The "new group" panel (shown above the table, not inside a <tr>) reuses
   the same .inline-editor-panel styling directly. */
.inline-editor-row > td {
    padding: 0;
    border-bottom: 1px solid var(--border);
}

.inline-editor-panel {
    background: var(--surface-alt);
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
    padding: 1rem 1.25rem;
    margin-bottom: 1rem;
    /* Same mount-reveal treatment as .override-fields (2026-09-06) - see
       that rule's own remarks for why this is a fade-and-slide-down
       animation rather than a true height transition. */
    animation: override-fields-reveal 0.35s ease;
}

.inline-editor-row .inline-editor-panel {
    border-top: none;
    border-bottom: none;
    margin-bottom: 0;
}

/* NEW (2026-09-20, round 2) - Karl: "Please smooth out the accordion
   expanding and shrinking animations." .inline-editor-panel above already
   had a smooth OPENING reveal (override-fields-reveal) - there was never
   a closing equivalent, since ShiftPatterns.razor's own @if just removed
   the panel from the DOM outright the instant a row/the "Add shift
   pattern" panel was toggled shut. See that file's own remarks for the
   new two-phase _closingPatternId/_addingNewClosing state that now keeps
   the panel mounted for exactly this animation's own duration before it's
   actually unmounted.

   Deliberately scoped to ShiftPatterns.razor ONLY, via this specific
   `--closing` class name - NOT a change to .inline-editor-panel itself,
   which CompanyGroupList.razor/EmployeeList.razor also use for their own
   unrelated inline editors and weren't asked to change. Same specificity
   as .inline-editor-panel (a single class selector, 0-1-0) - since both
   classes sit on the same element together
   (`class="inline-editor-panel inline-editor-panel--closing"`), this
   rule's own `animation` value wins cleanly over the base rule's by
   being declared LATER in this file, not by out-specificity-ing it - see
   this file's own documented specificity-tie trap further up
   (.absence-approval-table's own remarks) for why that ordering, not
   just "add another class", is what actually makes an override like this
   reliable.

   Plain fade-and-slide-up, the exact reverse of override-fields-reveal's
   own fade-and-slide-down, rather than a true height collapse - a real
   height/max-height animation would need the panel's own rendered height
   known up front (which isn't available without extra JS measurement,
   same reasoning override-fields-reveal's own remarks already give for
   why IT isn't a height transition either), and this table can run to
   hundreds of day rows for a long-cycle pattern, so a fixed max-height
   guess would either clip a tall table or do nothing for a short one.
   Rows below a closing panel therefore don't visually "ride up" smoothly
   as it shrinks - they simply appear once the panel's actually removed,
   same trade-off .absence-result-reveal's own fade-in already accepts
   elsewhere in this file. Flag if the smoother (but costlier) height-
   collapse treatment is wanted instead. 0.28s (vs. the opening reveal's
   0.35s) is deliberately a touch snappier - closing reads best that way -
   and MUST stay in step with ShiftPatterns.razor's own ClosingAnimationMs
   constant (280): that's the delay after which the C# side actually
   unmounts the panel, so a mismatch either direction would show either a
   visible unanimated jump (CSS finishes first) or a cut-off (C# removes
   it before the animation's played out). */
@keyframes inline-editor-panel-close {
    from {
        opacity: 1;
        transform: translateY(0);
    }
    to {
        opacity: 0;
        transform: translateY(-8px);
    }
}

.inline-editor-panel--closing {
    animation: inline-editor-panel-close 0.28s ease forwards;
    pointer-events: none;
}

/* Read-only field grid for the Employee list's expand panel (2026-09-06) -
   sits inside .inline-editor-panel alongside (not replacing) that class,
   which still supplies the panel's background/border/animation. Distinct
   from .form-grid since there's nothing editable here, just label/value
   pairs - three columns on wide screens, wrapping down on narrow ones. */
.detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 0.75rem 1.5rem;
}

.detail-item {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.detail-label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-muted);
}

.detail-value {
    font-size: 0.95rem;
}

/* CHANGED (2026-09-06, fourth time same day) - Karl: "Can you please also
   make the preview text the same size on each row?" font-size was already
   a fixed 0.8rem for every row - what actually varied row to row was the
   pill's own WIDTH, since it used to shrink-wrap however long each row's
   Description happened to be, so a short status ("Off") produced a small
   pill next to a long one ("Annual leave (unpaid)") producing a much
   wider one - inconsistent-looking even though the text itself was always
   the same size. Now block-level and stretched to the full Preview column
   width (the column's own 180px, set on the <th> in Statuses.razor) so
   every row's pill is the same size regardless of its text's length;
   overflow/ellipsis added so a description too long for that width is
   truncated with "…" rather than wrapping to a second line or overflowing
   the column, which would have reintroduced the same "some rows look
   bigger than others" problem this fix is for. */
/* CHANGED (2026-09-06, fifth time same day) - Karl: "Can you make the
   pills slightly taller too please." Vertical padding only (0.1rem ->
   0.35rem) - font-size and horizontal padding are unchanged, this is
   purely a height increase, not a bigger-looking pill overall. */
.status-pill {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 0.35rem 0.5rem;
    border-radius: 999px;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    font-size: 0.8rem;
}

.toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 1rem;
}

/* Employee list's Previous/Next paging control (2026-09-07, Karl: "Can you
   build in functionality to cope with a maximum number of employees shown
   per page"). Sits directly under .table-scroll - centred, same spacing
   idea as .toolbar above it but centred rather than space-between, since
   there's only the one row of controls to lay out rather than a count on
   one side and a button on the other. */
.pagination-toolbar {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 1rem;
    margin-top: 1rem;
}

/* NEW (2026-09-24, next round again) - Karl: "Can you please add a space in
   between the table and the total row count below. The text of '1-8 of 8
   shown' is overlapping the table." Root cause: the two AbsenceTypeSummary.
   razor pages (HR and manager-tools) put that "X-Y of Z shown" text straight
   after </table> in a <p class="subtitle"> - but .subtitle (further up this
   file) is `margin-top: -0.5rem`, a deliberate NEGATIVE pull written for
   its usual job of sitting close under a page's own <h1>. Reused here, that
   same negative margin pulls the text UP into the table's own bottom edge
   instead - exactly the overlap Karl's seeing. EmployeeList.razor's own
   "X-Y of Z shown" text doesn't have this problem - it's a <span> inside
   .toolbar, ABOVE its table, not a <p class="subtitle"> below one.

   Fixed with a new, dedicated class for this one job (a positive gap under
   a table, nothing else) rather than adjusting .subtitle itself, which
   plenty of unrelated pages rely on for its own original purpose. */
.pagination-range-label {
    color: var(--text-muted);
    margin: 1rem 0 0;
}

/* --- Employee list: filter toolbar + sortable headers (2026-09-04, Karl's
   request - "filtered and sorted by shift, department, area, site, name
   etc."). Sits above the existing .toolbar (Add employee button) as its own
   row, since it can wrap onto several lines on a narrow screen. */
.filter-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: flex-end;
    margin-bottom: 1rem;
}

.filter-toolbar label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.filter-toolbar input, .filter-toolbar select {
    font: inherit;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    min-width: 160px;
}

.filter-toolbar button { align-self: flex-end; }

/* MultiSelectDropdown.razor (2026-09-06, later again) - Karl: "Is it
   possible the drop downs can all be multi-select, but where the items are
   selected (ticked?) via the drop-down, not 5-line multi select box?"
   Every filter on the Employee list toolbar is now one of these, replacing
   both the old single-select <select>s and the native <select multiple>
   Status filter. min-width/padding/border deliberately echo
   .filter-toolbar select above (same trigger height/look as the controls
   it replaces), so the toolbar doesn't visually jump now that some
   controls are buttons instead of <select>s. */
.multiselect-dropdown {
    display: inline-block;
}

.multiselect-trigger {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.15rem;
    font: inherit;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    min-width: 160px;
    background: var(--surface);
    cursor: pointer;
    text-align: left;
}

.multiselect-trigger-label {
    font-size: 0.85rem;
    color: var(--text-muted);
}

.multiselect-trigger-value {
    font-size: 0.95rem;
}

/* Same [popover] inset/margin reset ColourSwatchPicker's own
   .colour-swatch-popover already needs, and the same reasoning -
   popover-position.js's top/left only take effect once the browser's own
   UA-default centred-dialog placement (inset: 0; margin: auto) stops
   fighting it. */
.multiselect-popover {
    position: fixed;
    inset: auto;
    margin: 0;
    padding: 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
    max-height: min(70vh, 320px);
    overflow-y: auto;
    min-width: 200px;
}

.multiselect-popover-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    font-weight: 600;
    padding-bottom: 0.35rem;
    margin-bottom: 0.35rem;
    border-bottom: 1px solid var(--border);
}

.multiselect-clear-link {
    font: inherit;
    font-weight: 400;
    font-size: 0.8rem;
    color: var(--primary);
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    text-decoration: underline;
}

.multiselect-options {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.multiselect-option {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.9rem;
    white-space: nowrap;
    cursor: pointer;
}

.multiselect-empty {
    font-size: 0.85rem;
    color: var(--text-muted);
}

.sortable-header {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.sortable-header .sort-indicator {
    margin-left: 0.25rem;
    color: var(--text-muted);
}

.form-grid {
    display: grid;
    /* 3 columns / 1120px max-width (2026-09-04, Karl's request - "make the
       total width about the same as the Company Settings page", matching
       .variable-section's own 1120px below) - was 2 columns/720px, which
       read as too narrow next to the rest of Company settings. */
    grid-template-columns: repeat(3, minmax(220px, 1fr));
    gap: 1rem;
    max-width: 1120px;
}

.form-grid label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.form-grid input, .form-grid select {
    font: inherit;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
}

/* NEW (2026-09-23) - AbsenceYears.razor's own Step 1 "create" row, Karl:
   "In the same style as other pages, please indent the input boxes from
   the titles above them. The Start and End date inputs can be much
   narrower. The 'Create absence and allowance period' button can be
   relabelled as 'Create period', made smaller and put on the same row as
   the 3 inputs, ensuring it is inline vertically with the 3 input boxes,
   not the labels above them." A page-scoped override that sits ALONGSIDE
   .form-grid rather than changing it - same reasoning
   .shift-pattern-header-grid (below) already established for
   ShiftPatternEditor.razor's own header row: .form-grid itself is shared
   by most other settings/edit pages on this site and stays untouched.

   Flex, not grid, and align-items:flex-end - the exact same reasoning
   .shift-pattern-header-grid already used (see that rule's own SWITCHED
   FROM GRID TO FLEX remarks): a grid with auto-sized tracks stretches
   every column to share the container's own width evenly, which is
   exactly what made the 3-column .form-grid version of this row look too
   spread out for a date field. flex-end aligns every item's own BOTTOM
   edge - the label/select pair's bottom (the select itself) lines up with
   the two narrower date inputs' own bottom, and with the button (which
   has no label text above it at all) - so the button sits inline with the
   3 inputs, not the labels above them, exactly as asked. */
.absence-year-create-grid {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 1rem;
}

.absence-year-create-grid label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

/* The indent itself - "please indent the input boxes from the titles
   above them... as per other pages." Reuses the exact technique
   .shift-pattern-header-grid label:not(.checkbox-field) > input/select
   (below) already established for the same request on that page: a small
   left margin on the input/select ELEMENT, not the label text, so the
   title sits slightly to the left of its own box - see that rule's own
   HEADER FIELD INDENT remarks for why this (rather than copying some
   other, pre-existing pattern) is "as per other pages" here too. */
.absence-year-create-grid label > select,
.absence-year-create-grid label > input {
    font: inherit;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-left: 0.4rem;
}

/* "The Start and End date inputs can be much narrower." A date input only
   ever needs to show "dd/mm/yyyy" plus the picker icon - 9rem (144px)
   comfortably fits that without the extra width a shared .form-grid
   column (minmax(220px, 1fr)) gave it. */
.absence-year-create-grid__narrow > input {
    width: 9rem;
}

/* "made smaller" - a compact variant of the general save-button look
   (.save-button/button.save-button--saving etc., further down this file),
   deliberately not reusing .save-button itself since that class is a
   fixed 8rem wide, sized for a full-width settings-page Save button - this
   one sits inline with two narrow date inputs instead and reads as too
   large at that width. */
.absence-year-create-button {
    padding: 0.35rem 0.75rem;
    font-size: 0.85rem;
}

/* hr/employees/new (and hr/employees/{id}, shared) - REDESIGNED
   (2026-09-25, still later), Karl's own row-by-row layout spec, then
   RE-PIXELLED (2026-09-25, later still - "the layout of the fields looks
   a bit of a mess") to his own literal per-field pixel widths, then
   CORRECTED AGAIN (2026-09-25, later still again - "that was not my
   intention... let me try again") once he saw the first pixel pass render
   flush/cramped with zero column gap: he wanted an explicit 10px gap
   between every field all along, with his own per-field figures already
   sized to leave room for it. Sits ALONGSIDE .form-grid, same as
   .absence-year-create-grid above - .form-grid itself stays untouched
   (it's shared by most other settings/edit pages), EmployeeDetail.razor
   just stopped using it entirely, in favour of these purpose-built
   classes.

   No shared ".employee-form-row" base class - the three row shapes below
   (--names/--structured/--equal4) differ enough in spacing/track counts
   that a shared base would save little; each is styled standalone
   instead. .employee-field IS shared - the label/input wrapper every
   field on this page now uses (replacing .form-grid label - same
   flex-column/gap/font shape, just under a page-specific name so it can
   diverge from .form-grid's own general one later without affecting other
   pages).

   IMPORTANT re: the pixel figures below - each one is a GRID COLUMN
   TRACK width, and every field's own <label> (.employee-field) is a grid
   item that stretches to fill its track (CSS Grid's default
   justify-items: stretch) - so e.g. "345px" is the COMPLETE width for
   that field's slot, title text and input both, exactly as Karl specified
   ("including title, indent, padding etc."). Every row now uses a real
   10px `gap` between columns, per Karl's own explicit figures - and his
   own numbers for rows 2/3/4 each still sum to exactly 1000px INCLUDING
   those gaps (e.g. row 2: 140+10+140+10+345+10+345=1000), so the 1000px
   ceiling and the 10px gap both hold at once, nothing needed shrinking
   further. Row 1's figures (140+10+240+10+240=640px) leave headroom under
   1000px, same as before. */
.employee-field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--text-muted);
    min-width: 0;
}

/* "The inputs for each field should be indented slightly from the titles
   for each. This should match formatting elsewhere." Same technique
   .absence-year-create-grid label > select/input (above) already
   established for the identical request on that page - a small left
   margin on the input/select ELEMENT itself, not the label text, so the
   title sits slightly left of its own box. Blazor's InputText/InputSelect/
   InputNumber, a plain <input type="date">, and EmployeePicker's own root
   <select> (it renders no wrapper element of its own) are all matched by
   this one selector, since every one of them ends up as a direct
   input/select child of its .employee-field <label>.

   Karl's pixel figures are meant to include this indent ("including
   title, indent, padding etc."), and since margin is NOT covered by the
   page's global box-sizing: border-box reset (that only pulls padding/
   border inside an element's own width, not a sibling margin), the input/
   select's width is explicitly reduced by the same 0.4rem so the indented
   box's right edge still lands exactly on its column's right edge instead
   of overflowing past it. */
.employee-field > input,
.employee-field > select,
.employee-field > textarea {
    font: inherit;
    width: calc(100% - 0.4rem);
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-left: 0.4rem;
}

/* Row 1 - Title 140px, First name 240px, Surname 240px, 10px gaps (Karl's
   own corrected figures, 2026-09-25 later still again). Grid, same
   mechanism as rows 2-4 below, so these are exact pixel column tracks
   rather than approximate content widths. 140+10+240+10+240=640px total,
   leaving headroom under Karl's 1000px ceiling. Deliberately NOT sharing
   .employee-form-structured's own max-width with rows 2-4 below (Karl
   only asked for THOSE three rows to line up with each other). */
.employee-form-row--names {
    display: grid;
    grid-template-columns: 140px 240px 240px;
    gap: 10px;
    margin-bottom: 1rem;
}

/* Rows 2-4 (Start/End date + status/presence; Manager + Employee group +
   Shift pattern; Business unit/Site/Department/Area) share this one
   max-width so row 4's four EQUAL columns still end up "[taking] up the
   total width of the 4 from row 2, and the 3 from row 3" (Karl's own
   wording) - now literally 1000px for all three rows, per his own "fixing
   the width of each row at a maximum of 1000px" figure. */
.employee-form-structured {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    max-width: 1000px;
}

/* Row 2 (Start date 140px / End date 140px / Employment status 345px /
   Presence 345px, 10px gaps) and row 3 (Manager 290px / Employee group
   345px / Shift pattern 345px, 10px gaps) share the EXACT SAME column
   tracks - Karl: "Manager could line up with the first two fields from
   row 2, with Employee Group and Shift Pattern lining up with the 3rd and
   4th fields respectively." His own row 3 figures confirm this still
   holds under the corrected spec: Manager's 290px = Start date's 140px +
   the 10px gap + End date's 140px exactly (CSS Grid's `grid-column: span
   2` naturally includes the gap BETWEEN the two tracks it spans, so no
   extra arithmetic is needed in the span rule itself - see
   .employee-field--span2 below), and Employee group/Shift pattern's 345px
   each match Employment status/Presence's 345px individually - so the
   same 4-column track/span mechanism from the original redesign still
   works, just with literal pixel tracks and a real 10px gap
   (140+10+140+10+345+10+345=1000px, Karl's own ceiling, gap included). */
.employee-form-row--structured {
    display: grid;
    grid-template-columns: 140px 140px 345px 345px;
    gap: 10px;
}

/* Manager (row 3, column 1) spans both date columns from row 2 directly
   above it - the actual mechanism that makes it "line up with the first
   two fields from row 2" per Karl's own wording. Spanning 2 tracks in CSS
   Grid automatically includes the gap that sits between them, so this
   element's rendered width is 140+10+140=290px - exactly Karl's own
   explicit Manager figure - with no change needed to this rule itself. */
.employee-field--span2 {
    grid-column: span 2;
}

/* Row 4 (Business unit 240px / Site 240px / Department 240px / Area
   250px, 10px gaps, Karl's own figures) - "the width of these [can] be
   split equally" - Area is 10px wider than the other three so the row
   still totals exactly 1000px including its three 10px gaps
   (240+10+240+10+240+10+250=1000), matching rows 2/3's own 1000px
   total. */
.employee-form-row--equal4 {
    display: grid;
    grid-template-columns: 240px 240px 240px 250px;
    gap: 10px;
}

/* NEW (2026-09-25, later still) - the three top-level section headings
   Karl's own "split the hr/employees/new into three data entry sections"
   request introduced (Employment details / Personal details / the renamed
   allowance-and-payroll-rules section). A bare <h2> here would render at
   the browser's default h2 size, which reads oversized next to
   .employee-field's own 0.85rem field labels - Karl: "The section titles
   and subtitles do not need to be overly large font." Same 1.05rem figure
   .variable-section h2 (Company settings) already uses, for consistency
   with how a "section heading, not a page heading" already looks
   elsewhere in this app, plus the same :has(+ .section-subtitle)
   margin-tightening trick that rule uses so a subtitle sits close
   underneath instead of leaving a doubled gap. */
.employee-section-heading {
    margin: 0 0 0.75rem;
    font-size: 1.05rem;
}

.employee-section-heading:has(+ .section-subtitle) {
    margin-bottom: 0.25rem;
}

/* NEW (2026-09-25, later still again) - Karl: "Can you please add a small
   vertical space between the section 2 and 3 titles, and the previous
   section above." Applied to every section heading AFTER the first
   (Personal details, the new Payroll details, and the custom rules
   section) - Employment details sits straight under the page title and
   doesn't need it. A modifier rather than a change to the base rule so
   the first heading keeps its flush top. */
.employee-section-heading--spaced {
    margin-top: 1.75rem;
}

/* NEW (2026-09-26) - EmployeeDetail.razor's View mode wraps every section
   in one <fieldset disabled="@IsReadOnly">. These resets make the fieldset
   itself invisible (no default border/padding/margin, and min-width 0 so
   it doesn't force the grid rows wider) in every mode. In View mode, the
   disabled controls are restyled to read as displayed information rather
   than greyed-out broken inputs: normal text colour, a light fill,
   default cursor. */
.employee-form-fieldset {
    border: 0;
    padding: 0;
    margin: 0;
    min-width: 0;
}

.employee-form-fieldset:disabled input,
.employee-form-fieldset:disabled select,
.employee-form-fieldset:disabled textarea {
    color: #1a1f24; /* body's own text colour */
    background: var(--surface-alt);
    opacity: 1;
    cursor: default;
}

/* NEW (2026-09-25, later still again) - Karl: "Please put a red asterisk
   after the title of each field which is mandatory." Uses the app's own
   --danger red. Each mandatory title is wrapped as
   <span>Title<span class="required-marker">*</span></span> - the wrapper
   matters, because .employee-field is a flex COLUMN, so a bare asterisk
   span would otherwise become its own flex item on a line of its own
   under the title. aria-hidden on the asterisk itself: it's a visual cue
   only (the fields' own validation messages carry the real meaning). */
.required-marker {
    color: var(--danger);
    margin-left: 0.15rem;
    font-weight: 600;
}

/* Section 2 "Personal details" own sub-groupings (Address/Contact/Next of
   kin/Notes) - MyDetails.razor's own identical group headings
   (self-service/my-details) render as bare <h2>s there since that page has
   no OUTER section heading to sit underneath; here they're nested one
   level deeper, under this page's own smaller .employee-section-heading,
   so a same-or-larger <h2> for them would invert that hierarchy. Sized
   between .employee-section-heading (1.05rem) and .employee-field's own
   field-label text (0.85rem), muted rather than full text colour, so
   these read clearly as sub-groupings without competing with the section
   heading above them. */
.employee-subsection-heading {
    /* CHANGED (2026-09-25, later still again) - top margin 0.25rem ->
       1rem, now that the rows above each sub-heading are tight pixel
       grids rather than .form-grid (which carried its own spacing). */
    margin: 1rem 0 0.5rem;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-muted);
}

/* NEW (2026-09-25, later still again) - Personal details re-laid-out to
   Karl's own pixel widths, same indented .employee-field treatment as
   Section 1 ("ensure the inputs are indented from their titles like
   section 1"). Each figure is a grid column track, so - exactly as in
   Section 1 - it's the COMPLETE width of that field (title, indent and
   input), with a 10px gap between fields.
   WIDTHS CORRECTED the same day (Karl: "I have messed up with the field
   widths") - every multi-field row now totals exactly 1000px:
     --pd-single   Preferred name                     290
     --pd-address  Address line 1/2/3, Town/city      240, 240, 240, 250
                   and County, Postcode, Country on the row below - the
                   SAME tracks, so those three line up exactly under
                   Address lines 1-3 (the 4th track is simply left empty)
     --pd-contact  Home/Mobile phone, Personal email, Preferred contact
                   method; and Next of kin Name/Phone/Email/Relationship
                                                      200, 200, 370, 200
     --pd-notes    "Anything else HR should know?"   1000
   .employee-form-group stacks several rows under one sub-heading with the
   same 1rem vertical gap .employee-form-structured uses in Section 1. */
.employee-form-group {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.employee-form-row--pd-single {
    display: grid;
    grid-template-columns: 290px;
    gap: 10px;
}

.employee-form-row--pd-address {
    display: grid;
    grid-template-columns: 240px 240px 240px 250px;
    gap: 10px;
}

.employee-form-row--pd-contact {
    display: grid;
    grid-template-columns: 200px 200px 370px 200px;
    gap: 10px;
}

.employee-form-row--pd-notes {
    display: grid;
    grid-template-columns: 1000px;
}

/* "2 lines height" - rows="2" on the InputTextArea itself sets the
   initial height; vertical resize still allowed if someone needs more
   room while typing, horizontal isn't (the width is fixed by the grid
   track above). */
.employee-field > textarea {
    resize: vertical;
}

/* NEW (2026-09-25, later still again) - Payroll details (HR only). Widths
   now Karl's own (replacing my first-pass guesses the same day): NI number
   alone on row 1 (140), then "Bank or building society name" 305, "Name on
   bank or building society account" 305, Sort code 180, Account number
   180 = exactly 1000px with three 10px gaps. */
/* WIDENED (same day) 140px -> 200px - Karl: "Can you please make National
   Insurance number field wider please as the title is wrapping." The
   title text alone is roughly 175px at .employee-field's 0.85rem. */
.employee-form-row--payroll-ni {
    display: grid;
    grid-template-columns: 200px;
    gap: 10px;
}

.employee-form-row--payroll-bank {
    display: grid;
    grid-template-columns: 305px 305px 180px 180px;
    gap: 10px;
}

/* NEW (2026-09-25, later still again) - Karl: "Can you please limit the
   width of the bottom custom section to 1000px as well." Every section
   subtitle on the employee screen (in practice, the custom rules
   section's long explanatory paragraph is the only one that would ever
   run past it) is capped at the same 1000px as the field rows above - see
   .override-section--employee just below .override-section for the four
   custom rules boxes themselves. .employee-section-heading is only used
   on EmployeeDetail.razor, so this can't affect any other page. */
.employee-section-heading + .section-subtitle {
    max-width: 1000px;
}

/* "a popup should be presented with a general warning... it should not
   mess with the line layout." Same native <div popover> reset
   .colour-swatch-popover (further down this file) already uses - position:
   fixed/inset:auto/margin:0 hands layout entirely to popover-position.js's
   own openNearId, and rendering in the browser's own top layer is what
   keeps this from disturbing the surrounding .data-table's own row
   heights the old inline "Confirm delete/Cancel" pair used to shift. */
.absence-year-delete-popover {
    position: fixed;
    inset: auto;
    margin: 0;
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
    max-width: 320px;
}

.absence-year-delete-popover p {
    margin-top: 0;
}

/* SINGLE-ROW HEADER FIELDS (2026-09-20, round 14, ShiftPatternEditor.razor
   only) - Karl: "Please try to fit the Name, Days in cycle, Allowance
   factor, Standard shift duration, Company default, and Active inputs in
   a single row." form-grid's own default is a 3-column grid (see its own
   2026-09-04 remarks) - deliberately unchanged here, since it's shared by
   every other settings/edit page on this site; this second class, added
   alongside it only in ShiftPatternEditor.razor, overrides just for this
   one page's header block.

   SWITCHED FROM GRID TO FLEX (2026-09-21, round 19) - Karl: "The header
   inputs are still all spread out, as if they are still in another table
   which is fixed to about 75% of the main days table beneath." Correct
   diagnosis of the actual bug, and it explains round 18's own "fixed
   width" fix not helping: this was `display: grid` with implicit
   `auto`-sized columns (`grid-auto-flow: column`), and a CSS grid
   container with auto tracks DISTRIBUTES any leftover space across those
   tracks once its own content is narrower than the container - this row
   still inherits form-grid's own 1120px max-width, so even with every
   field's own visible input/select given an explicit width (round 18),
   the surrounding grid CELL each one sits in kept stretching to fill a
   roughly-even share of that full 1120px, leaving the actual (still
   correctly fixed-width) input/select sitting inside a much wider,
   mostly-empty cell - exactly "spread out... about 75%" (912px of real
   content inside a 1120px-wide grid, ~81%, in the same ballpark Karl
   estimated by eye). Flexbox doesn't have this behaviour by default - a
   flex item's `flex-grow` starts at 0, so with none set here, no item ever
   grows to consume leftover main-axis space; each one just sits at its own
   natural/explicit width with a plain gap between them, and any leftover
   room is left as empty space after the LAST item instead of being spread
   between all of them. `flex-wrap`'s own initial value (`nowrap`) keeps
   the same "overflow sideways on a narrow enough viewport, no wrapping"
   trade-off the grid version already had, documented at the time - total
   content width (currently up to ~912px with every field shown) stays
   comfortably under form-grid's own 1120px cap either way, so this switch
   never needs the row to actually shrink or wrap in normal use. */
.shift-pattern-header-grid {
    display: flex;
    align-items: flex-end;
    gap: 1.5rem;
}

/* TWO LINES (2026-09-21, round 17) - Karl: "When expanded, please split
   the header inputs onto two lines." ShiftPatternEditor.razor now renders
   TWO divs sharing the .shift-pattern-header-grid class above (so each
   keeps that same single-row grid-auto-flow: column layout on its own),
   each also carrying its own -line-1/-line-2 modifier. Plain block-level
   grid divs stack with zero gap between them by default - line 2 gets a
   small margin-top to match form-grid's own row gap (1rem) elsewhere;
   line 1 needs nothing extra, .table-scroll/the day table etc. below it
   already carry their own top margin. */
.shift-pattern-header-line-2 {
    margin-top: 1rem;
}

/* SIDE-BY-SIDE HELP BOX (2026-09-21, round 20) - Karl: "When Days in cycle
   &lt;&gt; 7, please display the help box to the right of the inputs,
   taking up both lines. Please make this position fixed so it does not
   move around with the 'Pattern reset' and 'Reset day' inputs appearing."
   See ShiftPatternEditor.razor's own markup remarks on
   shift-pattern-header-layout for exactly what moved where - in short,
   the cycle-reference-bar ("Day 1 of this cycle falls on...") is now a
   flex sibling of a new column wrapping BOTH existing header lines,
   instead of a full-width row underneath them.

   FIXED 900PX WIDTH (2026-09-21, round 20 original REPLACED round 21,
   ROUND 21's 900px FIGURE ITSELF NOW REPLACED, round 26) - see the
   history this comment used to carry: round 20 left this column with no
   explicit width (natural "widest of the two lines" sizing); round 21
   worried that widening Reset pattern/Reset day (180px -> 220px) had
   made line 2 dangerously close to line 1's own width, and replaced that
   with a single hand-picked "safe" figure, 900px, "comfortably covering
   both lines' worst case with real margin to spare."

   THAT FIGURE WAS NEVER ACTUALLY MEASURED AGAINST THE REAL CONTENT, AND
   TURNED OUT TO BE FAR TOO WIDE (2026-09-21, round 26) - Karl: "Can you
   just have a look at why this box is appearing on the right with a
   gap." Line 1's real content (Name at 320px + two 112px steppers +
   the checkbox stack, plus three 1.5rem gaps) comes to roughly 730-760px
   depending on font metrics; line 2's real worst case (Allowance factor
   + Std. shift duration + Reset pattern + Reset day, all four fields'
   OWN caption text is what actually sets each field's width once it's
   wider than the input/select beneath it - "Std. shift duration
   (minutes)" alone is close to 225px) comes to roughly 650-750px. 900px
   was neither figure - it was a guess with margin added on top of a
   margin, and the real gap between the two lines is close enough that
   hand-measuring it again (the same trap round 21's own comment already
   flagged) isn't reliable either.

   FIXED WIDTH REMOVED ENTIRELY - RESERVED SPACE INSTEAD OF A GUESSED
   NUMBER (round 26) - rather than replace one hand-picked pixel figure
   with another, Reset pattern and Reset day (ShiftPatternEditor.razor)
   no longer come and go from the page via @@if - both are now always
   rendered, and simply made invisible (see .header-field--reserved
   below) when they don't currently apply. That makes line 2's own
   rendered width GENUINELY CONSTANT for a given pattern's day count/reset
   mode combination - it can no longer grow or shrink as ResetMode/
   DayCount change, which was the actual thing causing this column (and
   the help box beside it) to visibly jump back when this was first
   raised in round 18/20. With line 2 no longer variable, this column can
   safely go back to round 20's original idea - no explicit width, sized
   by the browser to whichever line is genuinely wider - without
   reintroducing the jump round 21 was guarding against, and without ever
   overshooting the real content the way the 900px guess did.

   align-items: stretch (flexbox's own default, not overridden here) is
   what makes the help box automatically match the combined height of both
   stacked lines with no extra sizing code needed - see
   .cycle-reference-bar--aside just below for its own internal layout. */
.shift-pattern-header-layout {
    display: flex;
    align-items: stretch;
    gap: 1.5rem;
}

/* NEVER SHRINKS (2026-09-21, round 27) - Karl: "The Company default text
   is just bunching up when the popup appears... ensure the Company
   default text does not move or wrap." Real cause: this column has no
   explicit flex-shrink, so it defaults to 1 (shrinkable) same as every
   other flex item - with .cycle-reference-bar--aside beside it ALSO
   growing to fill the rest of the row (round 23's own `flex: 1 1 auto`,
   narrowed this round - see that rule's own remarks), the row's total
   content width can genuinely exceed the space available, and the
   browser's only way to resolve that is to shrink whichever flex items
   are still willing to shrink - which was this column, squeezing "Company
   default" down below its own natural text width until it wrapped. Fixed
   by taking this column out of the shrinking pool entirely - the aside
   box (which already wraps its own description text by design) is a far
   more natural thing to give ground under real pressure than Name/Days in
   cycle/the checkbox labels, none of which were ever meant to reflow. */
.shift-pattern-header-lines {
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
}

/* RESERVED-BUT-HIDDEN (2026-09-21, round 26) - see
   .shift-pattern-header-lines's own remarks just above for why this
   exists: applied to Reset pattern/Reset day's wrapping &lt;label&gt;
   (ShiftPatternEditor.razor) instead of removing them from the page with
   @@if, so line 2 always reserves the same layout space for them whether
   or not they're currently relevant to this pattern. visibility: hidden
   (not display: none) keeps the space reserved while hiding the control
   itself and taking it out of the tab order - a real, empty-looking gap
   on line 2 for a 7-day pattern that will never need either field is the
   deliberate trade-off for this column's width never moving again. */
.header-field--reserved {
    visibility: hidden;
}

/* HEADER-FIELD (2026-09-21, round 19) - see ShiftPatternEditor.razor's own
   markup remarks on the Days in cycle/Shifts per day fields for why those
   two are now a plain &lt;div&gt; with this class instead of a &lt;label&gt;
   (a &lt;label&gt; wrapping more than one form control - here, two buttons
   plus an input/span - implicitly, ambiguously forwards a click anywhere
   inside it to ONE of them, which is what was making whitespace clicks
   sometimes land on the wrong button). Reproduces .form-grid label's own
   shared look exactly (stacked caption-above-control, same size/colour),
   since that rule no longer applies once this isn't a &lt;label&gt; any
   more. */
.header-field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.header-field-label {
    font-size: 0.85rem;
    color: var(--text-muted);
}

/* NAME WIDENED (2026-09-20, round 16) - Karl: "Make the Name input box
   wider." Was getting the browser's own default text-input width (no
   explicit `width` was ever set - .form-grid's own shared `input,
   select` rule further up only sets padding/border/radius, not width),
   sized by grid-auto-columns: auto above to whatever that default
   happens to be (~20 characters' worth). No exact figure given this
   round - 18rem (288px) is a judgment call, comfortably wider without
   making this one field dominate the single-row layout next to the six
   other, deliberately narrower fields beside it.

   RE-SCOPED OFF `:first-child` (2026-09-21, round 17) - the two-line split
   just above means there are now TWO header lines, each with their own
   "first field" (Name on line 1, Allowance factor on line 2) - a bare
   `.shift-pattern-header-grid label:first-child` would now incorrectly
   also widen Allowance factor's own input. Scoped to the new
   .shift-pattern-header-line-1 modifier specifically instead, which only
   Name's line carries.

   WIDENED AGAIN (2026-09-21, round 20) - Karl: "The Name input can be
   slightly wider." Again no exact figure given - 20rem (320px), a modest
   bump from 18rem, is a judgment call ("slightly"); flag if a bigger jump
   was actually wanted.

   SCOPED TO A DIRECT CHILD (2026-09-21, round 22) - found while chasing
   Karl's report that "Company default's checkbox is still aligned
   vertically with the other inputs' titles... Horizontally, the checkbox
   and title are far apart and way on the right hand side." Real cause:
   `label:first-child` (no `>` before it) is a DESCENDANT selector, and
   `:first-child` only checks whether an element is the first child of
   ITS OWN parent - not the first label anywhere inside
   .shift-pattern-header-line-1 as intended. Company default's own
   &lt;label class="checkbox-field"&gt; (round 20's new
   header-checkbox-stack wrapper) is genuinely the first child of ITS
   parent (the header-checkbox-stack div), so this rule was ALSO matching
   IT and its checkbox &lt;input&gt; - widening the checkbox itself to
   20rem (320px). That explains both symptoms at once: a 320px-wide
   checkbox reads as "far apart" from its own text label sitting 0.4rem
   after it, and the inflated/oddly-shaped box is what was throwing the
   whole row's height off, dragging Company default up toward the other
   fields' caption line. Active was never affected, because - guarded by
   `@@if (!IsNew)` - it's the SECOND child of header-checkbox-stack, not
   the first, so `:first-child` never matched it; exactly matching Karl's
   own observation that only Company default looked wrong. Fixed by
   adding `>` before `label:first-child`, so this only ever matches a
   label that's a DIRECT child of .shift-pattern-header-line-1 itself
   (Name's own label, the intended target) - Company default's own label,
   nested two levels deeper, can no longer match no matter what order it
   sits in inside its own wrapper. */
.shift-pattern-header-line-1 > label:first-child input {
    width: 20rem;
}

/* NEVER SHRINKS, ANY OF THEM (2026-09-21, round 29) - Karl: "Company
   default is now longer wrapping but... I think the checkbox text needs
   more space." Round 27 already gave .shift-pattern-header-lines (the
   OUTER column wrapping both header lines) its own flex-shrink: 0, on
   the theory that protecting the column as a whole would be enough - it
   wasn't, because flex-shrink isn't inherited down through nested flex
   containers: .shift-pattern-header-grid (this row) is ITS OWN separate
   flex context, and its own direct children - Name's label, both
   stepper fields, header-checkbox-stack - each still had the ordinary
   default flex-shrink: 1 regardless of what their grandparent column did
   or didn't allow. Whenever this row's own available width came in
   tighter than the combined natural width of all four fields (round 27's
   own widened reference box, further down this file, made exactly that
   more likely), the browser's only option was shrinking these fields -
   and header-checkbox-stack, having no fixed width of its own to resist
   with, is what actually gave: "Company default" compressed down below
   its own text's natural width until it wrapped. Every direct child of
   this row is now protected the same way the column itself already is -
   none of Name/Days in cycle/Shifts per day/the checkbox stack can be
   compressed any more, so nothing on this line has anywhere left to
   wrap from. See .checkbox-field's own new white-space: nowrap (further
   down this file) for the second, independent guarantee on the text
   itself - between the two, this shouldn't be able to recur from a
   different direction next time either. */
.shift-pattern-header-line-1 > * {
    flex-shrink: 0;
}

/* FIXED WIDTHS (2026-09-21, round 18) - Karl: "I think the inputs for the
   shift header are in their own table. When the 'First specific day of
   month' is selected, the extra input is added and it jumps around.
   Please make them all a fixed width." See ShiftPatternEditor.razor's own
   markup remarks (around the Reset pattern/Reset day fields) for the full
   diagnosis - grid-auto-columns: auto sizes every column to its own
   content by default, and this row still inherits form-grid's own
   max-width: 1120px, so an extra column appearing/disappearing could push
   the row's total content width over that cap and force EVERY column to
   compress to fit, not just the new one. Name already had an explicit
   width (round 16); the two steppers are already effectively fixed via
   their own button/value widths just below - only the two
   &lt;InputSelect&gt; fields (Reset pattern/Reset day) and the two plain
   &lt;InputNumber&gt; fields on line 2 (Allowance factor/Std. shift
   duration) needed an explicit width added, via these two new classes on
   the fields themselves (ShiftPatternEditor.razor) rather than a
   positional selector here, which would be fragile against any future
   reordering. Figures are judgment calls (no exact pixel numbers given) -
   11rem is comfortably wide enough for "First specific day of month", the
   longest option text either select ever shows; 8rem matches the sort of
   width a plain numeric field reasonably needs.

   WIDENED TO 180PX (2026-09-21, round 20) - Karl: "Increase the width of
   the 'Reset pattern' input so all options fit comfortably, around 180px
   I think." Karl's own figure, used verbatim (180px rather than 11rem, to
   match exactly what he asked for rather than converting to a slightly
   different rem value). This class stays shared between Reset pattern and
   Reset day (round 18's own decision, unchanged) - Karl only mentioned
   Reset pattern by name, but both selects sit on the same line together
   now (see round 20's other remarks on the block moving to line 2) and
   giving them different widths would look inconsistent; Reset day's own
   longest option ("Wednesday") fits comfortably at 180px regardless.

   WIDENED FURTHER, PAST KARL'S OWN FIGURE (2026-09-21, round 21) - Karl:
   "The Reset pattern input still needs increasing to around 180px." This
   rule already read exactly `width: 180px` after round 20, so - taken at
   face value - his own 180px estimate wasn't quite enough once actually
   seeing it rendered against the real option text ("First specific day of
   month" is the longest, 28 characters). Bumped to 220px, a more
   generous margin past that longest option so it's comfortably clear of
   any truncation, rather than nudging up by another small guess that
   might still fall short a second time. Flag: if this is still reading as
   unchanged from before, it may be worth a hard refresh/clearing the
   browser cache to rule that out, since the CSS value genuinely did
   change between rounds 20 and 21.

   SPLIT INTO TWO CLASSES, SIZED "BEFORE PADDING" (2026-09-21, round 22) -
   Karl: "Please make Reset pattern 185px wide before padding. Please make
   Reset day 100px before padding." Two real changes here. First, Reset
   pattern and Reset day now want genuinely DIFFERENT widths, so the one
   shared header-fixed-select class (used since round 18) is retired in
   favour of two new ones below, each on just its own field
   (ShiftPatternEditor.razor). Second, "before padding" specifically means
   Karl wants the WIDTH figure to describe the box's own content area, not
   its full rendered size including padding/border - this app's global
   reset (top of this file) sets `box-sizing: border-box` on every element,
   under which a plain `width` property already includes padding and
   border, which is the opposite of what's being asked for here. Rather
   than hand-calculating "185px + padding + border" and hard-coding that
   sum (fragile if the shared .form-grid input/select padding ever
   changes), these two new classes explicitly opt back OUT of border-box
   sizing with their own `box-sizing: content-box`, so the plain `width`
   value genuinely means content width, padding and border added on top
   automatically - exactly "before padding", and self-documenting about
   why. This is a deliberate, scoped exception to the app's usual
   border-box convention, not a mistake - flagged here so it isn't
   "corrected" back to border-box by accident in some future round. */
.header-select-reset-pattern {
    box-sizing: content-box;
    width: 185px;
}

.header-select-reset-day {
    box-sizing: content-box;
    width: 100px;
}

.header-fixed-input {
    width: 8rem;
}

/* CHECKBOX STACK (2026-09-21, round 20) - Karl: "Can you move 'Active' and
   'Company default' to the top options line, and place the two checkbox
   options on top of each other vertically with spacing?" A plain flex
   column wrapping both .checkbox-field &lt;label&gt;s (ShiftPatternEditor.
   razor) - "with spacing" is a judgment call on the exact figure, 0.5rem
   matches the vertical rhythm .form-grid's own row-gap already uses
   elsewhere on this page rather than inventing a new figure.

   BOTTOM-ALIGNED WITH THE OTHER INPUT BOXES (2026-09-21, round 21) - Karl:
   "Please ensure the two checkboxes are inline vertically, and
   horizontally lines up with the other input boxes on that 1st line. Both
   of them appear to be in a random position at the moment." Round 20's
   own version explicitly overrode this div's align-self to flex-start
   (top-aligned with the row), on the theory that top-aligning would look
   better than the row's own default bottom-alignment for a two-row
   stack - that was the wrong call: flex-start lines this div's TOP up
   with the row's top, which is roughly where the OTHER fields' own small
   caption text ("Name", "Days in cycle", …) sits, not where their actual
   white input BOXES are - exactly "random position" against everything
   else on the line. Removed that override entirely: with no align-self
   of its own, this div now falls back to the row's own align-items:
   flex-end (.shift-pattern-header-grid, round 19, used by every other
   field already) - the BOTTOM of the two stacked checkboxes now lines up
   with the bottom of Name/Days in cycle/Shifts per day's own input boxes,
   consistent with how every other field on this row already aligns
   against its neighbours.

   REAL CAUSE OF "COMPANY DEFAULT LOOKS WRONG" WAS ELSEWHERE (2026-09-21,
   round 22) - Karl, still: "Company default's checkbox is still aligned
   vertically with the other inputs' titles... Horizontally, the checkbox
   and title are far apart and way on the right hand side. The Active
   checkbox and text are in the right place." Turned out this wasn't
   really a flex-alignment problem at all - see
   .shift-pattern-header-line-1's own remarks further up for the actual
   bug (an over-broad `:first-child` selector was quietly widening
   Company default's own checkbox &lt;input&gt; to 320px, since it's the
   first child of THIS div specifically, which is what was both spreading
   it away from its own text and throwing its row height off). Gap
   trimmed slightly here too, 0.5rem -> 0.4rem, purely as a small
   complementary tidy-up now that both checkboxes render at their normal
   size again - not the main fix, that's the selector above.

   HEIGHT TIGHTENED TO MATCH A SINGLE INPUT BOX (2026-09-21, round 23) -
   Karl: "Can you push the Company default checkbox and text down a bit so
   it is inline with the other input boxes. The space between it and
   Active can be reduced. Combined, including the space between them,
   they should take up about the same height as the other input boxes."
   So round 22's width-bug fix wasn't the whole story after all - even at
   their normal (un-inflated) size, two stacked checkbox rows plus a gap
   are naturally a bit taller than one single-line input box, which is
   what was still pushing Company default up past the box zone. This
   container still bottom-aligns against the row (align-items: flex-end,
   unchanged, .shift-pattern-header-grid) - "push down" doesn't need its
   own explicit offset, because shrinking the STACK's own total height
   automatically brings its top down closer to the bottom-anchored box
   zone. Gap cut right down to 0.1rem, and the two checkbox rows' own
   line-height tightened (just below, scoped to this container only - the
   shared .checkbox-field class above is reused elsewhere on this site and
   shouldn't be tightened globally), aiming the combined height at
   roughly matching a single .form-grid input's own rendered height.
   **JUDGMENT CALL / ESTIMATE**: worked out by hand from each element's
   own CSS (font-size, padding, line-height) rather than measured in a
   real browser, so "about the same height" is a best estimate, not a
   guaranteed exact match - flag if it still reads as noticeably taller
   or shorter than the boxes beside it. */
.header-checkbox-stack {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.header-checkbox-stack .checkbox-field {
    line-height: 1;
}

/* STEPPER CONTROLS (2026-09-20, round 14) - Karl: "Please add a simple +
   and - buttons to the Days in cycle input" / "Please add a Shifts per
   day input in the same style as Days in cycle input." Shared by both
   fields (ShiftPatternEditor.razor) - Days in cycle wraps a real
   <InputNumber> (still typable, see that markup's own remarks), Shifts
   per day wraps a plain read-only <span> (there's no single settable
   property backing a shift COUNT directly - see .shift-count-field
   further down) - both get the exact same look either way. */
.stepper-field {
    display: flex;
    align-items: stretch;
    gap: 0;
    width: fit-content;
}

.stepper-button {
    width: 1.75rem;
    flex: 0 0 auto;
    border: 1px solid var(--border);
    /* WHITE BY DEFAULT, GREY AT THE LIMIT (2026-09-20, round 16) - Karl:
       "Make the 'Days in cycle' and 'Shifts per day' inputs and buttons
       white background, apart from when they reach their limits at
       which point they should be greyed out." This was actually
       BACKWARDS before this round - the enabled/normal state was
       --surface-alt (grey) and the :disabled/at-limit state below was
       --surface (white), the opposite of what Karl's now asking for (and
       arguably backwards from ordinary disabled-control convention
       generally - a disabled control usually reads as the MUTED one, not
       the more prominent white one). Swapped here rather than only
       reading it as "flip the two values", since white-by-default/grey-
       at-limit is also just the more conventional way round. */
    background: var(--surface);
    font: inherit;
    /* CENTRED AND BOLD (2026-09-21, round 17) - Karl: "The + and - buttons
       for 'Days in cycle' and 'Shifts per day' should be centred and
       bold." A plain &lt;button&gt;'s own default text-align already
       reads as roughly centred, but that's an unreliable default to lean
       on (and doesn't cover VERTICAL centring at all) - display: flex
       plus align-items/justify-content: center guarantees both axes
       explicitly, same technique .stepper-value--readonly's own plain
       &lt;span&gt; already uses just below. font-weight bumped from 600
       (a fairly light "semi-bold") to a genuine 700 ("bold"), rather than
       just leaving 600 and calling it close enough. */
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    color: var(--text-muted);
}

.stepper-button:disabled {
    color: var(--text-muted-light);
    cursor: not-allowed;
    background: var(--surface-alt);
}

.stepper-field .stepper-button:first-child {
    border-radius: 6px 0 0 6px;
    border-right: none;
}

.stepper-field .stepper-button:last-child {
    border-radius: 0 6px 6px 0;
    border-left: none;
}

.stepper-value {
    width: 3.5rem;
    flex: 0 0 auto;
    font: inherit;
    text-align: center;
    border: 1px solid var(--border);
    border-radius: 0;
    /* Applies directly to the Shifts per day stepper's own plain <span>
       (nothing else on that element competes for padding); duplicated,
       higher-specificity padding for the Days in cycle stepper's actual
       <input> is set by .stepper-field input.stepper-value just below -
       see that rule's own remarks for why the plain class selector here
       isn't enough for the <input> case on its own. */
    padding: 0.4rem 0.25rem;
    /* WHITE BY DEFAULT (2026-09-20, round 16) - same request/reasoning as
       .stepper-button just above - explicit now rather than left to
       whatever the browser's own default text-input background happens
       to be (white already for the real <input>, but this rule is
       shared with the Shifts per day stepper's plain <span> too, which
       previously got its background from .stepper-value--readonly's own
       --surface-alt default instead - see that rule's own remarks just
       below for why that's changed too). */
    background: var(--surface);
}

/* GREYED OUT AT THE LIMIT (2026-09-20, round 16) - Karl: "...apart from
   when they reach their limits at which point they should be greyed
   out." Applied to the stepper's own VALUE box (not just the +/-
   buttons either side) whenever the current count sits at either end of
   its new hard range - Days in cycle 1-31, Shifts per day 1-10 (see
   ShiftPatternEditor.razor's own AtDayCountLimit/AtShiftsPerDayLimit
   properties for where this class is actually applied, and their own
   remarks for why these are new HARD limits this round, not just the
   stepper buttons' disabled state as before). Declared after
   .stepper-value above with an extra class, so it reliably wins that
   background regardless of source order.

   NO LONGER APPLIED (2026-09-21, round 21) - Karl: "The Days per cycle
   and Shifts per day inputs should have a white background." Shifts per
   day defaults to 1 shift for most patterns, which is ALSO
   MinShiftsPerDay - so this rule was greying out the value box for the
   single most common case, not just genuine edge cases, which is clearly
   not what a "white background" ask wants. ShiftPatternEditor.razor no
   longer applies this class to either value box (see its own round 21
   remarks) - rule left in place, now dormant, rather than deleted,
   in case a future round wants some version of this distinction back
   (e.g. on the buttons only, which still greys out correctly via
   .stepper-button:disabled just above, unchanged). */
.stepper-value.stepper-value--at-limit {
    background: var(--surface-alt);
    color: var(--text-muted);
}

/* SPECIFICITY NOTE - the Days in cycle stepper's own <InputNumber> renders
   a real <input>, sitting inside .form-grid (see that rule's own "input,
   select" padding/border-radius further up) - `.form-grid input` is a
   class+element selector (0,1,1), MORE specific than a bare `.stepper-
   value` class selector (0,1,0) alone, so a plain `.stepper-value { padding
   ...; border-radius: ...; }` rule would silently lose to it regardless of
   source order - the exact same specificity trap this file's own
   .absence-approval-table remarks already document elsewhere. Qualified
   here as `.stepper-field input.stepper-value` (0,2,1) instead, which
   genuinely out-specifies `.form-grid input` rather than relying on
   declaration order to win a tie that doesn't actually exist. */
.stepper-field input.stepper-value {
    border-radius: 0;
    /* Overrides form-grid's own shared `input, select` padding further up
       - a stepper's own two flanking buttons already give the number
       enough visual breathing room, this just needs to vertically match
       .stepper-button's own height (no separate top/bottom padding stacked
       on top of that). */
    padding: 0.4rem 0.25rem;
}

/* The Shifts per day stepper's own middle value is a plain <span>, not an
   <input> - width/border/padding/text-align above (.stepper-value) apply
   to it directly with no specificity fight (a <span> isn't touched by
   .form-grid's own `input, select` rule at all), this just adds the extra
   vertical-centring/fill treatment a real <input> gets for free from its
   own line-height but a <span> doesn't. */
.stepper-value--readonly {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* WAS var(--surface-alt) (grey) unconditionally - round 16's white-by-
       default/grey-at-limit request (see .stepper-value/.stepper-button's
       own remarks above) means this span's background now just falls
       through to .stepper-value's own new white default instead of
       setting its own grey one here; .stepper-value--at-limit (above)
       still overrides it back to grey when Shifts per day is at 1 or 10. */
    color: var(--text-muted);
}

/* REMOVED (2026-09-20, round 15) - Karl: "Place the 'Shifts per day'
   input between 'Days in cycle' and 'Allowance factor'." The standalone
   .shift-count-field block (added round 14, directly above this comment
   in history) is gone - Shifts per day now lives INSIDE
   .shift-pattern-header-grid itself, as just another `<label>` between
   the Days in cycle and Allowance factor fields (see that markup and
   .shift-pattern-header-grid's own remarks further up), reusing
   form-grid label's shared look rather than its own rule. */

/* STEPPER SPINNER FIX (2026-09-20, round 15) - Karl: "Ensure the Days in
   cycle input box's text is centred." .stepper-value already had
   text-align: center (see above) and nothing else overrides it, so the
   off-centre look wasn't a missing rule - it's the browser's own native
   up/down spinner arrows on the Days in cycle <InputNumber>'s underlying
   <input type="number">, which eat space on the right side of the box
   only, visually pushing the digits left of true centre. Hiding the
   native spinner (Shifts per day's own stepper uses a plain <span>, so
   it was never affected) removes that asymmetry so the existing
   text-align: center actually reads as centred. Flag: unverified without
   a live browser, like the other diagnosis-driven fixes this round. */
.stepper-field input.stepper-value {
    appearance: textfield;
    -webkit-appearance: textfield;
    -moz-appearance: textfield;
}

.stepper-field input.stepper-value::-webkit-outer-spin-button,
.stepper-field input.stepper-value::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* HEADER FIELD INDENT (2026-09-20, round 15) - Karl: "Put an indent
   before all of the input boxes in the header, as per other pages, so
   the titles such as 'Name' appear slightly to the left of the input
   box. Please follow the layout of other pages." Flag: no other
   settings/edit page in this codebase was found to already indent its
   inputs relative to their own label text specifically - form-grid
   labels elsewhere all stack label text directly above their input with
   no horizontal offset - so this is an interpretation of the request
   rather than a copy of an existing pattern; a small left margin on the
   input/stepper only (not the label text itself) is applied here.
   Checkbox fields (Company default, Active) are deliberately excluded -
   their own label text sits inline AFTER the checkbox, so indenting the
   checkbox would misalign it from that text rather than matching the
   other fields' look.

   EXTENDED TO SELECTS (2026-09-21, round 17) - Karl: "Ensure all of these
   header inputs are indented from their titles." Round 16 added the Reset
   pattern/Reset day &lt;InputSelect&gt; fields, which render as plain
   &lt;select&gt; elements - this rule never matched `select`, only
   `input`/`.stepper-field`, so both sat flush against their own label text
   while every other field already had this same indent. `select` added to
   the selector list so they now match too.

   EXTENDED TO .header-field (2026-09-21, round 19) - the Days in cycle/
   Shifts per day fields are no longer &lt;label&gt;s (see .header-field's
   own remarks just above) - added alongside the existing `label` selector
   so their own `.stepper-field` keeps the same indent it always had. */
.shift-pattern-header-grid label:not(.checkbox-field) > input,
.shift-pattern-header-grid label:not(.checkbox-field) > select,
.shift-pattern-header-grid label:not(.checkbox-field) > .stepper-field,
.shift-pattern-header-grid .header-field > .stepper-field {
    margin-left: 0.4rem;
}

/* ACCORDION OPEN ANIMATION (2026-09-20, round 15 - REWRITTEN round 16) -
   Karl, round 15: "Ensure the accordion opens smoothly as per expansions
   on other pages. It is currently opening and snapping in instantly."
   Round 15's own fix (giving the real content its own override-fields-
   reveal animation once it mounts) turned out NOT to fix it - Karl's
   round 16 report: "None of the animations work that well on the
   portal. The ones on this shift-patterns page are the same. They seem
   to transition slightly but more snap into place instantly." That "a
   LITTLE bit of visible transition, then a snap" description is the
   real clue, and points at a different, more fundamental cause than
   round 15's diagnosis: a CSS `animation` (unlike a `transition`) starts
   its own internal clock the instant its style is computed, REGARDLESS
   of whether the browser's main thread is free to actually paint
   anything - and mounting this page's real content (an EditForm that,
   for an existing pattern, includes the entire day-by-day table, quite
   possibly hundreds of DOM nodes at once) is exactly the kind of
   synchronous, layout-heavy work that can keep a browser's main thread
   busy for a meaningful fraction of a 0.3s animation's own duration.
   Every millisecond spent constructing/laying out that DOM is a
   millisecond the animation's clock keeps ticking with nothing painted -
   so by the time the browser is finally free to paint a frame, the
   animation may already be most of the way through (or past) its own
   timeline, leaving only its last, fast-moving tail visible - which
   reads exactly like "transition slightly but snap into place
   instantly", not like a missing animation.

   THE FIX - two-phase mount, `transition` instead of `animation`. A
   `transition` only starts once the browser has genuinely already
   painted a starting style in an earlier frame - it can't be short-
   circuited by heavy work happening in between the same way `animation`
   can, PROVIDED the "before" and "after" styles are actually applied in
   two separate renders rather than both at once. So: this class no
   longer plays an animation on its own insertion. Instead,
   ShiftPatternEditor.razor/ShiftPatterns.razor now mount the (possibly
   large) real content FIRST, already in its hidden "before" state (a new
   --pending/--revealing modifier class, no animation/transition
   involved yet) - all the expensive DOM construction happens during
   THIS render, invisibly. A brief moment later (BeginRevealContentAsync/
   BeginRevealNewAsync/BeginRevealRowAsync, a small Task.Delay then
   StateHasChanged - the exact same fire-and-forget delay idiom this
   file's own FinishClosingRowAsync/FinishClosingAddNewAsync already use
   for the CLOSING animation), a SECOND, much cheaper render just removes
   that modifier class - nothing new is being constructed in that render,
   so the browser is free to actually paint the "before" state and then
   run the transition smoothly over its own full duration. Flag,
   strongly: this is a genuinely different, more involved mechanism than
   round 15's (which was reasonable CSS but evidently didn't fix what
   Karl's actually seeing) - still can't be confirmed working without a
   live browser, but it directly targets a cause that specifically
   explains the "slight transition, then snap" symptom in a way round
   15's diagnosis didn't. */
.shift-pattern-editor-reveal {
    transition: opacity 0.3s ease, transform 0.3s ease;
}

.shift-pattern-editor-reveal.shift-pattern-editor-reveal--pending {
    opacity: 0;
    transform: translateY(-8px);
}

/* EXPANDED PANEL BACKGROUND (2026-09-20, round 15) - Karl: "Make the
   background of the expanded section white, the same as the rest of the
   rows in the header table." Scoped to ShiftPatterns.razor's own two
   .inline-editor-panel divs only (via this added class), not to inline-
   editor-panel generally, since other pages' own expanded panels weren't
   mentioned and shouldn't change. --surface is the same token the header
   table's own row background already uses elsewhere in this file. */
.inline-editor-panel.shift-pattern-panel {
    background: var(--surface);
    /* TWO-PHASE OPEN, SAME REASONING AS .shift-pattern-editor-reveal
       ABOVE (2026-09-20, round 16) - this panel's own OPENING animation
       is replaced with the same mount-hidden-then-transition approach;
       its CLOSING animation (.inline-editor-panel--closing, further up
       this file) is UNCHANGED and deliberately excluded from this
       override via :not() - closing never had the "heavy DOM insert
       eating the animation's clock" problem in the first place (nothing
       new is being constructed when a panel starts closing, its content
       already exists), so there's no reason to think it's affected, and
       touching it wasn't asked for. `:not(.inline-editor-panel--closing)`
       (three classes/pseudo-class, 0-3-0) genuinely out-specifies both
       the shared base `.inline-editor-panel` rule's own `animation`
       (0-1-0, further up this file) and `.inline-editor-panel--closing`
       itself (0-1-0) when closing ISN'T present, so it reliably wins
       without relying on source order - same reasoning as this file's
       other documented specificity fixes. */
    transition: opacity 0.35s ease, transform 0.35s ease;
}

.inline-editor-panel.shift-pattern-panel:not(.inline-editor-panel--closing) {
    animation: none;
}

.inline-editor-panel.shift-pattern-panel.inline-editor-panel--revealing {
    opacity: 0;
    transform: translateY(-8px);
}

.form-actions {
    margin-top: 1.5rem;
    display: flex;
    gap: 0.75rem;
}

/* .save-button's own 8rem width (further down this file) is sized for the
   settings pages' plain "Save"/"Saving…"/"Success"/"Error" text - too
   narrow for this button's Idle-state text ("Create"/"Save changes",
   2026-09-04, later still). Scoped to .form-actions specifically so the
   settings pages' own per-section Save buttons are unaffected. */
.form-actions .save-button {
    width: 10rem;
}

/* CALCULATED ALLOWANCE FACTOR (2026-09-21, round 17; SIMPLIFIED round 18,
   ShiftPatternEditor.razor only) - Karl, round 17: "Underneath the days
   table, on the bottom right, in line with the top of the Save and Cancel
   buttons, can you please calculate the estimated allowance factor." Karl,
   round 18: "The calculated allowance factor is squashing the table up for
   some reason."

   Round 17's own version gave .form-actions a new modifier class
   (justify-content: space-between, plus a nested .form-actions-buttons
   wrapper div for Save/Cancel) so the readout could sit at the far right of
   an already-full-width row - both removed now. A space-between row lets
   its own children shrink to fit as their combined content grows, which is
   the most likely explanation for Karl's report - simplified to a
   technique this file already trusts elsewhere for "push one item to the
   far right of an otherwise left-aligned flex row" without touching the
   row's own justify-content/alignment at all: a plain `margin-left: auto`
   on the pushed item itself, same as .shift-number-tab--remove's own rule
   further up this file. .form-actions is completely UNCHANGED from before
   round 17 as a result - still exactly what the "Copy to" popup's own
   Copy/Cancel row uses too. */
.calculated-allowance-factor {
    margin-left: auto;
    align-self: flex-start; /* "in line with the top of the Save and Cancel buttons" */
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--text-muted);
    white-space: nowrap;
}

/* --- Employee edit: optional "custom rules" override sections (2026-09-04,
   Karl's request) - shift rounding, and Annual leave/Annual leave
   (unpaid)/Public holidays. Each is a plain checkbox-gated block (not a
   <details>/<summary> disclosure): ticking the box both reveals the fields
   and flips the section's HasCustomXxx flag together, since the two are the
   same on/off state in EmployeeEditModel - see EmployeeDetail.razor. */
.override-section {
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.75rem 1rem;
    margin-top: 1rem;
    max-width: 1120px; /* matches .form-grid's own widened max-width, 2026-09-04 */
    background: var(--surface);
}

/* NEW (2026-09-25, later still again) - Karl: "Can you please limit the
   width of the bottom custom section to 1000px as well." EmployeeDetail.razor's
   four custom rules boxes only (a modifier, so CompanyGroupEditor.razor's
   identical group-level boxes keep the base 1120px - Karl's request was
   about the employee screen). With the global box-sizing: border-box,
   1000px is the box's OUTER edge, so it lines up exactly with the right
   edge of the 1000px field rows above it. */
.override-section--employee {
    max-width: 1000px;
}

.override-toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 600;
    cursor: pointer;
}

.override-toggle input[type="checkbox"] { margin: 0; }

/* NEW (2026-09-25, later the same day) - the "Custom allowance overrides"
   table's own Custom allowance input, redesigned this round from a
   .variable-row/.variable-input layout into a genuine table (see
   AbsenceAllowanceOverrideRows.razor's own remarks). Karl: "Can the
   default text for this input be light grey and simply state 'Default'?" -
   reuses --text-muted-light, the same "lightest" placeholder token
   .shift-day-table--wide's own hh:mm/mins inputs already established
   (that rule's own remarks), rather than a third shade. */
.allowance-override-table input[type="number"]::placeholder {
    color: var(--text-muted-light);
}

.override-fields {
    margin-top: 0.75rem;
    /* Reveal animation (2026-09-06, Karl's request: "if there is an
       animation which controls when a section is enabled and it expands,
       can it be slowed down slightly?") - there wasn't actually one before
       this: ticking the box just added this block to the DOM instantly via
       Blazor's @if, so it popped in with no transition at all. A true
       height/max-height transition doesn't work here without extra
       wrapper markup (the element doesn't exist to transition from until
       Blazor's already mounted it at full size), so this plays a one-shot
       fade-and-slide-down animation on mount instead - genuinely smoother
       than the instant pop, and easy to re-tune (see override-fields-reveal
       below) without touching any Razor. */
    animation: override-fields-reveal 0.35s ease;
}

@keyframes override-fields-reveal {
    from {
        opacity: 0;
        transform: translateY(-8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* NEW (2026-09-10) - Karl, re: ApproveAbsences.razor's own result panel:
   "Can the confirmation that appears above be animated. At the moment, it
   is just instantly appearing." Same situation .override-fields was in
   before the round above - Blazor's own @if just adds the panel to the
   DOM at full size/opacity, no transition at all - so this reuses that
   exact same fade-and-slide-down keyframe rather than inventing a second,
   near-identical one. Applied as a second class alongside
   .absence-booking-result on ApproveAbsences.razor's own result div only
   (paired with a @key there, so a second action's fresh result actually
   remounts the element and replays this rather than Blazor just patching
   the existing one's text in place) - Karl only asked about this page, so
   BookAbsence.razor's own Preview/Confirmed panels (same
   .absence-booking-result class) are untouched; a natural follow-up if he
   wants the same treatment there too. */
.absence-result-reveal {
    animation: override-fields-reveal 0.3s ease;
}

/* CHANGED (2026-09-10, next round) - Karl: "I cannot see any animations on
   the approve-absences page. It is all very snappy." First guess was a
   stale cached app.css (a plain redeploy doesn't change the CSS file's own
   URL, so a browser that already had the old file cached wouldn't
   necessarily re-fetch it) - flagged to Karl on that basis.
   RULED OUT (2026-09-10, yet another round) - Karl redeployed fresh code
   AND fully cleared his browser cache, and still saw nothing. That forced
   a second, closer look, which found a real bug: see
   button.save-button--saving/--saved/--failed's own remarks further down
   this file (next to their definitions) - the Approve/Reject buttons'
   green/red flash was being silently overridden by button.primary/
   button.danger's higher CSS specificity, so only the button's TEXT was
   ever actually changing, not its colour. This div's own reveal animation
   (.absence-result-reveal, above) was re-checked again at the same time
   and still has no equivalent conflict - nothing else in this file sets
   `animation`/`opacity`/`transform` on .absence-booking-result or its
   variants - so it should already have been visible; worth Karl confirming
   again now that the button fix makes the whole page's flash unmistakable,
   in case the two were simply hard to tell apart from each other before. */

/* NEW (2026-09-10, next round) - Karl: "please also fix the width of the
   Approve and Reject buttons. They move about when they change to
   Approved or Rejected." Fixed width, same reasoning as .save-button's
   own 8rem just sized down for this table's tighter row.
   CHANGED (2026-09-11, yet later) - "Approving…"/"Rejecting…" (10
   characters including the ellipsis) is gone (ApproveAbsences.razor's own
   ActionButtonText/SuccessActionLabel remarks) - "Approved"/"Rejected" (8
   characters) is now the longest either button ever shows, so 6.5rem
   still leaves comfortable headroom, no longer right at the edge it was
   sized for originally. Also gains a genuine colour transition
   (.save-button--saving/--saved/--failed themselves don't have one, by
   design - see that class's own remarks - so this is scoped to ONLY these
   two buttons, not the shared Company-settings Save button) so the flash
   actually fades rather than hard-cutting between states - directly for
   Karl's "I cannot see any animations" report. */
.approval-action-button {
    width: 6.5rem;
    text-align: center;
    /* Duration bumped 0.2s -> 0.25s (2026-09-10, still another round) to
       match .save-button's own new transition (further down this file) -
       see that rule's remarks for why it now has one too.
       Bumped again, 0.25s -> 0.4s (2026-09-11, still later) - Karl: "Please
       slow down the transitions to 0.4s + 0.4s" (paired with
       ApproveAbsences.razor's own ActionButtonText fix so the label only
       swaps once this transition has actually finished - see that method's
       own remarks). */
    transition: background-color 0.4s ease, color 0.4s ease, border-color 0.4s ease;
}

/* NEW (2026-09-11, later again) - Karl: "When I click the buttons, the
   text is snapping about when they change." ApproveAbsences.razor's own
   ActionButtonCssClass now walks a Success button through these two
   classes in turn (driven by _buttonTextRevealed/RevertRowActionAfterDelayAsync
   - see their own remarks) instead of jumping the label straight from
   "Approving…"/"Rejecting…" to "Approved"/"Rejected" the instant the
   server responds. Both rely on .approval-action-button's own existing
   `transition: background-color/color/border-color 0.25s ease` above to
   actually animate - these two classes only ever change the TARGET
   colours, the fade itself was already there.

   Fading stage: colour fades to match the button's own background, so the
   old label visually vanishes rather than hard-swapping under a visible
   label. Approve's background is already solid #1f6feb (button.primary) at
   every stage, so only its text colour needs to move there. Reject
   doesn't have a solid background of its own to hide into (button.danger
   is a plain white/outline button) - Karl specified #fdecea, the same
   pale danger tint already used elsewhere in this file
   (button.danger:hover, button.save-button--failed), so this stage gives
   Reject a genuine temporary background of that colour for its own text
   to fade into, rather than leaving the button white.

   Revealed stage: label has already swapped to "Approved"/"Rejected"
   (ActionButtonText) by the time this class is applied - colour now fades
   on to its own revealed target. Reject's pale background from the fading
   stage is left in place rather than fading back out - nothing in Karl's
   ask called for that, and the row disappears shortly afterward anyway
   (RevertRowActionAfterDelayAsync). !important on all four properties for
   the same belt-and-braces reason as
   button.save-button--saving/--saved/--failed further down this file -
   these are layered on top of button.primary/button.danger, which tie on
   specificity otherwise.

   CHANGED (2026-09-11, still later) - Reject's revealed colour corrected
   from #fff to var(--danger) - Karl: "Please also ensure the Reject text
   fades to #fdecea from red over 0.4s, changes to say Rejected, then
   fades back to red over 0.4s." The fading stage below was already
   correct (button.danger's own base colour is var(--danger), i.e. red, so
   it was already fading FROM red TO #fdecea); only the revealed stage's
   target was wrong, fading back to white instead of back to red. Approve
   is unchanged - Karl's original spec (fade back in to #fff) still stands
   there, no complaint raised about it this round. */
button.approval-action-button--success-fading.primary {
    color: #1f6feb !important;
}

button.approval-action-button--success-fading.danger {
    background: #fdecea !important;
    border-color: var(--danger) !important;
    color: #fdecea !important;
}

button.approval-action-button--success-revealed.primary {
    color: #fff !important;
}

button.approval-action-button--success-revealed.danger {
    background: #fdecea !important;
    border-color: var(--danger) !important;
    color: var(--danger) !important;
}

/* NEW (2026-09-10, still another round) - Karl: "very quickly it the line
   disappears... The line and button probably need to stay there for about
   a second. It would be much better if the button faded or transitioned,
   rather than just changed." The button colour fade above was already in
   place, but the ROW itself had no such treatment - once
   RevertRowActionAfterDelayAsync (ApproveAbsences.razor) decides a row
   needs to leave the list, the very next render simply drops that <tr>
   entirely - an instant, un-animated DOM removal, regardless of how smooth
   the button's own colour transition is.

   CHANGED (2026-09-10, next round) - Karl: "fade the line white-green-white
   then gently remove the line (as in remove the line, have the others
   below move up over 0.5s or so) for approve and white-red-white for
   reject, with similar behaviour to remove the line." Two separate things
   needed for that, both below:

   1. `.absence-row-flash-approve`/`.absence-row-flash-reject` (further
      down) - a one-shot white -> colour -> white background animation on
      the whole <tr>, not just its buttons, applied by RowCssClass
      (ApproveAbsences.razor) for the same ~1s window the button itself is
      showing "Approved"/"Rejected".

   2. `.absence-row-removing`, replacing the previous round's plain
      opacity-only `.absence-row-fade-out` - that fade genuinely faded the
      row's own colour, but did nothing for the ROWS BELOW IT: opacity
      doesn't animate the vertical space an element takes up, so the
      instant the row was actually removed from the list, everything below
      it still snapped up in one frame - not what "have the others below
      move up over 0.5s" asked for. A table row's rendered height comes
      from its cells' content, and `overflow`/clipping isn't reliably
      respected on <tr> itself in most browsers (it's one of the elements
      the CSS overflow spec explicitly excludes) - so the collapse is done
      per-<td> instead, which shrinks the row's effective height smoothly
      and lets the browser's normal table layout naturally move every row
      below it up as that happens, no JS needed.

   CHANGED AGAIN (2026-09-10, yet another round) - Karl: "it removes the
   text and buttons, reduces the box to about 70% of its original height
   gently, pauses, then instantly disappears." That matches a real gap in
   the previous version of this fix: `max-height`/`padding`/`overflow`
   were only ever put on the <td> itself, and the text/buttons sat inside
   it with no shrink or fade of their own. A table cell's rendered height
   can't be animated below what its own content needs (that content-height
   floor is exactly what most browsers enforce for table layout, `overflow`
   quirks aside) - so the <td> could only ever visually collapse down to
   roughly its content's own natural height (the "70%" Karl saw), then sit
   there doing nothing further ("pauses") for whatever's left of the 0.5s,
   until ApproveAbsences.razor's own timer finished and the row was
   actually deleted from the underlying list on reload - a plain,
   unanimated DOM removal with no CSS transition attached to it at all
   (the "instantly disappears"). Fix: each cell's own content is now
   wrapped in a `.cell-collapse` div (ApproveAbsences.razor) - an ordinary
   block box, not a table cell, so it has no such content-height floor and
   can genuinely animate all the way to 0 - and it's THAT div, not the
   <td> itself, that carries the max-height/overflow/opacity collapse
   below. The <td> keeps only its own padding/border-bottom-width
   transition, which were never the problem. */
.absence-approval-table tbody tr {
    transition: opacity 0.3s ease;
}

.absence-approval-table tbody tr.absence-row-removing {
    opacity: 0;
}

.absence-approval-table tbody tr td {
    transition: padding-top 0.5s ease, padding-bottom 0.5s ease, border-bottom-width 0.5s ease;
}

.absence-approval-table tbody tr.absence-row-removing td {
    padding-top: 0;
    padding-bottom: 0;
    border-bottom-width: 0; /* stops the row's own divider line lingering as a stray 1px sliver once the cell has fully collapsed */
}

.absence-approval-table tbody tr td .cell-collapse {
    overflow: hidden;
    max-height: 4rem; /* comfortably larger than any real cell's content, including the two-line Duration cell - never actually clips in the resting state */
    opacity: 1;
    transition: max-height 0.5s ease, opacity 0.35s ease;
}

.absence-approval-table tbody tr.absence-row-removing td .cell-collapse {
    max-height: 0;
    opacity: 0; /* fades the text/buttons out as the wrapper shrinks, rather than hard-clipping them mid-collapse */
}

/* NEW (2026-09-10, next round) - Karl: "fade the line white-green-white...
   for approve and white-red-white for reject". A one-shot background
   animation on the whole row (RowCssClass, ApproveAbsences.razor, applies
   one of these two classes for the same ~1s window the button itself
   shows "Approved"/"Rejected") - deliberately only on a Success outcome,
   not a Failure one: a failed action (NotAuthorized/ConflictOnApprove/
   Error) leaves the request genuinely still Pending, so nothing about the
   row is about to change or disappear, and the button's own
   .save-button--failed colour already carries that outcome on its own.
   Tints matched to the existing --success/--failed colours used elsewhere
   in this file (button.save-button--saved/--failed), just lightened to a
   pale background wash rather than the button's own solid fill, since this
   plays across an entire row of readable text rather than a small button
   label. */
@keyframes absence-row-flash-approve {
    0% { background-color: transparent; }
    50% { background-color: #d9f2e3; }
    100% { background-color: transparent; }
}

@keyframes absence-row-flash-reject {
    0% { background-color: transparent; }
    50% { background-color: #fbe4e1; }
    100% { background-color: transparent; }
}

.absence-approval-table tbody tr.absence-row-flash-approve {
    animation: absence-row-flash-approve 1s ease;
}

.absence-approval-table tbody tr.absence-row-flash-reject {
    animation: absence-row-flash-reject 1s ease;
}

/* NEW (2026-09-10, next round) - Karl: "I think the pop up... is making
   things jump around. Can you try making a genuine pop up or overlay where
   it does not move elements around." .absence-booking-result (shared with
   BookAbsence.razor's own Preview/Confirmed panels - unaffected, since
   they don't get this extra class) is a normal in-flow block there - it
   appearing, changing, or (in principle) disappearing pushes whatever's
   below it up and down the page. This turns ApproveAbsences.razor's own
   copy into a fixed-position overlay instead, so it no longer takes part
   in the page's normal box flow at all - nothing below it moves regardless
   of what this panel does.
   Positioning is a first cut, not checked against a live render (no
   browser available in this environment) - `top` is set to clear
   `.top-row`'s own height (padding + its content) with some room to
   spare, `right` matches `.content`'s own 1.5rem horizontal inset so it
   lines up with the page's existing right-hand edge. Karl: nudge either
   value if it sits too close to the header or the table's own edge once
   you've actually seen it rendered. */
.absence-approval-toast {
    position: fixed;
    top: 4.5rem;
    right: 1.5rem;
    max-width: 420px;
    z-index: 1000;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
}

/* All four override sections (shift rounding included, 2026-09-04, later
   same day - "ensure this section has the same layout as the other
   customisation ones") now render their fields as plain .variable-row/
   .variable-info/.variable-input/.variable-units markup (see
   CompanyVariableRows.razor and .variable-row's own rules further down this
   file) - .override-fields itself no longer needs to know or care whether
   that markup came from CompanyVariableRows or was written out directly, so
   there's nothing form-grid-specific left to override here any more. */

.error { color: var(--danger); }

/* Non-alarming confirmation text (e.g. "'Warehouse' deactivated - 3
   employees still assigned to it will keep it") - deliberately not
   .error's red, since deactivating a site/department always succeeds; this
   is informational, not a failure. Used on the Sites & departments page. */
.info { color: var(--success); }

/* --- Company settings: landing page cards --- */
.settings-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 1rem;
    max-width: 960px;
}

.settings-card {
    display: block;
    padding: 1rem 1.25rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    text-decoration: none;
    color: inherit;
}
.settings-card:hover { border-color: var(--primary); }
.settings-card h3 { margin: 0 0 0.25rem; }
.settings-card p { margin: 0; color: var(--text-muted); font-size: 0.85rem; }

/* --- Company settings: inline-editable reference tables --- */
.subtitle { color: var(--text-muted); margin-top: -0.5rem; }

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

.editable-table input[type="text"],
.editable-table input[type="number"],
.editable-table input[type="time"],
.editable-table select {
    font: inherit;
    width: 100%;
    min-width: 4rem;
    padding: 0.3rem 0.4rem;
    border: 1px solid var(--border);
    border-radius: 4px;
}

.editable-table input[type="checkbox"] {
    width: 1.1rem;
    height: 1.1rem;
}

.editable-table td.actions {
    white-space: nowrap;
    text-align: right;
}

/* Paid/Employee bookable/Manager bookable/Bradford factor columns on the
   Annual leave and absences page's absence-types table - centres both the
   header text and the checkbox itself, rather than the editable-table
   default left alignment (fine for a text/number column, but a lone
   checkbox reads oddly hugging the left edge of a wide column). */
.editable-table th.checkbox-cell,
.editable-table td.checkbox-cell {
    text-align: center;
}

/* Default allowance column, same table - narrows the number input from
   .editable-table's usual full-width so the "days" .variable-units label
   (Karl's own request: same variable-units style as the rest of the app)
   sits on the same line beside it, rather than filling the column and
   pushing the unit text onto its own line underneath. */
.absence-types-table input[type="number"] {
    width: 4.5rem;
}

/* Absence-types table (2026-09-02, later same day) - a NULL value in
   default_allowance/paid/employee_bookable/manager_bookable/bradford_factor
   greys out and disables that cell's input (Karl's own words: "grey out
   the option on the page and do not make it changeable") rather than the
   column being hidden per-section as an earlier round did. Relies on the
   input's own `disabled` attribute (AbsenceTypesTable.razor sets it per
   cell) - this just makes "disabled" read clearly as "not applicable"
   rather than "temporarily locked", and mutes the "days" label beside a
   disabled Default allowance input to match. `select` added to this rule
   (2026-09-18, next round) once Manager bookable became a three-option
   <select> instead of a checkbox - same greyed-out-when-not-applicable
   treatment, just also matching the new control type. `select` replaced
   with `.manager-bookable-pill` (2026-09-18, later the same round) once
   that select itself became a three-pill toggle group - see
   AbsenceTypesTable.razor's own remarks; same treatment again, just
   matching the newest control type. `.allowance-rounding-pill` added
   (2026-09-18, Karl's message 3) for the new pro-rata rounding column -
   same treatment again. `.manager-bookable-pill` REMOVED again (2026-09-19,
   "same design" round) - Manager bookable no longer uses a pill at all, it
   shares Pro rata rounding's own `.allowance-rounding-segmented button`
   rule further down (which already carries an identical opacity/cursor
   treatment on its own `:disabled` state), so there was nothing left for
   this selector to match. */
.absence-types-table input:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* Pro rata rounding, as an icon segmented control (REDESIGNED A FOURTH
   TIME, 2026-09-19, still later the same day) - REPLACES the clickable-
   sentence version from earlier the same day, which didn't land either:
   Karl, having tried it: "It still does not look great. Are you able to
   produce a demo page of all different styles?" Shown a live comparison
   page (five real, interactive options, built with this file's own
   colour tokens and .data-table/.checkbox-cell rules rather than generic
   ones, so the comparison was a fair one) - "Icon segmented control looks
   best" - then four specific refinements on that pick: "Can you add
   tooltips over the icons? Can you use a different colour for the middle
   segment please. Can you turn the pill into a rectangle with rounded
   corners, like the text input boxes, and make the text/icons bold?"

   One joined control (AbsenceTypesTable.razor's own
   .allowance-rounding-segmented div), three small icon buttons in a row -
   Fixed/Round, then direction, then granularity, the same three-way split
   every earlier version of this control already used (see that file's own
   AllowanceRoundingIsFixed/AllowanceRoundingDirectionIndex/
   AllowanceRoundingGranularityIndex remarks). Unlike the pill versions
   before it OR the sentence version, this one is a genuinely CONSTANT
   size in every state - an icon glyph never changes width the way a text
   label does, so nothing about the row can shift depending on which of
   the seven combinations is currently selected. This is what actually
   solves "the layout moves" rather than just reducing it.

   Rounded-RECTANGLE shape (border-radius: 4px) - Karl: "turn the pill
   into a rectangle with rounded corners, like the text input boxes" -
   matches .editable-table input[type=text/number/time]/select's own
   border-radius exactly (this file's own rule, further up), not the
   999px pill shape every other control on this table still uses.
   Icons/text are bold (font-weight: 700) - Karl's own request.

   CHANGED (2026-09-19, later the same day) - two visual fixes plus a
   structural change, all from the same follow-up message:

   Fix 1: "The three segments are not quite filling the box and they look more
      rounded" - a browser's own default button chrome (some apply a
      native rounded/capsule look unless explicitly turned off), not
      anything this file's own rules were doing. Fixed with
      `appearance: none` (plus -webkit-/-moz- prefixes) on every segment
      button, `box-sizing: border-box` throughout, and the container's
      `overflow: hidden` removed (see point 2) - so the corner radius is
      now applied directly to the first/last segment via
      .allowance-rounding-segment-wrap:first-child/:last-child instead.

   Fix 2: Height increased to match `.editable-table input`'s own content+
      padding box exactly, per Karl's own measurement: 21px content/
      line-height + 0.3rem top padding (4.8px) + 0.3rem bottom padding
      (4.8px) = 30.6px. Hardcoded as `height: 30.6px` rather than left to
      fall out of the button's own font/line-height (the previous round's
      approach), since the segmented control still needs its own smaller
      font-size (0.85rem) to fit three icons/letters in 108px - flag if
      your root font size differs from what Karl measured this against and
      the two boxes drift out of alignment again.

   Fix 3: Each button is now wrapped in its own .allowance-rounding-segment-wrap
      span (AbsenceTypesTable.razor's own markup) - needed so the new
      per-segment hover tooltip (further down this file) has something to
      position itself against and to receive hover events even while its
      own button is disabled (Fixed/Minutes). This is also why
      `overflow: hidden` came off the container: it would have clipped the
      tooltip, which needs to rise above the control.

   Fix 4 (2026-09-19, still later - Karl, after Fix 1-3: "The segments are
      not filling the boxes. I am not sure the three are equally divided,
      nor is the text always centred in the segment.") - the real bug:
      .allowance-rounding-segment-wrap never got its own `box-sizing:
      border-box`, only the container and the button did. Two of the three
      wraps carry a 1px `border-right` divider; without border-box, that
      1px was added ON TOP of each wrap's flex-computed width instead of
      being absorbed inside it, so those two segments rendered a hair
      wider than the third - the actual cause of "not equally divided",
      and, as a knock-on effect, of the centred glyph inside each button
      looking off-centre relative to the row as a whole (each button was
      still correctly centring its own content within its own box - the
      boxes themselves just weren't the same size). Fixed by adding
      `box-sizing: border-box` (and `min-width: 0`, so a wrap can still
      shrink below its content's intrinsic width if it ever needs to) to
      .allowance-rounding-segment-wrap itself.

   Fix 5 (2026-09-19, still later - Karl: "The segments are not filling the
      boxes. I am not sure the three are equally divided, nor is the text
      always centred in the segment. Also, the width of the text options
      are moving the 3 segments about each time one is clicked on.") - that
      last symptom (segments visibly resizing as you click through F/M/R or
      the direction icons) is the real tell: nested flexbox (a flex wrap
      containing a flex button, both flex:1) can still let a child's own
      intrinsic content width leak up and influence its container's size
      unless EVERY level in the chain has min-width explicitly zeroed - a
      well-known flexbox gotcha, and evidently Fix 4's min-width: 0 on the
      wrap alone wasn't enough to fully close it off.

      REPLACED the nested-flex layout with CSS Grid instead:
      `grid-template-columns: repeat(3, minmax(0, 1fr))` on the container.
      This is the standard, well-tested pattern for "N equal columns no
      matter what's inside them" - a minmax(0, 1fr) track's width comes
      only from the available grid space, never from its content, so
      there's no equivalent gotcha to work around and no min-width
      overrides needed at each nested level. Each
      .allowance-rounding-segment-wrap is now a plain grid cell (no
      display/flex of its own); the button inside fills it completely via
      width/height: 100% and centres its own glyph with a simple internal
      flex (align-items/justify-content: center) - unrelated to, and no
      longer nested inside, the outer grid's own column sizing. */
.allowance-rounding-segmented {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    width: 108px;
    height: 30.6px;
    box-sizing: border-box;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface);
}

.allowance-rounding-segment-wrap {
    position: relative;
    min-width: 0;
    box-sizing: border-box;
}

.allowance-rounding-segment-wrap:not(:last-child) {
    border-right: 1px solid var(--border);
}

.allowance-rounding-segment-wrap:first-child button {
    border-top-left-radius: 4px;
    border-bottom-left-radius: 4px;
}

.allowance-rounding-segment-wrap:last-child button {
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;
}

.allowance-rounding-segmented button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    border: none;
    /* Explicit 0, not left to the CSS default - some browsers' own UA
       stylesheet gives <button> a real border-radius of its own (not just
       native OS chrome), which `appearance: none` above clears the native
       CONTROL rendering but does not necessarily clear this explicit
       property value. Karl (2026-09-19, still later): "the corners are
       still rounding inside the cells" - every segment showing rounded
       corners, not just the outer two, is exactly what that leftover
       browser default looks like. Overridden back to the intended 4px on
       just the outer two corners by the more specific
       .allowance-rounding-segment-wrap:first-child/:last-child button
       rules below. */
    border-radius: 0;
    background: none;
    font: inherit;
    font-size: 0.85rem;
    font-weight: 700;
    line-height: 1;
    color: var(--text-muted);
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
}

.allowance-rounding-segmented button:disabled {
    cursor: not-allowed;
    opacity: 0.4;
}

.allowance-rounding-segmented button:not(:disabled):hover {
    background: color-mix(in srgb, currentColor 15%, var(--surface));
}

/* CHANGED AGAIN (2026-09-19, still later) - Karl: "For the first segment,
   can you also make it blue for all values." Combined with the previous
   change just above (segments 2/3 already unified to one blue), EVERY
   segment now shares the exact same colour whenever it's enabled,
   regardless of which value it's currently showing - F/M/R on the first
   segment included. Simplified accordingly: rather than a separate
   .active-* class per state (.active-fixed/.active-minutes/.active-generic/
   .active-round-option, all now identical), there's nothing left for a
   class to distinguish, so this is now one rule keyed purely off
   `:not(:disabled)` - every state of every segment that ISN'T disabled
   gets this colour, and AbsenceTypesTable.razor no longer sets a `class`
   on any of the three buttons at all. --rounding-fixed (the old Fixed
   yellow), --danger (the old Minutes red), --hover-preview-outline (the
   old direction amber) and --rounding-half-day (the old half-day purple)
   are no longer used anywhere on this control, though all four tokens
   stay defined at the top of this file since other controls still use
   them. Greyed out via the existing button:disabled rule (opacity: 0.4)
   while Fixed/Minutes is selected in the first segment - unchanged, Karl
   confirmed that part is "still fine" the previous round. */
.allowance-rounding-segmented button:not(:disabled) {
    color: var(--primary);
    background: color-mix(in srgb, var(--primary) 18%, var(--surface));
}

/* Manager bookable - REDESIGNED (2026-09-19, "same design" round) into the
   same icon segmented control as Pro rata rounding - Karl: "Can you try
   using the same design for the Manager bookable variable." Reuses
   .allowance-rounding-segmented/.allowance-rounding-segment-wrap/
   .allowance-rounding-tooltip/.allowance-rounding-na as-is (the structural
   grid/box/tooltip-panel/JS-hover-hook rules above and below) rather than
   duplicating them under a renamed class - those names still say
   "allowance-rounding" but nothing about them is actually specific to that
   column any more; say if you'd like them renamed to something more
   generic (e.g. .segment-control-*), this was the lower-risk option for
   now. See AbsenceTypesTable.razor's own remarks for the full redesign,
   including dropping the separate company-wide company_variables
   'manager_booking_scope' setting this control's 2nd segment replaces.

   Colour is the one place this control genuinely differs from Pro rata
   rounding's own final all-blue look - Karl's own spec: "colour this
   section red" for N (1st segment), "colour all sections green" for Y.
   Unlike Pro rata rounding, where the blanket button:not(:disabled) rule
   above was enough (every enabled state of every segment reads the same
   colour), that rule can't express this control's colouring on its own:
   segment 1's button is NEVER disabled, yet still needs to change colour
   between its two values, N and Y. So a modifier class
   (.manager-bookable-segmented.is-no/.is-yes) goes on the OUTER container
   instead, set from the 1st segment's own current value
   (AbsenceTypesTable.razor's own markup) - both rules below have higher
   specificity than the blanket rule above (two classes + tag, vs. one
   class + tag), so they correctly override it via the normal cascade, no
   !important needed. --danger/--success are the same red/green tokens the
   OLD .manager-bookable-pill--not-bookable/--direct-book rules already
   used, reused here rather than inventing new ones. The existing
   button:disabled rule above (opacity: 0.4) still greys out the 2nd/3rd
   segments while N is selected, regardless of which colour class is set -
   :disabled always wins visually since these two rules only ever target
   button:not(:disabled). */
.manager-bookable-segmented.is-no button:not(:disabled) {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 18%, var(--surface));
}

.manager-bookable-segmented.is-yes button:not(:disabled) {
    color: var(--success);
    background: color-mix(in srgb, var(--success) 18%, var(--surface));
}

/* NEW (2026-09-19) - Karl's own rich tooltip wording for the three Pro
   rata rounding segments, including <u>...</u> emphasis he wrote himself.
   A native `title` attribute can't render HTML (tags show up as literal
   text in the browser's own tooltip), so these render as a small custom
   panel instead - the first non-native tooltip in this app.

   CHANGED (2026-09-19, still later) - visibility/position are no longer
   driven by :hover/:focus-within CSS at all. Karl found two problems with
   that version: tooltips from neighbouring segments overlapped each other
   (each panel is 260px wide, wider than the whole 108px control) and could
   intercept the mouse, causing "odd behaviour... moving from one segment
   to another"; and .table-scroll's `overflow-x: auto` was silently
   clipping vertically too (a CSS spec rule - one non-visible overflow axis
   forces the other to `auto` as well), cutting off the top of a tooltip
   for rows near the top of the table ("missing the top few rows of text").
   Both are now handled by wwwroot/js/allowance-rounding-tooltip.js
   (referenced from App.razor) - `position: fixed`, computed there via
   getBoundingClientRect rather than left/bottom here in CSS, escapes
   .table-scroll's clipping entirely, and the script explicitly hides
   whichever tooltip was previously showing before it shows the next one.
   `pointer-events: none` stops the panel itself from ever being able to
   intercept the mouse, belt-and-braces alongside the script's own
   explicit single-active-tooltip tracking. Visibility is now a plain
   `.is-visible` class the script toggles, not a CSS selector - still
   shown even while its own button is disabled (Fixed/Minutes, explaining
   "Only applies once Round is selected."), since the script triggers off
   the wrapping .allowance-rounding-segment-wrap the same way the old CSS
   selector did. */
.allowance-rounding-tooltip {
    display: none;
    position: fixed;
    width: 260px;
    max-width: 70vw;
    padding: 0.55rem 0.7rem;
    background: #22282e;
    color: #ffffff;
    font-size: 0.78rem;
    font-weight: 400;
    line-height: 1.4;
    text-align: left;
    white-space: normal;
    border-radius: 4px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
    z-index: 1000;
    pointer-events: none;
}

.allowance-rounding-tooltip.is-visible {
    display: block;
}

.allowance-rounding-tooltip u {
    text-decoration: underline;
}

/* Karl: "If the default allowance is set to 0, please disable the pill
   and replace it with a grey one that has the text 'N/A'." No longer a
   pill (see above) - just plain muted, non-interactive text, matching how
   every other "doesn't apply to this row" cell on this table already
   reads (compare a disabled/greyed-out checkbox or number input elsewhere
   in this same table). */
.allowance-rounding-na {
    color: var(--text-muted);
    font-size: 0.82rem;
}

.absence-types-table input[type="number"]:disabled + .variable-units {
    opacity: 0.45;
}

/* Column-heading tooltips (2026-09-03, Karl's request) - Default allowance/
   Employee bookable/Manager bookable/Paid/Bradford factor each get a "?"
   .tooltip-icon (same shared style every per-variable tooltip on this app
   already uses) right after the heading text. A small explicit gap here,
   since a <th> isn't a flex container the way .variable-info label is
   (which gets its gap from `gap: 0.4rem` instead) - relying on the HTML
   whitespace between the text node and the span alone read a little tight
   against the header text in testing. */
.absence-types-table th .tooltip-icon {
    margin-left: 0.35rem;
}

.editable-table td.actions button + button {
    margin-left: 0.4rem;
}

/* ApproveAbsences.razor's Pending-requests list (NEW 2026-09-09, later
   still again) - same "actions column is right-aligned, buttons don't wrap
   onto their own line, adjacent buttons get a small gap" shape
   .editable-table td.actions/td.actions button + button already establish
   above, just under this table's own class name since it's a plain
   .data-table (read-only rows plus two action buttons), not an
   .editable-table (inline-editable cells). Approve reuses button.primary,
   Reject reuses button.danger - both already-defined shared button styles,
   no new colour introduced for this page. */
.absence-approval-table td.actions {
    white-space: nowrap;
    text-align: right;
}

.absence-approval-table td.actions button + button {
    margin-left: 0.4rem;
}

/* NEW (2026-09-09, yet another round) - Karl: the Duration cell's days/
   minutes are now two lines rather than one "X days (Y minutes)" line (see
   ApproveAbsences.razor's own remarks) - each line is a plain <div>, which
   already stacks by itself; this just tightens the line-height/adds a
   little breathing room between them and mutes the minutes line slightly
   so the days figure (the one people actually scan for) reads first. */
.absence-approval-table td div + div {
    margin-top: 0.15rem;
    color: var(--text-muted);
    font-size: 0.9em;
}

/* NEW (2026-09-11) - Karl: "I am not sure if we can differentiate between
   New request and Cancellation lines? Perhaps a large green '+' symbol at
   the start of a New Request row, before the 'New request' text, or a red
   '-' for cancellation requests." A large, bold, fixed-width glyph
   (ApproveAbsences.razor's own KindMarkerSymbol/KindMarkerCssClass) sitting
   right before KindLabel's own text in the Type column - fixed width so
   "New request"/"Cancellation" still line up underneath each other whether
   the row got a '+' or a '-'. Reuses the same --success/--danger tokens as
   the rest of this table (the row's own flash colour, the result panel)
   rather than introducing a third colour pairing just for this. */
.absence-kind-marker {
    display: inline-block;
    width: 1rem;
    font-size: 1.3rem;
    font-weight: 700;
    line-height: 1;
    text-align: center;
    margin-right: 0.3rem;
}

.absence-kind-marker--new {
    color: var(--success);
}

.absence-kind-marker--cancellation {
    color: var(--danger);
}

/* .editable-table td.updated-meta (the per-row "Updated" date/time + who-by
   style on the Sites & departments / Areas / Absence types tables) was
   removed 2026-08-29 at Karl's request - "not really adding much at this
   stage and only making the layout look crowded." The columns themselves
   (UpdatedAt/UpdatedBy) are still captured on save - see
   CompanyLookupsService.cs - just no longer rendered on these screens. */

.editable-table tr.add-row td {
    background: var(--surface-alt);
}

/* Deactivate/Add buttons on the Sites & departments screen match the Save
   button's own established width (8rem, same as CompanyVariables.razor's
   .save-button) - Karl's request. Scoped to this page's own tables, not a
   blanket change to every .editable-table - Business units still use an
   older per-row Save button (Employment/Presence statuses moved to the
   same one-Save-button-per-section pattern as this page 2026-09-06, see
   Statuses.razor). */
.sites-departments-table td.actions button {
    width: 8rem;
    text-align: center;
}

/* Statuses.razor's Add/Delete buttons (2026-09-06, sixth time same day) -
   Karl: "Can you please make the Add and Delete buttons the same width as
   the Save button?" Same 8rem as .save-button/.sites-departments-table
   above, same reasoning: a consistent button width across a page reads
   more deliberate than each button sizing itself to its own label's
   length. */
.statuses-table td.actions button {
    width: 8rem;
    text-align: center;
}

/* Personalisation.razor (2026-09-09, later still) - Karl: "narrow the
   actions columns for the Employment statuses and Presence statuses
   sections to the width of the Add and Save buttons. Please increase the
   Description column width. Fix the width of the Background, Text, and
   Preview columns." Every column on all three of this page's tables
   already declared an explicit px width on its own <th> (see
   Personalisation.razor), but table-layout defaults to auto, under which a
   declared width is only ever a hint the browser can override based on
   content/available space - not the same as it actually holding, which is
   what "fix the width" is asking for. table-layout:fixed makes every
   declared width authoritative, same technique EmployeeList.razor's own
   .employee-list-table already uses for the identical reason (see that
   rule's own remarks). Scoped to this table class specifically, not the
   shared .data-table rule, for the same reason .employee-list-table is -
   other .data-table pages rely on auto layout for columns that don't
   specify a width of their own at all.

   min-width is the sum of every <th>'s own declared width across this
   page's tables (locked-indicator 2.25rem/36px + Description 280px +
   Background/Text 90px each + Preview 180px + Actions 154px = 830px),
   rounded up a little for cell borders/padding - once the table can't
   shrink below that, .table-scroll's own horizontal scrollbar kicks in on
   a narrow viewport instead of every column being proportionally squeezed,
   same reasoning as .employee-list-table's own min-width. */
.statuses-table {
    table-layout: fixed;
    min-width: 850px;
}

.colour-cell {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.colour-swatch {
    display: inline-block;
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 4px;
    border: 1px solid var(--border);
    flex-shrink: 0;
}

/* ColourSwatchPicker.razor (2026-09-06, fifth time same day) - Karl: "Are
   there better colour pickers that could be used? Where more colours can
   be chosen from a pallet?" Replaces the old .colour-select rule (a plain
   native <select> - see the REMOVED note this comment itself replaces,
   further down, for that rule's own history) with a trigger button + grid
   popover. Background/text colour on the trigger and each grid cell are
   set inline (style="...") by the component, not here - they're
   per-swatch values, not something a static stylesheet rule could
   express. */
.colour-swatch-picker {
    display: inline-block;
}

.colour-swatch-trigger {
    width: 100%;
    min-width: 2.25rem;
    height: 1.9rem;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 4px;
    cursor: pointer;
}

/* [popover] elements get a UA-default centred-dialog position (inset: 0;
   margin: auto) - inset/margin are reset here so only the top/left that
   colour-swatch-picker.js sets right before showPopover() take effect,
   rather than fighting the browser's own centring.

   max-height/overflow-y ADDED (2026-09-06, sixth time same day) - Karl: "the
   colour picker does not display fully when you try to select one of the
   colours near the bottom of the page - the colour picker is half on the
   page, half of the bottom of the page." colour-swatch-picker.js's openNear
   now flips the popover above the trigger (or clamps it to the viewport
   top) when it wouldn't fit below - this is the other half of that fix: even
   after flipping/clamping, a very short window (or the palette growing
   taller than the window, now 19 rows) might still not have room for the
   whole grid, so it gets its own scrollbar rather than running off an edge
   either way. 480px keeps most of the grid visible at once on a normal
   window; min() with 70vh so it still shrinks to fit a genuinely short one. */
.colour-swatch-popover {
    position: fixed;
    inset: auto;
    margin: 0;
    padding: 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
    max-height: min(70vh, 480px);
    overflow-y: auto;
}

/* 9 columns (CHANGED 2026-09-06, eighth time same day - was 6). Karl asked
   for 4 more hues (keeping the existing 12) and 9 shades per hue instead of
   6 (was 6 shades/6 columns in the previous round; before that, 9 shades/9
   columns in the round before that - see BasicColourPalette.cs's own
   remarks for the full history). BasicColourPalette.Swatches is ordered so
   each hue's 9 shades fill exactly one row, with the greys as their own row
   on top - the grey count was bumped from 6 to 9 in this same round purely
   so that row still fills a complete row too, rather than leaving the first
   hue row starting mid-way across. This column count has to track
   `N_SHADES`/`N_GREY` in that file exactly, or the grid would either leave
   a ragged half-empty last column or wrap a hue's shades across two rows. */
.colour-swatch-grid {
    display: grid;
    grid-template-columns: repeat(9, 1.5rem);
    gap: 0.2rem;
}

.colour-swatch-cell {
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 3px;
    cursor: pointer;
}

.colour-swatch-cell--selected {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
}

/* REMOVED (2026-09-06, fifth time same day) - .colour-select (the plain
   native <select>'s own rule: pointer cursor + monospace font) is gone
   along with the <select> itself - ColourSwatchPicker.razor above replaced
   it with a trigger button + grid popover, styled by the rules above this
   comment instead. .colour-picker/.colour-custom-input/.colour-custom-error
   were removed even earlier (2026-09-06, third time same day) when the
   brief "Custom…" hex-entry option was pulled back out - see the git
   history/README for that round if the detail's ever needed again. */

button.danger {
    background: var(--surface);
    border-color: var(--danger);
    color: var(--danger);
}
button.danger:hover { background: #fdecea; }

/* --- Company settings: company_variables scalar settings page --- */
.variable-section {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 1rem 1.25rem;
    margin-bottom: 1.25rem;
    /* Previously grown by +120px so widening .variable-input didn't cost
       .variable-info any width; grown again here by +80px (this increase
       goes to .variable-info alone - .variable-input and .variable-units
       both stay fixed-width, see below - so it's the description column
       specifically that gets "slightly wider"). */
    max-width: 1120px;
}

.variable-section h2 {
    margin: 0 0 0.75rem;
    font-size: 1.05rem;
}

/* Tightens the gap under the heading specifically when a subtitle follows
   it, so .section-subtitle's own margin-bottom (not this) is what spaces
   the first row below - otherwise the two margins would stack and leave
   an oversized gap between heading and subtitle. Also matches
   .device-info-table (Devices.razor's per-device info table), same
   reasoning. */
.variable-section h2:has(+ .section-subtitle),
.variable-section h2:has(+ .device-info-table) {
    margin-bottom: 0.25rem;
}

/* A section's optional second line (company_labels.subtitle) - deliberately
   styled to match .variable-description below rather than sharing its
   class name outright, so the two can drift apart later if a section
   subtitle ever needs to look different from a per-field description. */
.section-subtitle {
    margin: 0 0 0.75rem;
    color: var(--text-muted);
    font-size: 0.85rem;
}

/* NEW (2026-09-25) - the "Custom allowance overrides" section's own
   warning paragraph, rendered alongside (not instead of) a normal
   .section-subtitle - see this file's own --warning-text remarks above
   for the colour choice. Boxed rather than plain text so it reads as a
   genuine caution, not just another quiet metadata line the way
   .section-subtitle itself does.

   CHANGED (2026-09-25, later the same day) - Karl: "Please add a space
   between the 'Custom allowance overrides for this employee' title and
   the warning box." Added margin-top (the base .section-subtitle rule
   this shares only sets a bottom margin) - this box is now the first
   thing inside .override-fields on both EmployeeDetail.razor/
   CompanyGroupEditor.razor (moved there the same message, only shown once
   the section is actually ticked on - see those files' own remarks), so
   this is what puts daylight between it and the toggle label above. Also
   used, harmlessly, by EmployeeDetail.razor's own unrelated _saveWarning
   banner near the bottom of the page - a small top gap reads fine there
   too, separating it from whatever's immediately above it. */
.section-subtitle--warning {
    margin-top: 0.5rem;
    color: var(--warning-text);
    background: var(--highlight-wrap-bg);
    border: 1px solid color-mix(in srgb, var(--warning-text) 35%, var(--highlight-wrap-bg));
    border-radius: 4px;
    padding: 0.5rem 0.75rem;
    font-size: 0.85rem;
}

/* Devices.razor's small per-device info table (Hardware ID/Type/Enrolled/
   Last seen/Uptime) - Karl's request for "a small table with no borders,
   one on each line" instead of the original single wrapped line of text.
   Sized/coloured to match .section-subtitle above so it reads as the same
   kind of "quiet metadata" content, just laid out as rows instead of one
   run-on line. */
.device-info-table {
    border-collapse: collapse;
    margin: 0 0 0.75rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.device-info-table th,
.device-info-table td {
    border: none;
    padding: 0 0.75rem 0.15rem 0;
    text-align: left;
    font-weight: normal;
    vertical-align: top;
}

/* The label column - kept to its own natural width (not a fixed one, so it
   never wastes space when every label on a given device happens to be
   short) and never allowed to wrap, so the value column lines up in a
   straight edge underneath itself regardless of which labels are present
   on a given device (e.g. a device with no Hardware ID row still lines up
   with one that has it). */
.device-info-table th {
    color: var(--text-muted);
    white-space: nowrap;
}

.variable-row {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    padding: 0.75rem 0;
    border-top: 1px solid var(--border);
}

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

/* Public holidays - "auto approve" row greyed out while "action" is "none"/
   "add_allowance" (2026-09-04, Karl's request - see AnnualLeaveAbsences.razor's
   IsPublicHolidaysAutoApproveDisabled). */
.variable-row--disabled { opacity: 0.45; }
.variable-row--disabled input,
.variable-row--disabled select,
.variable-row--disabled textarea { cursor: not-allowed; }

/* Record locking (2026-09-05) - applied to a section/record's whole
   editable area (RecordLockGuard.razor) when it's currently held by
   someone else, so nothing inside can be clicked or typed into at all -
   pointer-events blocks it outright rather than just dimming it, since a
   Save/Delete button being disabled alone wouldn't stop someone tabbing
   into a field and typing without being able to actually save it. Every
   page's own Save/Add/Delete button is still explicitly disabled too (see
   each page's own record-locking remarks) - belt-and-braces, since the
   real enforcement is the server-side lock check each Save method makes,
   not this CSS layer. */
.record-lock-locked {
    opacity: 0.55;
    pointer-events: none;
    cursor: not-allowed;
}

.record-lock-banner {
    color: #7a4a00;
    background: #fff6e5;
    border: 1px solid #f0c36d;
    border-radius: 4px;
    padding: 0.5rem 0.75rem;
    margin: 0 0 0.75rem;
    font-size: 0.9rem;
}

.variable-info { flex: 1 1 auto; min-width: 0; }

.variable-info label {
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.variable-description {
    margin: 0.25rem 0 0;
    color: var(--text-muted);
    font-size: 0.85rem;
}

.variable-error { margin: 0.35rem 0 0; font-size: 0.85rem; }

.tooltip-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 999px;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    color: var(--text-muted);
    font-size: 0.7rem;
    cursor: help;
}

.variable-input {
    flex: 0 0 auto;
    /* Widened again (220px -> 340px) - the 220px width was too narrow for
       the LIST dropdowns (default_timezone's options run up to ~36
       characters, e.g. "France, Spain, Benelux (UTC+01:00)", and were
       clipping in the closed <select> box). .variable-section's max-width
       grew by the same +120px alongside this, specifically so
       .variable-info (the description column) doesn't lose any of the
       width it gained in the previous pass - only this column's width
       needed to change. VARCHAR fields don't need this extra width for
       their own sake any more (they wrap across multiple textarea rows -
       see TextAreaRows in the code-behind) but share the same column. */
    width: 340px;
    /* Centered to line up with .variable-units (also align-self: center) -
       .variable-info is left at the row's default flex-start, since its
       label should stay pinned to the top even when a description/error
       makes the row taller than a single input line. */
    align-self: center;
    /* Border lives here (not on the input itself) so it wraps whichever
       control the row actually renders - text/number/select/checkbox/the
       two DATE dropdowns - without needing per-control invalid styling. */
    border: 2px solid transparent;
    border-radius: 6px;
    padding: 2px;
}

.variable-input--invalid {
    border-color: var(--danger);
}

.variable-input input[type="text"],
.variable-input input[type="number"],
.variable-input select,
.variable-input textarea {
    font: inherit;
    width: 100%;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 4px;
}

.variable-input input[type="checkbox"] {
    width: 1.1rem;
    height: 1.1rem;
}

/* Brightness1/Brightness2/BrightnessTimeout/Contrast - sliders instead of
   plain number spinners (Karl's request). Scoped to the --slider modifier
   only, so it doesn't affect any of the plain number/text/select controls
   .variable-input already styles above. Deliberately still sized by
   .variable-input's own fixed width (340px, set further up this file) -
   this just lays out the track + value readout *within* that same column,
   so a slider row takes up the same width as every other input on the
   page, Karl's explicit ask. */
.variable-input--slider {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.5rem;
}

.slider-track {
    flex: 1 1 auto;
    min-width: 0;
    accent-color: var(--primary);
}

.slider-value {
    flex: 0 0 2.75rem;
    text-align: right;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    font-size: 0.85rem;
}

/* VARCHAR fields (row count set per-row in the .razor code-behind, see
   TextAreaRows) - vertical resize only, so a row can't be dragged wider
   than the fixed-width column it lives in. */
.variable-input textarea {
    resize: vertical;
    line-height: 1.35;
}

.date-parts {
    display: flex;
    gap: 0.4rem;
}

.date-parts select { flex: 1 1 auto; }

/* Fixed width, always rendered (even when a variable has no Units set -
   see CompanyVariables.razor) so every row reserves the same space here
   and the Save buttons line up in a column regardless of which rows
   actually show unit text. */
.variable-units {
    flex: 0 0 auto;
    width: 4rem;
    align-self: center;
    color: var(--text-muted);
    font-size: 0.85rem;
    white-space: nowrap;
}

/* One per section, below its rows, pinned to the bottom-right of the
   section box - replaces the old per-row Save button (see
   CompanyVariables.razor - saving moved from per-variable to
   all-or-nothing per section). */
.section-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.75rem;
    padding-top: 0.75rem;
    margin-top: 0.25rem;
    border-top: 1px solid var(--border);
}

/* When a .section-actions row sits directly under a table (rather than
   under CompanyVariables.razor's div-based rows), the table's own last row
   already draws a border-bottom immediately above this point - adding
   .section-actions' own border-top on top of that produced two solid lines
   stacked a couple of pixels apart, which read as a rendering glitch rather
   than a divider (Karl, 2026-08-29). Drop the second line in that specific
   case; the table's bottom border is enough on its own. */
.table-scroll + .section-actions {
    border-top: none;
}

/* The button's own label carries the outcome (Karl's call). Every possible
   label is a single short word ("Save"/"Saving…"/"Success"/"Error") -
   deliberately so, since the actual validation detail lives next to the
   offending field instead (.variable-error / .variable-input--invalid) -
   so a fixed width is enough to stop the button resizing/shifting on
   click, no wrapping needed.

   .save-button--saving/--saved/--failed (NOT the fixed-width .save-button
   itself) are ALSO now used by ApproveAbsences.razor's own Approve/Reject
   buttons (2026-09-10, Karl: "animate the Approve and Reject buttons in
   the same way as the Save buttons") - those buttons keep their own
   "primary"/"danger" sizing and colour as the base class, with one of
   these three layered on top only while that specific row's button is
   mid-flash. See that page's ActionButtonCssClass for the exact
   composition.

   CHANGED (2026-09-10, still another round) - Karl, comparing this Save
   button directly against the Approve/Reject buttons: "The Save button
   changes to Saved for what seems like 500ms but it is not smooth, it
   just changes and changes back again." Checked directly against this
   rule as it stood before this change: correct, there was NO `transition`
   property here at all, on this class or on .save-button--saving/--saved/
   --failed themselves (further down this file) - the colour swap (and the
   swap back to idle once CompanyVariables.razor's own revert timer fires)
   has always been an instant hard cut, never a fade, since this button's
   very first version. That was a deliberate omission at the time
   .approval-action-button (ApproveAbsences.razor's own equivalent, further
   up this file) got its own transition - the doc comment there says so
   explicitly ("by design ... scoped to ONLY these two buttons, not the
   shared Company-settings Save button"), on the theory that changing this
   shared class's behaviour app-wide wasn't asked for. Karl's own
   comparison this round makes it clear he wants BOTH to fade, not just
   Approve/Reject - so the transition now lives here too, meaning every
   page using .save-button (every Company settings Save button, plus
   DeviceEditor/EmployeeDetail/CompanyGroupEditor's own Save buttons) picks
   it up in both directions: fading in when a state class
   (.save-button--saving/--saved/--failed) is added, and fading back out
   when it's removed (the transition property stays applied throughout,
   via this base class, so both directions animate - no JS needed). Same
   0.25s duration as .approval-action-button's own (below), tuned slightly
   up from that class's original 0.2s for a touch more visible smoothness
   on both. */
.save-button {
    width: 8rem;
    text-align: center;
    transition: background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}

/* CHANGED (2026-09-10, next round yet again) - a real bug, found while
   chasing Karl's "I cannot see any animations at all" report after he'd
   already redeployed fresh code and fully cleared his browser cache (so
   the earlier "stale app.css" theory, just above, is now ruled out). These
   three selectors were plain class selectors (specificity 0,1,0), but on
   ApproveAbsences.razor's own Approve/Reject buttons they're layered on
   TOP OF `primary`/`danger` - and `button.primary`/`button.danger`
   (further up this file) are element+class selectors (specificity 0,1,1),
   which is MORE specific and so wins the cascade regardless of source
   order, on every property these two sets both set (background,
   border-color, color). The practical effect: the button's green/red
   flash never actually rendered on this page - only its text label
   ("Approve" -> "Approving…" -> "Approved") ever visibly changed, which is
   easy to miss and reads as "no animation at all". The exact same
   button.primary + save-button pairing already existed on three OTHER
   pages before this round (DeviceEditor.razor, EmployeeDetail.razor,
   CompanyGroupEditor.razor - all `class="primary save-button ..."`), so
   this was a genuine pre-existing bug there too, just never reported -
   likely because Karl's own testing of the Save-button flash happened on
   one of the several OTHER Settings pages that use plain `class="save-button
   ..."` with no `primary`, where the specificity tie (0,1,0 vs 0,1,0) is
   broken by source order instead, in these classes' favour, so those pages
   were never actually broken.
   Fixed by adding the `button` element to each selector, raising their own
   specificity to 0,1,1 - now an exact tie with button.primary/button.danger,
   which source order (these three are declared later in the file) resolves
   in their favour, on every page that uses them. Every existing caller
   already only ever applies these classes to a <button> element, so this
   is a strictly safer/no-op change everywhere they were already working
   correctly.

   CHANGED AGAIN (2026-09-10, yet another round) - Karl: "I cannot see any
   difference" after deploying the fix above. The specificity math checked
   out (re-verified: no other selector in this file targets .actions/
   .absence-approval-table buttons with a background/color of its own - see
   .absence-approval-table's own rules further down, margin/width/text-align
   only), and there's no second stylesheet or Blazor CSS-isolation file
   (*.razor.css) anywhere in this project that could be adding a more
   specific rule invisibly from here. Rather than keep guessing at a cause
   that can't be directly observed from this environment, these three
   now carry `!important` as a decisive belt-and-braces measure - a plain
   state-toggle utility class like this ("while this specific button is
   mid-flash, it MUST show this colour, full stop") is exactly the case
   `!important` exists for, and it removes any possible doubt about a
   cascade conflict, current or hidden, being the explanation. If Karl
   still sees literally no difference after this - not even the button's
   own TEXT changing ("Approve" -> "Approving…" -> "Approved") - that would
   point away from CSS entirely and toward the deployed build not actually
   containing this feature's C# yet (worth checking view-source/dev tools
   for `button.save-button--saved` in the served app.css, and ruling out a
   CDN/reverse-proxy layer in front of the site caching the old file
   server-side, independently of the browser's own cache). */
button.save-button--saving {
    color: var(--text-muted) !important;
}

button.save-button--saved {
    background: var(--success) !important;
    border-color: var(--success) !important;
    color: #fff !important;
}

button.save-button--failed {
    background: #fdecea !important;
    border-color: var(--danger) !important;
    color: var(--danger) !important;
}

/* --- Login page --- */
.login-page {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--surface-alt);
}

.login-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 2rem;
    width: 320px;
}

.login-card h1 { margin: 0 0 0.25rem; }
.login-card .subtitle { color: var(--text-muted); margin-top: 0; margin-bottom: 1.5rem; }

.login-card form {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.login-card label {
    font-size: 0.85rem;
    color: var(--text-muted);
    margin-top: 0.5rem;
}

.login-card input {
    font: inherit;
    padding: 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
}

.login-card button[type="submit"] {
    margin-top: 1rem;
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}

.login-divider {
    display: flex;
    align-items: center;
    text-align: center;
    color: var(--text-muted);
    font-size: 0.8rem;
    margin: 1.25rem 0;
}
.login-divider::before, .login-divider::after {
    content: "";
    flex: 1;
    border-bottom: 1px solid var(--border);
}
.login-divider span { padding: 0 0.75rem; }

.btn-microsoft {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.55rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    text-decoration: none;
    font: inherit;
}
.btn-microsoft:hover { background: var(--surface-alt); }

/* --- Calendar views (Weekly/Monthly/Annual/Custom) --- */

.calendar-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 1.5rem;
    margin-bottom: 1rem;
}

.calendar-toolbar-field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.calendar-toolbar-field select,
.calendar-toolbar-field input[type="date"] {
    font: inherit;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    min-width: 220px;
}

/* NEW (2026-09-12, next round) - BookAbsence.razor only, Karl: "['Click a
   date...'] can be placed to the right of the Date selection boxes and
   'Clear dates' button, perhaps as two lines." .calendar-toolbar sits on
   the left at its own natural width; the instructional text takes the
   remaining space. Wraps to stack (text below the toolbar) on a narrow
   window via flex-wrap, same as every other toolbar row on this page.

   CHANGED (2026-09-12, another round) - Karl: "Is currently not that wide
   and going onto 4 lines" - "make [it] take up the rest of the width of the
   white calendar area." .calendar-toolbar-note's old 360px max-width (below)
   is gone; it now grows (flex: 1 1 auto) to fill whatever's left in the row
   beside .calendar-toolbar, same as .absence-booking-section--wide already
   does for the wide section itself. */
.calendar-toolbar-row {
    display: flex;
    align-items: flex-start;
    flex-wrap: wrap;
    gap: 0.75rem 1.5rem;
}

/* CHANGED (2026-09-13, later still) - MyCalendar.razor AND
   EmployeeCalendar.razor both (shared class - see the top-of-file comment
   on EmployeeCalendar.razor's own row, item 3, on why the two pages read
   the same CSS). Karl: "The helper text... should be at the same level as
   the date drop-down boxes, not the small titles above them." Each
   .calendar-toolbar-field is a small muted label ("From"/"To") stacked
   above its actual input box (.calendar-toolbar-field's own column-layout
   rule, plus a 0.25rem gap, further up this file) - with
   .calendar-toolbar-row's align-items: flex-start (unchanged, above), the
   note's own top used to line up with the row's cross-axis start, i.e. the
   top of those small labels, not the input boxes below them. This
   margin-top pushes the note down by roughly the height of one of those
   labels plus the gap beneath it (0.85rem text at the browser's default
   line-height, ~1rem tall, plus the 0.25rem gap = ~1.25rem) so its own top
   lines up with the input row instead. A judgement-call figure, not
   measured pixel-for-pixel against a live render (no dotnet run available
   here) - flag if it needs nudging either way. */
.calendar-toolbar-note {
    flex: 1 1 260px;
    margin: 0;
    margin-top: 1.25rem;
}

/* NEW (2026-09-12, next round) - BookAbsence.razor only, Karl: "The absence
   categories and additional types (On-site, Holiday, Public holiday,
   Working Off-site, Working From Home (WFH), etc) can be placed to the
   right of the absence type drop-down." The absence-type <label> sits on
   the left, CalendarCategoryToggles's own already-wrapping row of pills on
   the right - both align to their own top, so a tall category list doesn't
   push the (usually shorter) dropdown label out of alignment. */
.calendar-type-and-categories {
    display: flex;
    align-items: flex-start;
    flex-wrap: wrap;
    gap: 0.5rem 1.5rem;
    /* NEW (2026-09-12, another round) - Karl: "Add a space between the
       above Absence type and Display options, and the start of the
       calendar." */
    margin-bottom: 1.25rem;
}

/* Karl: "Absence type drop down does not need to be overly wide." Overrides
   .calendar-toolbar-field select's own 220px min-width just for this one
   field (chained with .calendar-toolbar-field for higher specificity, so it
   wins regardless of source order) - the From/To date inputs elsewhere on
   this page, which share the plain .calendar-toolbar-field class alone,
   keep their own 220px.

   CHANGED (2026-09-13, later still) - MyCalendar.razor AND
   EmployeeCalendar.razor both (shared class). Karl: "Please make the
   absence type drop down a fixed width, before padding, of around 275px."
   Replaces the previous min/max-width range with one fixed figure.
   box-sizing:content-box overrides this app's own global `* { box-sizing:
   border-box; }` reset (top of this file) just for this one rule, same
   technique as .calendar-day--compact's own fixed height and the new
   .page-title-row-select select width above - "275px" means the content
   box specifically, Karl's own "before padding" wording, with this
   select's padding/border (from the shared .calendar-toolbar-field select
   rule) added on top rather than eaten out of it. */
.calendar-toolbar-field.absence-type-field select {
    box-sizing: content-box;
    width: 275px;
}

.calendar-nav {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* REMOVED (2026-09-18, next round) - .calendar-nav-label was only used by
   WeeklyView/MonthlyView/AnnualView/CustomView (Pages/Calendar/, all four
   deleted this round per Karl's "remove calendar/weekly, calendar/monthly,
   calendar/annual, and calendar/custom from the portal" request). .calendar-nav
   itself (just above) is still used elsewhere and is unchanged. */

/* NEW (2026-09-12, latest round) - BookAbsence.razor only, Karl: "Add a
   title and subtitle to the calendar block. The title should be the year
   selected. The 'Previous year' and 'Next year' buttons should be moved to
   the right hand side, in line with the title." One row holding the title
   (left) and the Previous/Next year .calendar-nav buttons (right) - the
   subtitle underneath is a separate, full-width <p class="subtitle"> that
   isn't part of this row, so the nav buttons line up with the title
   specifically rather than the title+subtitle pair as a whole.

   MOVED (2026-09-12, next round) - Karl: "Should be moved to the top of the
   white area/ section." This whole row (+ its subtitle) now opens
   .absence-booking-section--wide instead of sitting just above the
   calendar grid - purely a change to where the markup using this class
   renders, nothing here in app.css needed to change for that move. */
.calendar-block-title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem;
}

/* RENAMED (2026-09-12, next round) from .calendar-block-title - Karl:
   "'Absence summary' can be bigger, the same size as '2026'." Now shared by
   both the year title above (inside .calendar-block-title-row) and the
   "Absence summary" heading further down BookAbsence.razor, so the old,
   calendar-specific name no longer fit. Also bumped up from 1.3rem per
   Karl's other new request this round, "'2026' needs to be bigger, perhaps
   h2?" - both headings are now <h2> elements using this class rather than
   relying on any browser or .absence-booking-section h3 default sizing. */
.absence-block-title {
    margin: 0;
    font-size: 1.6rem;
}

/* NEW (2026-09-12, another round) - Karl: "Please ensure there is space
   between the title ('2026') and the subtitle [...]" and, further down his
   same message, the same request for the "Absence summary" title. The base
   .subtitle rule (above, near the top of this file) sets a small NEGATIVE
   margin-top - fine for a subtitle sitting directly under a normal-size
   heading elsewhere on the site, but it was pulling these two subtitles
   flush against the now-larger .absence-block-title headings instead.
   Applied alongside .subtitle (not replacing it) so color/etc are
   unchanged - only margin-top is overridden, back to a small positive gap. */
.absence-block-subtitle {
    margin-top: 0.5rem;
}

/* NEW (2026-09-13, next round) - EmployeeCalendar.razor (Rhodes.Web/
   Components/Pages/ManagerTools/) only, so far - Karl: "Please place this
   employee drop down near the top of the page body, inline and to the
   right of the page title." A plain flex row so the <h1> and the new
   employee <select> sit side by side; wraps to two lines under narrow
   widths instead of overflowing (see the .page-body side-gutter rules
   elsewhere in this file - this row inherits those, nothing new needed
   there).

   CHANGED (2026-09-13, later still) - Karl: "please put the employee
   drop-down on the right-hand side of the page, with the 'Employee' title
   at the same level as the [top] of the page title." justify-content:
   space-between pushes the <h1> to the left edge and the employee <label>
   to the right edge of the row (previously they just sat side by side with
   a 1rem gap, wherever the h1's own width happened to end). align-items
   changed from baseline to flex-start - the row's cross-axis start is the
   TOP of its tallest child, so the "Employee" span (the first thing inside
   the label, stacked above the select itself) now lines up with the top of
   the h1's text instead of the h1's text baseline. */
.page-title-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.page-title-row h1 {
    margin: 0;
}

/* NEW (2026-09-13, later still) - EmployeeCalendar.razor only, Karl: "The
   subtitle for the employee-calendar crashes into the employee drop down.
   Can the element it's in have it's width limited so it stops before that
   drop down element." The <p class="subtitle"> right after .page-title-row
   is a plain full-width block, but .subtitle's own base rule (further up
   this file) gives it a small NEGATIVE margin-top, meant to pull it up
   snug under an ordinary single-line heading. .page-title-row is taller
   than a plain h1 alone now (the "Employee" label + select stack, on the
   right, is taller than the h1's own single line of text) - so that
   negative margin was pulling the subtitle up into space still occupied by
   the bottom of the dropdown, which sits at the row's right edge
   (justify-content: space-between, above). Reserves ~260px on the right
   (roughly the 220px select plus its own padding/border and a little
   headroom - the same 260px figure already used as a flex-basis elsewhere
   in this file) so the subtitle's own text wraps within the space to the
   left of the dropdown column instead of running under it. The adjacent-
   sibling selector (.page-title-row + .subtitle) targets this one page's
   subtitle only, without a new class in the markup - MyCalendar.razor's
   own subtitle follows a plain <h1>, not a .page-title-row, so it's
   untouched. */
.page-title-row + .subtitle {
    max-width: calc(100% - 260px);
}

/* CHANGED (2026-09-13, later still) - Karl: "before padding, the drop down
   should be 220px wide." Reuses .calendar-toolbar-field's own column layout
   (small muted label above a select) - this override sets the select's own
   width, replacing the previous min-width:200px "don't stretch too wide
   beside a page <h1>" rule now that Karl's given an exact figure instead.
   box-sizing:content-box overrides this app's own global `* { box-sizing:
   border-box; }` reset (top of this file) just for this one rule, so
   "220px" means the content box specifically - Karl's own "before padding"
   wording, same technique already used on .calendar-day--compact's own
   fixed height - with this select's own padding/border (from the shared
   .calendar-toolbar-field select rule) added on top of that, rather than
   eaten out of it the way border-box would. */
.page-title-row-select select {
    box-sizing: content-box;
    width: 220px;
}

.calendar-category-toggles {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 1.25rem;
}

.calendar-category-toggle {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: 0.85rem;
    cursor: pointer;
    background: var(--surface);
    opacity: 0.55;
}

.calendar-category-toggle.enabled {
    opacity: 1;
    border-color: var(--category-colour, var(--primary));
    background: color-mix(in srgb, var(--category-colour, var(--primary)) 18%, var(--surface));
}

/* NEW (2026-09-12, next round) - Karl: "For the 'Cancellation requested'
   pill, please make the background white and the border red and slightly
   wider." Overrides both the unchecked base rule and .enabled above for
   just this one pill (CalendarCategoryToggles.razor's own
   isPendingCancellationPill check) - white/var(--surface) rather than the
   usual --category-colour tint, red border matching the date cells' own
   .calendar-day--pending-cancellation, and 2px rather than the standard
   1px every other pill uses (still thinner than the 3px "look at this"
   rings elsewhere, per Karl's own "slightly wider" wording rather than
   "much wider"). Declared after .calendar-category-toggle.enabled so it
   wins the equal-specificity tie in the enabled case, and after the plain
   .calendar-category-toggle base rule for the unchecked case - both are
   two-class/one-class ties respectively, resolved by source order. */
.calendar-category-toggle--pending-cancellation,
.calendar-category-toggle--pending-cancellation.enabled {
    background: var(--surface);
    border-color: var(--danger);
    border-width: 2px;
}

.calendar-category-toggle input[type="checkbox"] { margin: 0; }

/* REMOVED (2026-09-18, next round) - .calendar-week-row ("one employee's
   week - a single row of seven larger day cards") was only used by
   WeeklyView.razor (Pages/Calendar/, deleted this round per Karl's "remove
   calendar/weekly, calendar/monthly, calendar/annual, and calendar/custom
   from the portal" request). Its own ".calendar-week-row .calendar-day"
   override (further down) is removed with it. */

/* Twelve month grids (used by MyCalendar/EmployeeCalendar, one grid per
   month of the leave year) - responsive, wraps to however many columns fit.
   Floor raised from 240px to 300px (Karl's own request: each day slightly
   larger, ~3 months across a 1080px browser window rather than 4) - still
   auto-fill/responsive, so a wider window still fits more than three. */
.calendar-annual-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 1.5rem;
}

.calendar-grid-wrap { min-width: 0; }

.calendar-grid-title {
    font-weight: 600;
    margin-bottom: 0.4rem;
}

.calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 3px;
}

.calendar-grid-weekday-label {
    font-size: 0.7rem;
    color: var(--text-muted);
    text-align: center;
    padding-bottom: 0.2rem;
}

.calendar-day {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.4rem;
    min-height: 3.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    /* NEW (2026-09-10, next round) - the Pending-half hatch overlay
       (.calendar-day-half-hatch, below) is an absolutely-positioned child
       clipped to a diagonal triangle via clip-path; position:relative makes
       this cell its positioning context, and overflow:hidden keeps that
       triangle (and its own border-radius corners) from ever bleeding past
       the cell's own rounded border. Every other rule on this cell already
       painted within these bounds anyway, so this is a no-op for every date
       that doesn't use the new hatch overlay. */
    position: relative;
    overflow: hidden;
}

/* CHANGED (2026-09-12, next round) - Karl: "Please make each date in the
   calendar be a fixed height of 32px before padding. When a dot is added,
   they jump around." min-height (a floor, letting the cell grow taller
   when its content - the dots row in particular - needs more room) is
   replaced with a fixed height, so every cell in BookAbsence.razor's
   annual grid (the only page that renders this Compact variant) is
   identical regardless of whether a given date happens to have dots.
   box-sizing:content-box overrides this app's own global `* {
   box-sizing: border-box; }` reset (top of this file) just for this one
   rule, so "32px" means the content box specifically - Karl's own "before
   padding" wording - with padding/border added on top of that, rather than
   eaten out of it the way border-box would. .calendar-day's own
   overflow:hidden (further up this file) still applies here, so genuinely
   oversized content (more dots than 32px minus padding/gap can fit) is
   clipped rather than growing the cell again.

   CORRECTED (2026-09-12, next round again) - Karl: "I have made the days
   why too high. Can they just be a little bit taller than they were
   before?" 32px content-box, plus this rule's own 0.3rem (4.8px) padding
   each side and .calendar-day's 1px border each side, came out at 43.6px
   total box height - noticeably MORE than the old min-height:2.3rem
   (36.8px total, back when box-sizing was still the global border-box
   default here, so that 36.8px already included its own padding/border).
   Dropped the content height to 28px, which - with padding/border
   unchanged - lands the total around 39.6px: a little over the old
   36.8px (matching "a little bit taller than before"), well short of the
   32px version's 43.6px ("way too high"). Still a FIXED height, not
   min-height - that part of the original ask (stopping dots from making
   the row jump) stands regardless of the exact number. Flagged in the
   reply: this is a judgement call on the actual pixel count, since Karl
   gave a description ("a little bit taller") rather than a number this
   time - straightforward to nudge further either way if 28px isn't it. */
.calendar-day--compact {
    height: 28px;
    box-sizing: content-box;
    padding: 0.3rem;
    border-radius: 4px;
}

.calendar-day--blank {
    background: transparent;
    border-color: transparent;
}

.calendar-day--muted {
    opacity: 0.4;
}

.calendar-day--today {
    border-color: var(--primary);
    box-shadow: inset 0 0 0 1px var(--primary);
}

.calendar-day--clickable {
    cursor: pointer;
}

.calendar-day--clickable:hover {
    border-color: var(--primary);
}

/* No shift scheduled that day (employee_payroll_staging.shift_duration_default
   = 0, Karl's own test) - light grey background. Layered under --muted/
   --today via source order, so a muted-and-no-shift day still reads as
   muted (opacity) and a today-and-no-shift day still keeps its highlight
   border. */
.calendar-day--no-shift {
    background: var(--surface-alt);
}

/* NEW (2026-09-08), Karl's own request: "shading out days before they
   started and after they are due to leave" - BookAbsence.razor only (see
   CalendarDayCell.IsOutsideEmployment's own remarks; CalendarDayCell
   suppresses --no-shift/full-day fills/dots whenever this applies, so this
   is the only shading such a cell ever shows). A fixed neutral grey hatch,
   deliberately NOT the same look as either the plain --no-shift grey (so
   "no shift scheduled, but still employed" reads differently from "not
   employed at all") or the Pending fill's white/colour hatch above (a
   different angle and a grey/white pairing instead of category-colour/
   white, so the two "hatched" meanings - pending vs. outside employment -
   don't get confused with each other). */
.calendar-day--outside-employment {
    background: repeating-linear-gradient(135deg, var(--surface-alt) 0px, var(--surface-alt) 6px, var(--surface) 6px 12px);
    opacity: 0.6;
}

/* .calendar-day--full - a single event that covers the WHOLE day
   (2026-09-07, Karl's own request). Set for BOTH a confirmed full-day event
   (solid fill) and, since 2026-09-08, a still-Pending one (a diagonal
   white/colour hatch instead - Karl's own "would a white pattern work?"
   suggestion, so a Pending day is still obviously distinct from a
   Confirmed one at a glance without a second colour). CalendarDayCell sets
   the actual fill itself via an inline style either way (it's per-category,
   not a fixed value a CSS class could hold), and deliberately doesn't touch
   border-color, so a full-day-shaded --today still keeps its highlight ring
   on top of the fill - the class itself carries no rule here, it's just a
   marker on the element for anyone inspecting the DOM. CalendarDayCell
   already omits --no-shift whenever either fill applies (see its own
   remarks), so that grey background never has to compete with this one
   either. */

/* .calendar-day--half - a single half-shift-worked event that day (NEW
   2026-09-09/10, Karl's own "would it be possible to shade the top or
   bottom half of the day?" request - see CalendarDayCoverageEntry.IsHalfDay/
   ICalendarService.GetDayCoverageAsync's own remarks for the exact
   threshold). Same shape as .calendar-day--full's own remarks above:
   CalendarDayCell sets the actual fill (a hard-stop two-colour gradient,
   category colour on the shaded half, transparent on the other) via an
   inline style, this class is just a DOM marker, no rule of its own here.
   Never set at the same time as --full (mutually exclusive by
   construction on the Worked entry itself - see CalendarDayCoverageEntry's
   own remarks). */

/* NEW (2026-09-10, next round) - the Pending-half hatch overlay,
   CalendarDayCell's own pendingHalfHatch/pendingSecondHalfHatch. Sits on top
   of .calendar-day's own cellFillStyle background (inline, painted first -
   see .calendar-day's own position:relative/overflow:hidden remarks above)
   rather than being part of that inline style itself, since a CSS
   background can't be clipped to just one diagonal triangle on its own -
   clip-path (set inline per-instance to whichever triangle,
   TriangleClipPath) does that instead. inset:0 anchors it to the padding
   box (same box the cell's own flex children sit in), so the hatch sits
   very slightly inside the outer border/background edge rather than exactly
   flush with it - close enough not to read as a gap in practice, and far
   simpler than tracking this cell's three different padding values
   (default/--compact/.calendar-week-row) to compensate for it. z-index:0
   plus the z-index:1 given to the cell's own content below keeps this
   overlay from ever painting over the date number or dot summary. */
.calendar-day-half-hatch {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

/* REMOVED (2026-09-12, yet another round) - .calendar-day-cancellation-hatch
   (the grey diagonal-stripe overlay for CalendarDayCell.IsPendingCancellation)
   is gone - Karl: "Can you remove the diagonal shading from the dates with
   cancellation requests pending. Instead, can you try making the borders
   red please." See .calendar-day--pending-cancellation, below the other
   ring-style states, for the red-border replacement. */

/* NEW (2026-09-07) - BookAbsence.razor's date-range picker. Distinct from
   --muted (which dims days OUTSIDE a fixed period on the read-only
   Annual/Custom views) - this highlights days INSIDE the range currently
   being built up by clicking.

   SPLIT (2026-09-12) - Karl: "When the date range is selected and the
   Preview button is pressed, the selected dates remain with a blue
   boarder and lighter blue shading. Is there any way to keep all dates
   highlighted with a blue border, but only shade the dates which will be
   booked (i.e. those that are scheduled to be worked)?" The border stays
   here, on every selected date unconditionally (unchanged); the
   background is gone from this rule and now lives on
   .calendar-day--selected-shade below, which CalendarDayCell only adds
   when the date also has a shift scheduled (!NoShiftScheduled) - see that
   component's own remarks. A selected rest day (weekend, or any other
   date NoShiftDates already flags as nothing scheduled) now keeps its
   blue ring but falls back to whatever background it would otherwise
   show (typically .calendar-day--no-shift's own light grey) instead of
   the blue tint, since the SP itself would exclude it from what's
   actually booked. Public holidays within a selected range aren't
   distinguished here - GetDayCoverageAsync doesn't currently surface a
   per-date public-holiday set to this page - so they still shade like
   any other working day even though the SP would also exclude them;
   flagged rather than silently handled, in case that's worth a follow-up. */
.calendar-day--selected {
    border-color: var(--primary);
}

.calendar-day--selected-shade {
    background: var(--calendar-selected-bg);
}

/* NEW (2026-09-11) - Karl: "when you click on an existing absence and the
   pop-up shows all of the dates and their status, can you please also
   highlight the dates on the calendar that form part of this absence."
   Deliberately a ring (inset box-shadow), not a background - unlike
   .calendar-day--selected above, this never replaces whatever the cell is
   already showing (the absence's own type-colour fill/dots), it just marks
   which dates belong to the absence currently open in the popup. A little
   thicker than .calendar-day--today's own 1px ring so the two read as
   distinct signals if a date is ever both; declared after --today so it
   wins that (rare) tie rather than fighting over which box-shadow applies. */
.calendar-day--popup-highlight {
    box-shadow: inset 0 0 0 3px var(--primary);
}

/* NEW (2026-09-11, still later) - Karl: "When I hover over a date which
   has an absence already booked, perhaps highlighting the whole absence
   duration when I do so by outlining the dates in a different colour."
   Same "ring, not a fill" shape as .calendar-day--popup-highlight above,
   just --hover-preview-outline's own amber so the two are never confused -
   this one marks the absence the MOUSE is currently over, not necessarily
   the one open in the day-details popup (which may not even be open). */
.calendar-day--hover-highlight {
    /* CHANGED (2026-10-09, final) - Karl: "When I hover over an absence, can
       you please change the outline of the cell to red. At the moment, I think
       it is adding a thick border inside the existing one." The cell's own 1px
       border turns red instead of a 3px amber ring inside it. */
    border-color: var(--danger);
    /* CHANGED (2026-10-10) - "please make the red outline slightly thicker,
       perhaps 2px": the red border plus a 1px red ring just inside it, so it
       reads as one 2px line without the cell changing size (the same way
       .calendar-day--today draws its 2px blue line). */
    box-shadow: inset 0 0 0 1px var(--danger);
}

/* NEW (2026-10-10) - "ensure the date I am hovering over also has a red
   outline. It is currently going blue. It only needs to do that if the date
   does not have an absence." The hovered date of a highlighted absence keeps
   the red outline (.calendar-day--clickable:hover's blue was winning); a
   date without an absence still goes blue on hover. */
.calendar-day--hover-highlight.calendar-day--clickable:hover {
    border-color: var(--danger);
    box-shadow: inset 0 0 0 1px var(--danger);
}

/* NEW (2026-09-12, yet another round) - CalendarDayCell's own
   IsPendingCancellation, fourth try. First a red day number, then a grey
   diagonal-stripe overlay, then an inset box-shadow ring (all abandoned on
   Karl's own feedback - see IsPendingCancellation's own remarks,
   CalendarDayCell.razor); now: "Can the red borders on the affected dates
   replace the existing grey borders, not fit inside them?"

   CHANGED from the inset box-shadow ring tried in the round just above -
   that painted a second red line just inside the cell's own existing 1px
   grey border (border: 1px solid var(--border), .calendar-day above)
   rather than replacing it, which is exactly what Karl's asking to stop.
   This instead overrides the SAME border-color property .calendar-day--
   selected already uses for its own blue ring, at the same 1px width -
   genuinely replacing the grey with red rather than adding a ring on top
   of it. Declared after .calendar-day--selected in this file, so on the
   rare date that's somehow both selected (building a new booking) and
   pending-cancellation (an existing absence there already awaiting a
   cancellation decision) at once, this rule's red wins the tie over
   --selected's blue - flagged rather than silently decided, in case Karl
   would rather the selection colour won that particular tie instead. */
.calendar-day--pending-cancellation {
    border-color: var(--danger);
}

/* NEW (2026-09-11, still later) - the one cell currently showing the
   "View this absence" popover (CalendarDayCell.ShowHoverPopover) needs to
   let that popover escape its own bounds - .calendar-day's own
   overflow:hidden (further up this file) exists to clip the diagonal
   Pending-hatch triangles to the cell's rounded corners, which this cell
   temporarily gives up while its popover is showing. z-index lifted above
   every sibling cell (and the row below it) so the popover always paints
   on top rather than being tucked behind neighbouring cells in DOM/paint
   order. */
.calendar-day--hover-popover-anchor {
    overflow: visible;
    z-index: 30;
}

/* The popover itself - a small floating card below the cell, Karl: "can it
   pop up with an option to select the current absence." Deliberately
   generic text rather than the absence's own type/date range - showing
   those would need a fresh DB read on every hover (see
   BookAbsence.razor's own _hoverPreviewDates remarks for why the
   highlight itself avoids that); the "View this absence" button already
   opens the full day-details popup with every real detail a moment later,
   so nothing here needs to duplicate it up front. */
.calendar-day-hover-popover {
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin-top: 4px;
    z-index: 40;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
    padding: 0.5rem 0.6rem;
    width: max-content;
    max-width: 12rem;
    text-align: center;
    cursor: default;
}

.calendar-day-hover-popover-text {
    font-size: 0.78rem;
    color: var(--text-muted);
    margin-bottom: 0.4rem;
}

.calendar-day-hover-popover-button {
    width: 100%;
    font-size: 0.85rem;
    padding: 0.3rem 0.6rem;
}

/* REMOVED (2026-09-18, next round) - .calendar-day-weekday was only used by
   WeeklyView.razor (Pages/Calendar/, deleted this round) via
   CalendarDayCell's own since-removed ShowWeekdayLabel parameter. */

.calendar-day-number {
    font-weight: 600;
    font-size: 0.9rem;
    /* NEW (2026-09-10, next round) - without this, the absolutely-positioned
       .calendar-day-half-hatch overlay (a positioned descendant) would paint
       AFTER this plain in-flow flex child regardless of DOM order, covering
       the date number. Explicit position+z-index makes this a positioned
       descendant too, so it's ordered by z-index (1, above the hatch's 0)
       within the same .calendar-day stacking context instead. */
    position: relative;
    z-index: 1;
}

/* Saturday/Sunday - Karl's own request: grey the date text (not the whole
   cell) on weekends. */
.calendar-day-number--weekend {
    color: var(--text-muted);
}

.calendar-day--compact .calendar-day-number {
    font-weight: 500;
    /* CHANGED (2026-10-04) - Karl: "Can you please increase the size of the
       text used for the dates slightly in the calendar." Was 0.75rem. Only
       the employee and manager calendars use compact cells (28px high), so
       nothing else moves. */
    /* CHANGED (2026-10-10) - "make the dates slightly larger": 0.85rem -> 0.95rem,
       with a slightly tighter line so a number and a row of dots still fit the
       28px cell. */
    font-size: 0.95rem;
    line-height: 1.1;
}

.calendar-day-dots {
    display: flex;
    flex-wrap: wrap;
    gap: 0.2rem;
    margin-top: auto;
    /* NEW (2026-09-10, next round) - same .calendar-day-half-hatch stacking
       reasoning as .calendar-day-number above. In practice this block never
       actually renders alongside the hatch overlay today (CalendarDayCell
       only shows the dot summary when halfDayCategory is null, and the
       hatch only ever renders when halfDayCategory is not null) - kept for
       the same defensive consistency every other content element here now
       has, in case that precedence ever changes. */
    position: relative;
    z-index: 1;
}

.calendar-day-dot {
    /* CHANGED (2026-10-10) - Karl: "Can you make all dots the same overall
       size. For confirmed absences, they should be solid colour. For
       unconfirmed requests, they should have a yellow outer ring and shaded
       inner dot." Every dot is now 10px overall (was 8px, 16px+ring for a
       request), solid, with no ring outside it. */
    width: 10px;
    height: 10px;
    box-sizing: border-box;
    border-radius: 999px;
    display: inline-block;
    flex: 0 0 auto;
}

/* NEW (2026-09-08), Karl's own "displaying the colour in a large dot"
   suggestion for a Pending request that shares a day with something else
   (a lone full-day Pending event instead gets the hatched fill - see
   .calendar-day--full's own remarks). Same colour as the dot underneath it
   (background is set inline per-category, same as every other dot) - just
   bigger, with a white ring around it so it visibly stands out among any
   normal-sized dots for confirmed events the same day, without needing a
   second colour. */
/* CHANGED (2026-10-10) - a request: the same 10px overall, a 3px ring in the
   absence type's colour (set inline, CalendarDayCell) round a white centre.
   Was a yellow ring round a striped centre - Karl: "Please do not make the
   dot striped - a simple colour outer ring and white centre is fine." */
.calendar-day-dot--pending {
    border: 3px solid;
}

/* REMOVED (2026-09-18, next round) - Monthly planner mode
   (.calendar-day--planner, .calendar-day-planner-row, .calendar-day-lines,
   .calendar-day-line) was MonthlyView.razor's own layout, via
   CalendarDayCell's since-removed Lines parameter - MonthlyView.razor
   (Pages/Calendar/) was deleted this round per Karl's "remove
   calendar/weekly, calendar/monthly, calendar/annual, and calendar/custom
   from the portal" request, and CalendarDayCell no longer has a planner
   branch to render any of these classes. */

/* REMOVED (2026-09-18, next round) - Annual view's allowance summary widget
   (.calendar-allowance-summary and its own table/cell classes -
   .calendar-allowance-table-wrap, .calendar-allowance-table,
   .calendar-allowance-minutes, .calendar-allowance-total) was AnnualView.razor
   only (Pages/Calendar/, deleted this round per the same request above). */

/* --- Book an absence (BookAbsence.razor) - NEW 2026-09-07 --- */

/* CHANGED (2026-09-12, next round) - Karl: "'What are they booking?' can be
   removed. The space to the white area/ section beneath can be removed."
   Was 1.75rem, matching the gap this page used between every other section;
   now 0 just for the gap between this form's own two direct children (the
   compact "What are they booking?"-less section and the wide "calendar"
   section) - Karl called out the space specifically once the heading that
   used to visually anchor the first section was gone, so removing it here
   (rather than, say, trimming the wide section's own top padding) is
   scoped to exactly the pair he was looking at, since this form only ever
   has these two children. */
.absence-booking-form {
    display: flex;
    flex-direction: column;
    gap: 0;
}

/* Karl's own feedback (2026-09-07): "try using the whole width of the
   page". The form used to cap ITSELF at 640px, which squeezed the calendar
   grid down along with everything else - narrower now applies per-section
   instead (below), so the compact "Who's booking?"/"What are they
   booking?" fields stay a readable width while the "When?" section (which
   holds the calendar) is free to use the page's full width. */
.absence-booking-section {
    max-width: 640px;
}

/* NEW (2026-09-12, another round) - BookAbsence.razor only, Karl: "ensure
   the Preview/ Confirm booking information box uses the available width
   between the buttons and the edge of the calendar section. It is
   currently not that wide and extending to 3-4 lines." The actions block
   (Preview/Confirm buttons + .absence-booking-results beside them) carries
   the plain .absence-booking-section class above for its other rules
   (padding/etc it inherited when it first lived outside the wide section),
   but that class's own 640px cap was re-capping it even though it now sits
   inside .absence-booking-section--wide, which otherwise opts out of that
   cap. This second class, chained onto the same div, overrides max-width
   back off - scoped to just this one div, not the OTHER
   .absence-booking-section on this page (the compact "What are they
   booking?" section), which keeps its own 640px. */
.absence-booking-actions-section {
    max-width: none;
}

.absence-booking-section h3 {
    margin: 0 0 0.6rem;
    font-size: 1rem;
}

/* Opts a section back OUT of the 640px cap above - only "When?" (the
   section holding the date picker/calendar) uses this.

   Also carries a white card background (2026-09-08, Karl's own feedback:
   "It is difficult to see the non-scheduled shifts because the shading
   matches that of the background."). Root cause: this page's sections
   never had their own card background (body's own background IS
   --surface-alt, the same colour .calendar-day--no-shift shades a cell
   with - see app.css's :root/body rules right at the top of this file),
   so a no-shift cell was effectively invisible against the page showing
   straight through this section's transparent background. Giving the
   calendar's own wrapping section an explicit white (--surface) card
   background - same background/border/radius/padding shape
   .calendar-allowance-summary already uses elsewhere on this page - means
   --surface-alt shading now reads as a real, visible grey against a white
   card instead of disappearing into an identically-grey page. Scoped to
   --wide specifically (the calendar's own section), not every
   .absence-booking-section, so the compact "Who's/What's booking?" fields
   above it are untouched. */
.absence-booking-section--wide {
    max-width: none;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 1rem;
}

/* NEW (2026-09-23) - AbsenceYears.razor only, Karl: "Is it possible to
   make the page, the body, on absence years slightly less than full
   screen? At the minute, it's taking up the entire width of the page...
   I don't think the page's content needs to be that wide." AbsenceYears.razor
   carries .absence-booking-section--wide (above) purely for its white
   card background/border/padding look, not because its own content
   (a 3-input create row, or a 6-column table) needs unlimited width the
   way MyCalendar.razor/BookAbsence.razor's own wide section genuinely does
   (a whole year of date columns) - but --wide's own `max-width: none`
   removed any cap for every page that uses it, this one included. Chained
   onto BOTH of this page's own two --wide sections (alongside the shared
   classes, not replacing them - same "override, don't edit the shared
   class" approach .absence-booking-actions-section above already uses in
   the other direction), re-capping just this page back down to 1120px -
   the same settings-page width .variable-section/.form-grid already use
   elsewhere on this site, so this page now reads as "a settings page",
   which is what it actually is. Karl's own reported side effect - the
   per-row delete popover (.absence-year-delete-popover) reading "really
   narrow but long, pushed to the right" - was never a bug in the popover's
   own sizing (still a plain max-width:320px box); with the table itself
   capped at 1120px instead of running the full width of a wide monitor,
   its own Delete button (and therefore the popover anchored to it) now
   sits well clear of the browser's right edge, where popover-position.js's
   own openNear has room to place it at its natural width instead of
   immediately hitting the overflow-right clamp. .data-table's own
   width:100% means capping this wrapping section is enough on its own -
   nothing on the table itself needed to change. */
.absence-year-page-width {
    max-width: 1120px;
}

/* Karl's own feedback (2026-09-07): "try... showing 4 months across" for
   this page's Year view specifically - overrides .calendar-annual-grid's
   own auto-fill/responsive column count (AnnualView.razor's leave-year
   grid keeps its original behaviour untouched, since this only applies
   inside .absence-booking-section--wide). Changed to 3 across (Karl's own
   follow-up number, 2026-09-08 - Year view is also the default view now,
   per that same round, so it's the first thing shown on this page). Still
   a fixed count rather than auto-fill, so it drops to fewer columns on a
   narrower window via the auto-fit minmax fallback below 3 * 260px, rather
   than ever overflowing/scrolling sideways. */
.absence-booking-section--wide .calendar-annual-grid {
    grid-template-columns: repeat(3, minmax(260px, 1fr));
    /* NEW (2026-09-12, another round) - Karl: "After the calendar, add a
       space between it and the Preview and Confirm booking buttons."
       Scoped to this page specifically (the plain .calendar-annual-grid
       rule, above, is shared with AnnualView.razor, which Karl didn't ask
       to change). */
    margin-bottom: 1.5rem;
}

@media (max-width: 1100px) {
    .absence-booking-section--wide .calendar-annual-grid {
        grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    }
}

.absence-booking-radio {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    font-size: 0.9rem;
}

.absence-booking-radio input[type="radio"] {
    margin-top: 0.2rem;
}

/* NEW (2026-09-12, latest round) - a horizontal wrapper for a group of
   .absence-booking-radio labels, which used to just stack one per line by
   virtue of each being its own flex/block box in normal document flow. Used
   for the length-mode radios ("Whole day(s)/Half day - first half/Half day
   - second half", under the date-selection toolbar). CHANGED (2026-09-12,
   yet another round) - used to also back the Days/Minutes toggle under the
   now-removed absence summary table (via the --right modifier, itself
   removed) - this is now its only use. Cancels each radio's own
   margin-bottom since the group's own `gap` handles spacing instead -
   without this, a wrapped-to-two-lines group on a narrow window would
   double up its vertical spacing. */
.absence-booking-radio-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1.5rem;
    /* NEW (2026-09-12, another round) - Karl: "Add a space under the 'Whole
       day(s)/ half days' options, between them and the Absence type and
       display selection." */
    margin-bottom: 1.25rem;
}

/* NEW (2026-09-12, one more round) - Karl: "Can you please ensure there is a
   space between the absence summary table and the Days/ Minutes selection
   radio boxes." Applied alongside .absence-booking-radio-group only on
   MyAbsenceSummary.razor's own Days/Minutes toggle (immediately below
   AbsenceAllowanceSummaryTable) - NOT a change to .absence-booking-radio-group
   itself, which BookTimeOff.razor's own, unrelated length-mode radios also
   use (there, above the calendar grid, where this extra top margin isn't
   wanted). See MyAbsenceSummary.razor's own remarks at this class's usage. */
.absence-summary-radio-group {
    margin-top: 1.25rem;
}

.absence-booking-radio-group .absence-booking-radio {
    margin-bottom: 0;
}

/* REMOVED (2026-09-12, yet another round) - .absence-booking-radio-group--right
   (Karl: "place it on the right-hand side", the Days/Minutes toggle under
   the now-removed absence summary table) went with that table -
   BookAbsence.razor no longer references it. */

/* CHANGED (2026-09-12, next round) - Karl: "Please add white space or
   blank text in here until the remaining text is displayed so it does not
   jump around." This block (BookAbsence.razor) is no longer conditionally
   rendered at all - it's always present, showing a blank line until an
   employee/absence type are both chosen - so a fixed min-height is what
   actually stops the calendar grid below from jumping as the real text
   (which can run onto two lines, e.g. "3 days (1980 minutes) remaining for
   the period 01 Jan 2026 to 31 Dec 2026.") fills in. Sized for two lines at
   this element's own font-size (inherited, ~0.9rem via .subtitle's parent
   context) plus a little breathing room - comfortably covers every branch's
   text, including the two-line "remaining" sentence. */
.absence-booking-balance {
    margin-top: 0.5rem;
    min-height: 2.8em;
}

/* NEW (2026-09-14) - MyCalendar.razor only, Karl: "Please move the number
   of days remaining preview underneath these buttons with a sufficient
   gap." A modifier class rather than changing .absence-booking-balance's
   own margin-top above - EmployeeCalendar.razor's own copy of this block
   hasn't moved this round (Karl: "perhaps we should just change one to
   begin with") and still sits directly under .calendar-type-and-categories,
   where the small 0.5rem margin is still correct; only MyCalendar.razor's
   markup adds this second class, on top of the plain one, now that its own
   copy sits right after .absence-booking-actions-row instead. Overrides
   margin-top only - min-height (above) still reserves layout space so the
   calendar/summary below it doesn't jump as the message fills in, same
   reasoning as before, just relevant to a taller block of text now that the
   message itself is several sentences long rather than one short line.

   CHANGED (2026-09-14, later still) - Karl: "There should be a sufficient
   gap, similar to the one under the calendar, between the bottom of the
   response popup and information text discussed above." 2rem was this
   delivery's own first guess; "the one under the calendar" is a real,
   already-established figure - .absence-booking-section--wide .calendar-
   annual-grid's own margin-bottom (1.5rem, added 2026-09-12 for "a space
   between it and the Preview and Confirm booking buttons") - so this now
   matches that exactly instead. */
.absence-booking-balance--gap {
    margin-top: 1.5rem;
}

/* NEW (2026-09-12, next round) - Karl: "Instead of just appearing
   instantly, if you could get it fade in over 0.4s, that would be great."
   (re: the remaining-allowance text above). Chained with .absence-result-
   reveal (higher specificity than that class alone, so it wins regardless
   of source order) to override just its animation DURATION for this one
   element - .absence-result-reveal's own 0.3s (used by ApproveAbsences.
   razor's result panel) is left alone; Karl asked for 0.4s here
   specifically. Same override-fields-reveal keyframe either way. */
.absence-booking-balance-text.absence-result-reveal {
    animation: override-fields-reveal 0.4s ease;
}

/* MOVED (2026-09-12, next round) - Karl: "Can you move the Preview/ Confirm
   booking results box... to the right of the buttons themselves. Please
   make it take up the remaining width of the calendar block." The buttons
   stay their own natural width on the left; .absence-booking-results (below)
   flex-grows to fill whatever's left on the right. Wraps to stack (results
   below the buttons) on a narrow window, same as every other row on this
   page that does this. */
.absence-booking-actions-row {
    display: flex;
    align-items: flex-start;
    flex-wrap: wrap;
    gap: 1rem 1.5rem;
}

/* CHANGED (2026-09-12, another round) - Karl: "The absence-booking-results
   DIV is about 73px high when it has a full line of text. This causes the
   absence-booking-actions-row DIV to jump about. Can this be fixed to
   74px?" This div is empty (0px) until a Preview/Confirm result actually
   exists, so .absence-booking-actions-row was collapsing to just the
   buttons' own height and then jumping taller the moment a result
   appeared. min-height reserves Karl's own measured height up front - same
   fix as .absence-booking-balance's own min-height, above - while still
   letting it grow past 74px if it ever needs to (a wrapped line, or both
   the Preview and booking results stacked at once). */
.absence-booking-results {
    flex: 1 1 260px;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    min-height: 74px;
}

.absence-booking-actions {
    display: flex;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

/* NEW (2026-09-14, later still) - MyCalendar.razor only. Karl: "The Preview,
   Confirm request buttons and the response popup should be aligned with the
   Absence type drop-down input. They are currently assigned with the
   'Absence type' text." Same root cause as the earlier .calendar-toolbar-
   note/.page-title-row + .subtitle fixes this same round: .absence-booking-
   actions-row's own align-items: flex-start (above) aligns every child to
   the row's cross-start, which is the TOP of its tallest child - the
   absence-type-field <label>, whose "Absence type" span sits ABOVE the
   actual <select>. The buttons/results were therefore lining up with that
   label text, not the select box below it.

   :has(.absence-type-field) scopes this to only a row that actually
   contains the dropdown - MyCalendar.razor's own .absence-booking-actions-
   row (the dropdown moved into this row earlier the same round), not
   EmployeeCalendar.razor's, whose own copy of this same row has no
   absence-type-field inside it (its dropdown is still in its original spot
   above the calendar) - pushing that page's buttons down with nothing above
   them to align with would just add a stray gap. Same :has() technique
   already used elsewhere in this file (the .variable-section heading/
   subtitle margin rule). 1.25rem matches the same "one small muted label's
   height plus its own gap" estimate already used for .calendar-toolbar-note
   this same round - not measured pixel-for-pixel against a live render (no
   dotnet run available here), so flag if it needs nudging either way. */
.absence-booking-actions-row:has(.absence-type-field) .absence-booking-actions,
.absence-booking-actions-row:has(.absence-type-field) .absence-booking-results {
    margin-top: 1.25rem;
}

/* NEW (2026-09-12, next round) - Karl: "If the Preview and Confirm booking
   buttons could have a 0.4s animation, that would be great." Ambiguous -
   there's no existing loading/success state for these two buttons to
   animate between (unlike, say, .approval-action-button's own colour fade
   as its label changes, further up this file) - read instead as wanting
   hover/press feedback: a small lift and soft shadow, both easing in/out
   over the requested 0.4s. Scoped to just these two buttons via the
   existing .absence-booking-actions wrapper, not every button on the page. */
.absence-booking-actions button {
    transition: transform 0.4s ease, box-shadow 0.4s ease, background-color 0.4s ease;
}

.absence-booking-actions button:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}

.absence-booking-actions button:active:not(:disabled) {
    transform: translateY(0);
    box-shadow: none;
}

/* CHANGED (2026-09-12, next round) - Karl: "Can the Preview/ Confirm
   booking popup fade in over 0.4s, and back out again when a different
   date is selected over the same 0.4s?" opacity/transition are new - base
   state is fully visible, and this transition is what actually plays the
   fade-OUT half (BookAbsence.razor toggles absence-booking-result--fading-
   out, below, then removes the element 400ms later once the transition's
   had time to finish - see _resultsFadingOut's own remarks). The fade-IN
   half is a separate mechanism (.absence-result-reveal, further down) -
   `animation`, not `transition`, since it needs to play once on mount
   rather than in response to a property change. */
.absence-booking-result {
    padding: 0.75rem 1rem;
    border-radius: 8px;
    border: 1px solid var(--border);
    font-size: 0.9rem;
    opacity: 1;
    transition: opacity 0.4s ease;
}

.absence-booking-result--fading-out {
    opacity: 0;
}

/* Same override-fields-reveal keyframe as .absence-booking-balance-text's
   own reveal (above) - chained for higher specificity than
   .absence-result-reveal alone, so it wins regardless of source order,
   overriding just the DURATION to Karl's own requested 0.4s here. The
   0.3s .absence-result-reveal alone still gives to ApproveAbsences.razor's
   result panel is untouched. */
.absence-booking-result.absence-result-reveal {
    animation: override-fields-reveal 0.4s ease;
}

.absence-booking-result p {
    margin: 0.15rem 0;
}

/* REMOVED (2026-09-12, yet another round) - .absence-booking-divider (the
   3px <hr> between the Preview/Confirm buttons and the absence summary
   table) went with that table - Karl: "remove the whole Absence summary
   section from beneath the calendar, including the dividing line." */

.absence-booking-result--ok {
    border-color: var(--success);
    background: color-mix(in srgb, var(--success) 12%, var(--surface));
}

.absence-booking-result--blocked {
    border-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, var(--surface));
}

/* --- Absence day-details popup (NEW 2026-09-11, BookAbsence.razor only) -
   Karl's original ask that started this whole round of work: "When you
   select a day with an existing absence on ... it can pop up with the
   absence booking details, which you can then click to cancel." No
   existing modal/dialog component or CSS anywhere in this codebase
   (confirmed via a full-codebase search before adding this), so this is a
   plain fixed-position backdrop + panel, matching the same CSS-variable
   palette (--border/--surface/--success/--danger/--text-muted) every other
   block on this page already uses rather than inventing new colours. ---- */
.absence-day-popup-backdrop {
    position: fixed;
    inset: 0;
    background: rgb(0 0 0 / 45%);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 3rem 1rem;
    z-index: 50;
}

.absence-day-popup {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
    padding: 1.25rem 1.5rem;
    max-width: 32rem;
    width: 100%;
    max-height: calc(100vh - 6rem);
    overflow-y: auto;
}

.absence-day-popup-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.5rem;
}

.absence-day-popup-header h3 {
    margin: 0;
}

.absence-day-popup-close {
    background: none;
    border: 1px solid var(--border);
    border-radius: 6px;
    line-height: 1;
    padding: 0.25rem 0.6rem;
    cursor: pointer;
}

.absence-day-popup-days {
    list-style: none;
    margin: 0.75rem 0;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
}

.absence-day-popup-day-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.4rem 0.75rem;
    font-size: 0.9rem;
}

.absence-day-popup-day-row + .absence-day-popup-day-row {
    border-top: 1px solid var(--border);
}

.absence-day-popup-status {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    border: 1px solid var(--border);
    white-space: nowrap;
}

.absence-day-popup-status--locked {
    border-color: var(--text-muted);
    color: var(--text-muted);
}

.absence-day-popup-status--pending {
    border-color: var(--warning, var(--border));
    color: var(--warning, var(--text-muted));
}

.absence-day-popup-status--cancelled,
.absence-day-popup-status--rejected {
    border-color: var(--danger);
    color: var(--danger);
}

.absence-day-popup-status--live {
    border-color: var(--success);
    color: var(--success);
}

.absence-day-popup-actions {
    display: flex;
    gap: 0.75rem;
    margin-top: 1rem;
}

/* --- Absence allowance summary table (AbsenceAllowanceSummaryTable.razor,
   shared by EmployeeAbsenceSummary.razor/AbsenceTypeSummary.razor) - NEW
   (2026-09-11). Originally: Karl's own column list used "||" to mark off
   four groups (label; Base/Carried forward/Additional allowance/
   Adjustments/Total allowance; Taken/Booked/Requested/Total; Remaining on
   its own; Rolled forward/Balance) - .absence-allowance-group-start puts a
   slightly heavier left border on the first column of each group (now
   applied to the GROUP HEADER row too, not just the individual-column
   header row below it and the body rows - see this round's own remarks)
   so those groupings are visible at a glance, not just implied by the
   two-row header.

   REGROUPED (2026-09-11, still later) - three groups instead of four
   (Allowances/Usage/Balance) - see the .razor file's own remarks for that
   round's full story.

   RESTYLED (2026-09-11, still later again) - Karl's own follow-up asked
   for five things at once, all below: (1) the three group headers
   ("Allowances"/"Usage"/"Balance") centred, bold, and genuinely looking
   like part of the table structure - a left border now carries down
   through them at each group boundary too, not just the row beneath them,
   via .absence-allowance-group-start also being applied to the group
   header cells themselves in the .razor markup; (2) the row-label header
   ("Absence type"/"Employee") bold; (3) every column TITLE left-aligned,
   every VALUE right-aligned - a genuine reversal from the previous
   round's opposite convention (right-aligned everything, left-aligned only
   the first column) - this is intentionally uniform across every column
   including the row-label one, so a row's own name/description is now
   right-aligned like every other value; flagged in the reply in case that
   specific consequence wasn't intended; (4) the three Total columns AND
   the Remaining column all bold, headers and values alike - Remaining
   reuses the existing .absence-allowance-total class (see the .razor
   file's own remarks on why one class does both jobs); (5) the row-label
   column wider, every other column narrower - .absence-allowance-label-col/
   -data-col's own widths below, both changed this round.

   A REAL BUG was also found and fixed in the previous round's own "only
   totals bold" attempt, independent of anything Karl asked for this
   round: `.absence-allowance-summary-table th`/`td` (font-weight: 400) and
   `.absence-allowance-total` (font-weight: 600) have the same class-count,
   but the former ALSO matches an element (th/td) - giving it (0,1,1)
   specificity against `.absence-allowance-total`'s own (0,1,0), so the 400
   default was silently winning wherever both applied. In practice, NOTHING
   in this table was actually rendering bold last round, Totals included -
   the exact same shape of bug as button.primary/button.danger vs
   .save-button--saving/--saved/--failed on the Approve-absences page
   (2026-09-10) - worth remembering as a recurring trap in this codebase.
   Fixed below with compound selectors (table class + specific class +
   element) that deliberately out-rank the table-wide defaults, same
   pattern used there.

   CORRECTED AGAIN (2026-09-11, still later again) - Karl's next round of
   feedback, seven items. Three are CSS-only and covered right where each
   one applies below: the table stretching to the page's full width, the
   row-label header's bold rule also catching "Base", and the group-header
   row's background blending into the page behind it. The other four
   (left-align the row-label VALUES, rename Remaining/Allowances, add
   pagination) are covered in AbsenceAllowanceSummaryTable.razor's and
   AbsenceTypeSummary.razor's own remarks. */
.absence-allowance-summary-table {
    table-layout: fixed;

    /* Karl: "I think the drop downs and the tables themselves are
       extending to the width of the page." .data-table (this table's
       OTHER class, right above in this file) sets width: 100% for every
       data table in the app - fine for the rest of them, since they're
       all auto-layout and so only ever grow to fit their own content
       regardless of what width the table element is given. This table is
       table-layout: fixed though, which means it respects the <col>
       widths above as closely as it can and then stretches whatever
       space is LEFT OVER between the columns to still fill the full
       100%-wide table - exactly the stretching Karl saw. Overridden back
       to auto so the table's real width is just the sum of its own <col>
       widths (15rem + 12 * 4.25rem = 66rem), the same as if .data-table's
       width rule didn't apply to it at all. */
    width: auto;
}

/* Row-label column widened, data columns narrowed (2026-09-11, still
   later again) - Karl: "The first column... should be wider. All other
   columns should be narrower." Both are first-cut figures, easy to
   re-tune if either still feels off once Karl's actually seen them
   rendered - there's no browser available in this environment to check
   pixel widths against real employee names/absence-type descriptions. */
/* WIDENED AGAIN (2026-09-12, next round) - Karl: "Please make the 'Absence
   type' column slightly wider" (on MyAbsenceSummary.razor, moved+renamed
   from EmployeeAbsenceSummary.razor - see that file's own remarks). This
   one class is shared with AbsenceTypeSummary.razor's own row-label
   column though (RowLabelHeader="Employee" there) - there's no
   page-specific hook for it, and a few extra rem reads as harmless,
   probably-welcome breathing room for employee names too, so widened here
   rather than forking a second, near-duplicate class just for one page.
   Flagged in the reply in case Karl wants the Absence type summary
   page's own Employee column left at the old width instead. */
.absence-allowance-label-col {
    width: 18rem;
}

/* Data column width changed to a literal 82px (2026-09-11, next round) -
   Karl: "please fix the width of the column on the employee and absence
   type summary screens to around 82px." Given in px rather than converted
   to rem, since that's the exact unit Karl gave and a rem conversion would
   only match his intended pixel figure if the browser's root font-size is
   exactly 16px. */
.absence-allowance-data-col {
    width: 82px;
}

/* NEW (2026-09-24, Round 13) - the Adj./R- Fwd. columns now also carry a
   custom up/down button column (see .allowance-adjustment-input-wrap's own
   remarks) on top of the actual figure - Karl's own screenshot showed the
   plain 82px .absence-allowance-data-col width above leaves almost nothing
   left over for digits once that button column and its surrounding
   padding are accounted for ("very small and only showing 1 digit").
   Applied only to those two <col> elements in both editable HR pages'
   markup (a wider figure column reads fine there; it's still a plain
   AbsenceAllowanceRow.AdjustmentsMinutes/RolledForwardMinutes value on
   every OTHER, read-only page that reuses AbsenceAllowanceSummaryTable.razor,
   which keeps the plain 82px width unchanged). 118px leaves comfortable
   room for a full 6-digit minutes value (up to the page's own 525,600
   limit) alongside the button column, even after Round 13's own reduced
   padding/button-column overhead. */
.absence-allowance-editable-data-col {
    width: 118px;
}

.absence-allowance-summary-table th,
.absence-allowance-summary-table td {
    font-weight: 400;
}

/* Column TITLES left-aligned (2026-09-11, still later again) - a genuine
   reversal of the previous round's convention, applied uniformly. The
   group-header row overrides back to centred, just below.

   VALUES centred, both ways (2026-09-24, next round) - Karl: "Please centre
   all of the values horizontally and vertically." Replaces that same
   round's own right-align convention above - vertical-align: middle is new
   here too (nothing in this table set it before, so every cell defaulted to
   the browser's own baseline alignment). Scoped to VALUES only, same as
   Karl's own wording: column TITLES (th, just above) are left unchanged,
   still left-aligned, and the row-label VALUES column (absence type/
   employee name, just below) keeps its own separate left-align override -
   "all of the values" is read here as the numeric figures, not that one
   text column, since Karl asked for that column's own left-alignment
   specifically in an earlier round and said nothing here to suggest
   undoing it; worth confirming if that reading's wrong. */
/* CHANGED (2026-09-26) - column TITLES now centred too. Karl: "Please
   ensure the Allowance, Usage and Balance subheadings are all centred."
   The three group headings themselves were already centred (see
   th.absence-allowance-group-header below), so this is read as the
   column titles underneath them (Base, Adj., C/F, Total, R/F, ...), which
   were still left-aligned over centred figures. The row-label title
   ("Absence type"/"Employee") stays left-aligned, matching the
   left-aligned names beneath it - see th.absence-allowance-row-label-header
   below. Applies to every table using this class (the shared read-only
   summary, and the two HR pages). */
.absence-allowance-summary-table th {
    text-align: center;
}

.absence-allowance-summary-table td {
    text-align: center;
    vertical-align: middle;
}

/* Compound selector (table class + this class + element) deliberately
   out-ranks the two general th/td rules just above - see this block's own
   top-of-file remarks on why the plain-class version of this rule never
   actually worked last round.

   background changed from var(--surface-alt) to var(--surface) (2026-09-11,
   next round) - Karl: "Allowance, Usage and Balance still appear outside
   the table as they have the page background grey, not the table
   background white." Root cause: var(--surface-alt) (#f5f7f9, a light
   grey) is the EXACT same colour <body> itself uses (see this file's own
   :root block) - so these three header cells were blending straight into
   the page behind the table instead of reading as part of it. var(--surface)
   is the plain white .data-table itself already uses for the whole table,
   so these cells now visually sit ON the table like every other cell in
   it, the same white all the way across. */
.absence-allowance-summary-table th.absence-allowance-group-header {
    text-align: center;
    font-weight: 600;
    background: var(--surface);
}

/* The row-label header ("Absence type"/"Employee") bold (2026-09-11,
   still later again) - Karl: "The top left cell... should be in bold."

   REPLACED :first-child WITH AN EXPLICIT CLASS (2026-09-11, next round) -
   Karl: "Base appears to be in bold and does not need to be." Root cause:
   this cell has rowspan="2", and HTML does not insert a placeholder cell
   into the header's second row for the column it spans - so that row's
   own genuinely-first <th> ("Base") was ALSO :first-child of its own <tr>
   (a structural pseudo-class is evaluated per parent element, and each
   <tr> is its own parent), and the rule above was silently bolding it too.
   Switched to a class added only to the row-label header cell itself in
   the .razor markup, so it can never again catch a cell it wasn't meant
   to just because of where that cell happens to sit in the DOM - worth
   remembering alongside the specificity trap already noted above as a
   second, different shape of "a rule matched more than intended" bug in
   this one table. */
.absence-allowance-summary-table th.absence-allowance-row-label-header {
    font-weight: 600;
    /* 2026-09-26 - stays left-aligned now the other titles are centred. */
    text-align: left;
}

/* Row-label VALUES (an absence type description or employee name)
   left-aligned (2026-09-11, next round) - Karl: "Please align the absence
   types or employee names to the left," a deliberate exception to the
   table-wide td right-align rule above. An explicit class rather than
   td:first-child, for the same reason noted just above - tbody rows have
   no rowspan cells so :first-child would in fact be safe here, but
   keeping both row-label selectors the same shape (an explicit class, not
   DOM position) seemed worth the one extra class attribute given how that
   exact trap just bit the header version of this same column. */
.absence-allowance-summary-table td.absence-allowance-row-label {
    text-align: left;
}

/* The three Total columns AND Remaining, bold - headers and values alike
   (2026-09-11, still later again). Remaining reuses this same class
   rather than a new one - see the .razor file's own remarks. Compound
   selector for the same specificity reason as .absence-allowance-group-header
   above. */
.absence-allowance-summary-table th.absence-allowance-total,
.absence-allowance-summary-table td.absence-allowance-total {
    font-weight: 600;
}

/* Thin lines between every column, group-boundary lines thickened
   (2026-09-11, next round) - Karl: "Please make the existing vertical
   lines (between column groups) in the table thicker. Please add thin
   lines between the columns."

   General 1px border-left on every th/td first, then the existing
   group-boundary rule strengthened to 3px (up from 2px) - both scoped to
   .absence-allowance-summary-table th/td (table class + element,
   specificity 0,1,1) so the thicker group-boundary version
   (.absence-allowance-summary-table th.absence-allowance-group-start,
   table class + group-start class + element, specificity 0,2,1) reliably
   out-ranks the general 1px rule regardless of source order - the ORIGINAL
   .absence-allowance-group-start rule here was a bare, uncompounded class
   selector (0,1,0) that would have silently LOST to the new general rule
   below it if left as-is, the exact same specificity shape already found
   and fixed twice elsewhere in this one table (see this block's own
   top-of-file remarks) - compounded here from the start rather than
   letting it happen a third time.

   The row-label column (its own header/value classes, not :first-child -
   see this block's own remarks on why) is excluded from the general 1px
   rule, so the table doesn't gain a stray vertical line right at its own
   left edge where there's no second column to actually divide it from. */
.absence-allowance-summary-table th,
.absence-allowance-summary-table td {
    border-left: 1px solid var(--border);
}

.absence-allowance-summary-table th.absence-allowance-row-label-header,
.absence-allowance-summary-table td.absence-allowance-row-label {
    border-left: none;
}

/* Left border marking a group boundary - now applied (via the .razor
   markup) to the group-header row's own cells as well as the individual-
   column header row and the body rows, so the divider line runs
   continuously from the very top of the table down through every row at
   each group boundary, rather than only starting one row down - see this
   block's own top-of-file remarks. Thickened 2px -> 3px this round, per
   Karl's "thicker" ask above; compounded with the table class/element for
   the specificity reason noted above. */
.absence-allowance-summary-table th.absence-allowance-group-start,
.absence-allowance-summary-table td.absence-allowance-group-start {
    border-left: 3px solid var(--border);
}

/* NEW (2026-09-12) - BookAbsence.razor's "preview" highlight (see
   AbsenceAllowanceSummaryTable.razor's own PreviewAbsenceTypeId remarks).
   The actual background/text colour is set inline per-instance (it's the
   previewed absence type's own colour, not a fixed value a CSS class
   could hold - same convention every per-category calendar fill already
   uses), so this rule only adds the ring that marks the cell as "about to
   change" independent of whatever colour happens to be behind it - a
   compound selector for the same specificity reason as
   .absence-allowance-total's own fix above (a bare .absence-allowance-preview
   class would lose to .absence-allowance-summary-table td's own rule). */
.absence-allowance-summary-table td.absence-allowance-preview {
    box-shadow: inset 0 0 0 2px var(--primary);
}

/* NEW (2026-09-12, next round) - MyAbsenceSummary.razor's "Updated at"
   line (moved+renamed from EmployeeAbsenceSummary.razor - see that file's
   own remarks). Karl: "Underneath the table, with a sufficient space,
   please add the following on the right hand side: 'Updated at:
   hh:mm:ss dd/mm/yyyy'." margin-top matches .page-diagnostics below (this
   page's own PageDiagnosticsFooter sits right underneath this line, so
   the two ended up needing the same "sufficient space" gap anyway) -
   text-align:right and no border/monospace font though, since this isn't
   a diagnostics readout, just a plain right-aligned caption under the
   table. */
.absence-summary-updated-at {
    margin-top: 1.5rem;
    text-align: right;
    font-size: 0.8rem;
    color: var(--text-muted);
}

/* NEW (2026-09-12, one more round) - MyDetails.razor's "View my current
   live record instead" / "Back to editing my pending changes" toggle,
   sitting inline inside the .record-lock-banner paragraph alongside the
   pending-review message text. A plain <button> styled to read as a link
   rather than a button (no existing class in this file already does
   this - every other button here is a real, bordered button) - inherits
   the surrounding banner's own text colour/size rather than fixing its
   own, so it reads as part of that sentence. */
.link-button {
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    text-decoration: underline;
    cursor: pointer;
}

/* --- Page diagnostics (page/database ms counters, PageDiagnosticsFooter) - */
.page-diagnostics {
    margin-top: 1.5rem;
    padding-top: 0.5rem;
    border-top: 1px dashed var(--border);
    font-size: 0.75rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: var(--text-muted);
}

/* --- Policy accordion (NEW, 2026-09-16) - CompanyPolicies.razor
   (self-service/company-policies) and PolicyAcceptance.razor
   (hr/policy-acceptance) both use this same shape: a list of clickable
   headings, each expanding its own body in place. Karl: "a series of
   headings/sections which can be clicked on and the documents to open in
   an expanded section that reveals itself." One shared class set rather
   than a page-specific one, since the two pages' headers only differ in
   what they SHOW (accepted-tick+timestamp vs an accepted-count), not how
   they behave. */
.policy-accordion {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-top: 1rem;
}

.policy-accordion-item {
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
}

.policy-accordion-header {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.85rem 1rem;
    background: var(--surface);
    border: none;
    cursor: pointer;
    text-align: left;
    font: inherit;
    color: inherit;
}

.policy-accordion-header:hover {
    background: var(--surface-alt);
}

.policy-accordion-header-text {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

.policy-accordion-title {
    font-weight: 600;
}

.policy-accordion-summary {
    font-size: 0.85rem;
    color: var(--text-muted);
}

.policy-accordion-status {
    flex: 0 0 auto;
    font-size: 0.8rem;
    white-space: nowrap;
}

.policy-accordion-status--accepted {
    color: var(--success);
}

.policy-accordion-status--pending {
    color: var(--text-muted);
}

.policy-accordion-chevron {
    flex: 0 0 auto;
    color: var(--text-muted);
    font-size: 0.75rem;
}

.policy-accordion-body {
    padding: 1rem;
    border-top: 1px solid var(--border);
    background: var(--surface-alt);
}

/* The policy's own HTML content (MarkupString) - a plain readable measure
   rather than stretching full width, same "don't let prose run edge to
   edge" reasoning as most reading-focused content elsewhere on the web;
   headings/lists inside get normal browser defaults back since nothing
   else in app.css resets them globally. */
.policy-accordion-content {
    max-width: 65ch;
}

.policy-accordion-content :first-child {
    margin-top: 0;
}

.policy-accordion-actions {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px dashed var(--border);
}

/* --- HR "Policy acceptance" page (PolicyAcceptance.razor) - the same
   .policy-accordion-* shell above, plus its own expanded-body table of
   every employee's own status for one policy. */
.policy-acceptance-table {
    width: 100%;
    border-collapse: collapse;
}

.policy-acceptance-table th,
.policy-acceptance-table td {
    text-align: left;
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--border);
    font-size: 0.9rem;
}

.policy-acceptance-table th {
    color: var(--text-muted);
    font-weight: 600;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

/* NEW (2026-09-16, next round) - Karl: "Add a download option at the
   bottom of the page to generate a .xls or .xlsx file..." Just a layout
   wrapper - the button itself reuses the site's plain button/button.primary
   rules (above, near the top of this file) unchanged rather than a new
   button style, matching every other primary action button on the site. */
.policy-acceptance-export {
    margin-top: 1.5rem;
}

/* --- Team calendar (Manager tools) - TeamCalendar.razor --- */

/* NEW (2026-09-16) - Karl: "the dates should be as columns, perhaps just a
   month or 4 weeks at a time, with smooth scrolling across a timeline."
   Deliberately a plain HTML table, not CalendarGrid.razor/CalendarDayCell.
   razor (confirmed too tightly coupled to the one-employee-per-grid visual
   model to reuse for a dates-as-columns/employees-as-rows layout) - see
   TeamCalendar.razor's own top-of-file remarks for the full reasoning.
   overflow-x:auto + scroll-behavior:smooth is Karl's own "smooth scrolling
   across a timeline" wording, applied literally. The white card background
   here is what makes .team-calendar-cell--grey's var(--surface-alt) tint
   actually visible - same fix already applied to .absence-booking-section
   --wide (see that rule's own remarks) for the identical reason: body's
   own background is that same colour, so a grey cell would be invisible
   without a white surface underneath it. */
.team-calendar-scroll {
    overflow-x: auto;
    scroll-behavior: smooth;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
}

.team-calendar-grid {
    border-collapse: separate;
    border-spacing: 0;
    width: max-content;
}

/* Sticky first column (employee names) - stays in view while the table
   scrolls horizontally through the date columns. z-index:2 keeps it above
   the plain date cells (which have none); the header row's OWN name cell
   (below) needs a higher z-index again, since it's sticky on BOTH axes at
   once (the top-left corner). */
.team-calendar-name-col {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--surface);
    min-width: 200px;
    max-width: 240px;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--border);
    border-right: 1px solid var(--border);
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Sticky header rows (dates) - stay in view while the table scrolls
   vertically through the employee rows. CHANGED (2026-09-16, next round) -
   was a single row; now TWO (see .team-calendar-month-row/--date-row
   below, and TeamCalendar.razor's own MonthHeaders()/markup) - the
   month-name row still sticks at top:0 (this generic rule, unchanged),
   the date/weekday row underneath it needs its own top offset (equal to
   the month row's own fixed height) so it sticks directly below the month
   row instead of overlapping it - see .team-calendar-date-row below. The
   name-col corner cell (rowspan="2", so it's physically part of the month
   row) still matches this generic rule's top:0, which is exactly right -
   it needs to stay pinned at the very top, spanning both header rows. */
.team-calendar-grid thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

/* The one cell sticky on BOTH axes - the header row's own "Employee"
   corner cell - needs to win over every other sticky cell it could ever
   overlap (a sticky date header scrolling past underneath it
   horizontally, or a sticky name cell scrolling past underneath it
   vertically), hence the higher z-index than either alone. */
.team-calendar-grid thead th.team-calendar-name-col {
    z-index: 3;
}

/* NEW (2026-09-16, next round) - Karl: "put the month name above the 1st
   date of each month." One <th colspan> per calendar month
   (TeamCalendar.razor's own MonthHeaders()) - text-align:left so the label
   sits flush against the LEFT edge of its own colspan block, directly
   above day 1 of that month, per Karl's own wording (rather than centred
   across the whole month, which wouldn't sit "above the 1st date"
   specifically). height is fixed and explicit so
   .team-calendar-date-row's own sticky `top` offset below can match it
   exactly. */
.team-calendar-month-col {
    height: 24px;
    padding: 0.2rem 0.4rem;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--text-muted);
    text-align: left;
    white-space: nowrap;
    border-right: 1px solid var(--border);
}

/* Sticks directly below the fixed-height month row above it, rather than
   at top:0 (which would put it UNDER the month row instead of below it) -
   see .team-calendar-month-col's own height, which this offset matches. */
.team-calendar-grid thead tr.team-calendar-date-row th {
    top: 24px;
}

.team-calendar-date-col {
    min-width: 40px;
    width: 40px;
    padding: 0.35rem 0.25rem;
    text-align: center;
    font-size: 0.75rem;
    color: var(--text-muted);
}

.team-calendar-date-weekday {
    display: block;
    font-size: 0.68rem;
}

.team-calendar-date-num {
    display: block;
    font-weight: 600;
    font-size: 0.85rem;
    color: #1a1f24;
}

.team-calendar-date-col--weekend .team-calendar-date-num {
    color: var(--text-muted);
}

/* CHANGED (2026-09-16, next round) - Karl: "putting the current date in a
   single outline border, rather than around each cell." Was a per-cell
   inset box-shadow ring (independently repeated on the header AND on every
   row's own today cell); now the header's own share of one continuous
   rectangle traced around the whole column - left/right/top borders here,
   the body cells add their own left/right (.team-calendar-col--today
   below) and the LAST row's cell adds the bottom edge
   (.team-calendar-col--today-last) - see TeamCalendar.razor's
   CellCssClass/DateHeaderCssClass for exactly which cell gets which. */
.team-calendar-date-col--today {
    border-left: 1px solid var(--primary);
    border-right: 1px solid var(--primary);
    border-top: 1px solid var(--primary);
}

/* A light tint on the current employee's own name cell, so their own row
   is easy to find at a glance among the manager/sibling rows above and
   below it. */
.team-calendar-row--self .team-calendar-name-col {
    background: color-mix(in srgb, var(--primary) 8%, var(--surface));
}

.team-calendar-name {
    font-weight: 600;
}

/* NEW (2026-09-16, next round) - Karl: "the employee all report into the
   manager at the top, can you please indent the rest of the team's
   names." A plain visual indent, not a real tree/nesting control - there's
   only ever one level here (the sibling group is flat beneath the
   manager, see TeamCalendarService's own remarks), so nothing more
   elaborate is needed. Applied via TeamCalendar.razor's own
   NameCellCssClass - every row except the manager's own. */
.team-calendar-name-col--indented {
    padding-left: 1.75rem;
}

/* NEW (2026-09-23) - Karl: "Please double-indent the third group." The
   third group (TeamCalendarTier.Subordinates) is one level further from
   the manager than the peer/self group above it, so it gets twice the
   --indented padding rather than a new, separate indent scale - applied
   via TeamCalendar.razor's own NameCellCssClass three-way switch on
   member.Tier. */
.team-calendar-name-col--double-indented {
    padding-left: 3.5rem;
}

/* NEW (2026-09-23) - Karl: "Please add a thicker line above and below
   each group." Applied via TeamCalendar.razor's own RowGroupCssClass,
   which walks consecutive rows in a TeamCalendarGroup (comparing each
   row's GroupIndex, CHANGED 2026-09-23 next round - see that method's own
   remarks for why) and adds team-calendar-row-group-top to the first row
   of the whole table and team-calendar-row-group-bottom to the last row
   of each group (so the boundary between two adjacent groups gets exactly
   one thick rule, not a doubled-up pair from both sides). .team-calendar-grid
   uses border-collapse:separate (not collapse), so this class goes on the
   <tr> and reaches its <td>/<th> cells via this descendant selector, same
   as .team-calendar-name-col/.team-calendar-cell's own existing 1px
   --border lines just above and below this rule.
   CHANGED (2026-09-23, next round) - Karl: "The new thick horizontal lines
   have become way too dark. Can you make them the same grey as the
   thinner lines which divide the employees." Switched from --text-muted
   to --border - the exact token those thinner dividing lines already use
   - so the only visible difference between the two is thickness (3px vs
   1px), not colour. */
.team-calendar-row-group-top > td,
.team-calendar-row-group-top > th {
    border-top: 3px solid var(--border);
}

.team-calendar-row-group-bottom > td,
.team-calendar-row-group-bottom > th {
    border-bottom: 3px solid var(--border);
}

.team-calendar-role-tag {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 600;
    background: var(--surface-alt);
    color: var(--text-muted);
}

.team-calendar-role-tag--self {
    background: color-mix(in srgb, var(--primary) 20%, var(--surface));
    color: var(--primary);
}

/* REDUCED FIDELITY, FLAGGED (see TeamCalendar.razor's own top-of-file
   remarks) - one flat inline background colour per cell (category colour
   for a coarse row, absence-type colour for the current employee's own
   row, set inline per-cell in the markup since it's tenant/type-specific
   data, not a fixed value a CSS class alone could hold - same "class for
   the marker, inline style for the actual colour" split CalendarDayCell.
   razor already uses for its own fills) plus the plain state classes
   below, rather than CalendarDayCell's own diagonal-gradient half-day
   split fill, pending hatch, and multi-state ring stack. */
.team-calendar-cell {
    width: 40px;
    height: 32px;
    border-bottom: 1px solid var(--border);
    border-right: 1px solid #eef1f3;
    padding: 0;
}

/* Weekend/non-scheduled shading, Karl's own "please only shade the
   managers and other employees dates for weekend and non scheduled days
   in the same greys" - the same var(--surface-alt) grey
   .calendar-day--no-shift already uses elsewhere on this site. */
.team-calendar-cell--grey {
    background: var(--surface-alt);
}

/* Same idea as .calendar-day--outside-employment (BookAbsence.razor/
   MyCalendar.razor) - a neutral grey hatch, deliberately distinct from the
   plain grey above so "not employed at all" reads differently from "no
   shift scheduled, but still employed". */
.team-calendar-cell--outside-employment {
    background: repeating-linear-gradient(135deg, var(--surface-alt) 0px, var(--surface-alt) 6px, var(--surface) 6px 12px);
    opacity: 0.6;
}

/* CHANGED (2026-09-16, next round) - the body-row half of the "today"
   column outline - see .team-calendar-date-col--today's own remarks just
   above for the full picture. Every cell in today's column gets the
   left/right edge; only the LAST employee row's own cell
   (--today-last, TeamCalendar.razor's own isLastRow) also gets the bottom
   edge, closing the rectangle the header cell opened. */
.team-calendar-col--today {
    border-left: 1px solid var(--primary);
    border-right: 1px solid var(--primary);
}

.team-calendar-col--today-last {
    border-bottom: 1px solid var(--primary);
}

.team-calendar-cell--clickable {
    cursor: pointer;
}

.team-calendar-cell--clickable:hover {
    box-shadow: inset 0 0 0 1px var(--primary);
}

/* CHANGED (2026-09-16, next round) - Karl: "try copying the date selection
   style from employee-calendar and my calendar - thinner blue lines and
   blue shading for scheduled work days." Was a single flat 2px inset ring
   on every selected cell (.team-calendar-cell--selected, removed); now the
   same 1px-border-plus-conditional-tint split CalendarDayCell.razor
   already uses for MyCalendar.razor/EmployeeCalendar.razor's own
   selection (.calendar-day--selected/--selected-shade, above) - a plain
   1px border on every selected date, and the tint
   (--calendar-selected-bg, same custom property those rules already use)
   only on a selected date that's genuinely scheduled to be worked
   (TeamCalendar.razor's CellCssClass only adds --selected-shade when the
   date isn't grey/outside-employment). A selected date that already
   carries a real absence colour (set inline, per-cell) still shows that
   colour underneath the border/tint rather than either replacing it -
   same "inline style always wins over a class" precedent this file's own
   REDUCED FIDELITY remarks (TeamCalendar.razor) already rely on.

   "As the dates are now linear across the page, can you make the blue
   borders around the block of dates, not the individual dates." A
   selection here is always one contiguous run (OnCalendarDateClick's own
   alternating start/end logic never produces a gap) - so only the FIRST
   and LAST selected date get a left/right border
   (--selected-start/--selected-end); every selected date gets the same
   top/bottom border (--selected-block) - this rule and the two below
   together trace one rectangle around the whole run, not a ring around
   each day. Only ever applied to the current employee's own row (the only
   clickable one). */
.team-calendar-cell--selected-block {
    border-top: 1px solid var(--primary);
    border-bottom: 1px solid var(--primary);
}

.team-calendar-cell--selected-start {
    border-left: 1px solid var(--primary);
}

.team-calendar-cell--selected-end {
    border-right: 1px solid var(--primary);
}

.team-calendar-cell--selected-shade {
    background: var(--calendar-selected-bg);
}

.team-calendar-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.25rem;
    margin: 0.75rem 0 1rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.team-calendar-legend-item {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.team-calendar-legend-swatch {
    width: 0.9rem;
    height: 0.9rem;
    border-radius: 3px;
    display: inline-block;
    border: 1px solid var(--border);
}

/* Karl: "copy the layout and information at the bottom of the self service
   calendar" - the booking section itself reuses MyCalendar.razor's own
   .absence-booking-section/.absence-booking-actions-row/etc. classes
   unchanged; this just adds the gap above it, since it now follows a
   sticky-header scrolling table rather than a plain annual grid. */
.team-calendar-booking-section {
    margin-top: 1.5rem;
}

/* NEW (2026-09-16, next round) - Karl: "Please reduce the size of 'Book
   time off' to the size of regular text but still keep it bold." The
   shared .absence-block-title rule (1.6rem, used elsewhere for the big
   "2026" year heading etc.) is left untouched - this is a second, more
   specific class added alongside it only on this one heading, so only
   Team calendar's "Book time off" wording shrinks. 1rem matches body's own
   own unset (browser-default) font-size, i.e. genuinely "regular text
   size", not just a smaller heading size; font-weight is set explicitly
   rather than relied on from the <h2> default, since 1rem text at a
   browser's normal weight wouldn't read as bold at this size. */
.team-calendar-booking-title {
    font-size: 1rem;
    font-weight: 700;
}

/* --- Shift patterns (NEW 2026-09-18, Karl's shift-editing page request) ---
   ShiftPatternEditor.razor's own header/day-grid styling. */

/* A checkbox that should sit inline with its own label text (Company
   default/Active on the header, "Ends the next day" per day) rather than
   .form-grid label's own default column stacking (label text above the
   control) - that default reads fine for a text/number/date field but
   looks odd for a plain checkbox. Scoped to this one modifier rather than
   changing .form-grid label itself, which every other settings page's
   checkbox (CompanyGroupEditor.razor's own "Active", for one) already
   relies on rendering the stacked way. */
.checkbox-field {
    flex-direction: row !important;
    align-items: center;
    gap: 0.4rem !important;
    /* NEVER WRAPS (2026-09-21, round 29) - Karl: "The Company default
       text is just bunching up... I think the checkbox text needs more
       space." Belt-and-braces alongside .shift-pattern-header-line-1's
       own new flex-shrink: 0 (further up this file, the fix for the
       actual squeeze this text was reacting to) - this guarantees the
       label text itself can never wrap onto a second line regardless of
       whatever ends up squeezing its container, on any page this shared
       class is used on (every real use is a short label - "Company
       default", "Active", "Ends the next day" - none of which benefit
       from wrapping anyway). */
    white-space: nowrap;
}

/* REMOVED (2026-09-20) - .form-grid--compact (the per-day expanded-panel
   field grid) and .shift-day-panel (that panel's own padding) both went
   with the click-to-expand day-summary-row shape they styled - Karl: "I
   would like to try and edit all days in one table." Every day's fields
   are now real, always-visible columns on one wide table instead (see
   .shift-day-table--wide and its own rules further down) - confirmed via
   a search of ShiftPatternEditor.razor/the rest of the codebase that
   neither class is referenced anywhere any more before removing them. */

/* REMOVED (2026-09-20, round 12) - .shift-day-toolbar (the "Copy day 1 to
   all days" button's own small wrapper, sitting above the day table) is
   gone along with that button - Karl: "Please remove the 'Copy day 1 to
   all days' button and logic. I only need the ones on the end of each day
   row" (the per-day "Copy to" popup, unaffected - see
   ShiftPatternEditor.razor's own ROUND 12 remarks). Confirmed via a
   search of ShiftPatternEditor.razor that nothing references this class
   any more before removing it. */

/* Shift-number tabs (almost always just "Shift 1" - see
   ShiftPatternEditor.razor's own remarks on the one pattern in Karl's
   export, a split shift, that has a second). Plain button row rather than
   a real ARIA tabpanel pairing - there's only ever one visible day table
   under whichever tab is active, swapped by DaysForActiveShift(), so a
   full tablist/tabpanel relationship would be more ceremony than this
   small control needs. */
.shift-number-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 1rem 0;
}

.shift-number-tab {
    border: 1px solid var(--border);
    background: var(--surface);
    border-radius: 6px;
    padding: 0.35rem 0.75rem;
    font-size: 0.85rem;
    cursor: pointer;
}

.shift-number-tab--active {
    border-color: var(--primary);
    background: color-mix(in srgb, var(--primary) 12%, var(--surface));
    color: var(--primary);
    font-weight: 600;
}

/* REMOVED (2026-09-20, round 14) - .shift-number-tab--add styled the old
   "+ Add another shift" button, which is gone - Karl: "Please remove the
   '+ Add another shift' button but continue to allow the shifts to be
   selected with buttons for 'Shift 1', 'Shift 2' etc." Adding a shift is
   now done via the new "Shifts per day" stepper instead (see
   .shift-count-field further down) - confirmed via a search of
   ShiftPatternEditor.razor that nothing references this class any more
   before removing it. */

.shift-number-tab--remove {
    color: var(--danger);
    border-color: var(--danger);
    margin-left: auto;
}

/* The "Day 1 of this cycle falls on" reference bar - only shown when a
   pattern's day count isn't exactly 7 (see ShiftPatternEditor.razor's own
   remarks on why a 7-day pattern needs no such thing, its weekday labels
   are already fixed). Prev/Next step by a whole cycle length so the date
   shown is always a genuine day-1 occurrence, never an arbitrary pick. */
.cycle-reference-bar {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.6rem 0.85rem;
    margin: 1rem 0;
    font-size: 0.85rem;
}

.cycle-reference-bar button {
    border: 1px solid var(--border);
    background: var(--surface);
    border-radius: 4px;
    padding: 0.15rem 0.55rem;
    cursor: pointer;
}

.cycle-reference-bar .variable-description {
    flex-basis: 100%;
    margin: 0;
}

/* ASIDE MODIFIER (2026-09-21, round 20) - see
   ShiftPatternEditor.razor's own shift-pattern-header-layout remarks (and
   this file's own, further up) for why this box moved beside the header
   lines instead of sitting in its own full-width row below them. The base
   .cycle-reference-bar rule above (unchanged, still used as-is everywhere
   else this class might ever appear) is a horizontal, wrapping row sized
   to its own content - that's wrong once it's a flex item stretched to
   match a much taller sibling column (both header lines together), so
   this modifier switches it to a vertical stack instead: the arrow
   buttons/date centre themselves as a row up top, and the description
   text goes underneath with room to actually wrap across the box's own
   width rather than forcing the box wide.

   GROWS TO FILL THE REMAINING SPACE (2026-09-21, round 23) - Karl: "The
   box that appears when Days in cycle &lt;&gt; 7, looks like it is only
   taking up a third of the remaining room after the two option lines and,
   as a result, is becoming about 10 lines tall. It can use all of this
   space." Round 20's own fixed 220px width (a judgment call at the time,
   since Karl hadn't given a figure) turned out to be far narrower than
   the space actually available beside the now-fixed 900px
   shift-pattern-header-lines column - the description text was wrapping
   into a tall, narrow column instead of using the room genuinely free
   next to it. Switched from a fixed width to `flex: 1 1 auto`, so this
   box now grows to fill whatever space is left in
   .shift-pattern-header-layout after the 900px column and the gap between
   them, on whatever width screen this page happens to be viewed at - a
   min-width is kept only as a floor, so it can't collapse to nothing if
   the layout is ever squeezed onto a genuinely narrow viewport.

   THE "ROW UP TOP" NEVER ACTUALLY HAPPENED (2026-09-21, round 26) - Karl:
   "Its elements oddly aligned" - flex-direction: column here applies to
   EVERY direct child of .cycle-reference-bar--aside, and the markup
   (ShiftPatternEditor.razor) never wrapped the label/arrows/date in their
   own row - they were five separate direct children (the "Day 1 of this
   cycle falls on" span, both buttons, the date, and the description
   span), so this rule put every one of them on its own line: label, then
   Previous button, then the date, then Next button, then the
   description, each full width and left-aligned. This comment's own
   round 20 description of the intended result ("centre themselves as a
   row up top") was never actually true - it was the plan, not what the
   two rules together produced, and it went unnoticed until this was
   first seen rendered (the app didn't compile at all between rounds
   20-23; see round 24's own remarks). Fixed at the markup level: the
   label/arrows/date are now wrapped in their own
   .cycle-reference-bar__row div, which is what actually gets the
   horizontal-row treatment (below) - this rule keeps the two remaining
   top-level children (that row, and the description) stacked. */
/* NARROWED, NO LONGER GROWS TO FILL EVERYTHING (2026-09-21, round 27;
   WIDTH RECALCULATED round 28) - Karl, round 27: "I don't think the
   popup needs to be as wide as it is because ha[sn't got] table rows.
   Can it be made a bit narrower please." Fair point - `flex: 1 1 auto`
   (round 23) makes this box claim ALL space left over in the row, but
   there's nothing here that actually benefits from a wide box - just a
   short date line and one sentence of description. Switched to a fixed
   flex-basis instead of growing - the `1` shrink factor and min-width
   floor are both kept, so this can still give a little ground on a
   genuinely narrow viewport; flex-GROW is what's actually gone (0 now,
   was 1), which is what stops it competing with
   .shift-pattern-header-lines for space at all - see that column's own
   flex-shrink: 0 (further up this file) for the other half of that same
   fix, which was really about Company default's text wrapping, not this
   box's own width.

   380PX WAS TOO NARROW - THE DATE ROW ITSELF WRAPPED (2026-09-21, round
   28) - Karl: "Company default is now longer wrapping but the popup
   box's layout is still not ideal. It needs to wider and the date
   selection arrows and text on a single row." Round 27's own 380px was a
   plain guess, made with no arithmetic behind it beyond "narrower than
   before" - it turned out to be narrower than the label/arrows/date row
   itself needs (.cycle-reference-bar__row, just below), which is
   deliberately flex-wrap: nowrap-adjacent content (see that rule's own
   remarks) that has nowhere to go but onto a second line once its
   container is narrower than its own natural width - exactly what the
   screenshot showed ("&rarr;" alone on its own row).

   There's no way to make this box's own width both (a) driven purely by
   its content, the way .shift-pattern-header-lines now is, and (b) still
   look narrow - the label/arrows/date row NEEDS its full natural width
   respected (nothing there should ever wrap), while the description
   sentence's own natural, un-wrapped width is enormous (a whole
   paragraph on one line) and MUST be constrained to something narrower
   than that to look like a paragraph at all - those two requirements
   pull in opposite directions, so a fixed width that's at least as wide
   as the row's real content is genuinely the only correct tool here, not
   a shortcut being reached for again. Recalculated properly this time
   from the row's actual real pieces (label text + two buttons + the bold
   date, plus the gaps between them and this box's own padding/border) -
   roughly 495px by that arithmetic - and rounded up further to 540px
   specifically because round 27's own estimate came in short once
   already; the extra margin here is deliberate, not padding for its own
   sake. Flag, as ever: worked out by hand, not measured against a real
   render - if this is STILL wrapping (or now looks too wide the other
   way), the fastest way to nail it exactly would be Karl checking this
   row's own rendered width in the browser's dev tools once it's on
   screen, rather than a further guess from this end.

   FADES IN/OUT (2026-09-21, round 27; REBUILT round 29 - round 27's
   plain `animation` never actually played) - Karl, round 27: "ensure the
   popup fades nicely when it appears to disappears"; round 29: "The
   animations do not seem to be working. The popup box does not seem to
   fade in or out." Round 27's reasoning ("a plain animation just plays
   on mount, no JS needed") was correct in isolation, but missed
   something this file's OWN top-of-file "SMOOTH OPEN, TAKE TWO" remarks
   already diagnosed for a structurally identical problem elsewhere on
   this exact page: a CSS `animation` starts its own internal clock the
   MOMENT its style is computed, regardless of whether the browser is
   actually free to paint anything yet. IncrementDayCount/
   DecrementDayCount/OnDayCountChangedAsync (ShiftPatternEditor.razor)
   all call ResizeAllShiftGroups() - which can add or remove a genuinely
   large number of day-table rows - on the SAME render that this box
   also gets freshly mounted with its own reveal animation attached. By
   the time that render's heavy DOM work is done and the browser is
   finally free to paint a frame, the 0.3s animation's own clock may
   already have run out with nothing ever painted - "doesn't seem to
   animate" is exactly what that produces, not "no animation at all" but
   one that's invisible in practice. This is the SAME cause, and the SAME
   fix, as .shift-pattern-editor-reveal's own rewrite (see that rule's
   own remarks, further up this file) - `transition` instead of
   `animation`, with the "before" and "after" styles landing in two
   separate renders so the transition can't be short-circuited by heavy
   work the same way an animation can.

   REVEALING needs the full two-phase treatment (mount hidden THIS
   render - all the day-table churn happens invisibly - then a second,
   cheap render a moment later just removes the hidden class, letting
   the browser actually paint the "before" state and run the transition
   over its own full duration): see _referenceBoxRevealing/
   BeginRevealReferenceBoxAsync (ShiftPatternEditor.razor).

   CLOSING does NOT need that same two-phase treatment - same reasoning
   .inline-editor-panel.shift-pattern-panel's own remarks (further up
   this file) already give for why ITS closing animation was left alone
   during that same rewrite: nothing new is being constructed when this
   box starts closing, its "before" (fully visible) state was already
   painted in a genuinely separate, earlier render, so a plain
   `transition` reacting to the --closing class simply being added
   works reliably without any extra C# delay beyond the one already in
   place to keep the element mounted long enough for the fade to finish
   (_referenceBoxClosing/FinishClosingReferenceBoxAsync). */
.cycle-reference-bar--aside {
    flex-direction: column;
    align-items: flex-start;
    flex-wrap: nowrap;
    flex: 0 1 540px;
    min-width: 220px;
    margin: 0;
    transition: opacity 0.3s ease;
}

/* THE "BEFORE" STATE FOR REVEALING (2026-09-21, round 29) - applied on
   this box's very first render after @@if starts showing it again -
   invisible, and deliberately NOT what triggers the fade itself (no
   transition plays FROM nothing TO opacity: 0, there's nothing to
   animate yet). BeginRevealReferenceBoxAsync (ShiftPatternEditor.razor)
   removes this class a tick later, and THAT removal - a plain opacity 0
   -&gt; 1 style change on an already-painted element - is what the base
   rule's own `transition` actually animates. */
.cycle-reference-bar--aside.cycle-reference-bar--revealing {
    opacity: 0;
}

/* THE "AFTER" STATE FOR CLOSING (2026-09-21, round 27; simplified round
   29 - see .cycle-reference-bar--aside's own "FADES IN/OUT" remarks for
   why `animation` was replaced with a plain opacity change relying on
   the base rule's own `transition`). Kept mounted
   (ShiftPatternEditor.razor's own _referenceBoxClosing) for exactly
   0.3s after this class is applied, before it's actually removed - MUST
   stay in step with that file's own ReferenceBoxClosingMs constant, same
   reasoning .inline-editor-panel--closing's own remarks (further up this
   file) already give for its matching ClosingAnimationMs pairing.
   Specificity note: two classes on one element (0-2-0) already reliably
   outranks the base rule above (0-1-0) regardless of source order, same
   as .inline-editor-panel.inline-editor-panel--closing's own established
   pattern - declared after it anyway, for readability. */
.cycle-reference-bar--aside.cycle-reference-bar--closing {
    opacity: 0;
    pointer-events: none;
}

/* NEW (2026-09-21, round 26) - see .cycle-reference-bar--aside's own
   remarks just above. Reuses the base .cycle-reference-bar row's own
   look (flex row, centred, 0.6rem gap) without touching that shared
   class itself, since it's still used as a plain horizontal bar
   everywhere else this component might appear.

   flex-wrap KEPT AS A LAST-RESORT SAFETY NET, NOT THE NORMAL CASE
   (2026-09-21, round 28) - Karl: "the date selection arrows and text
   [need to be] on a single row" - this wrapping onto two lines (label +
   "&larr;" + date on one, "&rarr;" alone on the next) is exactly what
   round 27's own screenshot showed, caused by the box beside it
   (.cycle-reference-bar--aside) being narrower than this row's real
   content - fixed there, by widening the box enough that this row's
   content genuinely fits (see that rule's own round 28 remarks for the
   arithmetic). `flex-wrap: wrap` stays set here regardless, same
   defensive reasoning the base .cycle-reference-bar rule already applies
   everywhere else it's used - it has no effect at all once the box is
   actually wide enough, and only matters as a fallback against
   overflowing/clipping outright in some edge case this round's own
   arithmetic didn't anticipate (an unusually long localised date string,
   say) - wrapping onto two readable lines in that edge case is a much
   better failure mode than text spilling past the box's own border. */
.cycle-reference-bar__row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}

.cycle-reference-bar--aside .variable-description {
    flex-basis: auto;
}

/* REDESIGNED (2026-09-20) - the old day-summary table (Day/Start/End/
   Duration, one row per day, click to expand the full field set) is gone
   - Karl: "I would like to try and edit all days in one table." Every
   field is now a real column on ONE wide table (.shift-day-table--wide),
   grouped under a two-row header (rowspan/colspan) - same pattern
   AbsenceAllowanceSummaryTable.razor's own Allowance/Usage/Balance groups
   already use, reused here rather than inventing a second approach. */
.shift-day-table--wide th,
.shift-day-table--wide td {
    text-align: center;
    /* VERTICALLY ALIGNED TO TOP (2026-09-21, round 19) - Karl: "Can you
       please vertically align the column text to the top of their TDs."
       Table cells default to `vertical-align: middle` in every browser
       unless overridden - every cell on this table currently reads the
       same height regardless (round 12's own row-height collapse already
       trims each row down close to its own text height), so middle vs.
       top rarely shows in the common case, but becomes visible whenever
       one cell's content wraps taller than its neighbours (e.g. a
       long two-line "Automatic breaks" group header, or a future taller
       row) - `top` keeps every cell's text starting from the same
       baseline instead of drifting toward the row's own vertical centre.
       Applied to both `<th>` and `<td>` uniformly, matching how this rule
       already applies to both. */
    vertical-align: top;
    /* REDUCED (2026-09-20, round 5) - Karl: "reduce the column padding
       from 12.8px to 8px." Was inheriting .data-table th/td's own shared
       0.6rem 0.8rem (0.8rem = 12.8px horizontal, at this app's default
       16px root font-size) - overridden here to an explicit 8px
       horizontal, scoped to this one table rather than changing the
       shared .data-table rule every other settings page's table also
       uses. Vertical padding (0.6rem) is untouched - Karl's own number
       (12.8px) only matches the horizontal figure, so only that's
       reduced.

       REDUCED AGAIN, 8px -> 4px (2026-09-20, round 6) - Karl: "cell
       padding from 8px to 4px" (same message as the input-box widening
       just below). Still horizontal only, same reasoning as round 5.

       OVERFLOW CLIPPING ADDED (2026-09-20, round 10) - Karl: "Something
       else is still forcing the width of the input boxes and cells...
       Could it be the column headings?" - a good guess, and quite
       possibly right. Round 9's `width: auto` fix (see this table's own
       rule further down) stops the TABLE overall from being stretched to
       fill 100% of its container, but table-layout: fixed's column-width
       enforcement is a known weak spot specifically for COLSPANNED
       header cells across browsers - a long, unbreakable-at-that-width
       phrase in a row-1 group header ("Automatic break 1"/"Manual break
       duration"/etc, each spanning 2-3 columns) can still, in some
       browser implementations, push the table wider than its declared
       columns even under a nominally "fixed" layout, because the spec's
       fixed-layout algorithm is explicit about ROW widths but murkier
       about what a colspanned cell's own minimum content size is allowed
       to do to the columns it spans. `overflow: hidden` is the standard
       belt-and-braces fix for this - it stops a cell's own content from
       ever contributing to the browser's width calculation for that
       column, so even a browser that doesn't fully respect table-layout:
       fixed for colspanned cells has nothing left to expand around.
       Genuinely can't confirm from here whether this fully explains the
       remaining gap Karl's still measuring (no live browser in this
       environment) - flag if the numbers are still off after this.

       "CELL WITHIN A CELL" REMOVED (2026-09-20, round 11) - Karl: "It is
       still padding or a minimum is being set somewhere. Can we try
       removing the cell within a cell layout if that is what is
       happening?" Right idea - every value cell on this table was really
       TWO boxes stacked: this <td> (its own border/padding) wrapping an
       <input>/<select> that ALSO had its own border/padding/background
       (see those rules further down). Removing this double layer is the
       real fix, not another round of chasing a fractional pixel number:
       the input/select elements are now fully borderless/paddingless/
       transparent and fill their <td> completely (width/height: 100%) -
       the <td> IS the visible cell now, there's nothing nested inside it
       any more. This rule's own 4px horizontal padding (round 6) is
       still the default for the Day/Actions columns, but every VALUE
       column (the 18 columns holding an input or the Rounding logic
       select - Day and Actions are the only ones that stay as they
       were) now overrides it to 0 via the new .shift-day-value-col class
       just below - "fix each column at 56px with no padding," per Karl's
       own message.

       ROW HEIGHT COLLAPSED (2026-09-20, round 12) - Karl: "Please collapse
       the height of the rows down to somethings closer to what is
       required by the height of the text." This rule (and
       .shift-day-value-col just below) only ever override HORIZONTAL
       padding - vertical padding was still whatever `.data-table th, td`
       (this file's own shared rule, further up) sets: 0.6rem/9.6px top
       AND bottom, on every row, regardless of how tall the actual text
       inside is. Combined with the input/select's own now-zero padding
       (round 11), that inherited 9.6px top + 9.6px bottom was the entire
       remaining source of "extra" row height once the cell-within-a-cell
       fix landed. Now explicitly overridden down to 2px top/bottom here -
       a small margin is kept rather than 0, so the text doesn't look like
       it's touching the row's own divider border, but the bulk of that
       inherited space is gone. */
    padding-left: 4px;
    padding-right: 4px;
    padding-top: 2px;
    padding-bottom: 2px;
}

/* NEW (2026-09-20, round 11) - see the "CELL WITHIN A CELL REMOVED" note
   above. Zero horizontal padding for every value column specifically
   (Day keeps its own 4px via the rule above; Actions does too, since a
   button needs a little breathing room from the divider beside it) -
   declared after, and with higher specificity (class+element+class) than,
   the shared th/td rule above, so it reliably wins regardless of source
   order. */
.shift-day-table--wide th.shift-day-value-col,
.shift-day-table--wide td.shift-day-value-col {
    padding-left: 0;
    padding-right: 0;
}

.shift-day-table--wide td {
    white-space: nowrap;
    overflow: hidden;
}

.shift-day-table--wide th {
    overflow: hidden;
}

/* FIXED COLUMN WIDTHS (2026-09-20, round 2) - Karl: "Can you change the
   columns to fixed widths." table-layout: fixed is what actually makes
   ShiftPatternEditor.razor's own <colgroup> <col> widths take effect -
   without it the browser sizes every column from its content instead,
   same fixed-width-via-colgroup pattern AbsenceAllowanceSummaryTable.razor
   already uses for its own Allowance/Usage/Balance table. Once columns
   are fixed-width, row1's wider group-header labels ("Automatic break 1",
   "Manual break duration", etc.) no longer have room to sit on one line
   under white-space: nowrap (removed from `th` above, moved to `td` only,
   just above) - they wrap onto two lines instead, which reads fine for a
   header. min-width is the sum of every <col>'s own declared width
   (190 + 68*17 + 100*2 = 1546px) plus a little slack for cell borders -
   same "don't let a fixed-layout table get squeezed narrower than its own
   declared columns on a small screen" reasoning as .employee-list-table's
   own min-width further up this file; .table-scroll (overflow-x: auto,
   this file's own rule) takes over from there.

   FOUND THE REAL COLUMN-STRETCH BUG (2026-09-20, round 9) - Karl: "Is
   something else forcing to the columns to be a certain width? The
   input boxes are 42px +2px +2px. However, it is being forced to
   61.234px." Yes - found it. This rule never set its own `width`, so
   `.data-table`'s own shared `width: 100%` (further up this file, used
   by every other settings-page table) was still the one controlling
   this table's actual rendered width. Under `table-layout: fixed`, a
   <colgroup>'s <col> widths are only honoured LITERALLY when the
   table's own width equals their sum - the moment the table is told to
   be 100% of a container WIDER than that sum (a very ordinary desktop
   viewport, comfortably past ~1560px once the sidebar/content padding
   are accounted for), the browser instead treats the declared column
   widths as RATIOS and proportionally stretches every column to fill
   the extra space - each 68px column silently rendering wider than
   68px, each input (fixed at its own 48px, not 100%, so it doesn't
   stretch itself) sitting inside a now-oversized cell. This is almost
   certainly the source of every oddly-specific decimal-pixel figure
   Karl's reported across rounds 4/7/9 (52.406/36.406/61.234) - not
   arithmetic mistakes in any one padding/width value, but the whole
   table quietly rendering at a different scale than its <colgroup> ever
   declared. Fixed with an explicit `width: auto` below, overriding that
   inherited 100% - per the fixed-table-layout algorithm, `width: auto`
   makes a fixed-layout table size itself to the sum of its own column
   widths instead of its container's, so nothing stretches any more;
   min-width stays as the same "don't let it get squeezed narrower on a
   small screen" safety net as before, now backing up a table that
   should naturally land right at that width anyway rather than actively
   fighting a 100% that kept overriding it.

   COLUMNS NARROWED, 68px -> 56px (2026-09-20, round 11) - Karl: "Perhaps
   then fix each column at 56px with no padding." Every one of the 18
   value columns (the 17 that were 68px, PLUS the Rounding logic column,
   previously 100px - Karl's own instruction was literally "each column",
   so Rounding logic's <select> is included even though 56px is tight for
   "Intervals" plus a dropdown arrow; flag if that needs widening back out
   once it's actually visible) is now 56px in
   ShiftPatternEditor.razor's own <colgroup>. Actions stays 100px (it
   holds a "Copy to…" button, not a value field, and wasn't part of
   Karl's "each column" list). min-width recalculated: 190 (Day) + 56*18
   (every value column, Rounding logic included) + 100 (Actions) = 1298px,
   plus a little slack for the new per-column border lines just below.

   ROUNDING LOGIC WIDENED, 56px -> 75px (2026-09-20, round 12) - Karl:
   "Please increase the Rounding logic column to 75px" - exactly the
   widening flagged as possibly needed just above, now confirmed and
   applied. Every other value column stays at 56px. min-width
   recalculated again: 190 (Day) + 56*17 (every OTHER value column) + 75
   (Rounding) + 100 (Actions) = 1317px, plus the same slack as before.

   COLUMNS WIDENED AGAIN (2026-09-20, round 15) - Karl: "increase... the
   column widths" (see ShiftPatternEditor.razor's own <colgroup> remarks
   for the exact new per-column figures and the judgment-call flag on
   them, since no specific pixel numbers were given this round). min-width
   recalculated once more: 210 (Day) + 64*17 (every other value column) +
   86 (Rounding) + 112 (Actions) = 1496px, plus a little more slack than
   before since the enlarged text (see the font-size increases further
   down this file) needs slightly more breathing room than the old
   13px-ish margin did.

   DAY COLUMN NARROWED, 210px -> 150px (2026-09-21, round 16) - Karl:
   "Reduce the width of this column" (the Day column specifically, now
   that its own non-7-day text is shorter - see ShiftPatternEditor.
   razor's own DayLabel/<colgroup> remarks). min-width recalculated one
   more time to match: 150 (Day) + 64*17 (every other value column) + 86
   (Rounding) + 112 (Actions) = 1436px, plus the same slack as round 15.

   ACTIONS COLUMN NARROWED, 112px -> 80px (2026-09-21, round 18) - Karl:
   "The Copy to... column can be narrower." No exact figure given - 80px
   is a judgment call, comfortably fitting "Copy to…" itself (a short
   string, and the only content this column ever holds - its own header
   cell is blank) at this table's existing 0.85rem link text
   (.shift-day-copy-link, further down this file) without relying on this
   column's own `overflow: hidden` to clip it. min-width recalculated to
   match: 150 (Day) + 64*17 (every other value column) + 86 (Rounding) +
   80 (Actions) = 1404px, plus the same ~24px slack every earlier
   recalculation here has carried. */
.shift-day-table--wide {
    table-layout: fixed;
    width: auto;
    min-width: 1428px;
}

/* THIN PER-COLUMN DIVIDERS (2026-09-20, round 11) - Karl: "Thinner
   vertical lines will need adding between each column, not just the
   column groups." Every column now gets its own 1px left border - Day is
   excluded (it's the table's own leading edge, not a boundary between
   two columns) via :not(.shift-day-label-col). Declared BEFORE the
   (now-thicker) group-start rule just below, which overrides this back
   to a heavier line specifically at each GROUP boundary - both selectors
   land at the same specificity (class + element + class, via :not()'s
   own argument counting the same as an ordinary class per the
   specification), so source order is what actually decides the tie; see
   this file's own documented specificity-tie trap (.absence-approval-
   table's own remarks, further up this file) for why declaring the
   winner LATER, not just "more specific-looking," is what makes an
   override like this reliable. */
.shift-day-table--wide th:not(.shift-day-label-col),
.shift-day-table--wide td:not(.shift-day-label-col) {
    border-left: 1px solid var(--border);
}

/* Left divider marking where each field GROUP starts (Shift start/
   Shift end/Automatic break 1/Automatic break 2/Shift duration/Manual
   break count/Manual break duration) - same "group-start" naming/
   left-border idea AbsenceAllowanceSummaryTable's own
   .absence-allowance-group-start already uses, kept as its own class here
   since this table's groups/columns are a different shape and its rows
   are days, not absence types/employees.

   ALSO ON THE ACTIONS COLUMN (2026-09-20, round 3) - Karl: "Please add a
   vertical line between the Rounding logic column and the Copy to
   buttons." The Actions column's own header/row cells now carry this
   same class alongside "actions" - reused rather than a one-off rule,
   since it's visually the identical divider every other group boundary
   on this table already has.

   MADE SLIGHTLY THICKER, 2px -> 3px (2026-09-20, round 11) - Karl: "I do
   not mind if the existing lines, such as those between the column
   groups is made slightly thicker" (same message that asked for the new
   thin per-column dividers above) - a deliberately bigger jump than the
   new 1px per-column lines, so a genuine group boundary still reads as
   clearly more significant than an ordinary column boundary. Selector
   now explicitly qualified with th/td (was just `.shift-day-table--wide
   .shift-day-group-start`) so its specificity matches the new per-column
   rule above exactly - declared AFTER it, so it wins that tie for every
   group-start cell specifically. */
.shift-day-table--wide th.shift-day-group-start,
.shift-day-table--wide td.shift-day-group-start {
    border-left: 3px solid var(--border);
}

/* GROUP HEADER ROW EXPLICITLY BOLD (2026-09-21, round 18) - Karl: "The
   column headings no longer look bold." Row 1 of the &lt;thead&gt; ("Shift
   start"/"Shift end"/"Automatic breaks"/"Shift duration"/"Manual breaks",
   plus "Day"/the blank Actions header) was always relying on the browser's
   own default bold-&lt;th&gt; styling rather than an explicit font-weight
   of its own - exactly the thing the subheader rule just below was
   deliberately changed to NOT rely on, back in round 2, specifically so it
   wouldn't be at the mercy of whatever a given browser's default happens
   to render as. This row was simply never given the same treatment. Given
   one now, for the same reason - `thead tr:first-child th` (a tag-only
   selector chain, no class) scopes it to just that first header row,
   leaving the subheader row (`.shift-day-subheader`, below) to keep
   setting its own weight independently. */
.shift-day-table--wide thead tr:first-child th {
    font-weight: 700;
}

/* Sub-column header labels (row 2 of the <thead> - "Earliest"/"Default"/
   "Latest"/etc.) - NEW class (2026-09-20, round 2), Karl: "The individual
   column names... will likely need to be smaller but can remain strong."
   font-weight is set explicitly rather than left to the browser's own
   default bold-<th> styling, so "remain strong" is guaranteed regardless
   of that default. The wider group-header row above (row 1 - "Shift
   start"/"Automatic break 1"/etc.) is untouched - Karl's own list was
   specifically the individual column names, not the group headings. */
.shift-day-table--wide .shift-day-subheader {
    /* ENLARGED (2026-09-20, round 15) - Karl: "increase the size of...
       the column headings." Was 0.72rem, the smallest text on the whole
       table - no exact figure given this round, bumped to 0.82rem
       (judgment call, same reasoning as the column-width increases
       above - flag if a different size is actually wanted). Stays
       smaller than .shift-day-label-col's own 0.85rem (just below) so
       "Day" and the day labels still read as the more prominent text. */
    font-size: 0.82rem;
    font-weight: 600;
}

/* Day column left-align (2026-09-20, round 2) - Karl: "Please align the
   day or dates column to the left." Every other column on this table
   stays centred (the th/td rule above) - only the Day column's own header
   cell and every row's leading cell get this class, overriding that
   centring back to left. Same specificity as the th/td rule above (both
   are two-class-selector, descendant-combinator rules, 0-2-0) - declared
   AFTER it in this file so it wins the tie, same reasoning as
   .inline-editor-panel--closing's own remarks further up this file. */
.shift-day-table--wide .shift-day-label-col {
    text-align: left;
    /* SMALLER TEXT (2026-09-20, round 14) - Karl: "Can you please reduce
       the text size of the days/ dates." Was unset, so this column
       inherited whatever the page's own default body/table font-size is -
       every other column on this table already reads at 0.8rem (the
       input/rounding-toggle rule) or 0.72rem (.shift-day-subheader), so
       this was likely the single largest piece of text left on the whole
       table. 0.75rem also helps the new, longer non-7-day label format
       (ShiftPatternEditor.razor's own DayLabel - "Day 5 - Aug 05/08/2026",
       also changed this round) fit inside this column's own 190px width
       without relying on the shared td rule's overflow: hidden clipping
       it.

       ENLARGED AGAIN (2026-09-20, round 15) - Karl: "increase the size
       of the 'Day' title... [and] the day labels." This one rule
       already covers both - the "Day" header <th> and every row's own
       day-label <td> share this same .shift-day-label-col class (see
       ShiftPatternEditor.razor's own markup), so a single change here
       satisfies both parts of that request. Was 0.75rem, no exact
       figure given this round - bumped to 0.85rem, the largest text on
       the table (judgment call, same reasoning as the other round-15
       size increases in this file). The Day column's own widened 210px
       (ShiftPatternEditor.razor's <colgroup>, also this round) gives
       the longer non-7-day label format ("Day 5 - Aug 05/09/2026") room
       to keep fitting at this larger size. */
    font-size: 0.85rem;
}

/* Plain text inputs + the new Rounding logic <select> (2026-09-20, round
   2) - REPLACES the old `input[type="time"], input[type="number"]` rule:
   every time/number field on this table is now a plain
   <input type="text"> with a placeholder (Karl: "There is no need to a
   time input type with the clock popup") rather than a native time/number
   control, so this targets the new element type instead. Styled to match
   .editable-table's own input treatment elsewhere in this file (border/
   padding/radius) rather than a bare unstyled text box, since this table
   doesn't carry the .editable-table class itself.

   SMALLER TEXT/PADDING (2026-09-20, round 3) - Karl: "Please reduce the
   size of the text in all input boxes, as well as the padding." Was
   `font: inherit` (the page's own body size) - now an explicit,
   deliberately small font-size, and padding roughly two-thirds of its
   previous value on both axes. Scoped to this table only (see this
   file's own top-of-file ROUND 3 remarks for the "site-wide or just this
   table" judgment call).

   FIXED WIDTH, 40px (2026-09-20, round 4) - Karl: "Can you please change
   the width of the input boxes from 52.406px to 40px." That 52.406px was
   this rule's own `width: 100%` resolving against each column's declared
   `<colgroup>` width minus the surrounding cell's own padding/border -
   never a value set directly anywhere in this file, which is presumably
   why it reads as an oddly specific number. Replaced with a plain fixed
   `width: 40px` instead of a percentage, so it's no longer at the mercy
   of that arithmetic - `min-width` dropped too, since a fixed width makes
   it redundant. Split off from the <select> below, which Karl's own
   wording ("input boxes") doesn't obviously cover and which needs more
   room for "Intervals" plus its own dropdown arrow - kept at its
   previous 100%/min-width sizing; flag if the Rounding logic dropdown
   should be narrowed to match.

   WIDENED SLIGHTLY, 40px -> 44px (2026-09-20, round 5) - Karl: "increase
   the input box width to 44px" (same message as the column padding
   reduction just above, on the shared th/td rule).

   WIDENED AGAIN, 44px -> 48px (2026-09-20, round 6) - Karl: "increase the
   input boxes to a width of 48px" (same message as round 6's further
   padding reduction, 8px -> 4px, on the shared th/td rule above).

   INPUT'S OWN HORIZONTAL PADDING REMOVED (2026-09-20, round 7) - Karl:
   "I think there is another padding setting around the input boxes. It
   is currently set to 4.8px. Can you reduce this to 0px please. The
   input box width is actually rendering at 36.406px." That 4.8px is this
   rule's own `padding: 0.2rem 0.3rem` - 0.3rem horizontal, at this app's
   default 16px root font-size. `box-sizing: border-box` (both here and
   the app-wide `* { box-sizing: border-box; }` at the top of this file)
   means the 48px `width` above already counts this padding and the 1px
   border INSIDE it, not on top of it - so the 48px is the outer,
   border-box size Karl already set in round 6, and 36.406px is the
   INNER content/typing area left over once that padding and border are
   subtracted (48 - 2*4.8 - 2*1 = 36.4, matching almost exactly) - the
   number a lot of browser dev tools show by default when inspecting an
   element's box model, even under border-box. Horizontal padding is now
   0 - vertical (0.2rem) is untouched, same "only the horizontal figure
   matches what Karl quoted" reasoning as every other padding change this
   round. With this change the content area becomes 46px (48 minus only
   the 1px border each side), much closer to the 48px outer size - flag
   if the text still doesn't look properly centred/breathing without any
   horizontal padding at all once you see it rendered.

   UNIFORM 2px PADDING (2026-09-20, round 8) - Karl: "Can you change the
   input boxes' padding to 2px on all sides." Was `0.2rem 0` (3.2px
   vertical, 0 horizontal, round 7's fix) - now a flat `2px` on every
   side, so there's a small, equal amount of breathing room around the
   text on all four edges again rather than none horizontally/a
   rem-derived amount vertically. Also answers Karl's own question in the
   same message ("I think there are three paddings on each vertical
   side, unless the column itself is being forced to a certain width?") -
   see this file's own README/chat reply for the full answer, but in
   short: there are two real padding layers stacking vertically, not
   three - this rule's own (now 2px top/bottom) plus the surrounding
   <td>'s own 0.6rem/9.6px top/bottom (`.data-table th, td`'s shared
   rule, further up this file - never touched by any of the horizontal-
   only reductions in rounds 5/6/7), plus this input's own 1px border
   top/bottom, which isn't padding but does add a little more visible
   space on top of both. The fixed column WIDTH (the <colgroup>/
   table-layout: fixed from round 2) has no bearing on vertical
   spacing at all - width and height are independent here, nothing
   forces row height off of column width.

   "CELL WITHIN A CELL" REMOVED (2026-09-20, round 11) - Karl: "It is still
   padding or a minimum is being set somewhere. Can we try removing the
   cell within a cell layout if that is what is happening? So for each
   day, there is just a single cell for each column... Perhaps then fix
   each column at 56px with no padding. Thinner vertical lines will need
   adding between each column, not just the column groups." Correct
   diagnosis: every value cell was really TWO stacked boxes - the <td>
   itself (border/padding via the shared th/td rule and, since round 9,
   the .shift-day-value-col override) AND this input/select's own
   independent border/padding/border-radius box nested inside it - so
   the "one more layer of padding than expected" Karl kept measuring
   round after round was this input's own border+padding, not a
   mysterious extra one on the <td>. Border, border-radius and padding
   all removed here - the <td> is now the ONLY visible box per column,
   with its own divider borders added below (a new 1px-per-column rule,
   plus the pre-existing group-boundary rule thickened to 3px so it
   still reads as a stronger break). `width`/`height: 100%` (replacing
   the old fixed `width: 48px` / `width: 100%; min-width: 4rem`) makes
   the input/select fill its <td> completely - the <td>'s own fixed
   56px <colgroup> width (ShiftPatternEditor.razor) is what actually
   sizes the column now, this rule just stretches to match it exactly
   rather than adding a size of its own. `background: transparent` so
   the <td>'s own background (or the .shift-end-wraps highlight) shows
   through undimmed - previously invisible since the opaque page
   background of the bordered input sat on top of it anyway. Karl's
   "fix each column at 56px" was stated for the value columns in
   general; Rounding logic's <select> is included in this same 56px/
   no-padding treatment for consistency, even though round 4 had
   deliberately left it wider ("Intervals" plus its own dropdown arrow
   needing more room than the plain text inputs) - flag if "Intervals"
   now looks cramped or its arrow gets clipped once this renders; easy
   to widen that one column back via its own <col>/th/td override if
   so.

   ROUNDING LOGIC IS NO LONGER A <select> (2026-09-20, round 13) - Karl:
   "Can the rounding option be set by clicking on the text, rather than a
   drop down, to swap between the two options?" ShiftPatternEditor.razor's
   Rounding logic cell is now a plain click-to-toggle <button>
   (.shift-day-rounding-toggle) instead of a <select> - added to this
   same shared rule so it gets the exact same fill/borderless/paddingless
   treatment every other value column's input already has; a <select> can
   still appear elsewhere in this app (this rule's own `select` selector
   is left in place, it's simply unused by this table now), so it's not
   removed outright, just no longer the only element sharing this look
   here. See the dedicated .shift-day-rounding-toggle rule further below
   for the extra reset a <button> specifically needs on top of this
   (appearance/cursor) that an <input>/<select> didn't. */
.shift-day-table--wide input[type="text"],
.shift-day-table--wide select,
.shift-day-table--wide .shift-day-rounding-toggle {
    font-family: inherit;
    /* ENLARGED (2026-09-20, round 15) - Karl: "increase the size of...
       all values and inputs in the table." Was 0.8rem, no exact figure
       given this round - bumped to 0.9rem (judgment call, same
       reasoning as the other round-15 size increases in this file). The
       widened 64px value columns (ShiftPatternEditor.razor's <colgroup>,
       also this round) give this larger text the same room it had
       before relative to the column width. */
    font-size: 0.9rem;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    border: none;
    border-radius: 0;
    background: transparent;
    text-align: center;
}

/* NEW (2026-09-20, round 13) - see the "ROUNDING LOGIC IS NO LONGER A
   <select>" note just above. A <button> carries its own browser default
   chrome (appearance, cursor sometimes left as `default` rather than
   `pointer` depending on the browser/OS) that an <input>/<select> don't,
   on top of what the shared rule above already resets - cleared
   explicitly here rather than assumed. `color: inherit` keeps it reading
   as ordinary cell text (this is a value the day actually has, not a
   link/action like Copy to…, so it deliberately does NOT get the blue
   link treatment .shift-day-copy-link below uses) - the dotted underline
   on hover is the only hint that it's clickable, swapped in only on
   hover/focus so the table doesn't look busier at rest than it did with
   a plain dropdown. */
.shift-day-table--wide .shift-day-rounding-toggle {
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    color: inherit;
}

.shift-day-table--wide .shift-day-rounding-toggle:hover,
.shift-day-table--wide .shift-day-rounding-toggle:focus-visible {
    text-decoration: underline dotted;
}

/* Shift duration Default, now a disabled input (2026-09-20, round 3) -
   Karl: "shift_duration_default can be in the same format as minimum and
   maximum - a disabled input box with the value calculated." REPLACES
   the old plain-read-only-text .shift-duration-computed cell (removed -
   it's a real <input disabled> in ShiftPatternEditor.razor now, so
   nothing targets that class any more). Reuses every other input's own
   sizing/border/padding above automatically; this rule only supplies the
   "locked/computed" visual cue - deliberately NOT dimmed with opacity the
   way .absence-types-table's own input:disabled is elsewhere in this
   file (that one greys out fields that are genuinely NOT APPLICABLE to a
   row - this one is a real, important calculated value, just not
   directly editable, so it stays fully legible).

   CHECKED AGAINST ROUND 11 - this rule only sets a background fill,
   text colour, weight and cursor, none of which are part of the round-11
   "cell within a cell" fix above (that removed border/border-radius/
   padding only) - so it needs no change and still layers cleanly on top
   of the borderless/paddingless base input rule; the background now
   fills the <td> edge-to-edge instead of a smaller bordered box inset
   within it, which if anything should make the "computed/locked" cue
   more visible, not less.

   BACKGROUND MOVED TO THE <td> ITSELF (2026-09-20, round 12) - Karl:
   "Please colour the whole of the shift_duration_default td to grey."
   Functionally this input already filled its <td> completely with no
   padding (round 11), so the grey fill should have looked the same
   either way - but Karl's own wording asks for the colour on the CELL,
   not the input inside it, so the background now lives on a new
   shift-duration-default-col class added to that one <td> in
   ShiftPatternEditor.razor instead (see that rule just below) - removed
   from here so there's exactly one place this colour is set, matching
   this table's own "one box per column, not two" philosophy from round
   11. Text colour/weight/cursor stay on the input itself, since those
   are properties of the text/pointer, not the cell's own fill. */
.shift-day-table--wide input:disabled {
    color: var(--text-muted);
    font-weight: 600;
    cursor: not-allowed;
}

/* NEW (2026-09-20, round 12) - see the "BACKGROUND MOVED TO THE <td>
   ITSELF" note above. Same --surface-alt token the input:disabled rule
   used to carry directly, now on the cell instead so it reads as "the
   whole td" per Karl's own wording rather than depending on the input
   inside it being a perfect 100% fill. */
.shift-day-table--wide td.shift-duration-default-col {
    background: var(--surface-alt);
}

/* Grey placeholder text (2026-09-20, round 2) - Karl: "a grey 'hh:mm'
   when the value is empty or null"/"'minutes' displayed in grey when the
   input box is empty". Explicit rather than relying on each browser's own
   default placeholder colour (which varies, and isn't guaranteed to match
   this app's own --text-muted token), so "hh:mm"/"minutes"/"count" read
   consistently grey everywhere on this table.

   LIGHTER GREY (2026-09-20, round 10) - Karl: "Please make the hh:mm text
   lighter grey... make the same lighter grey" (of the "mins"/"count"
   placeholders too, per that message - see ShiftPatternEditor.razor's own
   remarks for the matching "minutes" -> "mins" wording change in the same
   message). Switched from --text-muted to the new, paler
   --text-muted-light token - every placeholder on this table shares this
   one rule, so all of them (hh:mm/mins/count) lighten together
   automatically. */
.shift-day-table--wide input[type="text"]::placeholder {
    color: var(--text-muted-light);
}

/* EVEN LIGHTER, hh:mm/mins ONLY (2026-09-20, round 12) - Karl: "Please
   make the 'hh:mm' and 'mins' default text to an even lighter grey."
   Named specifically, not every placeholder on the table this time -
   "count" (the manual break count fields) is left at round 10's
   --text-muted-light. **FLAG**: if "count" was meant to lighten too,
   this is a one-line change to fold it into this same rule - read
   literally for now since Karl named two exact placeholder strings, not
   "all of them" the way round 10's own message did.
   [placeholder="..."] attribute selectors make this MORE specific than
   the general rule just above (extra attribute selector = higher
   specificity), so it reliably wins for these two placeholder values
   regardless of source order - no tie to worry about here, unlike this
   table's border-order tricks elsewhere. New --text-muted-lighter token
   (a paler step again from --text-muted-light) added to :root below.

   LIGHTER STILL (2026-09-20, round 14) - Karl: "Please make hh:mm and
   mins default text lighter grey still." A fourth step - switched from
   --text-muted-lighter (round 12) to a new, paler-again
   --text-muted-lightest token (added to :root below). --text-muted-lighter
   itself is left in :root even though nothing on this table uses it any
   more after this change, in case a future round wants a colour a step
   between --text-muted-light and --text-muted-lightest again.

   "COUNT" FOLDED IN TOO (2026-09-20, round 14, same day) - Karl, straight
   after the above: "Can you please make sure the 'count' default text is
   also the same grey as the 'hh:mm' and 'mins' text." Answers the flag
   this comment used to carry (round 12 had deliberately left "count" at
   the round-10 shade since Karl's own wording that round named only two
   of the three placeholders) - now confirmed every placeholder on this
   table should move together, so "count" joins this same rule/token
   rather than getting its own separate one-off rule. The general
   ::placeholder rule just above (still --text-muted-light) is now
   effectively unreachable for THIS table specifically, since hh:mm/mins/
   count are the only three placeholder strings ShiftPatternEditor.razor
   ever sets - left in place rather than removed, since it's the correct
   fallback for any placeholder text a future round might add here that
   isn't meant to be quite this pale. */
.shift-day-table--wide input[type="text"][placeholder="hh:mm"]::placeholder,
.shift-day-table--wide input[type="text"][placeholder="mins"]::placeholder,
.shift-day-table--wide input[type="text"][placeholder="count"]::placeholder {
    color: var(--text-muted-lightest);
}

/* REMOVED (2026-09-20, round 3) - .shift-duration-computed (the plain
   read-only-text styling for the Shift duration Default cell) is gone -
   Karl: "shift_duration_default can be in the same format as minimum and
   maximum - a disabled input box with the value calculated." That cell
   is a real `<input disabled>` now, styled by
   .shift-day-table--wide input:disabled above instead - confirmed via a
   search of ShiftPatternEditor.razor that nothing references this class
   any more before removing it. */

/* NEW (2026-09-20) - Karl: "Please highlight the three shift_end_x cells
   in light yellow background if they are less than the
   shift_start_default times, indicating they fall after midnight the next
   day." A new light-yellow token, --highlight-wrap-bg - this app had no
   "warning"/highlight colour of its own before now (only
   --rounding-fixed, an amber used for the Pro rata rounding control's
   Fixed state specifically, not a general-purpose highlight - reusing it
   here would tie two unrelated features' colours together), so this is
   its own token rather than repurposing that one. Applied to the cell
   (not just the input inside it) so the highlight reads clearly even
   before/after the input itself is focused. */
.shift-day-table--wide .shift-end-wraps {
    background: var(--highlight-wrap-bg);
}

.shift-day-table--wide td.actions {
    text-align: center;
}

.shift-day-table--wide td.actions button {
    white-space: nowrap;
}

/* "COPY TO…" RESTYLED AS A LINK (2026-09-20, round 13) - Karl: "I think
   the 'Copy to...' buttons are forcing the height of the rows. Please
   just turn these into clickable text, perhaps in blue so it looks like
   a link." Plausible, and the likely explanation: this button was the
   one interactive element left on the table still carrying its browser's
   own default button chrome (padding/border/background, and in some
   browsers a taller built-in line-height baked into the UA stylesheet)
   after round 11 stripped every input/select down to borderless and
   paddingless - every other cell's own height comes from just this
   table's 2px padding (round 12) plus plain text/an input, but this one
   button's native box model would have kept adding on top of that
   regardless of how tight everything else got. Reset to read as inline
   text: no border/background/padding, `display: inline` (not the shared
   rule's block-level width/height: 100% - this is a short, short link,
   not a cell-filling control), coloured with the app's existing
   --primary blue token and underlined so it reads unmistakably as a
   link. Deliberately kept as a real <button> rather than an <a> with no
   href - it never navigates anywhere, it opens a popup, so a button is
   the semantically correct element regardless of how it looks; this is a
   purely visual change; @onclick/OpenCopyPopup underneath are untouched.
   Genuinely can't confirm from here whether this actually resolves the
   row-height issue Karl's reporting (no live browser in this
   environment) - it's the most likely single cause given everything else
   already stripped from this table, but flag if rows are still taller
   than expected once this renders. */
/* VERTICALLY ALIGNED (2026-09-21, round 17) - Karl: "Ensure the Copy to…
   text is vertically aligned." Was `display: inline` with `height: auto`
   (round 13, just above) - a plain inline text node has no box of its own
   to centre WITHIN, so it was left sitting on whatever the row's default
   text baseline happened to be, rather than genuinely centred in its
   &lt;td&gt; the way every other (height: 100%-filling) value cell on this
   table already is. Switched to `inline-flex` with `align-items: center`
   and `height: 100%`, so this button now fills its own cell's height
   exactly and centres its text within it - same "the cell itself is the
   only box" approach round 11 already established for every other column,
   just applied here too. Still deliberately NOT `width: 100%` - this
   stays a short, natural-width link/text element, not a cell-filling
   control, per round 13's own reasoning just above. */
.shift-day-table--wide .shift-day-copy-link {
    display: inline-flex;
    align-items: center;
    width: auto;
    height: 100%;
    appearance: none;
    -webkit-appearance: none;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    /* ENLARGED (2026-09-20, round 15) - not explicitly named in Karl's
       own list ("all values and inputs in the table" doesn't obviously
       cover the Copy to... link, which is an action, not a value), but
       bumped from 0.8rem to 0.85rem anyway so it doesn't end up looking
       small/orphaned next to every other now-larger piece of text on
       this table - flag if it was meant to stay at its previous size. */
    font-size: 0.85rem;
    color: var(--primary);
    text-decoration: underline;
    cursor: pointer;
}

.shift-day-table--wide .shift-day-copy-link:hover,
.shift-day-table--wide .shift-day-copy-link:focus-visible {
    color: color-mix(in srgb, var(--primary) 75%, black);
}

/* --- "Copy to" popup (NEW 2026-09-20, ShiftPatternEditor.razor only) -
   Karl: "At the end of each column, please add a 'Copy to' button to copy
   that day's values to other that can be selected via a pop up." Same
   fixed-backdrop-plus-panel shape as .absence-day-popup* (BookAbsence.razor,
   2026-09-11) - new class names since the content here (a day checklist,
   not absence details) is unrelated, but deliberately styled identically
   rather than inventing a third popup look. --- */
.shift-day-copy-popup-backdrop {
    position: fixed;
    inset: 0;
    background: rgb(0 0 0 / 45%);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 3rem 1rem;
    z-index: 50;
}

.shift-day-copy-popup {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
    padding: 1.25rem 1.5rem;
    max-width: 24rem;
    width: 100%;
    max-height: calc(100vh - 6rem);
    overflow-y: auto;
}

.shift-day-copy-popup-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.5rem;
}

.shift-day-copy-popup-header h3 {
    margin: 0;
}

.shift-day-copy-popup-close {
    background: none;
    border: 1px solid var(--border);
    border-radius: 6px;
    line-height: 1;
    padding: 0.25rem 0.6rem;
    cursor: pointer;
}

.shift-day-copy-popup-days {
    list-style: none;
    margin: 0.75rem 0;
    padding: 0;
    max-height: 16rem;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: 8px;
}

.shift-day-copy-popup-days li {
    padding: 0.4rem 0.75rem;
    border-bottom: 1px solid var(--border);
}

.shift-day-copy-popup-days li:last-child {
    border-bottom: none;
}

.shift-day-copy-popup-days label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
}

/* hr/employee-absence-summary.razor (2026-09-24) - Karl: "Please require
   the HR user to enter a note against the change in a popup before
   confirming the save."

   REDONE AS A TRUE CENTRED/DIMMED MODAL (2026-09-24, next round) - Karl:
   "The popup needs to be in the middle of the page, perhaps dimming the
   background. It is current off to the side, very narrow and tall." Last
   round's version copied .absence-year-delete-popover's own ANCHORED
   positioning (position: fixed/inset: auto/margin: 0, placed next to its
   trigger button by popover-position.js's own openNear) - right for a
   short per-row warning, wrong for a popup that now also holds a change
   summary, a cascade note, and a required multi-line note field. Dropped
   the anchored positioning entirely instead of overriding it: a native
   [popover] with no inline top/left left in place from JS falls back to
   its own UA stylesheet default of position: fixed; inset: 0; margin: auto
   - which centres it in the viewport for free - and showPopover() on an
   "auto" popover (this element's own popover="auto" attribute, unchanged)
   renders a real ::backdrop pseudo-element, styled below to dim the page.
   popover-position.js's own openNear is no longer called for this popup -
   see its own new showCentered export's remarks. close() is unchanged and
   still works (it only ever checks :popover-open/calls hidePopover()) -
   and the Popover API's native light-dismiss (Esc, or a click on the now-
   visible dimmed backdrop) already closes it with no extra code either
   side of the interop boundary. */
.allowance-adjustment-save-popover {
    padding: 1.1rem 1.35rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
    width: min(30rem, 90vw);
    max-height: 85vh;
    overflow-y: auto;
}

.allowance-adjustment-save-popover::backdrop {
    background: rgba(15, 23, 32, 0.55);
}

.allowance-adjustment-save-popover p {
    margin-top: 0;
}

.allowance-adjustment-note-label {
    display: block;
    margin: 0.5rem 0;
    font-weight: 600;
}

.allowance-adjustment-note-input {
    display: block;
    width: 100%;
    margin-top: 0.35rem;
    padding: 0.4rem 0.5rem;
    font: inherit;
    font-weight: normal;
    border: 1px solid var(--border);
    border-radius: 4px;
    resize: vertical;
    box-sizing: border-box;
}

/* REDONE (2026-09-24, next round) - Karl: "Are you able to combine the
   tables on the hr/employee-absence-summary screen so the carried forward
   and adjustment values can be edited inline?... The HR user should be
   able to edit either the minutes or days figure." First cut of this round
   put two small inputs (days + minutes) in every editable cell at once -
   Karl's own follow-up corrected that: "I meant it should be possible to
   edit the values either when minutes or days are selected as the unit
   shown." So each editable cell now holds exactly ONE input, switching
   which unit it edits along with the page's existing Days/Minutes radio -
   see the .razor file's own OnDaysInput/OnMinutesInput remarks. With only
   one input per cell (not two), the Adj./C-Fwd. columns no longer need
   their own wider <col> - they're back to the shared, plain
   .absence-allowance-data-col width every other column already uses.

   LESS OBVIOUS, SAME SIZE AS THE TD (2026-09-24, same follow-up) - Karl:
   "Can you please make the input box less obvious, perhaps having it the
   same size as the td, or using the td itself." No visible border or
   background until hover/focus - otherwise it reads as plain text, exactly
   like every other (read-only) cell in this table - and sized to fill its
   own <td> exactly: the td's own padding (the same 0.6rem 0.8rem
   .data-table th/.data-table td already give every cell in the app) is
   zeroed via .absence-allowance-editable-cell below and moved onto the
   input itself instead, so the input's own box IS the cell's box, not a
   smaller box floating inside it. */
.absence-allowance-summary-table td.absence-allowance-editable-cell {
    padding: 0;
}

/* REPLACED - PADDING GUESSWORK -> REAL CUSTOM SPIN BUTTONS (2026-09-24,
   Round 12) - Karl: "The up and down buttons are still missing with the
   centring of the values in the Adj. and R/ Fwd columns. Can up and down
   text buttons be rendered there instead, with javascript changing the
   values? If so, could equal padding be put on the left hand side of the
   TDs, keeping the text in the centre?"

   Rounds 10-11 tried to guess a right-hand padding value that visually
   compensated for the BROWSER's own native number-input spin buttons - an
   unmeasurable, browser/OS-varying control this app has no way to size
   exactly, which is why neither guess (+18px, then +6px) ever quite
   landed. This round removes that guesswork problem at the source instead
   of tuning it further: the native spinner is hidden entirely (see the
   ::-webkit-*-spin-button/-moz-appearance rules below), and
   allowance-input-spinner.js renders two real "▲"/"▼" buttons this app
   fully controls the size of - see that file's own remarks for how the
   click-to-adjust behaviour works.

   Because the replacement buttons have a KNOWN, fixed width, the cell can
   finally be balanced exactly rather than guessed at: .allowance-
   adjustment-input-wrap (new, wraps the input + button column, fills the
   <td> the same way the input alone used to) gives its LEFT padding that
   same known width MORE than its right padding - exactly Karl's own ask
   ("equal padding... on the left hand side... keeping the text in the
   centre"). The maths: with the button column occupying a fixed width B
   plus a small gap G only on the right, and left padding = (right padding
   + G + B), the INPUT's own box ends up exactly centred within the wrap
   regardless of B - and text-align: center on the input (unchanged, still
   centres within its own box) then lands the digits at the cell's true
   centre.

   CORRECTED (2026-09-24, Round 13) - Karl's own screenshot showed the real
   effect of the first attempt above: "The actual input box is very small
   and only showing 1 digit. Can you please push the buttons to the right
   hand side of the box and give plenty of space for digits." Root cause,
   found by actually doing the arithmetic this time rather than eyeballing
   it: the first attempt reserved 0.8rem (~12.8px) of right PADDING, plus a
   FULL SEPARATE 20px-wide button column on top of that (padding and the
   button column both consume width - the first attempt's own comment above
   only accounted for the padding, not the button column's own width too) -
   the button column then sat that same 12.8px short of the cell's true
   right edge, not flush against it ("push the buttons to the right hand
   side"). Total reserved width came to roughly 20 (left gutter) + 20
   (button) + 12.8 (right gutter) + 20 (left gutter's own +20px again) =
   over 60px, out of an 82px-wide column - leaving barely 20px for the
   input itself, hence "only showing 1 digit".

   Fixed on two fronts together: (1) the reserved overhead is cut right
   back - a 4px right gutter (buttons now sit right at the cell's true edge,
   "the right hand side of the box"), a 16px button column (down from 20px),
   a 2px gap between input and buttons, giving left padding = 4 + 2 + 16 =
   22px total (the same balancing principle as before, just with smaller,
   deliberately-added-up numbers instead of a rough guess); (2) the Adj./
   R- Fwd. columns get their OWN wider <col> - see .absence-allowance-
   editable-data-col below - rather than trying to fit a button column
   inside the same 82px every plain read-only column uses. Together these
   leave comfortably more than enough room for a value up to the page's own
   525,600-minute limit, satisfying "give plenty of space for digits"
   without needing to guess at exact pixel centring ever again - the maths
   here is exact, not eyeballed. */
.allowance-adjustment-input-wrap {
    display: flex;
    align-items: center;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    padding: 0.6rem 4px 0.6rem 22px;
}

.allowance-adjustment-input {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    width: 100%;
    box-sizing: border-box;
    padding: 0;
    font: inherit;
    /* Centred (2026-09-24, next round), matching the table-wide value
       centring above - was right-aligned to match the old td convention,
       which no longer applies. */
    text-align: center;
    color: inherit;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 4px;
}

/* Hides the native browser spin buttons - the whole point of this round's
   change is that this app now renders its own instead (see this block's
   own top-of-file remarks), so the native control needs to be fully out of
   the way rather than sitting underneath/behind the custom one. Firefox
   and Chromium/WebKit each need their own property - there's no single
   standard way to do this yet. Keyboard ArrowUp/ArrowDown on a focused
   number input still increments/decrements per its own step, even with the
   VISUAL spinner hidden - that's a separate, inherent behaviour of
   input[type=number] the browser keeps regardless of appearance, so hiding
   the widget here doesn't remove any keyboard capability. */
.allowance-adjustment-input::-webkit-outer-spin-button,
.allowance-adjustment-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.allowance-adjustment-input[type="number"] {
    -moz-appearance: textfield;
}

.allowance-adjustment-input:hover:not(:disabled),
.allowance-adjustment-input:focus:not(:disabled) {
    background: var(--surface-alt);
    border-color: var(--border);
}

.allowance-adjustment-input:disabled {
    color: var(--text-muted);
    cursor: not-allowed;
}

/* The custom spin button column - a narrow, fixed-width (16px, matching
   .allowance-adjustment-input-wrap's own left-padding compensation above -
   see that rule's own Round 13 remarks) flex sibling of the input, two
   buttons stacked top/bottom, sitting flush against the cell's own right
   edge (Karl: "push the buttons to the right hand side of the box"). A
   small 2px left margin gives it a hairline gap from the input rather than
   the two visually touching. aria-hidden on the wrapping <span> in the
   .razor markup: a focused input's own native ArrowUp/ArrowDown keys
   already provide the same increment/decrement to assistive tech and
   keyboard users, so these are a mouse-only visual convenience layered on
   top, not a second way of reaching functionality that would otherwise be
   missing - tabindex="-1" on the buttons themselves for the same reason,
   so they don't add two extra stops to the page's tab order for something
   already reachable from the input itself. */
.allowance-adjustment-spin {
    flex: 0 0 16px;
    width: 16px;
    margin-left: 2px;
    display: flex;
    flex-direction: column;
    align-self: stretch;
}

.allowance-adjustment-spin-btn {
    flex: 1 1 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 0;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--text-muted);
    font-size: 0.6rem;
    line-height: 1;
    cursor: pointer;
    border-radius: 3px;
}

.allowance-adjustment-spin-btn:hover:not(:disabled) {
    background: var(--surface-alt);
    color: inherit;
}

.allowance-adjustment-spin-btn:disabled {
    cursor: not-allowed;
    opacity: 0.4;
}

/* NEW (2026-09-24, next round again) - Karl: "When the user changes the
   [adjustment] or [rolled_forward] value and pauses for a second, can you
   also update the Allowance and Balance totals. If you could transition
   this to show it has just updated, that would be good." This is the
   "transition" itself - EmployeeAbsenceSummary.razor's own
   RunTotalsPreviewUpdateAsync applies this class (via a version-keyed @key
   that forces the cell to be recreated, so the animation restarts on every
   update rather than only playing once - see that method's own remarks)
   to whichever Total <td> (Allowance group's or Balance group's) just got
   a debounced preview recalculation.

   A @keyframes animation, not a plain CSS transition, is what's needed
   here - a transition only animates a property between two states the
   element already has; this needs to APPEAR highlighted instantly, then
   fade back out, which a keyframe animation running once does naturally.
   animation-fill-mode: forwards locks the final (transparent) keyframe in
   place once it finishes, so the cell doesn't sit highlighted forever if
   this class happens to still be present on some later, unrelated render.

   Colour is --success (#1a7f37, this file's own :root block) at low
   opacity - written as a literal rgba() rather than a CSS color-mix()
   (which would need a fairly recent browser) so this works everywhere
   .absence-allowance-summary-table already does; the "success" token was
   chosen since a green flash reads as "this saved/updated OK", matching
   how this app already uses --success elsewhere. Compound selector
   (table class + total class + this class) for the same specificity
   reason as every other .absence-allowance-summary-table td override in
   this file - see this file's own top-of-file remarks on that
   convention.

   UPDATED (2026-09-24, next round again) - the Left/Remaining cell now
   pulses too (Karl's Balance-group formula means an Adjustments edit
   ripples into it - see EmployeeAbsenceSummary.razor's
   RunTotalsPreviewUpdateAsync remarks), but that cell carries
   .absence-allowance-group-start, not .absence-allowance-total (it's the
   first cell of the Balance group's three, not a Total cell itself) - so
   the selector below now lists both base classes rather than just the one,
   still combined with the table class for the same specificity reason as
   before. */
@keyframes absence-allowance-total-pulse {
    0% {
        background-color: rgba(26, 127, 55, 0.22); /* --success at ~22% */
    }
    100% {
        background-color: transparent;
    }
}

.absence-allowance-summary-table td.absence-allowance-total.absence-allowance-total-pulse,
.absence-allowance-summary-table td.absence-allowance-group-start.absence-allowance-total-pulse {
    animation: absence-allowance-total-pulse 1.4s ease-out;
    animation-fill-mode: forwards;
}

/* NEW (2026-09-24, next round again, Round 9) - Karl: "Can you please put
   blue bold asterisk in the top right of the Adjust title td, and a green
   bold asterisk in the top right of the Balance R/ Fwd. title TD. I would
   like to labels these below and highlight when they can be changed."

   Header markers: .absence-allowance-header-marked goes on the <th> itself
   (Adj. and R/ Fwd. only, in EmployeeAbsenceSummary.razor's own thead) -
   position: relative plus a little extra right-hand padding so the
   asterisk has room without crowding the header text. The asterisk itself
   is a <span> positioned absolutely to that th's own top-right corner.
   Colours reuse this app's existing tokens rather than inventing new ones -
   --primary (#1f6feb, this file's own :root block) for blue, matching every
   other "this is the interactive/primary accent" use in this app; --success
   (already used above for the totals-preview pulse) for green, so the same
   green consistently means "this figure is about to update"/"this is
   editable" throughout this one page.

   UPDATED (2026-09-24, next round again, Round 10) - Karl: "Sorry, I did not
   mean highlight the columns. Can you please remove the shading. I just
   meant highlight what can be done with each row with the descriptions
   beneath. The sample text you have added in is perfect. Can you make the
   two asterisks bigger please." So this round: (1) the per-row tinted-
   background classes this comment used to also describe
   (.absence-allowance-editable-highlight-blue/-green, applied to the
   editable <td> itself) are REMOVED entirely, both the CSS below and the
   two conditional class expressions in EmployeeAbsenceSummary.razor that
   applied them - "highlighting what can be done with each row" turned out
   to mean the LEGEND text alone (confirmed "perfect" as already written),
   not a second, separate visual treatment on top of it; (2) the asterisk
   itself grew from inheriting the header's own font-size to an explicit,
   noticeably larger 1.3rem, with its own top offset nudged to keep it
   sitting at the header's own top-right corner at the new size rather than
   drifting as the glyph got taller.

   UPDATED (2026-09-24, Round 11) - Karl: "Can you add some padding to the
   right and top of the asterisks." Read as: the asterisk now sits too hard
   against the header cell's own top-right corner at the bigger 1.3rem size
   from Round 10 - it needs breathing room on both those sides, not to move
   closer to them. Pushed the glyph away from the corner: `top` moved from
   -0.05rem (slightly ABOVE the header's own text, right at the cell edge)
   down to 0.35rem, and `right` moved from 0.3rem out to 0.6rem. The th's
   own padding-right widened to match (1.5rem -> 1.8rem) so the extra
   breathing room doesn't start crowding the header text on the left
   instead.

   REPLACED - ASTERISK GLYPH -> BACKGROUND CORNER MARK (2026-09-24, Round
   12) - Karl: "the asterisks are making a bit of a mess of the title text
   alignment. Instead, would it be possible to put a background mark of the
   same colour in the top-right of those TDs, with the text still centred
   in the middle." Root cause: th.absence-allowance-header-marked's own
   padding-right (added across Rounds 9-11 purely to reserve room for the
   glyph) was extra padding no other header in this table carries - even
   though the glyph itself is position: absolute and so never actually
   pushes the "Adj."/"R/ Fwd." text out of the way, the header CELL was
   wider/differently spaced than its neighbours, which is the "mess" Karl
   is seeing.

   Fixed by dropping the text glyph (and the padding it needed) entirely,
   replacing it with a small solid-colour triangle tucked into the actual
   top-right CORNER of the cell - a background decoration, not a character
   in the text flow, so it needs no reserved padding at all.
   .absence-allowance-header-marked now carries ONLY position: relative
   (still needed, so the mark's own position: absolute is relative to this
   cell and not the table); with the padding-right override gone, "Adj."/
   "R/ Fwd." fall back to the exact same text-align: left/0.8rem padding
   every other header in this table already uses - "the text still centred
   in the middle" is read here as "leave the text exactly where it
   naturally sits, undisturbed by the mark" (this table's own headers are
   left-aligned by deliberate, table-wide convention - see this file's own
   2026-09-11 remarks above - not individually centred, so literally
   centring just these two would look inconsistent with every other header
   in the same row; flagged in the chat reply in case literal centring was
   actually meant).

   The triangle shape itself was originally attempted with the classic
   zero-size-box/mitred-border trick (a 0x0 box with only its top and right
   borders given width and colour) - CORRECTED (2026-09-24, Round 13): Karl's
   screenshot showed it actually rendering as a plain rectangle, not a
   triangle, in his real browser. Rather than re-guess that border-mitring
   geometry a second time with no browser here to check it in, replaced with
   an unambiguous, literal shape instead: a small square positioned in the
   cell's corner, with clip-path cutting it down to a triangle by three
   explicit corner coordinates (100% 0 / 100% 100% / 0 0 - the square's own
   top-right, bottom-right and top-left corners) rather than relying on how
   a browser happens to mitre two adjacent border edges. pointer-events:
   none since it's purely decorative, never meant to intercept a click. */
.absence-allowance-summary-table th.absence-allowance-header-marked {
    position: relative;
}

.absence-allowance-header-corner-mark {
    position: absolute;
    top: 0;
    right: 0;
    width: 14px;
    height: 14px;
    clip-path: polygon(100% 0, 100% 100%, 0 0);
    pointer-events: none;
}

.absence-allowance-header-corner-mark-blue {
    background: var(--primary);
}

.absence-allowance-header-corner-mark-green {
    background: var(--success);
}

/* Legend - a short key underneath the table spelling out what each coloured
   asterisk above means, in the same two colours. Plain <ul>, no bullets (a
   coloured asterisk marker already does that job), laid out as a small
   block rather than inline so it reads as its own note, not part of the
   sentence in the subtitle just above it. Confirmed as-is by Karl in Round
   10 ("The sample text you have added in is perfect.") - unchanged this
   round. */
.absence-allowance-legend {
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.9rem;
    color: var(--text-muted);
}

.absence-allowance-legend-marker {
    display: inline-block;
    font-weight: 700;
    margin-right: 0.35rem;
}

.absence-allowance-legend-marker-blue {
    color: var(--primary);
}

.absence-allowance-legend-marker-green {
    color: var(--success);
}

/* NEW (2026-09-24) - EmployeeDetail.razor's own "Calculate expected
   allowances" preview popup (Karl: "All of this can be presented in a pop
   up"). Same centred/dimmed native-Popover-API modal shape as
   .allowance-adjustment-save-popover above (see that rule's own extensive
   remarks for why this needs no JS positioning maths and gets a real
   ::backdrop for free) - deliberately copied rather than shared via a
   generic class, same "duplicate small proven patterns per-consuming-page"
   convention this codebase already uses for SectionSaveTracker/etc. */
.allowance-forecast-popover {
    padding: 1.1rem 1.35rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
    /* REVISED (2026-09-24) - widened from 36rem now that the table shows
       both calculation stages (Standard full year allowance AND Calculated
       for this employee, alongside Default allowance/Pro rata rounding) -
       see EmployeeDetail.razor's own remarks on the new columns. Still
       capped by 90vw, and .allowance-forecast-table-wrap below gives the
       table its own horizontal scroll for anything narrower than that. */
    width: min(52rem, 90vw);
    max-height: 85vh;
    overflow-y: auto;
}

.allowance-forecast-popover::backdrop {
    background: rgba(15, 23, 32, 0.55);
}

.allowance-forecast-popover h2 {
    margin-top: 0;
}

.allowance-forecast-popover p {
    margin-top: 0;
}

/* NEW (2026-09-24) - wraps .allowance-forecast-table so its now-five
   columns get their own horizontal scroll on a narrow viewport, rather than
   squeezing every column (same .table-scroll convention used everywhere
   else in this codebase - see that rule's own remarks). */
.allowance-forecast-table-wrap {
    overflow-x: auto;
}

.allowance-forecast-table {
    width: 100%;
    min-width: 40rem;
    border-collapse: collapse;
    margin: 0.75rem 0;
    font-size: 0.9rem;
}

.allowance-forecast-table th,
.allowance-forecast-table td {
    text-align: left;
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--border);
}

.allowance-forecast-table th {
    color: var(--text-muted);
    font-weight: 600;
}


/* --- RFID badges (NEW, 2026-09-26) - hr/badges (BadgeManagement.razor),
   the employee list's Badges popup and the Badges section on the employee
   screen (EmployeeBadgesPanel.razor), and the shared BadgeIssueForm.razor.
   Fields reuse .employee-field (title above, input indented) so the forms
   look like the employee screen; each .badge-field--* just fixes a width.
   Rows wrap on a narrow screen rather than overflowing. */
.badge-form-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 10px;
    margin-bottom: 1rem;
}

.badge-field--tag { width: 200px; }
.badge-field--scan { width: 420px; }
.badge-field--holder-kind { width: 200px; }
.badge-field--holder { width: 350px; }
.badge-field--name { width: 200px; }
.badge-field--company { width: 300px; }
.badge-field--type { width: 200px; }
.badge-field--expires { width: 230px; }
.badge-field--notes { width: 350px; }
.badge-field--filter { width: 300px; }

/* The "Issue to" Employee/Visitor radios - lined up with the indented
   inputs of the fields beside them. */
.badge-radio-group {
    display: flex;
    gap: 1rem;
    margin-left: 0.4rem;
    padding: 0.4rem 0;
    color: #1a1f24;
}

.badge-radio-group label {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    cursor: pointer;
}

/* A button sitting in a .badge-form-row next to fields, lined up with
   their inputs rather than their titles. */
.badge-form-button {
    padding-bottom: 1px;
}

.badge-form-actions {
    margin-top: 0.25rem;
    align-items: center;
}

.badge-form-success {
    color: var(--success);
}

.badge-issue-form {
    max-width: 1000px;
}

.badge-page-block {
    max-width: 1000px;
    margin-bottom: 0.5rem;
}

.badge-page-toolbar {
    display: flex;
    align-items: flex-end;
    gap: 1rem;
    margin-bottom: 0.75rem;
}

.badge-table {
    font-size: 0.9rem;
}

/* Tags read as codes, so a monospace face keeps e.g. 0/O and 1/I distinct. */
.badge-tag {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    white-space: nowrap;
}

.badge-end-cell,
.badge-row-actions {
    white-space: nowrap;
}

.badge-end-cell select {
    font: inherit;
    padding: 0.25rem 0.35rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-right: 0.35rem;
}

.badge-row-actions button + button {
    margin-left: 0.35rem;
}

.employee-badges-section {
    max-width: 1000px;
}

/* NEW (2026-09-27) - "Set as leaver" popup on hr/employees
   (EmployeeLeaverPanel.razor). Reuses .employee-allowances-popover for the
   popup itself and the absence summary table classes for the table; these
   are just the bits around it. */
.leaver-date-row {
    display: flex;
    align-items: flex-end;
    gap: 1rem;
    margin: 0.75rem 0;
}

.leaver-date-row label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-weight: 600;
}

.leaver-date-row input[type="date"] {
    width: 160px;
    font-weight: 400;
}

.leaver-summary {
    margin: 0 0 0.75rem;
    max-width: 60rem;
}

.leaver-notes {
    margin: 0.5rem 0 0.75rem;
    padding-left: 1.25rem;
}

.leaver-notes li {
    margin-bottom: 0.25rem;
}

/* Over-usage in red (Karl: "Over-usage should be shown in red"). Compound
   selector so it out-ranks the table's own td colour/weight defaults - the
   same specificity trap noted in AbsenceAllowanceSummaryTable.razor. */
.absence-allowance-summary-table td.leaver-over-usage {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, var(--surface));
}

/* Round 2 (2026-09-27) - leaver popup: the "already set as a leaver"
   banner, the day-by-day cancelled days table at the bottom, and the Undo
   "are you sure" line. */
.leaver-active-banner {
    margin: 0.5rem 0 0.25rem;
    padding: 0.6rem 0.8rem;
    border: 1px solid var(--border);
    border-left: 4px solid var(--danger);
    border-radius: 6px;
    background: var(--surface-alt);
    max-width: 60rem;
}

.leaver-days-table {
    width: auto;
    min-width: 24rem;
    margin-bottom: 0.5rem;
}

.leaver-days-table th,
.leaver-days-table td {
    padding: 0.3rem 0.8rem;
    white-space: nowrap;
}

.leaver-days-table td:nth-child(2) {
    text-align: right;
}

.leaver-days-table tr.leaver-days-group td {
    font-weight: 600;
    background: var(--surface-alt);
}

.leaver-undo-confirm {
    margin: 0.75rem 0 0;
    color: var(--danger);
    font-weight: 600;
    max-width: 60rem;
}

.leaver-table th[rowspan] {
    vertical-align: middle;
}

/* NEW (2026-09-28) - leaver popup: thicker grey line between the "paid on
   leaving" absence types (listed first) and the rest. */
.absence-allowance-summary-table tr.leaver-group-break td {
    border-top: 3px solid var(--border);
}

/* NEW (2026-09-29) - leaver popup: same thicker grey line above the first
   row and below the last row, framing the two groups.
   2026-09-29 (later) - all three now use 3px solid var(--border), the same
   colour and weight as the vertical group dividers (they previously used an
   undefined --border-strong, so fell back to a darker #9aa3ad). */
.absence-allowance-summary-table tr.leaver-group-top td {
    border-top: 3px solid var(--border);
}

.absence-allowance-summary-table tr.leaver-group-bottom td {
    border-bottom: 3px solid var(--border);
}

/* REMOVED (2026-09-29, later) - .calendar-day-inlieu-corner /
   .team-calendar-cell--inlieu / .team-calendar-inlieu-corner (the shaded
   top-left corner for in-lieu credits). Karl: "I don't think highlighting the
   corners ... really works. Can you try changing the date text to the public
   holiday group colour please." CalendarDayCell now colours the date number
   inline instead. */

/* NEW (2026-09-29) - hr/employees: the pending-leaver row icon (leave date
   set, status not yet Left). Amber so it stands out from the muted row
   icons; hover keeps the amber rather than switching to primary blue. */
.icon-button.icon-button--pending-leaver,
.icon-button.icon-button--pending-leaver:hover,
.icon-button.icon-button--pending-leaver:focus-visible {
    color: #c77700;
}

/* NEW (2026-09-29) - "Move / change" popup on hr/employees
   (EmployeeMovePanel.razor). Reuses the leaver popup's classes for notes,
   day lists, errors and the undo line; these are just the form and tables. */
.move-form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    gap: 0.75rem 1rem;
    margin-bottom: 0.75rem;
}

.move-form-grid select,
.move-form-grid input[type="date"] {
    width: 100%;
}

.move-mode {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.25rem;
    margin: 0 0 0.75rem;
    padding: 0.6rem 0.8rem;
    border: 1px solid var(--border);
    border-radius: 6px;
}

.move-mode legend {
    font-weight: 600;
    padding: 0 0.3rem;
}

.move-note {
    max-width: 40rem;
    margin-bottom: 0.75rem;
}

.move-note input {
    width: 100%;
}

.move-changes-table,
.move-history-table {
    width: auto;
    min-width: 28rem;
    margin-bottom: 0.75rem;
}

.move-changes-table td,
.move-changes-table th,
.move-history-table td,
.move-history-table th {
    padding: 0.3rem 0.8rem;
}

.move-allowance-table th,
.move-allowance-table td {
    text-align: center;
    white-space: nowrap;
    padding: 0.3rem 0.6rem;
}

.move-allowance-table th:first-child,
.move-allowance-table td:first-child {
    text-align: left;
}

/* CHANGED (2026-09-30) - Karl: "fixing the width of the sub columns
   (days/ mins) around 55px, including padding, and the columns around
   110px including padding." Fixed table layout with a <colgroup>
   (EmployeeMovePanel): Absence type 11rem, then 20 sub-columns of exactly
   55px (10 main columns of 110px). The table's width is the sum, so it no
   longer stretches to the popup's width (the earlier "narrower Absence
   type" request). A long type description wraps rather than widening its
   column; a figure too long for 55px would be clipped, so the figure
   padding is kept small. */
/* CHANGED again (2026-09-30, later) - Karl: "make the Absence type column
   slightly wider" (11rem -> 13rem) and "add a column between the two column
   groups called Adjustments" (2 more 55px sub-columns: 22 in all). */
.move-allowance-table {
    table-layout: fixed;
    /* CHANGED (2026-09-30, later still) - no Days/Mins sub-columns any
       more: 11 days columns of 80px + the old Balance's 55px minutes cell. */
    width: calc(13rem + 935px);
}

.move-allowance-table col.move-col-day {
    width: 80px;
}

.move-allowance-table col.move-col-name {
    width: 13rem;
}

.move-allowance-table col.move-col-num {
    width: 55px;
}

.move-allowance-table tbody tr:not(.move-group-label) td:first-child {
    white-space: normal;
}

.move-allowance-table tr.move-row-changes td {
    background: color-mix(in srgb, var(--primary) 6%, var(--surface));
}

.move-history-table tr.move-history-undone td {
    color: var(--text-muted);
    text-decoration: line-through;
}

.move-history-table tr.move-history-undone td:last-child {
    text-decoration: none;
}

/* CHANGED (2026-09-29, later again) - Move popup allowance table: three
   row groups (pro-rated, public holidays, fixed), each with a label row and
   its own pale tint; rows that change get a stronger tint of the same
   colour. Groups are split by the same 3px grey line as the leaver popup
   (EmployeeMovePanel.RowClass / IsFirstInGroup). Replaces the
   payable-on-leaving split. */
/* CHANGED (2026-09-30) - Karl: "change them, and the text, to better match
   the absence group colours for Holiday, Public holiday, and Planned absence
   (use this colour for fixed absence types). Perhaps the colours for these
   groups could be the text colour for the headings, and a lighter shade used
   for the backgrounds." Each row now carries its absence category colour as
   an inline --move-tint (EmployeeMovePanel.GroupStyle); the three classes
   below are only a fallback. Heading text = the colour (darkened a little so
   a pale calendar colour stays readable), backgrounds = lighter shades of
   it. */
.move-allowance-table tr.move-tint-prorated { --move-tint: #bfdbfe; }
.move-allowance-table tr.move-tint-public-holiday { --move-tint: #fecaca; }
.move-allowance-table tr.move-tint-fixed { --move-tint: #fbcfe8; }

.move-allowance-table tbody tr[class*="move-tint-"] td {
    background: color-mix(in srgb, var(--move-tint) 14%, var(--surface));
}

.move-allowance-table tbody tr.move-row-changes td {
    background: color-mix(in srgb, var(--move-tint) 28%, var(--surface));
}

/* "tbody" added (review 2026-09-30): the label row also carries a
   move-tint-* class, so the pale-row rule above
   (tbody tr[class*="move-tint-"] td, specificity 0,2,3) was beating this
   one (0,2,2) and the label row got the 14% row tint instead of its 24%. */
.move-allowance-table tbody tr.move-group-label td {
    border-top: 3px solid var(--border);
    background: color-mix(in srgb, var(--move-tint) 24%, var(--surface));
    color: color-mix(in srgb, var(--move-tint) 70%, #000);
    font-size: 0.8rem;
    font-weight: 600;
    text-align: left;
    padding: 0.2rem 0.6rem;
}

.move-allowance-table tr.move-group-bottom td {
    border-bottom: 3px solid var(--border);
}

/* Main columns (Allowance, Used, ...) - CHANGED (2026-09-30), Karl: "make
   the popup wider and slightly increase the individual column widths. The
   minutes sub column values can be slightly smaller than the days values."
   CHANGED again (2026-09-30, later) - each main column is 110px: Days 55px
   + Mins 55px including padding, fixed (see the table-layout: fixed rule
   above), right-aligned, minutes a little smaller and muted. */
.employee-move-popover {
    width: min(100rem, 97vw);
}

.move-allowance-table th.move-main {
    padding-left: 0.25rem;
    padding-right: 0.25rem;
}

/* NEW (2026-09-30, later still) - Karl: "make the column headings normal
   font weight". */
.move-allowance-table thead th {
    font-weight: 400;
}

.move-allowance-table th.move-num,
.move-allowance-table td.move-num {
    box-sizing: border-box;
    text-align: right;
    padding: 0.3rem 0.4rem;
    overflow: hidden;
    font-variant-numeric: tabular-nums;
}

.move-allowance-table td.move-mins {
    color: var(--text-muted);
    font-size: 0.85em;
}

/* NEW (2026-09-30) - Karl: "thin vertical lines between the
   Allowance/Used/Booked/Balance columns but not the days/minutes sub
   columns". Every Days cell starts a main column, so it carries the 1px
   line; the Old/New schedule boundary keeps its 3px line (.move-group-start). */
.move-allowance-table th.move-main:not(.move-group-start),
.move-allowance-table th.move-num:not(.move-mins):not(.move-group-start),
.move-allowance-table td.move-num:not(.move-mins):not(.move-group-start) {
    border-left: 1px solid var(--border);
}

/* Over-used (negative) balances - red, over the group tint. */
.move-allowance-table tbody tr td.leaver-over-usage {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, var(--surface));
}

.move-allowance-table tr.move-sub-header th {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--text-muted);
    text-align: center;
}

/* NEW (2026-09-30) - Karl: "make all of the category group, category, and
   sub-category column headings centred horizontally". Old/New schedule,
   Allowance/Used/Booked/Balance/Remaining and Days/Mins are all centred -
   this beats the table's th:first-child left-align (which made the first
   "Allowance" sit left) and the .move-num right-align. Absence type stays
   left, over the type names. */
.move-allowance-table thead th.move-group-start,
.move-allowance-table thead th.move-main,
.move-allowance-table thead tr.move-sub-header th.move-num {
    text-align: center;
}

.move-asterisk {
    color: var(--primary);
    font-weight: 700;
    margin-left: 0.15rem;
}

.move-table-note {
    margin: 0.4rem 0 0.8rem;
    font-size: 0.8rem;
    color: var(--text-muted);
}

.move-table-note .move-asterisk {
    margin-left: 0;
    margin-right: 0.2rem;
}

/* NEW (2026-09-29, later) - Move popup allowance table: Old schedule / New
   schedule groups, split by the same 3px line as the other group
   boundaries. */
.move-allowance-table th.move-group-start,
.move-allowance-table td.move-group-start {
    border-left: 3px solid var(--border);
}

.move-allowance-table thead th {
    text-align: center;
}


/* NEW (2026-09-30, later still) - Move popup: the row groups as a key above
   the table (Karl: "so it does not break the table up") instead of label
   rows; a line above the first row of each later group. The swatch uses the
   same --move-tint (absence category colour) as the rows. */
.move-group-key {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1.25rem;
    margin: 0.4rem 0 0.5rem;
    font-size: 0.8rem;
    color: var(--text-muted);
}

.move-group-key__item {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.move-group-key__item.move-tint-prorated { --move-tint: #bfdbfe; }
.move-group-key__item.move-tint-public-holiday { --move-tint: #fecaca; }
.move-group-key__item.move-tint-fixed { --move-tint: #fbcfe8; }

.move-group-key__swatch {
    display: inline-block;
    width: 1.1rem;
    height: 0.8rem;
    border-radius: 2px;
    border: 1px solid var(--move-tint);
    background: color-mix(in srgb, var(--move-tint) 28%, var(--surface));
}

.move-allowance-table tr.move-group-break td {
    border-top: 2px solid var(--border);
}

/* NEW (2026-09-30, later still) - Karl: "split the Remaining column into
   days and minutes ... reduce the size of the column headings text, and
   align the heading to the left. Please fix the column widths to 70px
   before padding for Allowance, Used, Booked, and Requested. Please make
   the Balance and Remaining columns 110px wide after padding, with each TD
   around 55px wide after padding." Figure cells get a fixed 6px side
   padding, so a 82px day column = 70px + padding; the Balance / Remaining
   day and minute cells are 55px each including padding. Placed last so it
   wins over the earlier move-allowance-table rules. */
.move-allowance-table {
    width: calc(13rem + 958px);
}

.move-allowance-table col.move-col-day {
    width: 82px;
}

.move-allowance-table col.move-col-num {
    width: 55px;
}

.move-allowance-table th.move-num,
.move-allowance-table td.move-num,
.move-allowance-table th.move-main,
.move-allowance-table td.move-mins {
    padding-left: 6px;
    padding-right: 6px;
}

.move-allowance-table thead th,
.move-allowance-table thead th.move-group-start,
.move-allowance-table thead th.move-main {
    font-size: 0.8rem;
    /* CHANGED (2026-09-30, later still) - Karl: "centre all of the column
       and column group titles" (was left). Absence type stays left, over
       the type names. */
    text-align: center;
}

.move-allowance-table thead th:first-child:not(.move-group-start):not(.move-main) {
    text-align: left;
}

/* NEW (2026-09-30, later still) - Karl: "change "Absence type" and the Old
   and New group headings back to the original size" - the first header row
   back to 1rem; the column headings below stay at 0.8rem. */
.move-allowance-table thead tr:first-child th {
    font-size: 1rem;
}

/* CHANGED (2026-10-01) - Karl: days and (minutes) back in every column, no
   sub-column headings, "limit the TDs to about 42.5 before padding": 22
   figure cells of 42.5px + 6px padding a side = 54.5px. */
.move-allowance-table {
    width: calc(13rem + 1199px);
}

.move-allowance-table col.move-col-num {
    width: 54.5px;
}

/* NEW (2026-10-01) - Karl: a thick grey line to the right of the New
   schedule group (matching the one between the groups), a thicker line
   between the column titles and the first row, and the lines between the
   row groups as thick as the one under the last row (3px). */
.move-allowance-table th.move-group-end,
.move-allowance-table td.move-group-end {
    border-right: 3px solid var(--border);
}

.move-allowance-table thead tr:last-child th {
    border-bottom: 3px solid var(--border);
}

.move-allowance-table tr.move-group-break td {
    border-top: 3px solid var(--border);
}

.move-allowance-table thead th[rowspan="2"] {
    border-bottom: 3px solid var(--border);
}

/* NEW (2026-10-01) - planned / unplanned absence groups (fallback colours;
   the rows carry their category colour inline). Planned uses the same
   category colour as Fixed, so the Fixed rows take a stronger shade to tell
   them apart. */
.move-allowance-table tr.move-tint-planned,
.move-group-key__item.move-tint-planned { --move-tint: #fbcfe8; }
.move-allowance-table tr.move-tint-unplanned,
.move-group-key__item.move-tint-unplanned { --move-tint: #ddd6fe; }

.move-allowance-table tbody tr.move-tint-fixed td {
    background: color-mix(in srgb, var(--move-tint) 26%, var(--surface));
}

.move-allowance-table tbody tr.move-tint-fixed.move-row-changes td {
    background: color-mix(in srgb, var(--move-tint) 38%, var(--surface));
}

.move-group-key__item.move-tint-fixed .move-group-key__swatch {
    background: color-mix(in srgb, var(--move-tint) 45%, var(--surface));
}

.move-allowance-table tbody tr.move-tint-fixed td.leaver-over-usage {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, var(--surface));
}

/* NEW (2026-10-01) - Removed column in the Old schedule group: 24 figure
   cells of 54.5px. */
.move-allowance-table {
    width: calc(13rem + 1308px);
}

/* NEW (2026-10-01) - Karl: "apply the thick grey border to the whole
   table", "make the columns slightly narrower - perhaps reduce the padding
   between days and minutes", and a green asterisk on Removed.
   - 3px grey border round the whole table.
   - Figure cells: 6px padding on the outer side, 2px between a days cell
     and its minutes cell, so each is 42.5px + 8px = 50.5px (was 54.5px).
   - The key swatches now use the same shade as the rows (14%; Fixed 26%) -
     they were darker (28% / 45%), which made the key look a different colour
     from the rows, most noticeably for Planned absences. */
.move-allowance-table {
    border: 3px solid var(--border);
    width: calc(13rem + 1212px);
}

.move-allowance-table col.move-col-num {
    width: 50.5px;
}

.move-allowance-table td.move-num:not(.move-mins) {
    padding-left: 6px;
    padding-right: 2px;
}

.move-allowance-table td.move-mins {
    padding-left: 2px;
    padding-right: 6px;
}

.move-group-key__swatch {
    background: color-mix(in srgb, var(--move-tint) 14%, var(--surface));
}

.move-group-key__item.move-tint-fixed .move-group-key__swatch {
    background: color-mix(in srgb, var(--move-tint) 26%, var(--surface));
}

.move-asterisk--removed {
    color: var(--success);
}

/* Rows the move changes no longer get a stronger shade - it made rows in the
   same group two different colours, neither always matching the key. */
.move-allowance-table tbody tr.move-row-changes td {
    background: color-mix(in srgb, var(--move-tint) 14%, var(--surface));
}

.move-allowance-table tbody tr.move-tint-fixed.move-row-changes td {
    background: color-mix(in srgb, var(--move-tint) 26%, var(--surface));
}

.move-allowance-table tbody tr td.leaver-over-usage,
.move-allowance-table tbody tr.move-tint-fixed td.leaver-over-usage {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, var(--surface));
}

/* NEW (2026-10-01) - Payroll adjustments column between the schedules:
   26 figure cells of 50.5px. */
.move-allowance-table {
    width: calc(13rem + 1313px);
}

/* NEW (2026-10-01) - Karl: "make this column heading [Payroll adjustments]
   and the other three bold - Absence type, Old schedule, New schedule"; red
   asterisk for the "balances paid out or recovered" option. */
.move-allowance-table thead tr:first-child th {
    font-weight: 700;
}

.move-asterisk--payout {
    color: var(--danger);
}

/* NEW (2026-10-01) - "Payroll" over ">>" with no line between them, and
   the payroll total note. */
/* CHANGED (2026-10-01, later) - Karl: "make sure the 'Payroll' and '>>'
   text is vertically aligned in the same way as the adjacent labels ... it
   needs padding or a white border instead": normal padding, and a line in
   the background colour (instead of none) between the two cells. */
.move-allowance-table thead th.move-payroll-head {
    border-bottom: 1px solid var(--surface);
}

.move-allowance-table thead tr:last-child th.move-payroll-tail {
    border-top: 1px solid var(--surface);
}

.move-payroll-total {
    font-weight: 600;
    color: inherit;
}

/* NEW (2026-10-01) - hr/employee-absence-summary: a heading above each
   allowance span's table (dates and shift pattern). */
.absence-allowance-span-heading {
    margin: 1rem 0 0.4rem;
}

/* NEW (2026-10-01) - the allowance summary tables' single Balance column
   (Allowance Total - Usage Total) spans both header rows; centre it
   vertically like a group heading. */
.absence-allowance-summary-table th.absence-allowance-group-header[rowspan] {
    vertical-align: middle;
}

/* NEW (2026-10-02) - HR employee absence summary: the employee move popup's
   row groups, colours and key (Karl: "apply the same grouping and background
   colour + key to the employee-absence-summary table as used in the employee
   move popup"). Each row carries its group's absence category colour inline
   as --move-tint (AllowanceGroups.Style); the classes below are fallbacks.
   Same shades as the popup (14%, Fixed 26% so it stands apart from Planned,
   which shares its colour) and the same 3px line above each later group.
   The tint is on the <tr> rather than the cells, so the Total / Balance
   cells' preview pulse (which animates the cell's own background to
   transparent) fades back to the row colour. The key uses the popup's
   .move-group-key styles as they are. */
.absence-allowance-summary-table--grouped tr.move-tint-prorated { --move-tint: #bfdbfe; }
.absence-allowance-summary-table--grouped tr.move-tint-public-holiday { --move-tint: #fecaca; }
.absence-allowance-summary-table--grouped tr.move-tint-fixed { --move-tint: #fbcfe8; }
.absence-allowance-summary-table--grouped tr.move-tint-planned { --move-tint: #fbcfe8; }
.absence-allowance-summary-table--grouped tr.move-tint-unplanned { --move-tint: #ddd6fe; }

.absence-allowance-summary-table--grouped tbody tr[class*="move-tint-"] {
    background: color-mix(in srgb, var(--move-tint) 14%, var(--surface));
}

.absence-allowance-summary-table--grouped tbody tr.move-tint-fixed {
    background: color-mix(in srgb, var(--move-tint) 26%, var(--surface));
}

.absence-allowance-summary-table--grouped tbody tr.move-group-break td {
    border-top: 3px solid var(--border);
}

/* NEW (2026-10-02, later) - Karl: "Please ensure there sufficient gap
   between the multiple tables." When an employee's year is split into
   several allowance tables, each later table's heading sits well clear of
   the table above it. The single group key now sits above the first
   heading, so that heading only needs a small gap. */
.absence-allowance-summary-table--grouped + .absence-allowance-span-heading {
    margin-top: 2.5rem;
}

.move-group-key + .absence-allowance-span-heading {
    margin-top: 0.6rem;
}

/* NEW (2026-10-03) - hr/payroll-locks. */
.payroll-lock-page {
    max-width: 1100px;
}

.payroll-lock-table td.payroll-lock-num,
.payroll-lock-table th.payroll-lock-num {
    text-align: right;
    white-space: nowrap;
}

.payroll-lock-actions {
    white-space: nowrap;
}

.payroll-lock-actions button + button {
    margin-left: 0.5rem;
}

.payroll-lock-form {
    margin-top: 1.25rem;
    padding: 1rem 1.25rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
}

.payroll-lock-form h3 {
    margin-top: 0;
}

.payroll-lock-form__fields {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-end;
}

.payroll-lock-form__fields label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.payroll-lock-form__note input {
    width: 22rem;
}

.payroll-lock-preview {
    margin: 0.9rem 0;
}

.payroll-lock-message {
    margin-top: 0.75rem;
    color: var(--success);
}

.payroll-lock-message--error {
    color: var(--danger);
}

.payroll-lock-table tr.payroll-lock-undone td {
    color: var(--text-muted);
    text-decoration: line-through;
}

.payroll-lock-table tr.payroll-lock-undone td:last-child {
    text-decoration: none;
}

.payroll-lock-undone-note {
    font-size: 0.85em;
    color: var(--text-muted);
}

/* NEW (2026-10-03) - hr/employee-absence-summary. Karl: "always show this
   shift pattern in smaller grey font than the title" and "put a border
   around the whole table with the same thickness grey border as the thick
   grey vertical and horizontal lines" (3px var(--border)). */
.absence-summary-pattern-subtitle {
    font-size: 0.9rem;
    color: var(--text-muted);
}

.absence-allowance-summary-table.absence-allowance-summary-table--grouped {
    border: 3px solid var(--border);
}

/* REMOVED (2026-10-05) - .calendar-day--in-lieu (the orange in-lieu
   border, 2026-10-04). Karl: "Can you please remove the code that changes
   the border to an orange colour on an earned public holiday." */

/* NEW (2026-10-04) - Karl: "add a simple summary under the calendar, above
   the Preview and Confirm Booking buttons, with the absence types with
   allowances for the whole year shown on the calendar." YearAllowanceSummary
   .razor, on the employee and manager calendars. Sits in the gap the annual
   grid's own margin-bottom leaves, with the same gap again below it. */
.year-allowance-summary {
    margin-bottom: 1.5rem;
}

.year-allowance-summary-title {
    font-weight: 600;
    margin-bottom: 0.4rem;
}

/* REWRITTEN (2026-10-05) - Karl: "two tables, with columns that line up
   nicely ... Please make sure the columns are fixed width and not too wide."
   table-layout: fixed with one <colgroup> per table (same widths in both),
   so the minutes table and the fixed-days table line up. The thicker
   borders (Karl's "||") go before Used and before Balance. */
.year-allowance-summary-block + .year-allowance-summary-block {
    margin-top: 0.75rem;
}

.year-allowance-summary-caption {
    margin: 0 0 0.3rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.year-allowance-summary-table {
    border-collapse: collapse;
    table-layout: fixed;
    width: auto;
}

.year-allowance-summary-label-col {
    width: 14rem;
}

.year-allowance-summary-value-col {
    width: 88px;
}

/* NEW (2026-10-05, later) - "Balance (days)": the balance / the latest
   allowance set's standard shift. "This column can be slightly wider." */
.year-allowance-summary-days-col {
    width: 104px;
}

/* CHANGED (2026-10-05, latest) - "remove the border between Balance and
   Balance (days)". hidden wins over every other style in the collapsed
   border model, so the shared Balance | (days) border disappears. */
.year-allowance-summary-table .year-allowance-summary-days {
    border-left-style: hidden;
}

.year-allowance-summary-table th,
.year-allowance-summary-table td {
    border: 1px solid var(--border);
    padding: 5px 8px;
    text-align: left;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.year-allowance-summary-table th {
    background: #f3f4f6;
    font-weight: 600;
    font-size: 0.8rem;
    white-space: normal;
}

.year-allowance-summary-table .numeric {
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.year-allowance-summary-table .year-allowance-summary-group-start {
    border-left: 3px solid var(--border);
}

.year-allowance-summary-table td.year-allowance-summary-total {
    font-weight: 600;
}

.year-allowance-summary-negative {
    color: #b91c1c;
}

/* NEW (2026-10-04) - deprecated pages (AppPage.IsDeprecated): the notice
   under the page heading (DeprecatedPageNotice.razor) and the greyed,
   italic menu link. */
.deprecated-page-notice {
    background: #fff7ed;
    border: 1px solid #fdba74;
    border-left: 4px solid #ea580c;
    border-radius: 6px;
    padding: 0.6rem 0.9rem;
    margin: 0.5rem 0 1rem;
    max-width: 60rem;
}

.nav-link--deprecated {
    font-style: italic;
    opacity: 0.6;
}

/* NEW (2026-10-04) - hr/employee-absence-summary only (the one page whose
   table carries .absence-allowance-summary-table--grouped). Karl: "I
   believe there is padding of 9.6px at the top and bottom of all cells,
   for both values and headings. Can you reduce this to 5px please. I think
   the columns are currently 55.4px with 12.8px of padding to the left and
   right. Can you try reducing that to 8.8px please."
   - Cell padding 0.6rem 0.8rem (9.6px / 12.8px, from .data-table) ->
     5px top and bottom, 8.8px left and right, headings and values alike.
   - The table is table-layout: fixed, so a plain column's width is its
     <col> width (82px = 55.4px content + 2 x 12.8px padding + 1px border).
     Its <col> goes down by the 8px of padding saved, to 74px, so the
     content stays 55.4px and the columns get narrower rather than just
     emptier.
   - The Adj. / R/F edit cells have no padding of their own (their input
     fills the cell). CHANGED (2026-10-04, later) - they're plain 74px
     columns now too; see the input rule further down.
   Editable cells (padding 0) and other more specific cell rules still win
   over this one. */
.absence-allowance-summary-table--grouped th,
.absence-allowance-summary-table--grouped td {
    padding: 5px 8.8px;
}

.absence-allowance-summary-table--grouped .absence-allowance-data-col {
    width: 74px;
}

/* NEW (2026-10-04) - hr/employee-absence-summary: Karl: "The two which
   allow for inputs and the up/down buttons have not [narrowed]. Can you
   please remove the buttons and code, and turn them into simple inputs for
   someone to click into and type over." The Adj. / R/F cells are normal
   74px columns now (absence-allowance-data-col) holding a plain text box:
   no spin-button column, so the wrapper's 22px left / 4px right
   compensation goes, and the box has a light border all the time so it
   reads as something to click into. Sized so the row stays the same height
   as a plain 5px-padded cell. Scoped to this page's table - the HR
   absence-type summary still uses the spin buttons. */
.absence-allowance-summary-table--grouped .allowance-adjustment-input-wrap {
    padding: 3px 6px;
}

.absence-allowance-summary-table--grouped .allowance-adjustment-input {
    padding: 1px 2px;
    /* CHANGED (2026-10-04, later) - Karl: "Can you please remove the white
       background of the input fields." Transparent, so the row's group tint
       shows through; the border alone marks it as a box to type in. */
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 3px;
}

.absence-allowance-summary-table--grouped .allowance-adjustment-input:focus:not(:disabled) {
    outline: 2px solid var(--primary);
    outline-offset: -1px;
}

/* NEW (2026-10-05) - manager-tools/employee-calendar: the note listing a
   split year's allowance sets. CHANGED (2026-10-05, later still) - Karl:
   "Let's go back to the inline text, not the borderless table." One
   subtitle-style paragraph, in line with the subtitle, no box. */
.calendar-allowance-sets-note {
    margin: 0 0 0.75rem;
}

/* REMOVED (2026-10-05, latest) - .calendar-day--payroll-locked (black,
   then darker grey borders on payroll-locked dates). Karl: "The darker
   shading is not working either. Please go back to the original grey
   shading for all dates." */

/* REMOVED (2026-10-05, latest) - .calendar-day-lock (the payroll padlock).
   Karl: "Can you please remove the padlock icon. It is not working how I
   was hoping." */

/* NEW (2026-10-05) - CalendarCategoryToggles ThreeLines: presence, absence
   types and Cancellation requested each on their own line. */
.calendar-category-toggle-lines {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

/* NEW (2026-10-05) - manager-tools/employee-calendar Display options. Karl:
   "please ensure they are slightly indented from the title, as on other
   pages, and there is the same vertical gap between the title and the first
   row ... as between the rows themselves ... around 10px." The same 0.4rem
   indent the other pages give their inputs (.absence-year-create-grid,
   .employee-field), and 10px between the title and each line. */
.calendar-display-options {
    gap: 10px;
}

.calendar-display-options .calendar-category-toggle-lines {
    gap: 10px;
    margin-left: 0.4rem;
}

.calendar-display-options .calendar-category-toggle-lines .calendar-category-toggles {
    margin-bottom: 0;
}

/* NEW (2026-10-05) - the Employee bookable column's two-part control (Y/N +
   approval - / HR / M) on Settings > Annual leave & absences. The same
   segmented control as Manager bookable, two equal segments at the same
   36px per segment. */
.allowance-rounding-segmented.allowance-rounding-segmented--two {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    width: 72px;
}

/* NEW (2026-10-06) - the calendar absence popup (manager-tools/employee-calendar):
   the basics (dates, scheduled days), then a "More information" link. */
.absence-day-popup-basics {
    margin: 0.25rem 0 0.5rem;
}

.absence-day-popup-more {
    margin-bottom: 0.5rem;
}

.absence-day-popup-detail {
    margin-bottom: 0.5rem;
}

/* NEW (2026-10-06) - the hover popover's absence summary (type, dates,
   scheduled days): the type in the normal text colour, each line kept on one
   line so the dates don't wrap. */
.calendar-day-hover-popover-text strong {
    color: var(--text, inherit);
}

.calendar-day-hover-popover-text {
    white-space: nowrap;
}

.calendar-day-hover-popover {
    max-width: 18rem;
}

/* NEW (2026-10-06) - Karl: "make sure "More information" is centred in the
   button horizontally and vertically." Flex-centred, whatever the general
   button rules set, with a symmetric line-height. */
.calendar-day-hover-popover .calendar-day-hover-popover-button {
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    line-height: 1.2;
    box-sizing: border-box;
}

/* NEW (2026-10-06) - Karl: "make the title (absence type) slightly bigger.
   Make the More information button's padding smaller - so the button is
   smaller but the text is about the same size." */
.calendar-day-hover-popover-text .calendar-day-hover-popover-title {
    font-size: 0.9rem;
}

.calendar-day-hover-popover .calendar-day-hover-popover-button {
    padding: 0.15rem 0.5rem;
    min-height: 0;
    height: auto;
}

/* NEW (2026-10-06) - manager-tools/employee-calendar, the booking area under
   the calendar. Karl: "Please create two different columns, both around 50%
   width." Column 1: From / To / Clear dates, the length radios, the help
   text. Column 2: Absence type with the remaining allowance to its right,
   then the preview / booking result and Confirm booking. Stacks on a narrow
   window. */
.booking-columns {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 1.5rem 2rem;
    align-items: start;
}

@media (max-width: 900px) {
    .booking-columns {
        grid-template-columns: minmax(0, 1fr);
    }
}

.booking-column {
    min-width: 0;
}

/* "The From and To inputs should be indented from the titles, as per other
   pages, the Clear dates button should be vertically aligned to the To and
   From input boxes." The same 0.4rem indent as the other pages; the row
   aligns to the bottom, and the inputs and button share one fixed height, so
   the button lines up with the boxes rather than their titles. The date
   boxes are narrower than the 220px used elsewhere so both fit in half the
   width. */
.booking-dates-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem 1rem;
    margin-bottom: 1rem;
}

.booking-dates-row .calendar-toolbar-field input[type="date"] {
    margin-left: 0.4rem;
    min-width: 0;
    width: 160px;
    height: 2.2rem;
}

.booking-dates-row .booking-clear-dates {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 2.2rem;
    padding: 0 0.75rem;
    font-size: 0.85rem;
    line-height: 1.2;
    box-sizing: border-box;
}

.booking-column .absence-booking-radio-group {
    margin-bottom: 0.75rem;
}

.booking-help-text {
    margin: 0;
    font-size: 0.85rem;
}

/* "The second column should contain the Absence type drop down, again with
   the input indented from the title. The preview allowance can be to the
   right, with it vertically aligned with the top of the Absence type input
   box." The allowance text is pushed down by the height of the field's
   title plus its gap (the same 1.25rem estimate used for the other
   title-height alignments on these pages). */
.booking-type-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.5rem 1.25rem;
    margin-bottom: 0.75rem;
}

.booking-type-row .calendar-toolbar-field.absence-type-field select {
    margin-left: 0.4rem;
}

.booking-type-row .absence-booking-balance {
    flex: 1 1 180px;
    min-width: 0;
    margin-top: 1.25rem;
    min-height: 2.8em;
}

.booking-type-row .absence-booking-balance-text {
    margin: 0;
}

/* The preview / result box sits under the type row; no reserved height now
   it's at the bottom of its column. Confirm booking appears under it. */
.booking-column .absence-booking-results {
    min-height: 0;
}

.booking-column .booking-confirm-row {
    margin-top: 0.75rem;
    margin-bottom: 0;
}

/* NEW (2026-10-06) - RevealPanel (Components/Shared/RevealPanel.razor).
   Karl: "Can you please add in nice transitions to the preview and allowance
   text, and the Confirm button. They are just snapping into place at the
   moment." The panel's height grows / shrinks smoothly (grid rows 0fr <->
   1fr, so whatever is underneath slides rather than jumps) while it fades;
   new content inside an open panel fades and slides in.

   REWRITTEN (2026-10-06, later) - Karl: "there does not appear to be any nice
   transitions or animations." Opening now uses @starting-style: the browser
   gives a newly inserted --open panel (and a newly inserted inner element)
   the hidden starting look itself and transitions from it, so it no longer
   depends on two Blazor renders reaching the browser in separate frames.
   Needs Chrome / Edge 117+, Safari 17.5+ or Firefox 129+ (older browsers
   just show it without the opening animation). 0.35s must match
   RevealPanel.TransitionMs. */
.reveal-panel {
    display: grid;
    transition: grid-template-rows 0.35s ease, opacity 0.35s ease;
}

.reveal-panel.reveal-panel--open {
    grid-template-rows: 1fr;
    opacity: 1;
}

.reveal-panel.reveal-panel--closed {
    grid-template-rows: 0fr;
    opacity: 0;
}

@starting-style {
    .reveal-panel.reveal-panel--open {
        grid-template-rows: 0fr;
        opacity: 0;
    }
}

.reveal-panel-inner {
    min-height: 0;
    overflow: hidden;
    opacity: 1;
    transform: translateY(0);
    transition: opacity 0.35s ease, transform 0.35s ease;
}

@starting-style {
    .reveal-panel-inner {
        opacity: 0;
        transform: translateY(-6px);
    }
}

/* Once fully open, nothing is clipped (focus rings, shadows). */
.reveal-panel.reveal-panel--settled > .reveal-panel-inner {
    overflow: visible;
}

/* REMOVED (2026-10-06, later) - a prefers-reduced-motion rule that turned
   these transitions off. Karl: "I still cannot see any animations or
   transitions." Windows reports "reduce motion" to the browser whenever its
   animation effects are off - which is the default over Remote Desktop - and
   that rule then hid every transition here. */

/* The Confirm button's old 1rem bottom margin (.absence-booking-actions) and
   the result box's top spacing now sit inside the panels. */
.booking-column .reveal-panel + .reveal-panel .booking-confirm-row {
    padding-top: 0.75rem;
    margin-top: 0;
}

/* NEW (2026-10-06, later) - Karl: "Both columns are still jumping around."
   - The preview / Confirm area keeps a minimum height, so the page doesn't get
     shorter and taller as they come and go (at the bottom of the page the
     browser re-scrolls when the page shrinks, which moved everything).
   - While a new automatic preview is on its way the old result is dimmed
     rather than removed (EmployeeCalendar._resultsUpdating).
   - The half-day radios are always in the page, just faded out and
     unclickable until a single date is selected, so the left column never
     changes height. */
.booking-result-area {
    min-height: 8rem;
}

.absence-booking-results {
    transition: opacity 0.25s ease;
}

.absence-booking-results.absence-booking-results--updating {
    opacity: 0.55;
}

.absence-booking-radio {
    transition: opacity 0.3s ease, visibility 0.3s;
}

.absence-booking-radio.absence-booking-radio--unavailable {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

/* REMOVED (2026-10-08, later) - the worked duration in the cells
   (.calendar-day-payable*, incl. the half-day positions, cell-size scaling and
   its hover tip), the L / E flags (.calendar-day-flag*) and the trial bar
   layout (.calendar-day--bars, .calendar-day-bar*). Karl: "This looks good but
   there is just too much information. Can we go back to just 1-2 colour
   blocks ... Please remove durations and L and E from the cells."

   REMOVED (2026-10-10) - the worked-day tooltip (.calendar-day-tooltip).
   Karl: "I think this just needs to be an absence and holiday planner". */

/* NEW (2026-10-09) - Karl: "Can we please change the two popups, the one for
   working schedules and the other for absences, to make them more similar in
   appearance. Please use the dark background and light text theme for both.
   The working schedule appears above the date you hover over, the absence
   appears beneath ... Please make all text left-aligned."
   The absence hover popover now matches the worked-day tooltip
   (.calendar-day-tooltip): the same dark background, white text, padding,
   corners, shadow and text size, left-aligned, the first line (the absence
   type) bold. Its "More information" button keeps the primary style, full
   width under the text. */
.calendar-day-hover-popover {
    background: #1f2937;
    border-color: #1f2937;
    border-radius: 6px;
    padding: 0.4rem 0.6rem;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
    text-align: left;
}

.calendar-day-hover-popover .calendar-day-hover-popover-text {
    color: #ffffff;
    font-size: 0.75rem;
    line-height: 1.35;
    text-align: left;
}

.calendar-day-hover-popover .calendar-day-hover-popover-text strong,
.calendar-day-hover-popover .calendar-day-hover-popover-text .calendar-day-hover-popover-title {
    color: #ffffff;
    font-size: 0.75rem;
    font-weight: 600;
}

/* REMOVED (2026-10-10) - the tooltip tables (.calendar-tooltip-title,
   .calendar-tooltip-table and friends) used by the worked-day tooltip and the
   work + absence popover. */

/* NEW (2026-10-09, later) - the link that opens the absence popup: a plain
   underlined light-blue text link, the same size as the rows around it. A
   <button> (it runs a Blazor handler), so every page button style is reset. */
.calendar-day-hover-popover .calendar-tooltip-link {
    display: inline;
    margin: 0;
    padding: 0;
    min-height: 0;
    width: auto;
    border: none;
    border-radius: 0;
    background: none;
    box-shadow: none;
    color: #93c5fd;
    font: inherit;
    font-size: 0.75rem;
    line-height: 1.35;
    text-align: left;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}

.calendar-day-hover-popover .calendar-tooltip-link:hover {
    color: #bfdbfe;
    background: none;
}

.calendar-day-hover-popover .calendar-tooltip-link:focus-visible {
    outline: 2px solid #93c5fd;
    outline-offset: 2px;
}

/* REMOVED (2026-10-10) - the white ring round a dot on a shaded cell (it
   made those dots look bigger). Karl: "Can you make all dots the same overall
   size. For confirmed absences, they should be solid colour." */

/* NEW (2026-10-09, latest) - Karl: "I would have expected the green dot to
   fall into the second half of the shift's shaded area". Dots for an entry in
   the second half of the shift sit at the bottom of the right-hand half,
   level with the usual bottom-left dots (the cell's padding: 0.3rem compact,
   0.4rem otherwise). */
.calendar-day-dots--second-half {
    position: absolute;
    left: calc(50% + 0.3rem);
    bottom: 0.3rem;
    margin-top: 0;
    z-index: 1;
}

.calendar-day:not(.calendar-day--compact) .calendar-day-dots--second-half {
    left: calc(50% + 0.4rem);
    bottom: 0.4rem;
}

/* REMOVED (2026-10-10) - the thin white outline on the date numbers. Karl:
   "Can you please remove the white outline, but make the dates slightly
   larger" - see .calendar-day--compact .calendar-day-number. */

/* NEW (2026-10-11) - Karl: "Can you please change the date borders. For hover/
   selection, please highlight in blue 2px. For pending cancellations, please
   highlight in red 2px. For pending approvals, please highlight in green 2px."
   Each is the cell's own 1px border in that colour plus a 1px ring just inside
   it, so it reads as one 2px line and the cell doesn't change size. Declared
   last, in this order, so hover / selection (blue) wins over red and green
   while the mouse is on a date or it is selected; red wins over green.
   Replaces the earlier rules: hover over an absence was red, a selected date
   1px blue, the popup's absence a 3px ring, a pending cancellation 1px red. */
.calendar-day--pending-approval {
    border-color: #16a34a;
    box-shadow: inset 0 0 0 1px #16a34a;
}

.calendar-day--pending-cancellation {
    border-color: var(--danger);
    box-shadow: inset 0 0 0 1px var(--danger);
}

.calendar-day--selected,
.calendar-day--popup-highlight,
.calendar-day--hover-highlight,
.calendar-day--clickable:hover,
.calendar-day--hover-highlight.calendar-day--clickable:hover {
    border-color: var(--primary);
    box-shadow: inset 0 0 0 1px var(--primary);
}

/* NEW (2026-10-11) - the manager calendar's 3x3 dot grid (CalendarDayCell
   DotPlacements): 9px dots centred at 25 / 50 / 75% across and 67 / 84 / 50%
   down (middle row first). Karl chose "option C" with "slightly bigger dots",
   and "The middle row at 76Px looks good to me" - so the compact cells are now
   a fixed 64px content height (about 76px with padding and border; they were
   about 68px, the general 3.5rem minimum overriding the 28px height). */
.calendar-day--compact {
    height: 64px;
    min-height: 64px;
}

.calendar-day-grid-dot {
    position: absolute;
    width: 9px;
    height: 9px;
    border-radius: 999px;
    transform: translate(-50%, -50%);
    z-index: 1;
    pointer-events: none;
}

/* NEW (2026-10-11) - the booking form's Specific times row (start / end
   drop-downs, then the shift's times) and the preview's warning when the times
   chosen use more minutes than the shift after its breaks. */
.booking-times-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem 0.75rem;
    margin: 0.25rem 0 0.5rem 1.6rem;
}

.booking-times-row .subtitle {
    margin: 0;
}

.booking-times-shift {
    flex-basis: 100%;
}

.absence-booking-warning {
    margin-top: 0.4rem;
    padding: 0.35rem 0.5rem;
    border-left: 3px solid #d97706;
    background: #fffbeb;
    color: #78350f;
}

/* NEW (2026-10-11, later) - more than one absence on a date: the hover popover
   shows a section per absence, and the day popup a section per absence, each
   with its own details and buttons, divided by a line. */
.calendar-day-hover-popover .calendar-day-hover-popover-section + .calendar-day-hover-popover-section {
    margin-top: 0.45rem;
    padding-top: 0.45rem;
    border-top: 1px solid rgba(255, 255, 255, 0.25);
}

.calendar-day-hover-popover .calendar-day-hover-popover-section:last-of-type {
    margin-bottom: 0.4rem;
}

.absence-day-popup-absence + .absence-day-popup-absence {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border);
}

.absence-day-popup-absence-title {
    margin: 0 0 0.4rem;
    font-size: 1rem;
}


/* CHANGED (2026-10-11, later) - Karl: "Can you put the Specific time option on
   a second line, with the Start and End drop downs appearing when this option
   is selected. Then can be narrower than they currently are. Please indent the
   drop downs from the Start and End titles as per usual." The Specific times
   radio has its own line under the other three; Start / End sit beside it
   (bottom-aligned with the radio), 165px wide (room for "02:00 (next day)") and indented 0.4rem from their
   titles like From / To. The line keeps the drop-downs' height even before
   they appear, so nothing below moves when they do. The shift's times (or why
   times can't be booked) go underneath. */
.booking-column .absence-booking-radio-group {
    margin-bottom: 0.25rem;
}

.booking-specific-times-line {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem 1rem;
    min-height: 3.6rem;
    margin-bottom: 0.25rem;
}

.booking-specific-times-line .absence-booking-radio {
    margin-bottom: 0.55rem;
}

.booking-specific-times-line .booking-time-field select {
    margin-left: 0.4rem;
    min-width: 0;
    width: 165px;
    height: 2.2rem;
}

.booking-times-note {
    margin: 0 0 0.75rem;
    font-size: 0.85rem;
}

/* CHANGED (2026-10-11, later still) - Karl: "Can you align the Specific times
   to vertically to the top with Start and End. Please make sure there is a
   small gap between this row and the one above ... Please make the Start and
   End drop downs appear smoothly as a nice transition." The radio now lines up
   with the tops of the Start / End titles; a 0.9rem gap above the line; Start
   and End fade and slide in (@starting-style, as RevealPanel does) when the
   option is chosen. */
.booking-specific-times-line {
    align-items: flex-start;
    margin-top: 0.9rem;
}

.booking-specific-times-line .absence-booking-radio {
    margin-bottom: 0;
}

.booking-specific-times-line .booking-time-field {
    transition: opacity 0.3s ease, transform 0.3s ease;
}

@starting-style {
    .booking-specific-times-line .booking-time-field {
        opacity: 0;
        transform: translateX(-0.5rem);
    }
}

.booking-specific-times-line .booking-time-field + .booking-time-field {
    transition-delay: 0.06s;
}

/* CHANGED (2026-10-11, last) - Karl: "Can you give a bit more of a horizontal
   gap between the Specific time text and the Start drop-down input. Please
   make the From, To and Clear dates button the same width as each other."
   1.75rem between "Specific times" and Start (was 1rem); the From and To
   boxes and the Clear dates button are all 160px wide (the button was its
   text's width). */
.booking-specific-times-line .absence-booking-radio {
    margin-right: 0.75rem;
}

/* REVERTED (2026-10-11) - the 160px Clear dates width. Karl: "Sorry, can you
   revert the Clear dates button to the previous size." */

/* CHANGED (2026-10-11, last+) - Karl: "Can you try putting the Whole day(s),
   Half days and Specific times on individual rows." The booking form's length
   options stack one per row, with the same 0.6rem between each row, including
   before Specific times (its Start / End still appear beside it). */
.booking-column .absence-booking-radio-group {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
    margin-bottom: 0;
}

.booking-specific-times-line {
    margin-top: 0.6rem;
}

/* NEW (2026-10-11) - HR > Leave grants (LeaveGrants.razor). Karl: "What would
   be a good way to handle maternity, paternity, shared parental leave, and
   career break ...". Bars: blue = used, green = a full phase / grant, grey =
   an unpaid phase, red = more booked than granted. */
.leave-grants-filters {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

/* CHANGED (2026-10-12, later) - two rows (5 + 4, then Reset and Add a grant),
   wider boxes for longer names. */
.leave-grants-filters__row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem 1rem;
}

/* CHANGED (2026-10-12) - the hr/employees filters, every one with its title
   above and its box indented from it (0.4rem, as AbsenceYears /
   ShiftPatternEditor) - Karl: "titles above the option, with the input
   indented as per other pages". */
.leave-grants-filters__row > label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.leave-grants-filters__row > label > input,
.leave-grants-filters__row > label > select {
    font: inherit;
    font-size: 0.95rem;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-left: 0.4rem;
    width: 210px;
}

.leave-grants-filters__row > label > select {
    background-color: var(--surface);
}

.leave-grants-filters .multiselect-dropdown--label-above .multiselect-trigger {
    width: 210px;
}

.leave-grants-filters__actions {
    margin-left: auto;
}

/* NEW (2026-10-12, later) - Reset filters sits in a slot as wide as a
   filter (box + indent), right-aligned: its right edge is under Area's. */
.leave-grants-filters__reset {
    display: flex;
    justify-content: flex-end;
    width: calc(210px + 0.4rem);
}

.leave-grants-message {
    margin: 0 0 0.75rem;
    color: var(--success);
}

.leave-grants-message--error {
    color: var(--danger);
}

.leave-grants-table td {
    vertical-align: middle;
}

.leave-grants-nowrap {
    white-space: nowrap;
}

.leave-grants-muted {
    color: var(--text-muted);
}

.leave-grants-num {
    text-align: right;
    white-space: nowrap;
}

.leave-grants-row--cancelled td {
    color: var(--text-muted);
}

/* CHANGED (2026-10-11, later) - a smaller Used bar with no text under it:
   a section per pay phase, solid = taken up to today, pale = booked ahead,
   grey = unpaid phase, red = more booked than granted; the figures are in
   each section's tooltip. */
.leave-grants-used {
    display: flex;
    align-items: center;
    gap: 6px;
}

.leave-grants-seg {
    display: flex;
    gap: 2px;
    width: 100px;   /* CHANGED (2026-10-12) - was 120px */
    flex: none;
}

.leave-grants-seg > div {
    position: relative;
    height: 10px;
    border-radius: 3px;
    overflow: hidden;
    cursor: help;
    background: color-mix(in srgb, var(--text-muted) 18%, transparent);
}

.leave-grants-seg > div > span {
    position: absolute;
    inset: 0 auto 0 0;
}

.leave-grants-seg > div > span.is-booked {
    background: color-mix(in srgb, var(--primary) 35%, transparent);
}

.leave-grants-seg > div > span.is-taken {
    background: var(--primary);
}

.leave-grants-seg > div.is-unpaid > span.is-booked {
    background: color-mix(in srgb, var(--text-muted) 40%, transparent);
}

.leave-grants-seg > div.is-unpaid > span.is-taken {
    background: color-mix(in srgb, var(--text-muted) 85%, transparent);
}

.leave-grants-seg > div.is-over > span.is-booked {
    background: color-mix(in srgb, var(--danger) 40%, transparent);
}

.leave-grants-seg > div.is-over > span.is-taken {
    background: var(--danger);
}

.leave-grants-pct {
    min-width: 2.6em;
    text-align: right;
    font-size: 0.85rem;
    cursor: help;
    white-space: nowrap;
}

.leave-grants-over {
    color: var(--danger);
    font-weight: 600;
    cursor: help;
}

.leave-grants-tag {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
}

.leave-grants-tag--active {
    background: color-mix(in srgb, var(--success) 18%, transparent);
    color: var(--success);
}

.leave-grants-tag--closed {
    background: color-mix(in srgb, var(--text-muted) 18%, transparent);
    color: var(--text-muted);
}

.leave-grants-tag--over {
    background: color-mix(in srgb, var(--danger) 15%, transparent);
    color: var(--danger);
}

.leave-grants-actions {
    white-space: nowrap;
}

.leave-grants-actions button + button {
    margin-left: 0.4rem;
}

.leave-grants-footnote {
    margin-top: 0.5rem;
}

.leave-grants-form {
    margin-top: 1.25rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--border);
    border-radius: 8px;
}

.leave-grants-form h2 {
    margin-top: 0;
}

.leave-grants-form__row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem 1rem;
    margin-bottom: 0.8rem;
}

.leave-grants-form__note input {
    width: 280px;
}

.leave-grants-form__notes {
    display: block;
    margin: 0.6rem 0;
}

.leave-grants-form__notes textarea {
    display: block;
    width: min(560px, 100%);
}

.leave-grants-phases {
    border-collapse: collapse;
    margin: 0.2rem 0 0.4rem;
}

.leave-grants-phases th {
    text-align: left;
    font-weight: 600;
    padding: 0.25rem 0.5rem 0.25rem 0;
}

.leave-grants-phases td {
    padding: 0.2rem 0.5rem 0.2rem 0;
}

.leave-grants-phases input[type="number"] {
    width: 80px;
}

.leave-grants-phases__label {
    width: 220px;
}

.leave-grants-form__total {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin: 0.2rem 0 0.6rem;
}

/* NEW (2026-10-11) - Settings > Annual leave and absences > Family and
   extended leave (AbsenceTypesTable GrantedMode): the granted types' own
   columns and the pay phases editor row. */
.absence-types-table .granted-type-unit {
    min-width: 0;
    width: 150px;
}

.absence-types-table .granted-type-length input[type="number"],
.absence-types-table .granted-type-window input[type="number"] {
    width: 56px;
}

.absence-types-table .granted-type-length,
.absence-types-table .granted-type-window {
    white-space: nowrap;
}

.granted-type-pill {
    display: inline-block;
    margin: 1px 4px 1px 0;
    padding: 1px 8px;
    border-radius: 6px;
    font-size: 0.8rem;
    white-space: nowrap;
    background: color-mix(in srgb, var(--primary) 12%, transparent);
    color: var(--primary);
}

.granted-type-pill.is-unpaid {
    background: color-mix(in srgb, var(--text-muted) 15%, transparent);
    color: var(--text-muted);
}

.granted-type-phases-row > td {
    background: var(--surface-alt);
}

/* NEW (2026-10-11) - leave grants on the employee calendar's booking form:
   the Grant drop-down under the absence type, and the preview's split by pay
   phase. */
.booking-grant-row {
    margin: 0.4rem 0 0.6rem;
}

.booking-grant-row .absence-booking-warning {
    margin: 0;
}

.calendar-toolbar-field.booking-grant-field select {
    max-width: 100%;
    width: 560px;
}

.booking-grant-parts {
    margin: 0.15rem 0 0.25rem 1rem;
}

/* Text, number and note inputs on the Leave grants form and the pay phase
   editors, matching the date inputs beside them. */
.leave-grants-form input[type="text"],
.leave-grants-form textarea,
.leave-grants-phases input[type="text"],
.leave-grants-phases input[type="number"] {
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    font: inherit;
    background: var(--surface);
    color: inherit;
}

.leave-grants-form__total .link-button {
    white-space: nowrap;
}

/* NEW (2026-10-12) - MultiSelectDropdown LabelAbove: the title above, the
   box indented from it and showing only the selection (HR > Leave grants). */
.multiselect-dropdown--label-above {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.multiselect-field-label {
    font-size: 0.85rem;
    color: var(--text-muted);
}

.multiselect-dropdown--label-above .multiselect-trigger {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-left: 0.4rem;
    padding: 0.4rem 0.5rem;
    min-width: 0;
    width: 150px;
}

.multiselect-dropdown--label-above .multiselect-trigger::after {
    content: "\25BE";
    font-size: 0.8rem;
    color: var(--text-muted);
}

.multiselect-dropdown--label-above .multiselect-trigger-value {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* NEW (2026-10-12) - HR > Leave grants: column widths (set on each <th>),
   short values kept on one line, the Actions icons, the add / edit popup. */
.leave-grants-table th.sortable-header {
    white-space: nowrap;
}

/* CHANGED (2026-10-12, later) - Karl: "make the width fixed so they do not
   jump around when sort options are changed". Fixed layout: the <th> widths
   decide the columns, whatever is in them. Short values stay on one line;
   names and For end in "..." when too long (the whole text in a tooltip);
   Absence type wraps. */
.leave-grants-table {
    table-layout: fixed;
    width: 100%;
}

.leave-grants-table td:nth-child(-n+4),
.leave-grants-table td:nth-child(n+7),
.leave-grants-table td.leave-grants-actions {
    white-space: nowrap;
}

.leave-grants-table td.leave-grants-clip {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.leave-grants-table .icon-button {
    margin-right: 0;
}

/* Tighter cells and slightly smaller text than the default .data-table, so
   the columns fit across the page. */
.leave-grants-table {
    font-size: 0.9rem;
}

.leave-grants-table th,
.leave-grants-table td {
    padding: 0.45rem 0.45rem;
}

.leave-grant-popover .save-button {
    white-space: nowrap;
}

.icon-button.icon-button--danger:hover:not(:disabled),
.icon-button.icon-button--danger:focus-visible {
    color: var(--danger);
}

.leave-grants-confirm {
    padding: 0.2rem 0.5rem;
    font-size: 0.85rem;
}

.leave-grant-popover {
    width: min(54rem, 95vw);
}

.leave-grants-form--popup {
    margin-top: 0.75rem;
    padding: 0;
    border: none;
}

/* NEW (2026-10-12, later) - the add / edit popup's fields: three rows
   (Employee, Absence type, Reference date / From, To, For / Notes), titles
   above and inputs indented 0.4rem from them, as the other pages. */
.leave-grant-fields {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

.leave-grant-fields__row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;   /* titles level, whatever the input heights */
    gap: 0.75rem 1rem;
}

.leave-grant-field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.leave-grants-form .leave-grant-field > input,
.leave-grants-form .leave-grant-field > select,
.leave-grants-form .leave-grant-field > textarea {
    font: inherit;
    font-size: 0.95rem;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-left: 0.4rem;
    background-color: var(--surface);
    box-sizing: border-box;
}

.leave-grant-field--employee > select {
    width: 250px;
}

/* CHANGED (2026-10-12, later) - Absence type takes the rest of row 1, so
   Reference date's right edge lines up with For's and Notes' (Karl). */
.leave-grant-field--type {
    flex: 1 1 220px;
}

.leave-grant-field--type > select {
    width: calc(100% - 0.4rem);
}

.leave-grant-field--date > input {
    width: 155px;
}

.leave-grant-field--for {
    flex: 1 1 200px;
}

.leave-grant-field--for > input {
    width: calc(100% - 0.4rem);
}

.leave-grant-field--notes {
    flex: 1 1 100%;
}

.leave-grant-field--notes > textarea {
    width: calc(100% - 0.4rem);
    resize: vertical;
}

/* NEW (2026-10-12) - leave grants: each pay phase's pay basis (% of normal
   pay / statutory rate / unpaid) in the grant popup and the settings phase
   editor, and the Statutory pay rates table on Settings > Annual leave and
   absences. */
.leave-grants-phases__pay {
    white-space: nowrap;
}

.leave-grants-phases__pay select {
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    font: inherit;
    background: var(--surface);
    color: inherit;
    width: 185px;
}

.leave-grants-phases input.leave-grants-phases__percent {
    width: 68px;
    margin-left: 0.4rem;
}

.leave-grants-phases input.leave-grants-phases__percent.is-invalid {
    border-color: var(--danger, #dc2626);
}

.leave-grants-phases__percent-sign {
    margin-left: 0.25rem;
}

.statutory-rates__table input[type="date"],
.statutory-rates__table input[type="number"],
.statutory-rates__table input[type="text"] {
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    font: inherit;
    background: var(--surface);
    color: inherit;
}

.statutory-rates__table input[type="number"] {
    width: 110px;
}

.statutory-rates__table input[type="text"] {
    width: min(420px, 100%);
}

.statutory-rates__current {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 6px;
    font-size: 0.8rem;
    background: color-mix(in srgb, var(--primary) 12%, transparent);
    color: var(--primary);
}

/* NEW (2026-10-12, later) - the grant popup's pay phases (Karl): fixed
   column widths, so nothing moves when the absence type (and with it the
   phases and Weeks / Days columns) changes.
   CHANGED (2026-10-12, later again) - Karl: "put Pay phase, Days, Pay,
   Percentage, and Remove in a table instead, it does not need to use the
   whole width, nor really have an outer border, perhaps just an inner one.
   If the '+ Add a phase' text could be right-aligned with the edge of the
   table please." So: a table of its own width with inner lines only, in a
   wrapper as wide as the table, so the Total granted / + Add a phase line
   below runs from the table's left edge to its right edge. */
.leave-grants-phases-wrap {
    display: inline-block;
    max-width: 100%;
    vertical-align: top;
}

.leave-grants-phases.leave-grants-phases--popup {
    table-layout: fixed;
    width: 746px;   /* the columns below added up */
    max-width: 100%;
    border-collapse: collapse;
    margin: 0.3rem 0 0.4rem;
}

.leave-grants-phases--popup .leave-grants-phases__col-label {
    width: 212px;
}

.leave-grants-phases--popup .leave-grants-phases__col-num {
    width: 76px;
}

.leave-grants-phases--popup .leave-grants-phases__col-pay {
    width: 190px;
}

.leave-grants-phases--popup .leave-grants-phases__col-percent {
    width: 116px;
}

.leave-grants-phases--popup .leave-grants-phases__col-remove {
    width: 76px;
}

/* Inner lines only: every cell gets a line, then the ones on the table's
   outside edges are taken off. */
.leave-grants-phases--popup th,
.leave-grants-phases--popup td {
    border: 1px solid var(--border);
    padding: 0.3rem 0.45rem;
}

.leave-grants-phases--popup th {
    padding-top: 0.2rem;
    padding-bottom: 0.35rem;
    white-space: nowrap;
}

.leave-grants-phases--popup thead tr:first-child > th {
    border-top: none;
}

.leave-grants-phases--popup tbody tr:last-child > td {
    border-bottom: none;
}

.leave-grants-phases--popup tr > :first-child {
    border-left: none;
}

.leave-grants-phases--popup tr > :last-child {
    border-right: none;
}

.leave-grants-phases--popup .leave-grants-phases__label,
.leave-grants-phases--popup input[type="number"],
.leave-grants-phases--popup .leave-grants-phases__pay select {
    width: 100%;
}

.leave-grants-phases--popup input.leave-grants-phases__percent {
    margin-left: 0;
}

.leave-grants-phases--popup td.leave-grants-phases__remove {
    text-align: center;
}

.leave-grants-form__total.leave-grants-form__total--popup {
    justify-content: space-between;
    margin: 0.35rem 0 1.4rem;
}

/* NEW (2026-10-12, later again) - Karl: "Are you able to remove the inner
   input boxes so the whole TD becomes the input?" The popup's pay phase
   cells hold their input / drop-down edge to edge with no box of its own;
   the table's inner lines are the only lines. Hover shades the cell, focus
   draws the primary colour round the inside of it, and a percentage out of
   range shows red the same way. */
.leave-grants-phases.leave-grants-phases--popup td {
    padding: 0;
    height: 2.4rem;
}

.leave-grants-phases.leave-grants-phases--popup td.leave-grants-phases__remove {
    padding: 0 0.45rem;
}

.leave-grants-phases.leave-grants-phases--popup td input,
.leave-grants-phases.leave-grants-phases--popup td select {
    display: block;
    width: 100%;
    height: 2.4rem;
    margin: 0;
    padding: 0 0.45rem;   /* text level with the headings */
    border: none;
    border-radius: 0;
    background-color: transparent;
    font: inherit;
    color: inherit;
}

.leave-grants-phases.leave-grants-phases--popup td input:hover,
.leave-grants-phases.leave-grants-phases--popup td select:hover {
    background-color: color-mix(in srgb, var(--primary) 6%, transparent);
}

.leave-grants-phases.leave-grants-phases--popup td input:focus,
.leave-grants-phases.leave-grants-phases--popup td select:focus {
    outline: 2px solid var(--primary);
    outline-offset: -2px;
}

.leave-grants-phases.leave-grants-phases--popup td input.is-invalid {
    outline: 2px solid var(--danger);
    outline-offset: -2px;
}

/* NEW (2026-10-12, later still) - Karl: "Can you please right-align the
   '+ Add a phase' text to the [right edge] of the Reference date, For, and
   Notes boxes above ... keep the Weeks column on the Absence types which are
   based on the number of days, place a N/A in the field. Can you try
   removing all borders and perhaps making the rows slightly shorter."
   - The wrapper is a block again, so the Total granted / + Add a phase line
     spans the form: Total granted at the left, + Add a phase ending at the
     right edge of the boxes above. The table keeps its own width.
   - No lines at all on the pay phase table.
   - Rows 2.1rem (were 2.4rem).
   - Scheduled-day types: Weeks shows "N/A" (muted, not editable). */
.leave-grants-phases-wrap {
    display: block;
}

.leave-grants-phases.leave-grants-phases--popup th,
.leave-grants-phases.leave-grants-phases--popup td {
    border: none;
}

.leave-grants-phases.leave-grants-phases--popup td {
    height: 2.1rem;
}

.leave-grants-phases.leave-grants-phases--popup td input,
.leave-grants-phases.leave-grants-phases--popup td select {
    height: 2.1rem;
}

.leave-grants-phases.leave-grants-phases--popup td.leave-grants-phases__na {
    padding: 0 0.45rem;
    color: var(--text-muted);
    cursor: default;
}

/* NEW (2026-10-12, later still) - Karl: "make sure the Remove text is also
   right-aligned with the right edge of the inputs above." The table runs
   the width of the form again; every column keeps its fixed width except
   Remove's, which takes what's left, with Remove at its right-hand end
   (level with "+ Add a phase" and the Reference date / For / Notes boxes). */
.leave-grants-phases.leave-grants-phases--popup {
    width: 100%;
}

.leave-grants-phases--popup col.leave-grants-phases__col-remove {
    width: auto;
}

.leave-grants-phases.leave-grants-phases--popup td.leave-grants-phases__remove {
    padding: 0;
    text-align: right;
}

/* NEW (2026-10-12, later still) - Karl: "add a gap in between the Pay and
   Percentage columns. the Percentage input boxes do not need to be overly
   wide. Only a maximum of 3 digits ... add a % character after the
   Percentage input." A 1rem gap before the column (its heading moves with
   it), a box for 3 digits, then "%". */
.leave-grants-phases--popup .leave-grants-phases__col-percent {
    width: 124px;
}

.leave-grants-phases.leave-grants-phases--popup td.leave-grants-phases__percent-cell {
    padding-left: 1rem;
    white-space: nowrap;
}

.leave-grants-phases.leave-grants-phases--popup th.leave-grants-phases__percent-cell {
    padding-left: calc(1rem + 0.45rem);
}

.leave-grants-phases.leave-grants-phases--popup td input.leave-grants-phases__percent {
    display: inline-block;
    width: 4.3rem;   /* 3 digits plus the up / down buttons (back 2026-10-12, later - Karl) */
    vertical-align: middle;
}

.leave-grants-phases--popup .leave-grants-phases__percent-sign {
    margin-left: 0.1rem;
    vertical-align: middle;
}

/* NEW (2026-10-12) - Karl: "increase the width of all the sections on the
   /settings/company/annual-leave-absences page. With the extra options,
   everything is very squashed. I believe it is currently around 1120px but
   needs to be at least 1500px." Only this page - the other settings pages
   keep .variable-section's 1120px. The absence type tables' column widths
   were widened to match (AbsenceTypesTable.razor). */
.annual-leave-absences-page .variable-section {
    max-width: 1500px;
}

/* NEW (2026-10-12) - Karl: "make the left-hand navigation menu slightly
   wider, but also collapsible or pin-able for those using smaller
   resolution displays." (js/nav-pin.js, App.razor, NavMenu.razor.)
   Pinned (the default on screens 1400px and wider): the menu sits beside
   the page as before, 250px wide, with a « button to collapse it.
   Collapsed (html.nav-unpinned): a 48px strip with a menu button; hovering
   over the strip, or tapping the button, slides the whole menu out over
   the page (the page doesn't move), with a » button to pin it open again.
   The choice is remembered in the browser. */
.nav-menu .nav-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.nav-header__title {
    flex: 1 1 auto;
}

.nav-pin-button {
    display: none;
    align-items: center;
    justify-content: center;
    padding: 0.25rem;
    border: none;
    border-radius: 6px;
    background: none;
    color: #9aa7b2;
    cursor: pointer;
}

.nav-pin-button:hover,
.nav-pin-button:focus-visible {
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
}

html:not(.nav-unpinned) .nav-pin-button--collapse {
    display: inline-flex;
}

html.nav-unpinned .app-shell {
    padding-left: 48px;   /* the strip */
}

html.nav-unpinned .nav-menu {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: 900;
    transform: translateX(calc(-100% + 48px));
    transition: transform 0.18s ease, box-shadow 0.18s ease;
}

/* Out: hovered, focused into, or opened with the button. */
html.nav-unpinned .nav-menu:hover,
html.nav-unpinned .nav-menu:focus-within,
html.nav-unpinned.nav-open .nav-menu {
    transform: none;
    box-shadow: 4px 0 18px rgba(0, 0, 0, 0.25);
}

/* In: only the strip's menu button shows - everything else is hidden
   rather than left half off the screen. */
html.nav-unpinned:not(.nav-open) .nav-menu:not(:hover):not(:focus-within) > *:not(.nav-header),
html.nav-unpinned:not(.nav-open) .nav-menu:not(:hover):not(:focus-within) .nav-header__title {
    visibility: hidden;
}

html.nav-unpinned:not(.nav-open) .nav-menu:not(:hover):not(:focus-within) .nav-pin-button--open {
    display: inline-flex;
    margin-right: -0.45rem;   /* centred in the 48px strip */
}

/* Out: the » button to pin it open again. */
html.nav-unpinned .nav-menu:hover .nav-pin-button--pin,
html.nav-unpinned .nav-menu:focus-within .nav-pin-button--pin,
html.nav-unpinned.nav-open .nav-menu .nav-pin-button--pin {
    display: inline-flex;
}

@media (prefers-reduced-motion: reduce) {
    html.nav-unpinned .nav-menu {
        transition: none;
    }
}

/* NEW (2026-10-12, later) - Karl: "can you add the up down buttons to the
   percentage inputs. Can you try adding in grey horizontal lines
   underneath the titles, between the rows, and under the last row." Grey
   lines across the popup's pay phase table only - no upright lines. */
.leave-grants-phases.leave-grants-phases--popup th,
.leave-grants-phases.leave-grants-phases--popup td {
    border-bottom: 1px solid var(--border);
}

.leave-grants-phases.leave-grants-phases--popup tbody tr:last-child > td {
    border-bottom: 1px solid var(--border);   /* and under the last row */
}

/* NEW (2026-10-12, later) - Annual leave and absences: column titles on one
   line (Karl), a little smaller with tighter side padding so the setting
   columns stay narrow and Absence type gets the room; their tooltips are
   the headings' own hover titles now (no "?" icons). */
.absence-types-table thead th {
    white-space: nowrap;
}

.absence-types-table thead th[title] {
    cursor: help;
}

/* NEW (2026-10-12, later again) - the absence type tables' headings back at
   their normal size, grouped (Karl): "Allowance" over Tracked / Default,
   "Permissions" over Employees / Managers, in a first heading row; every
   other heading spans both rows, sitting at the bottom with its column's
   sub-headings. */
.absence-types-table thead th {
    vertical-align: bottom;
}

.absence-types-table thead th.absence-types-group {
    text-align: center;
    padding-bottom: 0.2rem;
    border-bottom: 1px solid var(--border);
}

/* NEW (2026-10-12, later still) - Karl: "Can you try centring all fields and
   titles, and making the titles and individual rows shorter." Every heading
   and cell centred except the Absence type column; less padding top and
   bottom. Child combinators only, so the pay phase editor nested in a
   granted type's row is left as it is. */
.absence-types-table > thead > tr > th,
.absence-types-table > tbody > tr > td {
    text-align: center;
}

.absence-types-table > thead > tr:first-child > th:first-child,
.absence-types-table > tbody > tr > td:first-child {
    text-align: left;
}

.absence-types-table > thead > tr > th {
    padding-top: 0.3rem;
    padding-bottom: 0.3rem;
}

.absence-types-table > tbody > tr > td {
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
}

.absence-types-table > tbody > tr > td > .allowance-rounding-segmented {
    margin-left: auto;
    margin-right: auto;   /* a grid box, so text-align alone doesn't centre it */
}

/* NEW (2026-10-12, later still) - Karl: "make the days input field take up
   the whole TD, like on the leave-grants popup." The Default allowance cell
   is the input: no box of its own, edge to edge, the number centred and
   "days" inside the cell on the right. Hover shades the cell, focus draws
   the primary colour round the inside of it; greyed out as before when it
   doesn't apply. */
.absence-types-table > tbody > tr > td.absence-types-days-cell {
    position: relative;
    padding: 0;
}

.absence-types-table td.absence-types-days-cell {
    white-space: nowrap;
}

/* The number box runs from the cell's left edge up to "days", so its
   up / down buttons sit just before the word rather than over it. */
.absence-types-table td.absence-types-days-cell input[type="number"] {
    display: inline-block;
    vertical-align: middle;
    width: calc(100% - 3.1rem);
    height: 2.4rem;
    margin: 0;
    padding: 0 0.3rem 0 0.75rem;
    border: none;
    border-radius: 0;
    background: transparent;
    font: inherit;
    color: inherit;
    text-align: center;
}

.absence-types-table td.absence-types-days-cell .variable-units {
    display: inline-block;
    vertical-align: middle;
    width: 3.1rem;
    margin: 0;
    padding-left: 0.2rem;
    text-align: left;
    pointer-events: none;
}

.absence-types-table td.absence-types-days-cell:hover:not(:has(input:disabled)) {
    background: color-mix(in srgb, var(--primary) 6%, transparent);
}

.absence-types-table td.absence-types-days-cell:focus-within {
    box-shadow: inset 0 0 0 2px var(--primary);
}

.absence-types-table td.absence-types-days-cell input[type="number"]:focus {
    outline: none;
}

/* NEW (2026-10-12, later still) - Karl: "split the column group horizontal
   line so Allowance and Permissions are clearly separate ... They only need
   to cover the column text for the columns that belong to those two
   groups." The group heading's line is drawn inset, from the start of its
   first sub-heading's text to the end of its last's (the insets are set on
   the heading in AbsenceTypesTable.razor). */
.absence-types-table thead th.absence-types-group {
    position: relative;
    border-bottom: none;
}

.absence-types-table thead th.absence-types-group::after {
    content: "";
    position: absolute;
    left: var(--group-line-left, 0.8rem);
    right: var(--group-line-right, 0.8rem);
    bottom: 0;
    border-bottom: 1px solid var(--border);
}

/* NEW (2026-10-13) - Company settings > Payroll (PayrollSettings.razor), HR >
   Payroll extract (PayrollExtract.razor), the employee's pay type
   (EmployeePayTypesPanel.razor) and KIT days in the leave grant popup. */
.payroll-settings-page .variable-section {
    max-width: 1500px;
}

.editable-table input[type="date"] {
    font: inherit;
    width: 100%;
    padding: 0.3rem 0.4rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface);
    color: inherit;
}

.payroll-elements-table td,
.payroll-periods-table td,
.payroll-kit-table td {
    vertical-align: middle;
}

.payroll-elements-table .multiselect-dropdown,
.payroll-elements-table .multiselect-trigger {
    width: 100%;
}

.payroll-code-input,
.payroll-code {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    text-transform: uppercase;
}

.payroll-na {
    color: var(--text-muted);
}

.payroll-elements-table tr.is-inactive td,
.payroll-periods-table tr.is-inactive td {
    color: var(--text-muted);
}

.payroll-kit-explanation {
    color: var(--text-muted);
    font-size: 0.9rem;
}

/* Payroll extract */
.payroll-extract-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem 1rem;
    margin-bottom: 1rem;
}

.payroll-extract-filters .leave-grant-field > select,
.payroll-extract-filters .leave-grant-field > input {
    font: inherit;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-left: 0.4rem;
    background: var(--surface);
    color: inherit;
}

.payroll-extract-filters .leave-grant-field > select {
    min-width: 230px;
}

.payroll-extract-filters__actions {
    padding-bottom: 0.1rem;
}

.payroll-extract-note {
    margin-top: -0.4rem;
}

.payroll-extract-heading {
    display: flex;
    align-items: baseline;
    gap: 1rem;
    margin: 1.25rem 0 0.5rem;
    font-size: 1.15rem;
}

.payroll-extract-heading .subtitle {
    font-size: 0.9rem;
    font-weight: normal;
}

.payroll-extract-warnings {
    margin: 0.5rem 0 0.75rem;
    padding: 0.6rem 0.9rem;
    border: 1px solid color-mix(in srgb, #d97706 45%, transparent);
    border-radius: 6px;
    background: color-mix(in srgb, #f59e0b 10%, transparent);
}

.payroll-extract-warnings ul {
    margin: 0.3rem 0 0 1.1rem;
    padding: 0;
}

.payroll-extract-table th,
.payroll-extract-table td {
    padding: 0.35rem 0.6rem;
    white-space: nowrap;
}

.payroll-extract-table .num,
.payroll-extract-runs .num {
    text-align: right;
}

.payroll-extract-preview {
    max-height: 60vh;
    overflow: auto;
}

.payroll-extract-download {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem 1.5rem;
    margin: 1rem 0 0.5rem;
}

.payroll-extract-record {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.payroll-extract-notes > input {
    font: inherit;
    width: 280px;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-left: 0.4rem;
}

/* Employee pay type */
.employee-pay-types {
    margin-top: 1.25rem;
    max-width: 900px;
}

.employee-pay-types__actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 0.4rem;
}

.employee-pay-types__table tr.is-current td {
    background: color-mix(in srgb, var(--primary) 6%, transparent);
}

/* KIT days in the leave grant popup */
.leave-grants-kit {
    margin: 0.4rem 0 0.8rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--border);
}

.leave-grants-kit__header {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    flex-wrap: wrap;
}

.leave-grants-kit__header h3 {
    margin: 0;
    font-size: 1rem;
}

.leave-grants-kit__allowed {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.leave-grants-kit__allowed input {
    width: 4.5rem;
    padding: 0.3rem 0.4rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    font: inherit;
}

.leave-grants-kit__count {
    color: var(--text-muted);
}

.leave-grants-kit__count.is-over {
    color: var(--danger);
    font-weight: 600;
}

.leave-grants-kit__warning {
    margin: 0;
    color: var(--danger);
}

.leave-grants-kit .leave-grants-form__total--popup .subtitle {
    margin: 0;
}

.payroll-periods-table td,
.payroll-extract-runs td:not(:nth-last-child(-n+2)) {
    white-space: nowrap;
}

/* NEW (2026-10-13) - Annual leave and absences > Family and extended leave
   tidied up (Karl: "perhaps using a style similar to the absence sections
   above. The rows could perhaps be reduced in height and the Calendar/
   Scheduled days selection turned into an icon to save space? I think the
   phases may need static icons and a popup to edit them.")
   - Counted in: a one-segment icon control (calendar / clock), the same box
     and blue as the other icon controls.
   - Default length (Weeks, Days) and Window (Before, After): whole-cell
     inputs as the Default allowance cell above, without the unit word (the
     headings say it). Weeks shows N/A for scheduled-day types.
   - Pay phases: a joined bar of static chips - the pay in bold, then the
     length - coloured by pay basis (blue % of normal pay, green statutory,
     grey unpaid), each with a hover panel; the pencil opens the popup. */
.allowance-rounding-segmented.allowance-rounding-segmented--one {
    grid-template-columns: minmax(0, 1fr);
    width: 40px;
}

.absence-types-table > tbody > tr > td.absence-types-cell-input {
    padding: 0;
}

.absence-types-table td.absence-types-cell-input input[type="number"] {
    display: block;
    box-sizing: border-box;
    width: 100%;
    height: 2.4rem;
    margin: 0;
    padding: 0 0.3rem 0 0.75rem;
    border: none;
    border-radius: 0;
    background: transparent;
    font: inherit;
    color: inherit;
    text-align: center;
}

.absence-types-table td.absence-types-cell-input:hover {
    background: color-mix(in srgb, var(--primary) 6%, transparent);
}

.absence-types-table td.absence-types-cell-input:focus-within {
    box-shadow: inset 0 0 0 2px var(--primary);
}

.absence-types-table td.absence-types-cell-input input[type="number"]:focus {
    outline: none;
}

.absence-types-table td.absence-types-cell-na {
    color: var(--text-muted);
    cursor: default;
}

.absence-types-table > thead > tr > th.granted-type-phases-heading,
.absence-types-table > tbody > tr > td.granted-type-phases {
    text-align: left;
}

.granted-type-phases__cell {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.granted-type-phases__edit {
    margin-left: auto;
    flex: none;
}

.granted-phase-chips {
    display: inline-flex;
    height: 30.6px;
    box-sizing: border-box;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface);
}

.granted-phase-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0 0.55rem;
    font-size: 0.85rem;
    line-height: 1;
    white-space: nowrap;
    cursor: default;
}

.granted-phase-chip:first-child {
    border-top-left-radius: 3px;
    border-bottom-left-radius: 3px;
}

.granted-phase-chip:last-child {
    border-top-right-radius: 3px;
    border-bottom-right-radius: 3px;
}

.granted-phase-chip__pay {
    font-weight: 700;
}

.granted-phase-chip.is-percent {
    color: var(--primary);
    background: color-mix(in srgb, var(--primary) 18%, var(--surface));
}

.granted-phase-chip.is-statutory {
    color: var(--success);
    background: color-mix(in srgb, var(--success) 16%, var(--surface));
}

.granted-phase-chip.is-unpaid {
    color: var(--text-muted);
    background: color-mix(in srgb, var(--text-muted) 12%, var(--surface));
}

.granted-type-phases-popover__hint {
    margin-right: auto;
}
