﻿/* ============================================================================
   HotelTRS workdays board

   The staff-rostering screen: a drag-and-drop board of department columns with
   a worker panel down the left, plus the card that represents one worker in
   either zone.

   Three of these rules were reaching across component boundaries. A <style>
   block in a .razor file is only in the DOM while that component is rendered,
   so:

     - .picker-class was defined in WorkdaysIndex and used by AddWorkHoursModal
       and EditWorkdayModal;
     - .wwc-role-badge and the .role-badge-* set were defined in
       WorkerWorkdayCard and used by EditWorkdayModal;
     - .hover-transform was defined in WorkdaysIndex and used by
       WorkerWorkdayCard.

   Each of those happened to work only because the defining component was on
   screen at the time. In a stylesheet they simply apply.

   Every class is prefixed wd- because the originals were named for the page
   they lived in, not for the app: .main-container, .side-container,
   .location-item and .toggler are fine inside one <style> block and are
   landmines in a global sheet. The bare .nav-tabs override is the same
   problem in reverse - it was restyling every tab strip in the application
   for as long as this page stayed mounted.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   BOARD LAYOUT

   This section used to carry three `calc(100vh - N)` offsets - 80 for the board,
   530 for the workers zone, 245 for a department card - each one a hand-measured
   guess at the height of everything above the thing it sized. All three were
   wrong: the app chrome had changed underneath them, so the board ran 16px past
   the content area and the pagination pinned to its bottom edge sat behind the
   footer.

   None of them are needed. `main` computes the leftover height already, so the
   board inherits it and each box below takes 100% of the one it is actually in.
   The only remaining constants are the two below, and both are the size of a
   thing rather than a guess about the space around it.
   ---------------------------------------------------------------------------- */

:root {
    /* The pager is one row of page buttons. It measured 49px with Bootstrap's
       stock .page-link padding and a 1rem font; the compact rules at the bottom
       of the BOARD LAYOUT section take it to ~26px. The reserve is that, plus the
       gap on BOTH sides of it - the pager is absolutely positioned, so the space
       above it is not a margin it can set, it is room the scrolling list has to
       stop short of. 8 + 26 + 8. See the .wd-side / .wd-department-list rule. */
    --wd-pagination-height: 42px;

    /* Pinned at bottom:0 the pager's edge met the app footer's at exactly the
       same y (MEASURED: both 1224.67), so the digits sat directly on the EU
       funding strip with nothing between them. The same 8px goes above it, out of
   the scroll reserve. */
    --wd-pagination-gap: 8px;

    /* The side panel is a fixed rail, not a column that measures its contents -
       see .wd-side. It was 280px, sized to the date/time picker groups, which
       were the widest fixed thing in it; the worker cards are now the widest
       thing instead, and at the old 280 a two-word name wrapped to a second
       line. 320 fixed the wrap but overshot, and 300 was still wide for what a
       card holds. 280 works now only because the density pass below shrank the
       card's padding and the avatar - the name has more room inside the same
       width than it did the first time round. */
    --wd-side-width: 280px;

    /* A department column. Same story: 275px was sized around a 215px card and
       left a dead margin either side of the well. Both columns are the same
       width now so the two lists read as one board rather than two. */
    --wd-department-width: 280px;
}

/* THE HEIGHT IS INHERITED, NOT CALCULATED.

   This was `height: calc(100vh - 80px)`, and 80 was the app chrome as it stood
   when the rule was written. The chrome has changed since - the header is 56 and
   the footer 29, the page adds 12 of padding above the board - so the board was
   16px taller than the space it had. It does not overflow visibly, because the
   thing at its bottom edge is the pagination, pinned there with `bottom: 0`: the
   pager simply sat BEHIND the footer. MEASURED at 1000px tall: content area ends
   at 971, pager ran 939-988.

   Any constant here is wrong the moment the chrome moves again - the same lesson
   the datagrid reserve constants taught. `main` already computes the leftover
   height for us, so the board takes 100% of it and the number disappears. */
.content:has(.wd-board) {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
}

/* The unnamed page markup between .content and the board, whatever it is. */
.content:has(.wd-board) *:has(.wd-board) {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.wd-board {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

/* Same story, worse constant: `max-height: calc(100vh - 530px)`, where 530 was
   the measured height of everything stacked above the zone in the side panel -
   date pickers, the Workers heading, search, tabs and the pager. Every one of
   those can change. The zone takes what is left of the column instead. */
.wd-available-zone {
    display: flex;
    flex-direction: column;
    /* STRETCH, not center. Centering was here to place a fixed 215px card in the
       middle of the rail, and it silently broke the card once the card started
       sizing itself: Blazorise wraps each item in .b-drop-zone-draggable, that
       wrapper is the flex item, and align-items:center shrink-wraps it to its
       content - so the card's width:100% resolved against a box that was itself
       measuring the card. It collapsed to 135px, NARROWER than the 215 it
       replaced. MEASURED: zone 287, wrapper 135, card 135. */
    align-items: stretch;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* Was Bootstrap's .bg-light, which dark.css flattens onto --trs-surface -
       the same colour as the cards inside it. See .wd-department-zone. */
    background: var(--trs-surface-sunken);
    border: 1px solid var(--trs-border);
    /* The DropZone carried Border="Border.Rounded" - Bootstrap's .rounded, which
       is 11px and !important, so the class could not have set this. The attribute
       is gone from the markup and the shape lives here with the rest. */
    border-radius: var(--trs-radius-lg);
    /* Chromium gets no standard scrollbar property - see .trs-nav-menu in
       layout.css. Since Chromium 121, a non-initial `scrollbar-width` or
       `scrollbar-color` switches the element to standard rendering and the
       ::-webkit-scrollbar rules below are ignored outright. `auto` here is a
       RESET of the scrollbar-color body passes down from base.css, not a style,
       and it is what lets the 6px bar apply. */
    scrollbar-color: auto;
}

/* The app-wide bar is 14px. Down a 280px rail that is 5% of the panel spent on
   a scrollbar, and with the worker cards centred in the zone it pushed them
   visibly off-centre. 6px is enough to grab and leaves the width to the cards. */
.wd-available-zone::-webkit-scrollbar {
    width: 6px;
}

.wd-available-zone::-webkit-scrollbar-track {
    background: transparent;
}

.wd-available-zone::-webkit-scrollbar-thumb {
    border: none;
    border-radius: var(--trs-radius-full);
    background-color: var(--trs-border-strong);
}

.wd-available-zone::-webkit-scrollbar-thumb:hover {
    background-color: var(--trs-fill);
}

/* Firefox has no ::-webkit-scrollbar, so it needs the standard property. A real
   feature test, not a sniff: Chromium answers `selector(::-webkit-scrollbar)`
   true, Firefox false. */
@supports not selector(::-webkit-scrollbar) {
    .wd-available-zone {
        scrollbar-width: thin;
    }
}

/* A FIXED RAIL. It had no width of its own, so it took whatever .wd-loading
   measured - and .wd-loading was `width: min-content`, i.e. as wide as the widest
   thing inside it. With workers in the list that is a worker card plus its
   scrollbar (248px); with none it collapses to the tab strip (233px). So the
   panel, the zone and the border between the two columns all JUMPED 15px the
   moment the list emptied, or the last worker was dragged out, or you switched to
   a tab that happened to be empty. A rail that moves while you drag things across
   it is the worst possible place for that. */
.wd-side {
    position: relative;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    flex: 0 0 var(--wd-side-width);
    width: var(--wd-side-width);
    height: 100%;
    border-right: 1px solid var(--trs-border);
    box-shadow: 4px 0 8px -2px rgba(60, 65, 60, .2);
}

/* The rail is the column the zone takes the remainder of, and the chain between
   them - a LoadingIndicator wrapper, the Tabs component's own tab-content and
   tab-pane - is markup this page does not name, so :has() selects it. */
.wd-side *:has(.wd-available-zone) {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.wd-departments {
    display: flex;
    flex-direction: column;
    flex: 1;
    box-sizing: border-box;
    height: 100%;
    overflow: auto;
}

/* SHAPE. This and .wd-department were both var(--trs-radius-sm) - 6px - which
   is the token reserved for things 11px would swallow: grid cells, chips,
   checkboxes. A full-width toolbar and a 275px column card are neither, and at
   6px they read as square next to everything around them. MEASURED on the page
   before this change: the drop zone 11px, a worker card 16px, the tab strip
   11px, these two 6px, with no rule behind the difference.

   Both take --trs-radius-lg, the same shape as .trs-card and as the Blazorise
   theme's own .card - so a department column looks like the card it is, and the
   worker cards inside it are no longer rounder than the box holding them. */
/* 71px tall for a search field and four icon buttons, because the markup asks
   for p-3 on all four sides. Trimmed on the block axis only - the horizontal
   16px is what keeps the search field off the rounded corner. The !important is
   Bootstrap's: p-3 is a utility, so a plain declaration cannot beat it. */
.wd-toolbar {
    padding-block: var(--trs-space-2) !important;
    background: var(--trs-surface);
    border: 1px solid var(--trs-border);
    border-radius: var(--trs-radius-lg);
}

.wd-department-list {
    position: relative;
    display: flex;
    flex: 1;
    box-sizing: border-box;
    gap: var(--trs-space-2);
    height: 100%;
    overflow: auto;
}

.wd-department {
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: var(--wd-department-width);
    min-width: var(--wd-department-width);
    max-width: var(--wd-department-width);
    height: fit-content;
    /* The third viewport constant on this page, and the last. 245 was the chrome
       plus the toolbar above the row; the card only ever needed to fit the row it
       is in, which is a flex item with a definite height, so 100% resolves against
       exactly the right box - and the row already reserves the pager's height. */
    max-height: 100%;
    background: var(--trs-surface);
    border: 1px solid var(--trs-border);
    border-radius: var(--trs-radius-lg);
}

/* THE WELL INSIDE THE CARD, and the reason the department columns had no depth.

   THREE IDENTICAL LAYERS were stacked here. The column carried Bootstrap's
   .bg-white, this zone set no background so it inherited that white, and the
   worker cards inside it are .card - white again. A card on a card on a card,
   #FFFFFF throughout, separated only by a 1px border and a small shadow.

   Dark mode was worse, and for a different reason: dark.css maps BOTH .bg-light
   and .bg-white onto var(--trs-surface) with !important, so the workers zone,
   the department column and every card in either became one flat colour with
   nothing to override it from here.

   The fix is the ladder the token layer already describes - page ground,
   surface, sunken - and all three flip with the theme, which is exactly what
   the Bootstrap background classes could not do. The classes are gone from the
   markup so dark.css has nothing left to match:

     --trs-bg              the board behind everything
     --trs-surface         the toolbar, the department column, a worker card
     --trs-surface-sunken  the wells those cards sit in (this rule, and
                           .wd-available-zone)

   Radius nests rather than repeats: the column is 16px with 8px of padding, so
   the well inside it takes 11px. */
.wd-department-zone {
    /* Was align-self: center, so this well shrank to the width of the 215px
       card inside it and floated in the middle of a 275px column with ~26px of
       dead card either side. Stretching it makes the well the column, which is
       also what gives the cards room to widen. */
    align-self: stretch;
    margin-bottom: var(--trs-space-3);
    background: var(--trs-surface-sunken);
    border-top: 1px solid var(--trs-border);
    border-bottom: 1px solid var(--trs-border);
    border-radius: var(--trs-radius-md);
}

/* The per-department gear menu, pinned to the column's top-right corner and
   pulled out over the border. */
.wd-department-menu {
    top: -14px;
    right: -16px;
}

/* Absolute on purpose: in the departments column the pager's parent is a
   HORIZONTALLY scrolling row of department cards, so a flow item would line up
   beside the last department instead of under it. Both parents are positioned. */
.wd-pagination {
    position: absolute;
    bottom: var(--wd-pagination-gap);
    right: 50%;
    transform: translate(50%, 0);
}

/* ...which means it floats over whatever ends at the same edge. Both lists scroll
   under it, so the last worker card and the last department row were reachable
   only by scrolling them beneath the pager. The two containers reserve its height
   instead. Padding rather than a bottom offset on the pager: the space has to come
   out of the SCROLLABLE area, not sit under it. */
.wd-side,
.wd-department-list {
    padding-bottom: var(--wd-pagination-height);
}

/* A COMPACT PAGER. The shared PaginationComponent renders at Bootstrap's stock
   size - a 1rem font in .375rem/.75rem of padding, inside the component's own
   p-2 - which is 52px of vertical chrome for one row of digits. On this page the
   pager appears TWICE, once per column, and both are reserved out of a scroll
   area, so the stock size costs the board 104px of list.

   Scoped to .wd-pagination rather than fixed in the shared component: every
   other pager in the app sits under a datagrid where the stock size is fine.
   The !important is Bootstrap's - `p-2` is a utility, so a plain declaration
   cannot beat it however specific the selector.

   Geometry only: the joined-pill shape, the borders and every colour stay
   Bootstrap's, so this pager still reads as the same control as the one under
   every datagrid - it is just not 52px tall. */
.wd-pagination.pagination {
    padding: 0 !important;
}

.wd-pagination .page-link {
    min-width: 24px;
    padding: .2rem .4rem;
    font-size: var(--trs-text-sm);
    line-height: 1.35;
    text-align: center;
}

/* ----------------------------------------------------------------------------
   CONTROLS

   The three width rules were inline styles. The pickers sit inside an Addons
   group between two arrow buttons, so the width has to be fixed or the group
   reflows every time the value changes length.
   ---------------------------------------------------------------------------- */

.wd-picker {
    width: 150px !important;
}

.wd-search {
    width: fit-content !important;
}

/* Was `width: min-content` - the cause of the jump above. It fills the rail now,
   so the workers list is the same width whatever is in it. */
.wd-loading {
    width: 100%;
}

/* Scoped so it stops restyling every other tab strip in the app.
   Blazorise puts Tabs' Class straight onto the <ul class="nav nav-tabs">, not a
   wrapper - so this has to match the element itself, not a descendant. Written
   as a descendant first, which silently matched nothing; caught in the browser. */
/* HALF THE RAIL EACH. `width: max-content` sized the strip to its two labels,
   so Unassigned and Assigned were different widths (the words differ, and only
   one of them ever carries a count), the pair floated at the left edge of a
   280px rail, and the active-tab underline stopped short of the list it belongs
   to. Filling the rail and splitting it evenly makes the strip read as the
   header of the zone underneath it, and stops the boundary between the two tabs
   moving when a label changes. */
.wd-tabs.nav-tabs,
.wd-tabs .nav-tabs {
    width: 100%;
    flex-wrap: nowrap;
}

.wd-tabs.nav-tabs > .nav-item {
    flex: 1 1 50%;
    min-width: 0;
}

.wd-tabs.nav-tabs > .nav-item > .nav-link {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--trs-space-1);
    width: 100%;
    padding-inline: var(--trs-space-1);
    white-space: nowrap;
}

/* ----------------------------------------------------------------------------
   WORKER CARD
   ---------------------------------------------------------------------------- */

/* FILL THE ZONE, don't sit in the middle of it. The card carried a hard
   Width="Width.Px(215)" - an inline style, so no class could have widened it -
   which was narrow enough that a two-word name like "Borna Administrator"
   wrapped onto a second line and made that card taller than its neighbours.

   A width in px on the card also meant the card and the box holding it were
   sized independently, so every change to the rail or the column width left a
   margin nobody chose. The zones own the width now and the card takes all of
   it, in both places. */
/* DENSITY AND SHAPE.

   MEASURED before this rule: a rail card was 264x78 holding a 24px name line
   and an 18px role badge - 42px of content in a 78px box, because Bootstrap's
   .card-body puts 16px on all four sides (--bs-card-spacer-y/x default to 1rem).
   41% of every card was padding, and the department card was the same shape at
   117px. That, not the width, is what made them read as oversized.

   Setting the two Bootstrap variables rather than .card-body's padding keeps
   the value in one place and lets the header/footer rules resolve off it, the
   same reasoning as --bs-card-bg in app.css.

   The radius comes down with it. 16px is the panel shape - the toolbar, the
   department column, .trs-card - and on a 66px card it reads as bulbous. These
   take the 11px signature, which also keeps the nesting honest: a card inside a
   16px column should not be rounder than the column. */
.wd-available-zone .card,
.wd-department-zone .card {
    --bs-card-spacer-y: var(--trs-space-2);
    --bs-card-spacer-x: var(--trs-space-3);
    width: 100%;
    border-radius: var(--trs-radius-md);
}

/* The rail's drop-zone item wrapper. The department zone lays its items out as
   blocks so they fill on their own; only the flex column needs telling. */
.wd-available-zone .b-drop-zone-draggable {
    width: 100%;
}

.wd-hover-lift {
    cursor: pointer;
    transition: transform var(--trs-motion-fast) var(--trs-ease);
}

.wd-hover-lift:hover {
    transform: scale(1.03);
}

/* Availability edge marker on the left of the card: assigned reads as settled,
   unassigned as outstanding. */
.wd-card--assigned {
    border-left: 3px solid var(--trs-success);
}

.wd-card--unassigned {
    border-left: 3px solid var(--trs-danger);
}

/* THE AVATAR SLOT.

   ProfileImage renders .trs-avatar, which utilities.css fixes at 2.5rem - 40px.
   The card puts it in a holder declared Height="Height.Px(24)", because that
   holder was sized for the OTHER branch of the @if: the fallback user icon,
   which renders 21x16. So a worker with a photo got a 40px circle in a 24px
   box, and it overflowed in three directions at once.

   MEASURED in a 62px card: the avatar ran y=1..41 while the badge row starts at
   y=33 (8px of vertical overlap) and x=15..55 while the badge starts at x=40
   (15px horizontal). At the top it sat 1px off the card's outer border, because
   the card has 8px of padding and the circle overhung its holder by 8.

   Sizing the avatar to the slot the markup already declares fixes all three.
   24px is not a new number - it is the holder's own Height. */
.wd-available-zone .trs-avatar,
.wd-department-zone .trs-avatar {
    width: 24px;
    min-width: 24px;
    height: 24px;
}

/* ...and the holder is pinned to the same 24px so BOTH branches occupy an
   identical slot. Without this the icon branch is 21px wide and the photo
   branch 24px, so the name starts 3px further left on workers who have no
   photo - visible as a ragged edge when you scan the list. The selector is
   structural because the holder has no class of its own; it is scoped to the
   two workday zones, and the markup it depends on is in WorkerWorkdayCard. */
.wd-available-zone .card-body > strong > div:first-child,
.wd-department-zone .card-body > strong > div:first-child {
    width: 24px;
    min-width: 24px;
    justify-content: center;
}

/* The card body indents past the avatar so the detail lines stack under the
   name rather than under the picture. 25px was a guess at "the icon plus a bit"
   and never matched anything; the name actually starts at avatar + gap, which
   is 24 + 8. Now the badges line up under the name in both branches. */
.wd-card-body {
    margin-left: var(--trs-space-8);
}

.wd-card-detail {
    font-size: var(--trs-text-sm);
    color: var(--trs-text-secondary);
}

.wd-card-lock {
    position: absolute;
    top: 0;
    right: 5px;
    color: var(--trs-text-primary);
}

.wd-card-delete {
    position: absolute;
    top: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    /* Outer corner matches the card it sits in, or it cuts the curve. */
    border-radius: 0 var(--trs-radius-md) 0 10px;
    background: var(--trs-danger);
}

/* ----------------------------------------------------------------------------
   ROLE BADGES

   One chip, not six. The pre-rebrand set used five foreign palettes - a
   Bootstrap blue, a green, an amber, a grey and a magenta - and the first pass
   of this file remapped them onto six semantic aliases, which is where the
   problem showed: eucalyptus is a monochrome direction, and board, service and
   default came out as rgb(224,235,228), rgb(231,239,233) and rgb(220,233,227).
   Measured in the browser and looked at side by side at real size, they are the
   same colour. Only kitchen, maintenance and client were ever distinguishable.

   Three tints that look meaningful and are not is worse than none, so the
   colour coding is gone. Each badge already carries a distinct Font Awesome
   icon per role plus its localized label, and those do the work - see
   AccountRoleDisplay.IconClass, which is now the only thing that varies by
   role.
   ---------------------------------------------------------------------------- */

.wd-role-badge {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    margin-right: var(--trs-space-1);
    padding: .18rem .5rem;
    border-radius: var(--trs-radius-sm);
    background: var(--trs-surface-sunken);
    color: var(--trs-text-secondary);
    font-size: var(--trs-text-xs);
}

/* ----------------------------------------------------------------------------
   TRANSFER WORKDAYS MODAL

   This one rendered its own wrapper as <Div Class="modal-content"> - Bootstrap's
   own class, on an element that is not a modal shell. So its rule set both
   restyled that div AND every real .modal-content on the page, and the two
   could not be told apart. The wrapper has its own name now, and the two rules
   that genuinely do belong to the surrounding shell reach it through :has().

   The disabled-day treatment was the last #ed880e in the application - one of
   the five pre-rebrand brand colours - over a literal `blue` border and white
   text, all three !important. flatpickr's own rule is .flatpickr-day.flatpickr-disabled
   at (0,2,0); matching that shape here scores (0,3,0) and wins on specificity
   instead.
   ---------------------------------------------------------------------------- */

.wd-transfer {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--trs-space-4);
    border: none;
}

.modal-content:has(.wd-transfer) .modal-body {
    width: 100%;
    padding: 0;
}

.modal-content:has(.wd-transfer) .modal-header {
    width: 100%;
}

.wd-transfer-intro {
    max-width: 420px;
    text-align: center;
}

.wd-transfer-intro p {
    margin-bottom: var(--trs-space-2);
    font-size: var(--trs-text-md);
    color: var(--trs-text-secondary);
}

.wd-transfer .flatpickr-calendar.inline {
    top: 3px;
}

.wd-transfer .dayContainer {
    gap: 1px;
}

/* A date that already has workdays, or is otherwise not a valid target.

   :root:root, not a bare selector, and for the reason the price grid needed the
   same prefix: dark.css is generated, linked last, and blankets .flatpickr-day
   at (0,3,0) with a background plus .flatpickr-day.flatpickr-disabled at (0,4,0)
   with a colour. This rule was (0,3,0), so in dark BOTH of those beat it and
   only the border - which dark.css does not set - survived.

   MEASURED before: light gave a filled amber chip (bg #F2E7D5, text #7A6234);
   dark gave a plain muted day with a thin amber outline, so "this date already
   has workdays" was almost indistinguishable from "not selectable". The doubled
   :root scores (0,5,0) and clears both, in both themes, on tokens that flip. */
:root:root .wd-transfer .flatpickr-day.flatpickr-disabled {
    background-color: var(--trs-warning-bg);
    border: 1px solid var(--trs-warning);
    color: var(--trs-warning);
}
