html, body {
    font-family: 'Heebo', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

html {
    scroll-behavior: smooth;
}

/* Subtle selection color (brand blue) */
::selection {
    background: rgba(13, 71, 161, 0.18);
}

/* Slim, unobtrusive scrollbar — RTL-safe (logical, side-agnostic) */
* {
    scrollbar-width: thin;
    scrollbar-color: rgba(120, 130, 150, 0.45) transparent;
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background-color: rgba(120, 130, 150, 0.45);
    border-radius: 8px;
    border: 2px solid transparent;
    background-clip: content-box;
}

::-webkit-scrollbar-thumb:hover {
    background-color: rgba(120, 130, 150, 0.7);
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* Input text is normal weight. The framework renders the .mud-input wrapper at 600 and the input slot
   inherits it, so normalize the wrapper + slot (!important to win over the framework's inherit rule). */
.mud-input,
.mud-input-slot,
.mud-input input,
.mud-input textarea { font-weight: 400 !important; }

/* ============================================================
   HtmlEditor — lightweight WYSIWYG for free-text (FieldType 4) fields.
   ============================================================ */
.html-editor {
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--mud-default-borderradius, 8px);
    margin-top: 8px;
    background: var(--mud-palette-surface);
}
.html-editor:focus-within { border-color: var(--mud-palette-primary); box-shadow: 0 0 0 1px var(--mud-palette-primary); }
.html-editor-invalid { border-color: var(--mud-palette-error) !important; box-shadow: 0 0 0 1px var(--mud-palette-error); }
.html-editor-label { display: block; font-size: 0.75rem; color: var(--mud-palette-text-secondary); padding: 6px 12px 0; }
.html-editor-invalid .html-editor-label { color: var(--mud-palette-error); }
.html-editor-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 1px; padding: 2px 4px; border-bottom: 1px solid var(--mud-palette-lines-default); }
.html-editor-sep { width: 1px; align-self: stretch; margin: 4px 4px; background: var(--mud-palette-lines-default); }
.html-editor-content { min-height: 110px; padding: 10px 14px; outline: none; font-weight: 400; line-height: 1.7; }
.html-editor-content:empty::before { content: ""; }
.html-editor-content p { margin: 0 0 8px; }
.html-editor-content h2, .html-editor-content h3 { margin: 4px 0 8px; }
.html-editor-content ul, .html-editor-content ol { margin: 0 0 8px; padding-inline-start: 24px; }
.html-editor-errortext { color: var(--mud-palette-error); font-size: 0.75rem; padding: 4px 12px 0; }

/* New-call contact picker (NewCall.razor): the select + "add contact" icon button
   share one flex row (align-center). When the "יש לבחור איש קשר" required error shows,
   the helper text would grow the select's height and shove the centered button off the
   box's line. Pull the helper text out of flow (absolute, just below the box) so the
   select's height is constant — the button then stays centered on the box whether the
   error is shown or not. Scoped to .contact-row so no other input is affected. */
.contact-row { position: relative; }
.contact-row .mud-input-control-helper-container {
    position: absolute;
    inset-block-start: 100%;
    inset-inline: 0;
    margin-top: 2px;
}

/* ============================================================
   Employee statistics (/reports/employee-stats) — KPI tiles, hour×day
   heatmap, comparison bars, and the app's FIRST animation set.

   ANIMATION RULES (this block establishes the house convention):
   1. RTL-SAFE — animate ONLY opacity / transform: scale|translateY /
      stroke-dashoffset. NEVER translateX, left or right: `direction: rtl`
      does NOT mirror translateX, so a slide-in flies the wrong way in Hebrew.
   2. THEME-SAFE — geometry and opacity only, never color. Colors come from
      MudBlazor theme vars, so light/dark are free.
   3. PURPOSEFUL — motion may only mean "this value changed", "loading", or
      "read this in order". Nothing decorative, nothing loops but the shimmer.
   4. CHEAP — GPU properties only. Never width/height/top: layout thrash on a
      Blazor Server circuit with dozens of rows.
   5. DEGRADES — animation carries no information absent from the static render.
   ============================================================ */
@keyframes dosik-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes dosik-pop { from { opacity: 0; transform: scale(.9); } to { opacity: 1; transform: none; } }
/* Transform ONLY. An animation must never end on a property an inline style owns: CSS puts animation declarations
   ABOVE normal author declarations, so a `to { opacity: 1 }` would beat the heatmap's per-cell inline opacity for
   good (fill-mode: both keeps applying it) and flatten the whole quantile scale to a solid block. */
@keyframes dosik-pop-scale { from { transform: scale(.6); } to { transform: none; } }
@keyframes dosik-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes dosik-draw { from { stroke-dashoffset: var(--dash, 2000); } to { stroke-dashoffset: 0; } }

.stat-tile {
    position: relative;
    padding: 14px 16px;
    border-radius: var(--mud-default-borderradius, 8px);
    border: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-surface);
    animation: dosik-rise .35s ease-out both;
    animation-delay: calc(var(--i, 0) * 45ms);
    overflow: hidden;
}
/* The accent sits on the INLINE-START edge — logical, so it flips with the page direction. */
.stat-tile::before {
    content: ""; position: absolute; inset-block: 0; inset-inline-start: 0; width: 3px;
    background: var(--mud-palette-primary); opacity: .75;
}
.stat-tile__label { font-size: .8rem; color: var(--mud-palette-text-secondary); }
/* tabular-nums keeps the digits aligned across the tile row. NOTE: nothing may animate the TEXT of this element —
   a JS count-up used to own it and could leave a half-counted value on screen when superseded, which made the tile
   disagree with the filters. The number is server-rendered and nothing but Blazor touches it. */
.stat-tile__value { font-size: 1.6rem; font-weight: 600; line-height: 1.25; font-variant-numeric: tabular-nums; }
.stat-tile__sub { font-size: .72rem; color: var(--mud-palette-text-secondary); }
.stat-delta { display: inline-flex; align-items: center; gap: 2px; font-size: .72rem; font-weight: 600; }
.stat-delta--up { color: var(--mud-palette-success); }
.stat-delta--down { color: var(--mud-palette-error); }
.stat-delta--flat { color: var(--mud-palette-text-secondary); }

/* --- hour × day heatmap ---------------------------------------------------
   The grid is direction:ltr ON PURPOSE inside this RTL page: the x-axis is
   TIME (00:00 -> 23:00), and a time axis is never mirrored. Hebrew day labels
   still shape correctly — bidi is resolved per text run, not per container. */
.stat-heat { direction: ltr; overflow-x: auto; }
.stat-heat__grid { display: grid; grid-template-columns: max-content repeat(24, minmax(14px, 1fr)); gap: 2px; min-width: 520px; }
.stat-heat__day { font-size: .72rem; color: var(--mud-palette-text-secondary); padding-inline-end: 6px; align-self: center; white-space: nowrap; }
.stat-heat__hour { font-size: .6rem; color: var(--mud-palette-text-secondary); text-align: center; }
/* The per-cell shade is an INLINE opacity (the quantile bin) — so this animation touches transform only. */
.stat-heat__cell {
    aspect-ratio: 1; border-radius: 2px; min-height: 14px;
    background: var(--mud-palette-primary);
    animation: dosik-pop-scale .25s ease-out both;
    animation-delay: calc(var(--i, 0) * 3ms);
}
.stat-heat__cell--empty { background: var(--mud-palette-lines-default); opacity: .35; animation: none; }
.stat-heat__off { font-size: .6rem; opacity: .7; margin-inline-start: 4px; }
/* The band above the grid marks 08:30–17:00, so an off-hours cell reads as off-hours rather than as a mystery. */
.stat-heat__band { height: 4px; border-radius: 2px; background: transparent; }
.stat-heat__band--work { background: var(--mud-palette-primary); opacity: .35; }
.stat-heat__swatch { display: inline-block; width: 14px; height: 14px; border-radius: 2px; background: var(--mud-palette-primary); }

/* --- KPI strip (סטטיסטיקות עובדים) ----------------------------------------
   One row of numbers instead of a grid of cards. The table below is the screen;
   eight tiles used to push it under the fold. Cells share one border, so the
   strip reads as a single object rather than as eight competing boxes. */
.stat-strip {
    /* A GRID with a fixed column count, not a wrapping flex row: 8 metrics over 4 columns is always 4+4, while
       flex-wrap leaves whatever does not fit stretched across a half-empty second line. The 1px gap over a
       line-coloured ground draws every separator — horizontal ones included — without a single border rule. */
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px;
    background: var(--mud-palette-lines-default);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius, 8px);
    overflow: hidden;
}
.stat-strip__cell {
    min-width: 0; padding: 10px 14px;
    background: var(--mud-palette-surface);
    animation: dosik-rise .3s ease-out both;
    animation-delay: calc(var(--i, 0) * 35ms);
}
.stat-strip__label { font-size: .74rem; color: var(--mud-palette-text-secondary); }
.stat-strip__value { font-size: 1.35rem; font-weight: 600; line-height: 1.25; font-variant-numeric: tabular-nums; }
.stat-strip__foot { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
/* The sub-line is the first thing to give up space — it explains, the number states. */
.stat-strip__sub {
    font-size: .68rem; color: var(--mud-palette-text-secondary);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}

/* --- the league table ------------------------------------------------------
   A hand-rolled table rather than MudTable: every cell carries a bar or a
   sparkline drawn against the OTHER rows, which a generic data grid has no
   place to hang. */
.emp-table__wrap { overflow-x: auto; border: 1px solid var(--mud-palette-lines-default); border-radius: var(--mud-default-borderradius, 8px); }
/* min-width sized for the column count: below it the columns squeeze each other instead of the wrap
   scrolling, which is what makes a phone readable. Raise it when a column is added. */
.emp-table { width: 100%; border-collapse: collapse; font-size: .85rem; min-width: 980px; }
.emp-table thead th {
    text-align: start; font-weight: 600; font-size: .72rem; white-space: nowrap;
    color: var(--mud-palette-text-secondary);
    background: var(--mud-palette-background-grey);
    border-bottom: 1px solid var(--mud-palette-lines-default);
    padding: 0;
}
.emp-table__sort {
    all: unset; box-sizing: border-box; cursor: pointer; width: 100%;
    padding: 8px 12px; display: flex; align-items: center; gap: 4px; white-space: nowrap;
}
.emp-table__num .emp-table__sort { justify-content: flex-start; }
.emp-table__sort:hover { color: var(--mud-palette-primary); }
.emp-table__sort:focus-visible { outline: 2px solid var(--mud-palette-primary); outline-offset: -2px; }
.emp-table__caret { color: var(--mud-palette-primary); font-size: .7rem; }
.emp-table td { padding: 7px 12px; border-bottom: 1px solid var(--mud-palette-table-lines); vertical-align: middle; }
.emp-table tbody tr:last-child td { border-bottom: none; }
.emp-table tbody tr:hover td { background: var(--mud-palette-action-default-hover); }
.emp-table__rank { width: 34px; color: var(--mud-palette-text-secondary); font-size: .75rem; padding-inline: 12px; }
.emp-table__name { font-weight: 600; white-space: nowrap; }
.emp-table__off {
    font-size: .65rem; font-weight: 500; margin-inline-start: 6px; padding: 1px 6px; border-radius: 999px;
    border: 1px solid var(--mud-palette-lines-default); color: var(--mud-palette-text-secondary);
}
.emp-table__num { white-space: nowrap; font-variant-numeric: tabular-nums; }
.emp-table__link {
    all: unset; cursor: pointer; font-weight: 600; font-variant-numeric: tabular-nums;
    color: var(--mud-palette-primary); border-bottom: 1px dashed currentColor;
}
.emp-table__link[disabled] { cursor: default; color: var(--mud-palette-text-secondary); border-bottom-color: transparent; font-weight: 400; }
.emp-table__link:focus-visible { outline: 2px solid var(--mud-palette-primary); outline-offset: 2px; }
.emp-table__spark-h, .emp-table__spark { width: 130px; }
.emp-table__spark-empty { color: var(--mud-palette-text-disabled); }

.emp-cell { min-width: 132px; }
.emp-cell__top { display: flex; align-items: baseline; gap: 6px; font-variant-numeric: tabular-nums; }
.emp-cell__aside { font-size: .68rem; color: var(--mud-palette-text-secondary); }
.emp-cell__aside--warn { color: var(--mud-palette-warning); }

/* The bar and its team-average marker. inset-inline-start, so the marker sits
   where the eye reads it in Hebrew AND still lands correctly if this ever
   renders LTR. */
.emp-bar { position: relative; height: 6px; margin-top: 4px; border-radius: 3px; background: var(--mud-palette-action-disabled-background); overflow: hidden; }
.emp-bar__fill {
    height: 100%; border-radius: 3px; background: var(--mud-palette-primary);
    transform-origin: right center;
    animation: dosik-grow .4s cubic-bezier(.22, 1, .36, 1) both;
}
[dir="ltr"] .emp-bar__fill { transform-origin: left center; }
.emp-bar__fill--alt { background: var(--mud-palette-secondary); }
.emp-bar__fill--muted { background: var(--mud-palette-text-disabled); }
.emp-bar__avg { position: absolute; inset-block: -2px; width: 0; border-inline-start: 2px dashed var(--mud-palette-text-secondary); opacity: .65; }

.emp-spark { display: block; width: 120px; height: 26px; }
.emp-spark__line { fill: none; stroke: var(--mud-palette-primary); stroke-width: 1.4; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.emp-spark__area { fill: var(--mud-palette-primary); opacity: .13; stroke: none; }
.emp-spark__line--muted { stroke: var(--mud-palette-text-disabled); }
.emp-spark__area--muted { fill: var(--mud-palette-text-disabled); }

/* On a phone the table scrolls sideways inside its own box (app-wide rule), so
   only the strip needs to give: two columns instead of eight in a row. */
/* 8 divides evenly by 4 and by 2, so neither breakpoint can leave an orphan cell on its own row. */
@media (max-width: 900px) {
    .stat-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --- employee comparison bars --------------------------------------------- */
.stat-bar__track { flex: 1; height: 18px; border-radius: 3px; background: var(--mud-palette-action-disabled-background); overflow: hidden; }
.stat-bar__fill {
    height: 100%; border-radius: 3px; background: var(--mud-palette-primary);
    /* `right`, not a logical keyword: transform-origin does NOT accept inline-start/inline-end in any browser.
       The app is Hebrew/RTL throughout, so the track's content edge is the right one and the bar grows leftward
       out of it. [dir=ltr] below keeps it honest if this ever renders in an LTR context. */
    transform-origin: right center;
    animation: dosik-grow .4s cubic-bezier(.22, 1, .36, 1) both;
    animation-delay: calc(var(--i, 0) * 40ms);
}
[dir="ltr"] .stat-bar__fill { transform-origin: left center; }
.stat-bar__fill--muted { background: var(--mud-palette-text-disabled); }
.stat-bar__row { animation: dosik-rise .3s ease-out both; animation-delay: calc(var(--i, 0) * 40ms); }

/* --- the cumulative-race SVG ---------------------------------------------- */
.stat-race { width: 100%; height: 260px; }
.stat-race path { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.stat-race .race-line { animation: dosik-draw .9s ease-out both; }

.stat-card { animation: dosik-rise .35s ease-out both; }

/* Motion is decoration on top of a complete static render — strip it entirely on request. */
@media (prefers-reduced-motion: reduce) {
    .stat-tile, .stat-tile *, .stat-heat__cell, .stat-bar__fill, .stat-bar__row, .stat-card, .stat-race .race-line,
    .stat-strip__cell, .emp-bar__fill {
        animation: none !important;
        transition: none !important;
    }
}

/* ============================================================
   יומן טכנאים — the on-site dispatch calendar (/calendar)
   Hand-rolled CSS grid, like the employee-stats heatmap above: MudBlazor 9.5 ships no calendar component, and a
   third-party one could not give us per-technician lanes. Everything is driven by --mud-palette-* variables, so
   both themes are covered by the SAME rules — there is no dark-mode block to keep in sync.
   Direction: LEFT AS RTL on purpose. The lanes are technicians/days (both read right-to-left in Hebrew); only the
   heatmap above forces LTR, because its x-axis is a time axis and a time axis is never mirrored.
   ============================================================ */

/* A technician's colour swatch — used in the lane header and the filter dropdown. */
.cal-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }

.cal-toolbar { row-gap: 12px; }

/* ---- the hour grid (day + week views) ---- */
.cal-grid__scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* One grid for headers AND lanes, so a lane can never drift out of line with its header: the ruler/corner sit in
   column 1 and every lane gets an equal fraction of the rest. */
.cal-grid__inner { display: grid; align-items: start; }

.cal-grid__corner { position: sticky; inset-inline-start: 0; z-index: 3; background: var(--mud-palette-surface); }

.cal-grid__colhead {
    padding: 6px 8px;
    text-align: center;
    border-bottom: 2px solid var(--mud-palette-lines-default);
    border-inline-start: 1px solid var(--mud-palette-lines-default);
}
.cal-grid__colhead.is-today { background: var(--mud-palette-primary-hover); }
.cal-grid__colhead-title {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    font-size: .85rem; font-weight: 600; color: var(--mud-palette-text-primary);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-grid__colhead-sub { font-size: .7rem; color: var(--mud-palette-text-secondary); margin-top: 2px; }

/* The hour ruler sticks to the inline-start edge so the times stay readable while the lanes scroll sideways. */
.cal-grid__ruler { position: sticky; inset-inline-start: 0; z-index: 2; background: var(--mud-palette-surface); }
.cal-grid__hour {
    position: relative;
    font-size: .68rem; color: var(--mud-palette-text-secondary);
    text-align: center;
}
/* Sit the label ON the hour line rather than inside the slot, so it lines up with the rule it names. */
.cal-grid__hour span { position: absolute; top: -0.5em; inset-inline-end: 6px; background: var(--mud-palette-surface); padding: 0 2px; }

.cal-grid__col { position: relative; border-inline-start: 1px solid var(--mud-palette-lines-default); }
.cal-grid__col.is-today { background: var(--mud-palette-action-default-hover); }

/* One box per hour, drawn behind the bookings. `is-working` lightly tints the office day (08:00–17:00). */
.cal-grid__slot {
    position: absolute; inset-inline: 0;
    border-top: 1px solid var(--mud-palette-lines-default);
    pointer-events: none;
}
.cal-grid__slot.is-working { background: var(--mud-palette-background-gray); opacity: .45; }

/* "Now" — a hairline across today's lane with a dot on the inline-start edge. */
.cal-grid__now { position: absolute; inset-inline: 0; height: 2px; background: var(--mud-palette-error); z-index: 4; pointer-events: none; }
.cal-grid__now::before {
    content: ""; position: absolute; inset-inline-start: 0; top: -3px;
    width: 8px; height: 8px; border-radius: 50%; background: var(--mud-palette-error);
}

/* ---- a booking block ---- */
.cal-item {
    position: absolute; z-index: 5;
    display: flex; flex-direction: column; gap: 1px;
    padding: 3px 6px; overflow: hidden;
    border-radius: 5px;
    border-inline-start: 3px solid var(--cal-item-color, var(--mud-palette-primary));
    /* colour-mix keeps ONE rule for both themes: the block is its technician's colour blended into whatever the
       current surface is, so it stays legible on white and on the dark surface alike. */
    background: color-mix(in srgb, var(--cal-item-color, var(--mud-palette-primary)) 18%, var(--mud-palette-surface));
    color: var(--mud-palette-text-primary);
    text-decoration: none;
    font-size: .74rem; line-height: 1.25;
    transition: filter .15s, box-shadow .15s;
}
.cal-item:hover { filter: brightness(1.06); box-shadow: 0 2px 8px rgb(0 0 0 / .18); z-index: 6; }
/* The block is a container so the × can sit OUTSIDE the anchor; the link fills it and carries the text. */
.cal-item__link { display: flex; flex-direction: column; gap: 1px; color: inherit; text-decoration: none; min-width: 0; }
.cal-item--movable, .cal-item.is-movable { cursor: grab; }
.cal-item.is-movable:active { cursor: grabbing; }
/* Un-book button: appears on hover so it never competes with the block's own text. */
.cal-item__remove {
    position: absolute; top: 1px; inset-inline-end: 2px;
    width: 16px; height: 16px; padding: 0; line-height: 14px;
    border: none; border-radius: 50%; cursor: pointer;
    background: var(--mud-palette-error); color: #fff;
    font-size: .8rem; opacity: 0; transition: opacity .12s;
}
.cal-item:hover .cal-item__remove, .cal-item__remove:focus-visible { opacity: 1; }

/* Drop targets: invisible until something is actually being dragged over them. */
.cal-grid__slot.is-droppable { pointer-events: auto; }
/* While a drag is in flight, the booking blocks stop swallowing pointer events so the hour slots UNDER them can
   receive dragover/drop. Without this, an occupied hour is un-droppable and the gesture fails silently. */
.cal-grid--dragging .cal-item { pointer-events: none; }
.cal-grid__slot.is-drop-over {
    background: var(--mud-palette-primary);
    opacity: .28;
    outline: 2px dashed var(--mud-palette-primary);
    outline-offset: -2px;
}
/* The queue card being dragged fades so the eye follows the cursor, not the origin. */
.cal-queue__item[draggable="true"] { cursor: grab; }
.cal-queue__item.is-dragging { opacity: .4; }
.cal-item.is-urgent { border-inline-start-color: var(--mud-palette-error); box-shadow: inset 0 0 0 1px var(--mud-palette-error); }
.cal-item__time { font-size: .66rem; color: var(--mud-palette-text-secondary); white-space: nowrap; }
.cal-item__title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-item__sub { color: var(--mud-palette-text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- month view ---- */
.cal-month__head, .cal-month__grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
.cal-month__head { margin-bottom: 4px; }
.cal-month__dayname { text-align: center; font-size: .78rem; font-weight: 600; color: var(--mud-palette-text-secondary); padding: 4px 0; }
.cal-month__dayname.is-off { opacity: .6; }

.cal-month__cell {
    min-height: 108px; padding: 4px;
    border: 1px solid var(--mud-palette-lines-default); border-radius: 6px;
    background: var(--mud-palette-surface);
    display: flex; flex-direction: column; gap: 3px;
}
.cal-month__cell.is-outside { opacity: .45; }               /* spill-over days of the neighbouring months */
.cal-month__cell.is-off { background: var(--mud-palette-background-gray); }
.cal-month__cell.is-today { border-color: var(--mud-palette-primary); box-shadow: inset 0 0 0 1px var(--mud-palette-primary); }

.cal-month__daynum {
    align-self: flex-start;
    background: none; border: none; cursor: pointer;
    font-size: .8rem; font-weight: 700; color: var(--mud-palette-text-primary);
    padding: 1px 6px; border-radius: 10px;
}
.cal-month__daynum:hover { background: var(--mud-palette-action-default-hover); }
.cal-month__cell.is-today .cal-month__daynum { background: var(--mud-palette-primary); color: var(--mud-palette-primary-text); }

.cal-chip {
    display: flex; align-items: center; gap: 4px;
    padding: 1px 5px; border-radius: 4px;
    border-inline-start: 3px solid var(--cal-item-color, var(--mud-palette-primary));
    background: color-mix(in srgb, var(--cal-item-color, var(--mud-palette-primary)) 18%, var(--mud-palette-surface));
    color: var(--mud-palette-text-primary); text-decoration: none;
    font-size: .7rem; line-height: 1.4;
}
.cal-chip:hover { filter: brightness(1.06); }
.cal-chip.is-urgent { border-inline-start-color: var(--mud-palette-error); }
.cal-chip__time { color: var(--mud-palette-text-secondary); flex: 0 0 auto; }
.cal-chip__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.cal-month__more {
    background: none; border: none; cursor: pointer; text-align: start;
    font-size: .68rem; color: var(--mud-palette-primary); padding: 0 4px;
}
.cal-month__more:hover { text-decoration: underline; }

/* ---- the "waiting to be scheduled" queue ----
   The heading + search are OUTSIDE the scroll, structurally: the panel itself never scrolls (flex column, its own
   overflow hidden) and only the list below does. This replaced a `position: sticky` header — sticky kept the
   header in place but it remained part of the scrolling box, so the scrollbar spanned the whole panel and cards
   could graze its edges. Taking it out of the flow entirely removes that class of problem rather than patching it. */
.cal-queue {
    max-height: 78vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
/* Fixed header. The negative side margins let it span the panel's own 12px gutter so its rule reaches both edges. */
.cal-queue__head {
    flex: 0 0 auto;
    margin: -12px -12px 0;
    padding: 12px 12px 12px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}
/* The header owns the spacing under the search field, so the field itself adds none. */
.cal-queue__head .mud-input-control { margin-bottom: 0; }
/* The ONLY scrolling part. */
.cal-queue__list { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; padding-top: 12px; }
.cal-queue__item {
    display: block; padding: 8px 10px; margin-bottom: 8px;
    border: 1px solid var(--mud-palette-lines-default); border-radius: 6px;
    border-inline-start: 3px solid var(--mud-palette-warning);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary); text-decoration: none;
    transition: box-shadow .15s, border-color .15s;
}
.cal-queue__item:hover { box-shadow: 0 2px 8px rgb(0 0 0 / .15); border-color: var(--mud-palette-primary); }
.cal-queue__item.is-urgent { border-inline-start-color: var(--mud-palette-error); }
.cal-queue__ticket { font-weight: 700; font-size: .8rem; color: var(--mud-palette-primary); }
.cal-queue__chip { height: 18px; font-size: .65rem; }
.cal-queue__title { font-size: .82rem; font-weight: 600; margin-bottom: 2px; }
.cal-queue__meta { font-size: .7rem; color: var(--mud-palette-text-secondary); }

.cal-empty { padding: 28px 12px; text-align: center; color: var(--mud-palette-text-secondary); font-size: .85rem; }

@media (prefers-reduced-motion: reduce) {
    .cal-item, .cal-chip, .cal-queue__item { transition: none !important; }
}

/* ============================================================
   Mobile responsiveness — PHONE-ONLY (≤600px). Everything below is
   inside the media query, so the desktop appearance is unchanged.
   ============================================================ */
/* ---------- The app-wide image viewer (ImageLightbox / ImageLightboxHost) ----------
   Every picture in the CRM opens here, over the page, instead of navigating to a new tab. Global rather than
   page-scoped precisely because it is one viewer for all of them. z-index sits above MudBlazor's dialogs (1400)
   so an image opened FROM a dialog is not trapped behind it. */
.img-lightbox {
    position: fixed; inset: 0; z-index: 3000;
    background: rgba(0,0,0,0.85);
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
    cursor: zoom-out; padding: 24px;
}
.img-lightbox__img {
    max-width: 95vw; max-height: 88vh; object-fit: contain;
    border-radius: 6px; box-shadow: 0 6px 40px rgba(0,0,0,0.6);
}
.img-lightbox__caption { color: #fff; font-size: 0.875rem; opacity: 0.85; text-align: center; max-width: 90vw; }
.img-lightbox__close { position: fixed; top: 12px; inset-inline-end: 16px; color: #fff; }

/* A thumbnail that opens in the viewer. The cursor is the affordance — there is no link to hover. */
.img-zoomable { cursor: zoom-in; }


/* ---------- A date/time RANGE is ONE control, not two filters (owner, 2026-08-16) ----------
   Wrap the two ends in .dosik-range and they sit flush: no gap, and the two outlined borders share a single edge
   instead of drawing a double line where they meet — the -1px pull is what removes that seam, and the corner radii
   are flattened only on the touching sides so the pair keeps rounded outer corners.

   The wrapper is its own flex box on purpose: inside a toolbar, the toolbar's own gap would otherwise push the two
   halves of one filter apart like two unrelated ones. Per-screen width via --range-w; where the wrapper is given a
   width of its own (a form grid cell, a dialog) the two ends simply split it. */
.dosik-range { display: flex; align-items: center; }
.dosik-range > .mud-input-control { flex: 1 1 var(--range-w, 170px); min-width: 0; }
.dosik-range > .mud-input-control + .mud-input-control { margin-inline-start: -1px; }
.dosik-range > .mud-input-control:first-child .mud-input-outlined-border {
    border-start-end-radius: 0; border-end-end-radius: 0;
}
.dosik-range > .mud-input-control:last-child .mud-input-outlined-border {
    border-start-start-radius: 0; border-end-start-radius: 0;
}
/* Whichever end has focus draws on top, so its highlight is not clipped by its neighbour. */
.dosik-range > .mud-input-control:focus-within { position: relative; z-index: 1; }


@media (max-width: 600px) {
    /* A range still reads as one control on a phone — it just spans the width instead of holding two fixed halves,
       which is what keeps 2×170px from forcing a horizontal scroll on a 390px screen. */
    .dosik-range { width: 100%; }
    .dosik-range > .mud-input-control { flex: 1 1 0; }

    /* Calendar: the hour grid already scrolls inside .cal-grid__scroll — just shrink the lanes and the month
       cells so a phone shows a usable slice instead of a squashed one. The queue drops its own scroll box and
       flows with the page (it sits BELOW the grid at xs, so a nested scroller would be a trap). */
    .cal-month__cell { min-height: 76px; padding: 3px; }
    .cal-chip__time { display: none; }
    .cal-month__dayname { font-size: .68rem; }
    /* On a phone the queue sits BELOW the grid and flows with the page — a nested scroll box there would be a trap
       (two scrollbars competing under a thumb). Both the panel and its list scroll with the document instead. */
    .cal-queue { max-height: none; overflow: visible; display: block; }
    .cal-queue__list { overflow-y: visible; }
    .cal-toolbar > * { flex: 1 1 100%; }
    .cal-toolbar .mud-button-group { width: 100%; }
    /* Employee-stats: the heatmap keeps its own scroll box (set above); tiles go full width via MudGrid xs=12. */
    .stat-heat__grid { min-width: 460px; }
    /* No page-level horizontal scroll; wide grids/tables scroll inside their own box. `clip` on the main
       content absorbs minor child overflow (MudGrid negative-margin rows) without creating a scroll container. */
    body { overflow-x: hidden; }
    .mud-main-content { overflow-x: clip; }
    .mud-table-container, .mud-simple-table { overflow-x: auto; -webkit-overflow-scrolling: touch; }

    /* Use the full screen width — trim the big page gutters & vertical margins. */
    .mud-main-content .mud-container { padding-left: 12px !important; padding-right: 12px !important; }
    .dosik-main .mud-container { margin-top: 0.9rem !important; margin-bottom: 0.9rem !important; }

    /* Dialogs go (almost) full-width and never force horizontal scroll. */
    .mud-dialog { width: calc(100vw - 16px) !important; max-width: calc(100vw - 16px) !important; margin: 8px auto !important; }
    .mud-dialog .mud-dialog-content { padding: 12px 14px !important; max-height: 78vh; overflow-y: auto; }
    /* Neutralize any fixed min-width set inside dialogs/forms so they shrink to fit. */
    .mud-dialog [style*="min-width"], .mud-dialog .mud-form { min-width: 0 !important; }
    .mud-dialog-actions { flex-wrap: wrap; }

    /* Smaller hero headings so titles don't wrap awkwardly. */
    .mud-typography-h4 { font-size: 1.45rem !important; }
    .mud-typography-h5 { font-size: 1.2rem !important; }
    .mud-typography-h6 { font-size: 1.05rem !important; }

    /* Tighter data-grid cells; hide the verbose "1-10 of N" pager text (arrows remain). */
    .mud-table-cell { padding-left: 8px !important; padding-right: 8px !important; }
    .mud-table-pagination-caption, .mud-table-pagination-select { display: none !important; }
    .mud-table-toolbar { flex-wrap: wrap; height: auto; }

    /* Inline fixed min-widths on selects (e.g. call reassign) shrink to fit. */
    .mud-input-control[style*="min-width"] { min-width: 0 !important; }

    /* Calls/products search row: search field full-width and the advanced button wraps below. */
    .calls-search, .products-search { flex-wrap: wrap; }
    .calls-search .mud-input-control, .products-search .mud-input-control { width: 100% !important; max-width: 100% !important; }

    /* Waze navigation button next to a call's client address — phones only (opens the Waze app). */
    .call-waze-btn { display: inline-flex !important; }
}