.tm-scheduler {
    display: flex;
    flex-direction: column;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    overflow: hidden;
    min-height: 0;
    height: 100%;
    color: var(--mud-palette-text-primary);
}

.tm-scheduler-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-surface);
    flex-wrap: wrap;
}

.tm-scheduler-nav {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1 1 auto;
    min-width: 0;
}

.tm-scheduler-date-label {
    font-size: 1.05rem;
    font-weight: 600;
    margin-left: 8px;
    color: var(--mud-palette-text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tm-scheduler-view-switch {
    display: inline-flex;
    gap: 4px;
}

.tm-view-btn {
    min-width: 64px;
}

/* ----- MONTH ----- */
.tm-month {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.tm-month-header {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    background: var(--mud-palette-background-gray);
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.tm-month-header-cell {
    padding: 8px 10px;
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
}

.tm-month-grid {
    flex: 1 1 auto;
    display: grid;
    grid-template-rows: repeat(6, 1fr);
    min-height: 0;
}

.tm-month-row {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    border-bottom: 1px solid var(--mud-palette-lines-default);
    min-height: 96px;
}

.tm-month-row:last-child {
    border-bottom: none;
}

.tm-month-cell {
    border-right: 1px solid var(--mud-palette-lines-default);
    padding: 4px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    cursor: pointer;
    background: var(--mud-palette-surface);
    transition: background-color 0.12s ease;
    min-width: 0;
    overflow: hidden;
}

.tm-month-cell:hover {
    background: var(--mud-palette-action-default-hover);
}

.tm-month-cell:last-child {
    border-right: none;
}

.tm-month-cell.tm-out-of-month {
    background: var(--mud-palette-background-gray);
    color: var(--mud-palette-text-disabled);
}

.tm-month-cell.tm-out-of-month .tm-month-day-number {
    color: var(--mud-palette-text-disabled);
}

.tm-month-cell-header {
    display: flex;
    justify-content: flex-end;
    padding: 2px 4px 0;
}

.tm-month-day-number {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--mud-palette-text-primary);
}

.tm-today-num {
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-contrastText, #fff) !important;
    border-radius: 999px;
    padding: 2px 8px;
    min-width: 24px;
    text-align: center;
}

.tm-month-cell-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1 1 auto;
    overflow: hidden;
}

.tm-month-event {
    color: #fff;
    font-size: 0.78rem;
    padding: 2px 6px;
    border-radius: 4px;
    cursor: grab;
    display: flex;
    align-items: center;
    gap: 4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    user-select: none;
}

.tm-month-event:active {
    cursor: grabbing;
}

.tm-month-event-time {
    font-weight: 600;
    opacity: 0.9;
}

.tm-month-event-title {
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 1 1 auto;
}

/* Month hover card. A month row only fits time + subject, so the detail the Day-view card
   shows inline (job no, property, address, attendees, status) lives on a hover card instead —
   the month-view equivalent of the Day view's hover-reveal. The MudTooltip root has to behave
   as the month row's flex child, otherwise the time/title stop laying out side by side. */
.tm-month-tip-root {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
}

/* Neutralise the default dark tooltip chrome — the card supplies its own surface. */
.mud-tooltip.tm-month-tip {
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    padding: 0;
    max-width: 340px;
    border-radius: 8px;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);
    opacity: 1;
    text-align: left;
}

/* --tm-ev-accent is the appointment's technician colour, set per card by the page, so the
   hover card carries the same identity as the month row it came from. */
.tm-month-tip-card {
    display: flex;
    min-width: 200px;
    border-left: 3px solid var(--tm-ev-accent, var(--mud-palette-primary));
    border-radius: 8px;
    overflow: hidden;
}

.tm-month-tip-body {
    width: 100%;
    padding: 8px 10px;
    white-space: normal;
}

.tm-month-more {
    font-size: 0.75rem;
    color: var(--mud-palette-primary);
    cursor: pointer;
    padding: 2px 6px;
    font-weight: 500;
}

.tm-month-more:hover {
    text-decoration: underline;
}

/* ----- TIME GRID (DAY / WEEK) ----- */
.tm-timegrid {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.tm-timegrid-header,
.tm-timegrid-allday {
    display: grid;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-background-gray);
}

.tm-timegrid-header > .tm-timegrid-gutter,
.tm-timegrid-allday > .tm-timegrid-gutter {
    border-right: 1px solid var(--mud-palette-lines-default);
}

.tm-timegrid-day-header {
    text-align: center;
    padding: 8px 4px;
    border-right: 1px solid var(--mud-palette-lines-default);
    cursor: pointer;
}

.tm-timegrid-day-header:last-child {
    border-right: none;
}

.tm-timegrid-day-name {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
    letter-spacing: 0.04em;
}

.tm-timegrid-day-num {
    font-size: 1.1rem;
    font-weight: 500;
    margin-top: 2px;
    color: var(--mud-palette-text-primary);
    display: inline-block;
}

.tm-timegrid-allday {
    border-bottom: 1px solid var(--mud-palette-lines-default);
    min-height: 36px;
    max-height: 160px;
    overflow-y: auto;
    background: var(--mud-palette-surface);
}

.tm-allday-label {
    font-size: 0.7rem;
    color: var(--mud-palette-text-secondary);
    text-align: right;
    padding: 6px 8px 0 0;
}

.tm-timegrid-allday-cell {
    border-right: 1px solid var(--mud-palette-lines-default);
    padding: 4px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-height: 32px;
    cursor: pointer;
}

.tm-timegrid-allday-cell:last-child {
    border-right: none;
}

.tm-allday-event {
    color: #fff;
    font-size: 0.78rem;
    font-weight: 600;
    padding: 2px 10px;
    border-radius: 999px;
    cursor: grab;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    user-select: none;
    display: inline-flex;
    align-self: flex-start;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    min-height: 22px;
    line-height: 18px;
    background: #64748b;
    text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);
}

.tm-allday-event:active {
    cursor: grabbing;
}

.tm-allday-event-icon {
    font-size: 16px !important;
    width: 16px;
    height: 16px;
    flex: 0 0 16px;
}

.tm-allday-event-label {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Empty spacer rendered in resource columns that don't own the current row's event.
   Keeps row Y-positions aligned across the all-day band. */
.tm-allday-event-row-placeholder {
    min-height: 22px;
    height: 22px;
    visibility: hidden;
}

/* Per-type all-day event styling. Applied IN ADDITION to .tm-allday-event. */
.tm-allday-holiday {
    color: #fff;
    background: #dc2626;
    border: 1px solid #991b1b;
    box-shadow: inset 3px 0 0 #fca5a5;
}

.tm-allday-leave {
    color: #1f2937;
    background: #f59e0b;
    border: 1px solid #d97706;
}

.tm-allday-training {
    color: #fff;
    background: #2563eb;
    border: 1px solid #1d4ed8;
}

.tm-allday-event-generic {
    color: #fff;
    background: #64748b;
    border: 1px solid #475569;
}

.tm-allday-block {
    color: #fff;
    background: #6b7280;
    border: 1px solid #4b5563;
}

.tm-timegrid-scroll {
    flex: 1 1 auto;
    overflow-y: auto;
    overflow-x: hidden;
    min-height: 0;
}

.tm-timegrid-body {
    display: grid;
    min-height: 100%;
}

.tm-timegrid-times {
    border-right: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-surface);
}

.tm-timegrid-time {
    height: 48px;
    position: relative;
    font-size: 0.72rem;
    color: var(--mud-palette-text-secondary);
}

.tm-timegrid-time span {
    position: absolute;
    top: -6px;
    right: 6px;
}

.tm-timegrid-day-col {
    position: relative;
    border-right: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-surface);
}

.tm-timegrid-day-col:last-child {
    border-right: none;
}

.tm-timegrid-hour {
    height: 48px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    display: flex;
    flex-direction: column;
}

.tm-timegrid-slot {
    flex: 1 1 auto;
    cursor: pointer;
    border-bottom: 1px dashed transparent;
}

.tm-timegrid-slot:hover {
    background: var(--mud-palette-action-default-hover);
}

.tm-timegrid-event {
    position: absolute;
    left: 4px;
    right: 4px;
    color: #fff;
    border-radius: 4px;
    padding: 4px 6px;
    font-size: 0.78rem;
    box-shadow: 0 1px 2px rgba(0,0,0,0.15);
    cursor: grab;
    overflow: hidden;
    z-index: 2;
    user-select: none;
}

.tm-timegrid-event:active {
    cursor: grabbing;
}

/* Live drop target: while a backlog job is dragged, appointments at the same property pulse so the
   user can see exactly where the job is allowed to land. Ring + glow ride on top of the event's
   own inline background colour, so it reads on any property/technician tint. */
.tm-timegrid-event.tm-drop-candidate {
    z-index: 5;
    outline: 2px solid var(--mud-palette-primary, #2196f3);
    outline-offset: 1px;
    animation: tm-drop-candidate-pulse 1.1s ease-in-out infinite;
}

/* Intensify the specific card the pointer is over (where supported during a native drag). */
.tm-timegrid-event.tm-drop-candidate:hover {
    animation: none;
    outline-width: 3px;
    outline-color: var(--mud-palette-secondary, #ff4081);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--mud-palette-secondary, #ff4081) 55%, transparent),
                0 4px 14px rgba(0,0,0,0.35);
    transform: scale(1.015);
}

@keyframes tm-drop-candidate-pulse {
    0%, 100% {
        box-shadow: 0 0 0 1px color-mix(in srgb, var(--mud-palette-primary, #2196f3) 40%, transparent),
                    0 1px 2px rgba(0,0,0,0.15);
    }
    50% {
        box-shadow: 0 0 0 5px color-mix(in srgb, var(--mud-palette-primary, #2196f3) 35%, transparent),
                    0 2px 10px rgba(0,0,0,0.28);
    }
}

@media (prefers-reduced-motion: reduce) {
    .tm-timegrid-event.tm-drop-candidate {
        animation: none;
        box-shadow: 0 0 0 3px color-mix(in srgb, var(--mud-palette-primary, #2196f3) 40%, transparent);
    }
}

.tm-timegrid-event-time {
    font-weight: 600;
    font-size: 0.72rem;
    opacity: 0.9;
}

.tm-timegrid-event-title {
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ----- RESIZE HANDLE ----- */
.tm-event-resize-handle {
    position: absolute;
    background: rgba(255, 255, 255, 0.001);
    z-index: 3;
}

.tm-event-resize-handle-top {
    left: 0;
    right: 0;
    top: 0;
    height: 6px;
    cursor: ns-resize;
}

.tm-event-resize-handle-bottom {
    left: 0;
    right: 0;
    bottom: 0;
    height: 6px;
    cursor: ns-resize;
}

.tm-event-resize-handle-right {
    top: 0;
    bottom: 0;
    right: 0;
    width: 6px;
    cursor: ew-resize;
}

.tm-event-resize-handle-left {
    top: 0;
    bottom: 0;
    left: 0;
    width: 6px;
    cursor: ew-resize;
}

/* ----- TIMELINE (resource swimlanes) ----- */
.tm-timeline {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.tm-timeline-scroll {
    flex: 1 1 auto;
    overflow: auto;
    min-height: 0;
}

.tm-timeline-body {
    display: flex;
    flex-direction: column;
}

.tm-timeline-header-row {
    display: flex;
    background: var(--mud-palette-background-gray);
    border-bottom: 1px solid var(--mud-palette-lines-default);
    position: sticky;
    top: 0;
    z-index: 4;
}

.tm-timeline-resource-header {
    flex: 0 0 160px;
    /* Pinned alongside the resource cells below it, so the column keeps its heading once the
       track is scrolled across days. Needs its own opaque background — the header row's
       background doesn't travel with a sticky child, so day blocks would show through. */
    position: sticky;
    left: 0;
    z-index: 5;
    background: var(--mud-palette-background-gray);
    padding: 8px 12px;
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
    border-right: 1px solid var(--mud-palette-lines-default);
}

.tm-timeline-time-header {
    flex: 1 1 auto;
    display: flex;
    min-width: 0;
}

.tm-timeline-day-block {
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--mud-palette-lines-default);
    min-width: 0;
}

.tm-timeline-day-block:last-child {
    border-right: none;
}

.tm-timeline-day-label {
    text-align: center;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
    padding: 4px 6px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.tm-timeline-day-label.tm-today {
    color: var(--mud-palette-primary);
}

.tm-timeline-hours {
    display: flex;
}

.tm-timeline-hour-label {
    flex: 1 0 0;
    /* Left-aligned so the label sits on the gridline where its hour actually starts.
       Events and drop-slots are positioned from each hour column's left edge, so a
       centred label would read ~30 min later than where a drop for that hour lands. */
    text-align: left;
    font-size: 0.7rem;
    color: var(--mud-palette-text-secondary);
    padding: 4px 0 4px 4px;
    border-right: 1px solid var(--mud-palette-lines-default);
    min-width: 56px;
}

.tm-timeline-hour-label:last-child {
    border-right: none;
}

.tm-timeline-resource-row {
    display: flex;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    min-height: 56px;
}

.tm-timeline-resource-cell {
    flex: 0 0 160px;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    border-right: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-surface);
    position: sticky;
    left: 0;
    /* Must beat .tm-timeline-event (z-index 2) — the events come later in the row's DOM, so an
       equal z-index lets them paint over the pinned technician name once the track scrolls. */
    z-index: 3;
}

.tm-timeline-resource-swatch {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
}

.tm-timeline-resource-name {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--mud-palette-text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tm-timeline-track {
    flex: 1 1 auto;
    position: relative;
    min-width: 0;
    background: var(--mud-palette-surface);
}

.tm-timeline-slots {
    display: flex;
    position: absolute;
    inset: 0;
    z-index: 1;
}

.tm-timeline-hour-cell {
    display: flex;
    border-right: 1px solid var(--mud-palette-lines-default);
    min-width: 56px;
}

.tm-timeline-hour-cell:last-child {
    border-right: none;
}

.tm-timeline-slot {
    flex: 1 1 0;
    cursor: pointer;
    border-right: 1px dashed transparent;
}

.tm-timeline-slot:hover {
    background: var(--mud-palette-action-default-hover);
}

/* After-hours tint for the horizontal timeline — same light gray as the ResourceDay
   (daily) view's .tm-rday-afterhours, so both views share cell colours. */
.tm-timeline-afterhours {
    position: absolute;
    top: 0;
    bottom: 0;
    background: color-mix(in srgb, var(--mud-palette-text-primary) 6%, transparent);
    z-index: 0;
    pointer-events: none;
}

.tm-timeline-event {
    position: absolute;
    top: 4px;
    bottom: 4px;
    color: #fff;
    border-radius: 4px;
    padding: 4px 8px;
    font-size: 0.78rem;
    box-shadow: 0 1px 2px rgba(0,0,0,0.15);
    cursor: grab;
    overflow: hidden;
    z-index: 2;
    user-select: none;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.tm-timeline-event:active {
    cursor: grabbing;
}

.tm-timeline-event-title {
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tm-timeline-event-sub {
    font-size: 0.72rem;
    opacity: 0.9;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tm-timeline-empty {
    padding: 24px;
    text-align: center;
    color: var(--mud-palette-text-secondary);
}

/* ----- RESOURCE DAY (time vertical, resources as columns) ----- */
.tm-resourceday {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    height: 100%;
}

.tm-rday-scroll {
    flex: 1 1 auto;
    overflow: auto;
    min-height: 0;
}

.tm-rday-inner {
    display: flex;
    flex-direction: column;
    min-height: 100%;
}

.tm-rday-header {
    display: grid;
    background: var(--mud-palette-background-gray);
    border-bottom: 1px solid var(--mud-palette-lines-default);
    position: sticky;
    top: 0;
    z-index: 5;
}

.tm-rday-header > .tm-rday-gutter {
    z-index: 6;
}

.tm-rday-gutter {
    border-right: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-surface);
    position: sticky;
    left: 0;
    z-index: 4;
}

.tm-rday-gutter-header {
    min-height: 32px;
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    align-items: center;
    justify-content: center;
}

.tm-rday-expand-btn {
    opacity: 0.6;
    transition: opacity 0.12s ease;
}

.tm-rday-expand-btn:hover {
    opacity: 1;
}

.tm-rday-toggle-active {
    opacity: 1 !important;
    color: var(--mud-palette-primary) !important;
}

/* Travel-time gap markers between consecutive appointments in a resource column */
.tm-rday-gap-marker {
    position: absolute;
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    z-index: 1;
    background:
        repeating-linear-gradient(
            45deg,
            color-mix(in srgb, var(--mud-palette-text-secondary) 8%, transparent),
            color-mix(in srgb, var(--mud-palette-text-secondary) 8%, transparent) 6px,
            transparent 6px,
            transparent 12px
        );
    border-top: 1px dashed color-mix(in srgb, var(--mud-palette-text-secondary) 35%, transparent);
    border-bottom: 1px dashed color-mix(in srgb, var(--mud-palette-text-secondary) 35%, transparent);
    overflow: hidden;
}

.tm-rday-gap-label {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
    background: var(--mud-palette-surface);
    padding: 1px 6px;
    border-radius: 10px;
    border: 1px solid color-mix(in srgb, var(--mud-palette-text-secondary) 25%, transparent);
    white-space: nowrap;
}

/* Traffic-light variants: green = enough slack, amber = tight, red = insufficient drive time. */
.tm-rday-gap-marker.tm-rday-gap-green {
    background:
        repeating-linear-gradient(
            45deg,
            color-mix(in srgb, var(--mud-palette-success) 15%, transparent),
            color-mix(in srgb, var(--mud-palette-success) 15%, transparent) 6px,
            transparent 6px,
            transparent 12px
        );
    border-top-color: color-mix(in srgb, var(--mud-palette-success) 55%, transparent);
    border-bottom-color: color-mix(in srgb, var(--mud-palette-success) 55%, transparent);
}

.tm-rday-gap-marker.tm-rday-gap-green .tm-rday-gap-label {
    color: var(--mud-palette-success-darken);
    border-color: color-mix(in srgb, var(--mud-palette-success) 55%, transparent);
}

.tm-rday-gap-marker.tm-rday-gap-amber {
    background:
        repeating-linear-gradient(
            45deg,
            color-mix(in srgb, var(--mud-palette-warning) 18%, transparent),
            color-mix(in srgb, var(--mud-palette-warning) 18%, transparent) 6px,
            transparent 6px,
            transparent 12px
        );
    border-top-color: color-mix(in srgb, var(--mud-palette-warning) 60%, transparent);
    border-bottom-color: color-mix(in srgb, var(--mud-palette-warning) 60%, transparent);
}

.tm-rday-gap-marker.tm-rday-gap-amber .tm-rday-gap-label {
    color: var(--mud-palette-warning-darken);
    border-color: color-mix(in srgb, var(--mud-palette-warning) 60%, transparent);
}

/* Excess-slack amber: same warning colour as "too tight", but the hatch leans the other
   way so a wasted-time gap is distinguishable at a glance from a short-on-drive-time one. */
.tm-rday-gap-marker.tm-rday-gap-amber.tm-rday-gap-slack {
    background:
        repeating-linear-gradient(
            -45deg,
            color-mix(in srgb, var(--mud-palette-warning) 18%, transparent),
            color-mix(in srgb, var(--mud-palette-warning) 18%, transparent) 6px,
            transparent 6px,
            transparent 12px
        );
}

.tm-rday-gap-marker.tm-rday-gap-red {
    background:
        repeating-linear-gradient(
            45deg,
            color-mix(in srgb, var(--mud-palette-error) 22%, transparent),
            color-mix(in srgb, var(--mud-palette-error) 22%, transparent) 6px,
            transparent 6px,
            transparent 12px
        );
    border-top-color: color-mix(in srgb, var(--mud-palette-error) 65%, transparent);
    border-bottom-color: color-mix(in srgb, var(--mud-palette-error) 65%, transparent);
}

.tm-rday-gap-marker.tm-rday-gap-red .tm-rday-gap-label {
    color: var(--mud-palette-error-darken);
    border-color: color-mix(in srgb, var(--mud-palette-error) 65%, transparent);
    font-weight: 700;
}

/* Row zoom: the ResourceDay hour rows + time-axis labels take their height from a CSS
   variable set on the grid root (--tm-hour-height), so the zoom in/out controls scale every
   appointment proportionally. Falls back to the historical 48px default when unset. */
.tm-resourceday .tm-timegrid-time,
.tm-resourceday .tm-timegrid-hour {
    height: var(--tm-hour-height, 48px);
}

/* Gutter that keeps event cards off the column borders. Applied to the cards only —
   the hour rules stay full-bleed so they read as one continuous line across columns. */
.tm-resourceday {
    --tm-rday-gutter: 6px;
}

/* In expanded mode let the event grow to fit its content (it may overflow downward past the
   end-of-slot mark; that's acceptable for content-rich cards and makes nothing get clipped). */
.tm-resourceday.tm-rows-expanded .tm-timegrid-event.tm-rday-event {
    height: auto !important;
    min-height: 0;
    overflow: visible;
}

.tm-resourceday.tm-rows-expanded .tm-rday-event-card {
    position: relative;
    inset: auto;
    margin: 2px 0;
    min-height: max-content;
}

/* In expanded mode show the intra-hour slot dividers (every SlotMinutes) so each
   row reads as a 30-min band instead of one tall hour cell. */
.tm-resourceday.tm-rows-expanded .tm-timegrid-slot {
    border-bottom: 1px dashed var(--mud-palette-lines-default);
}

.tm-resourceday.tm-rows-expanded .tm-timegrid-slot.tm-slot-last {
    border-bottom: none;
}

/* Sub-hour labels (HH:30, etc.) shown only in expanded mode. */
.tm-timegrid-subtime {
    position: absolute;
    right: 6px;
    font-size: 0.68rem;
    color: var(--mud-palette-text-secondary);
    opacity: 0.75;
    transform: translateY(-50%);
}

.tm-rday-body .tm-timegrid-times {
    position: sticky;
    left: 0;
    z-index: 3;
    background: var(--mud-palette-surface);
}

.tm-rday-res-header {
    border-right: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-surface);
    padding: 6px;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

/* Click-to-place: while backlog jobs are selected the grid is an armed target, so
   slots take a pointer cursor and highlight under it. Without this the slots look
   inert and the interaction is undiscoverable. */
.tm-rday-armed .tm-rday-slot {
    cursor: pointer;
}
.tm-rday-armed .tm-rday-slot:hover {
    background: color-mix(in srgb, var(--mud-palette-primary) 18%, transparent);
    box-shadow: inset 0 1px 0 var(--mud-palette-primary);
}

/* Same click-to-place affordance for the horizontal timeline view. Time runs
   left-to-right here, so the highlight edge is on the left of the hovered slot. */
.tm-timeline-armed .tm-timeline-slot {
    cursor: pointer;
}
.tm-timeline-armed .tm-timeline-slot:hover {
    background: color-mix(in srgb, var(--mud-palette-primary) 18%, transparent);
    box-shadow: inset 1px 0 0 var(--mud-palette-primary);
}

/* --- ResourceDay technician column header ------------------------------------
   Density pass: the header is fixed furniture above every column, so its height
   is stolen from the grid on every screen. Target is one identity row, a bar and
   a caption. Selection uses an inset shadow rather than a border so making a
   column active can't reflow the header. */
.tm-rday-tech {
    width: 100%;
    min-width: 0;
    cursor: pointer;
    padding: 4px 6px;
    border-radius: 6px;
    box-shadow: inset 0 0 0 2px transparent;
    transition: box-shadow 120ms ease;
}
.tm-rday-tech-active {
    box-shadow: inset 0 0 0 2px #1E88E5;
}
.tm-rday-tech-avatar {
    width: 20px !important;
    height: 20px !important;
    min-width: 20px !important;
    font-size: 0.625rem !important;
    flex: 0 0 20px;
}
/* Takes the row's free space itself rather than yielding it to a spacer, so the
   name gets every pixel the avatar, warning icon and kebab don't need before it
   starts ellipsing. */
.tm-rday-tech-name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
}
.tm-rday-tech-row {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}
/* Claw back the icon button's touch padding — at 20px avatar scale its default
   40px hit area sets the row height on its own. */
.tm-rday-tech-menu .mud-button-root {
    padding: 2px !important;
}
/* Carries the technician's accent rather than MudBlazor's semantic Color, so the
   bar, the avatar and that technician's cards all read as one colour. !important
   beats .mud-progress-linear-color-* on the inner bar. Load is conveyed by the
   bar's length and the % caption, not its hue. */
.tm-rday-tech-bar {
    height: 3px !important;
    margin: 5px 0 4px;
}
.tm-rday-tech-bar .mud-progress-linear-bar {
    background-color: var(--tm-tech-accent, var(--mud-palette-primary)) !important;
}
.tm-rday-tech-cap {
    display: block;
    font-size: 0.625rem;
    line-height: 1.4;
    white-space: nowrap;
    color: var(--mud-palette-text-secondary);
}
/* MudTooltip wraps its child in an inline-block span; without this the bar and
   caption collapse to content width instead of filling the column.
   Direct child ONLY — as a descendant selector this also matched the no-zone
   warning icon's tooltip in the name row, stretching it to the full row width
   and truncating the technician's name to a few characters. */
.tm-rday-tech > .mud-tooltip-root {
    display: block;
    width: 100%;
}
/* Inline tooltips inside a header row must never grow or shrink. */
.tm-rday-tech .tm-rday-tech-row .mud-tooltip-root {
    flex: 0 0 auto;
    display: inline-flex;
}

.tm-rday-res-header:last-child {
    border-right: none;
}

.tm-rday-empty-header {
    padding: 12px;
    color: var(--mud-palette-text-secondary);
}

.tm-rday-allday {
    display: grid;
    background: var(--mud-palette-surface);
    border-bottom: 1px solid var(--mud-palette-lines-default);
    min-height: 32px;
    max-height: 160px;
    overflow-y: auto;
    position: sticky;
    top: var(--tm-rday-header-h, 0);
    z-index: 4;
}

.tm-rday-allday-populated {
    padding-bottom: 2px;
}

.tm-rday-allday-cell {
    border-right: 1px solid var(--mud-palette-lines-default);
    padding: 4px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-height: 28px;
    cursor: pointer;
}

.tm-rday-allday-cell:last-child {
    border-right: none;
}

.tm-rday-body {
    display: grid;
    flex: 1 1 auto;
    min-height: 0;
}

.tm-rday-res-col {
    /* shares tm-timegrid-day-col so JS resize finds it */
    position: relative;
}

/* Business hours are plain surface; only the time outside them is tinted a light,
   theme-aware gray. Kept translucent — it paints over the hour rules, which must
   stay visible. */
.tm-rday-afterhours {
    position: absolute;
    left: 0;
    right: 0;
    background: color-mix(in srgb, var(--mud-palette-text-primary) 6%, transparent);
    z-index: 0;
    pointer-events: none;
}

.tm-rday-empty {
    padding: 24px;
    text-align: center;
    color: var(--mud-palette-text-secondary);
}

/* Drag-over feedback for slots */
.tm-slot-dragover {
    background: color-mix(in srgb, var(--mud-palette-primary) 18%, transparent) !important;
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
    position: relative;
    z-index: 1;
}

/* Hover time readout: show a cell's start time when the pointer is over it, in the
   ResourceDay (daily) and horizontal timeline grids. Scoped to slots that carry the
   attribute so cells without a time show nothing. Placed before the drag-over rule so
   that during an actual drag the primary-coloured label below wins on equal specificity. */
.tm-rday-slot[data-tm-slot-start]:hover,
.tm-timegrid-slot[data-tm-slot-start]:hover,
.tm-timeline-slot[data-tm-slot-start]:hover {
    position: relative;
}
.tm-rday-slot[data-tm-slot-start]:hover::after,
.tm-timegrid-slot[data-tm-slot-start]:hover::after,
.tm-timeline-slot[data-tm-slot-start]:hover::after {
    content: attr(data-tm-slot-start);
    position: absolute;
    left: 6px;
    top: 50%;
    transform: translateY(-50%);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    border: 1px solid var(--mud-palette-lines-default);
    font-size: 0.7rem;
    font-weight: 600;
    padding: 1px 6px;
    border-radius: 4px;
    pointer-events: none;
    white-space: nowrap;
    z-index: 3;
    box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}

.tm-rday-slot.tm-slot-dragover::after,
.tm-timegrid-slot.tm-slot-dragover::after,
.tm-timeline-slot.tm-slot-dragover::after {
    content: attr(data-tm-slot-start);
    position: absolute;
    left: 6px;
    top: 50%;
    transform: translateY(-50%);
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-contrastText, #fff);
    font-size: 0.7rem;
    font-weight: 600;
    padding: 2px 6px;
    border-radius: 4px;
    pointer-events: none;
    white-space: nowrap;
    z-index: 4;
    box-shadow: 0 1px 3px rgba(0,0,0,0.25);
}

/* Highlight the column receiving the drop */
.tm-rday-col-dragover {
    background: color-mix(in srgb, var(--mud-palette-primary) 4%, transparent);
}

/* Week view capacity grid: drop-target feedback while dragging a backlog job over a cell */
.tm-week-cell-dragover {
    background: color-mix(in srgb, var(--mud-palette-primary) 18%, transparent) !important;
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}

/* Cell that cannot receive the drop (technician has an all-day event that day) */
.tm-week-cell-nodrop {
    background: color-mix(in srgb, var(--mud-palette-error) 15%, transparent) !important;
    outline: 2px dashed var(--mud-palette-error);
    outline-offset: -2px;
    cursor: no-drop;
}

/* Day view event card: white surface, technician color as left stripe. The Scheduler sets
   an inline `background: <color>` on .tm-timegrid-event for resource identification —
   neutralise it here and let the inner template render the rich card. */
.tm-timegrid-event.tm-rday-event {
    background: transparent !important;
    color: var(--mud-palette-text-primary) !important;
    box-shadow: none !important;
    padding: 0 !important;
    border-radius: 0;
    overflow: hidden;
    left: var(--tm-rday-gutter, 6px) !important;
    right: var(--tm-rday-gutter, 6px) !important;
}

/* --tm-ev-accent is the owning technician's colour, set per card by the page.
   Every tint mixes into --mud-palette-surface rather than white so the card stays
   legible in dark mode. Falls back to the old neutral if the var is ever absent. */
.tm-rday-event-card {
    position: absolute;
    /* Top/bottom breathing room so back-to-back cards don't butt into each other or
       into the hour rule; the left/right gutter comes from .tm-rday-event. */
    inset: 2px 0;
    /* Fallbacks mix the surface into itself (a no-op) rather than `transparent`,
       which would mix the alpha down and leave the card see-through. */
    background: color-mix(in srgb, var(--tm-ev-accent, var(--mud-palette-surface)) 10%, var(--mud-palette-surface));
    /* Full-strength accent all the way around the card, per the design. */
    border: 1px solid var(--tm-ev-accent, var(--mud-palette-lines-default));
    border-radius: 4px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.08);
    padding: 6px 8px 6px 12px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    overflow: hidden;
}

.tm-rday-event-stripe {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 4px;
    border-radius: 0;
    background: var(--tm-ev-accent, transparent);
}

/* Event-type appointment: striped/hatched look so it visually reads as a blocked time rather
   than a billable job. The Scheduler still positions the box; we just restyle the surface. */
.tm-rday-event-card.tm-rday-event-card--event {
    background:
        repeating-linear-gradient(
            45deg,
            color-mix(in srgb, var(--mud-palette-error) 18%, var(--mud-palette-surface)),
            color-mix(in srgb, var(--mud-palette-error) 18%, var(--mud-palette-surface)) 8px,
            color-mix(in srgb, var(--mud-palette-error) 6%, var(--mud-palette-surface)) 8px,
            color-mix(in srgb, var(--mud-palette-error) 6%, var(--mud-palette-surface)) 16px
        );
    border: 1px solid color-mix(in srgb, var(--mud-palette-error) 35%, transparent);
    padding: 6px 8px;
}

.tm-rday-event-card--event .tm-rday-event-stripe {
    display: none;
}

/* Armed "add to this appointment" affordance: shown on job cards while backlog jobs are selected
   for placement, signalling the card is a drop/click target for adding the armed jobs. */
.tm-rday-event-card--armed {
    cursor: copy;
    outline: 2px dashed color-mix(in srgb, var(--mud-palette-primary) 60%, transparent);
    outline-offset: -2px;
}

.tm-rday-event-card--armed:hover {
    outline-style: solid;
    outline-color: var(--mud-palette-primary);
    background: color-mix(in srgb, var(--mud-palette-primary) 8%, var(--mud-palette-surface));
}

/* Hairline between the job number and the duration on the card's metadata line. A 1px
   rule rather than a dot or a bullet — the register separates with rules everywhere else. */
.tm-rday-meta-sep {
    flex: 0 0 auto;
    width: 1px;
    height: 10px;
    background: var(--mud-palette-lines-default);
}

/* The info icon: only present on cards JS has measured as actually clipped, so it doubles
   as the signal that there is more to see. Sits left of the unschedule cross. */
.tm-rday-info-btn {
    display: none;
    align-items: center;
    flex: 0 0 auto;
    cursor: help;
    opacity: 0.6;
}
.tm-rday-clipped .tm-rday-info-btn {
    display: inline-flex;
}
.tm-rday-info-btn:hover {
    opacity: 1;
}

/* Combined-appointment pill: a compact count chip shown after the primary job number
   when an appointment covers several jobs. Clicking it opens a menu listing every job,
   so the card header stays on one line instead of wrapping a run of job numbers. */
.job-planner-combined-pill {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 0 5px;
    height: 16px;
    border-radius: 8px;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    color: var(--tm-ev-accent, var(--mud-palette-primary));
    background: color-mix(in srgb, var(--tm-ev-accent, var(--mud-palette-primary)) 16%, var(--mud-palette-surface));
    border: 1px solid color-mix(in srgb, var(--tm-ev-accent, var(--mud-palette-primary)) 45%, transparent);
    transition: background 0.12s ease;
}
.job-planner-combined-pill:hover {
    background: color-mix(in srgb, var(--tm-ev-accent, var(--mud-palette-primary)) 30%, var(--mud-palette-surface));
}

/* ── Hover detail popup ───────────────────────────────────────────────────
   Short appointments clip their rich detail (address, duration, scheduling warnings,
   job link, status) because the card fills a small slot with overflow:hidden. Hovering
   a clipped card floats a copy of that same card above it, with a tail pointing back
   down at the appointment it belongs to.

   The popup is built in JS (see scheduler-interop.js) as a clone of the card's own DOM,
   so it can never drift out of sync with the template, and it is appended to <body>
   rather than the grid — inside the grid it would be clipped by the scroller. It is
   pointer-transparent so it cannot steal the hover that opened it, which also means it
   never needs hover-into handling. */
.tm-rday-peek-pop {
    position: fixed;
    z-index: 1200;               /* over the scheduler, under MudBlazor dialogs/overlays */
    width: 300px;
    max-width: calc(100vw - 16px);
    pointer-events: none;
    filter: drop-shadow(0 6px 20px rgba(0, 0, 0, 0.28));
}

/* The clone still carries .tm-rday-event-card, which is absolutely positioned to fill its
   slot. Released here so the popup sizes to its content instead. */
.tm-rday-peek-pop .tm-rday-event-card {
    position: relative;
    inset: auto;
    min-height: 0;
    height: auto;
    overflow: visible;
    /* The drop-shadow on the wrapper follows the tail's silhouette; a second box-shadow
       here would draw a rectangle through it. */
    box-shadow: none;
    border-radius: 6px;
}

/* Nothing in the popup is interactive, so drop the affordances that imply otherwise. */
.tm-rday-peek-pop .tm-rday-info-btn,
.tm-rday-peek-pop .tm-event-resize-handle {
    display: none;
}

/* Full text rather than the card's truncation — showing the detail is the whole point. */
.tm-rday-peek-pop .tm-rday-event-card * {
    white-space: normal !important;
    overflow: visible !important;
    text-overflow: clip !important;
}

/* The tail: a rotated square tucked under the card's edge, painted with the same accent
   tint and border as the card so it reads as one shape. --tm-peek-tail-x is set by JS to
   the appointment's horizontal centre, so the tail keeps pointing at the card even when the
   popup is clamped against a viewport edge.
   Default placement is above the appointment, so the tail sits on the bottom edge pointing
   down; its top/left borders are the ones the card hides, leaving bottom/right visible. */
.tm-rday-peek-pop::after {
    content: "";
    position: absolute;
    left: var(--tm-peek-tail-x, 50%);
    bottom: -6px;
    width: 10px;
    height: 10px;
    margin-left: -5px;
    transform: rotate(45deg);
    background: color-mix(in srgb, var(--tm-ev-accent, var(--mud-palette-surface)) 10%, var(--mud-palette-surface));
    border: 1px solid var(--tm-ev-accent, var(--mud-palette-lines-default));
    border-top: 0;
    border-left: 0;
}

/* Flipped below when there is no room above — tail moves to the top edge, pointing up. */
.tm-rday-peek-pop--below::after {
    bottom: auto;
    top: -6px;
    border-top: 1px solid var(--tm-ev-accent, var(--mud-palette-lines-default));
    border-left: 1px solid var(--tm-ev-accent, var(--mud-palette-lines-default));
    border-bottom: 0;
    border-right: 0;
}

/* The source card while its popup is open: a soft ring so it is obvious which appointment
   the floating detail belongs to. */
.tm-rday-event.tm-rday-peek .tm-rday-event-card {
    outline: 2px solid var(--tm-ev-accent, var(--mud-palette-primary));
    outline-offset: -1px;
}

/* ── "Add related jobs" pill ──────────────────────────────────────────────
   Non-blocking prompt shown after a single job is scheduled. Rendered directly in
   the page (not a MudBlazor snackbar) so it carries no snackbar chrome; pinned to
   the bottom-centre of the viewport. */
.related-jobs-pill-host {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 24px;
    display: flex;
    justify-content: center;
    pointer-events: none;                /* let clicks through the empty gutters */
    z-index: 2000;
}

.related-jobs-pill-host .related-jobs-pill {
    pointer-events: auto;                /* but the pill itself is interactive */
}

.related-jobs-pill {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 8px 8px 18px;
    border-radius: 999px;
    background: #12233a;                 /* dark navy pill from the mock */
    color: #eaf1fb;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
    min-width: 320px;
    max-width: 520px;
}

.related-jobs-pill__icon {
    color: #eaf1fb;
    font-size: 1.4rem;
    flex: 0 0 auto;
}

.related-jobs-pill__text {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
    flex: 1 1 auto;
    min-width: 0;
}

.related-jobs-pill__title {
    font-weight: 700;
    font-size: 0.95rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.related-jobs-pill__sub {
    font-size: 0.8rem;
    color: #9db4d4;
}

.related-jobs-pill__action.mud-button-root {
    flex: 0 0 auto;
    color: #4ea3ff;
    font-weight: 700;
    letter-spacing: 0.02em;
    padding: 4px 10px;
    min-width: 0;
}

.related-jobs-pill__action.mud-button-root:hover {
    background: rgba(78, 163, 255, 0.12);
}

/* Close button sits inside the pill, just after REVIEW. */
.related-jobs-pill__close.mud-button-root {
    flex: 0 0 auto;
    color: #9db4d4;
}

.related-jobs-pill__close.mud-button-root:hover {
    color: #eaf1fb;
    background: rgba(255, 255, 255, 0.08);
}
