/* Standard Planner grid. Blocks are positioned as a percentage
   of a 24-hour day column; a 30-minute raster is drawn with a CSS gradient so the
   DOM stays light. Colours use Tabler CSS variables to stay theme-aware. */

.planner {
    --planner-hour-height: 40px;
    --planner-day-height: calc(var(--planner-hour-height) * 24);
    /* Tall enough for a phase tag stacked above the date on setup/teardown days. */
    --planner-head-height: 48px;
    /* One parallel shift's worth of column. */
    --planner-lane-width: 170px;
}

/* Positioned so the selection band can be placed in the grid's own content coordinates and scroll
   with it. */
.planner-grid {
    position: relative;
    display: flex;
    align-items: stretch;
    overflow-x: auto;
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
    background: var(--tblr-bg-surface);
}

/*
 * The hour axis is frozen to the left edge: over a multi-day event the grid scrolls horizontally and
 * the manager would otherwise lose the only time reference on the screen. It needs an opaque
 * background and a z-index above .planner-block (which reaches 6 for the paint preview) so the blocks
 * scroll underneath it rather than through it.
 */
.planner-axis {
    flex: 0 0 56px;
    border-right: 1px solid var(--tblr-border-color);
    user-select: none;
    position: sticky;
    left: 0;
    z-index: 10;
    background: var(--tblr-bg-surface);
}

.planner-axis-head {
    height: var(--planner-head-height);
    border-bottom: 1px solid var(--tblr-border-color);
}

.planner-axis-hour {
    height: var(--planner-hour-height);
    font-size: 11px;
    color: var(--tblr-secondary);
    text-align: right;
    padding-right: 6px;
    transform: translateY(-7px);
}

/*
 * One lane is wide enough that a shift still shows its full "10:00-12:00" rather than falling back
 * to the start time alone, and a day is as many lanes wide as its busiest moment needs. A day with
 * eight parallel shifts is therefore eight times as wide as a quiet one, and the grid scrolls
 * horizontally to reach it: nothing is ever hidden for want of width.
 */
.planner-day {
    --planner-lanes: 1;
    flex: 1 1 calc(var(--planner-lanes) * var(--planner-lane-width));
    min-width: calc(var(--planner-lanes) * var(--planner-lane-width));
    border-right: 1px solid var(--tblr-border-color);
}

.planner-day:last-child {
    border-right: 0;
}

.planner-day-head {
    height: var(--planner-head-height);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    line-height: 1.3;
    font-weight: 600;
    font-size: 13px;
    border-bottom: 1px solid var(--tblr-border-color);
    position: sticky;
    top: 0;
}

.planner-day-label {
    white-space: nowrap;
}

.planner-phase-tag {
    font-size: 10px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--tblr-secondary);
}

/* Setup/teardown columns get a distinct background. */
.planner-phase-setup .planner-day-body,
.planner-phase-teardown .planner-day-body {
    background-color: color-mix(in srgb, var(--tblr-warning) 8%, transparent);
}

.planner-day-body {
    position: relative;
    height: var(--planner-day-height);
    background-image:
        repeating-linear-gradient(
            to bottom,
            var(--tblr-border-color-light, rgba(98, 105, 118, .16)) 0,
            var(--tblr-border-color-light, rgba(98, 105, 118, .16)) 1px,
            transparent 1px,
            transparent var(--planner-hour-height)
        );
    cursor: default;
    touch-action: none;
}

/* The 30-minute raster line, lighter than the hour line. */
.planner-day-body::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image:
        repeating-linear-gradient(
            to bottom,
            transparent 0,
            transparent calc(var(--planner-hour-height) / 2 - 1px),
            var(--tblr-border-color-light, rgba(98, 105, 118, .08)) calc(var(--planner-hour-height) / 2 - 1px),
            var(--tblr-border-color-light, rgba(98, 105, 118, .08)) calc(var(--planner-hour-height) / 2),
            transparent calc(var(--planner-hour-height) / 2)
        );
}

/*
 * A column laid out so the time survives the worst case. The block clips its overflow and a
 * half-hour shift is only 20px tall, so a wrapped time range lost its second line entirely - the
 * manager saw "10:00-" and nothing else. The time is therefore a nowrap row that never shrinks, and
 * the title takes whatever height is left.
 *
 * The block is also a query container so the range can adapt to how narrow its lane is.
 */
.planner-block {
    position: absolute;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    /* size, not inline-size: how the range is laid out depends on the block's height as well. */
    container-type: size;
    margin-left: 2px;
    min-width: 14px;
    min-height: 16px;
    border-radius: 4px;
    padding: 2px 5px;
    font-size: 11px;
    line-height: 1.25;
    overflow: hidden;
    cursor: grab;
    border: 1px solid transparent;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .12);
}

.planner-block:focus-visible {
    outline: 2px solid var(--tblr-primary);
    outline-offset: 1px;
}

.planner-block.is-dragging {
    cursor: grabbing;
    opacity: .85;
    z-index: 5;
}

.planner-block.is-selected {
    outline: 2px solid var(--tblr-primary);
    outline-offset: 1px;
    z-index: 4;
}

/* Public volunteer shifts vs staff-only get clearly different treatment. */
.planner-audience-public {
    background-color: color-mix(in srgb, var(--tblr-blue) 18%, var(--tblr-bg-surface));
    border-color: var(--tblr-blue);
}

.planner-audience-staff {
    background-color: color-mix(in srgb, var(--tblr-purple) 18%, var(--tblr-bg-surface));
    border-color: var(--tblr-purple);
}

.planner-block-invalid {
    border: 2px solid var(--tblr-red) !important;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--tblr-red) 25%, transparent);
    z-index: 3;
}

/* Draft shifts read differently from published (dashed, not colour alone). */
.planner-block-draft {
    border-style: dashed;
    opacity: .9;
}

.planner-block-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 4px;
    /* Never give up height to the title: the time is what the block exists to tell you. */
    flex: 0 0 auto;
}

.planner-block-time {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.planner-block-title {
    display: block;
    flex: 1 1 auto;
    min-height: 0;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
}

.planner-block-badge {
    flex: 0 0 auto;
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: .03em;
    opacity: .75;
}

/*
 * Lanes split a day column between simultaneous shifts, so a block can get far too narrow for
 * "10:00-12:00" on one line - the range needs about 76px and two lanes leave under 70px. Rather
 * than clip it mid-digit:
 *
 *   - a block with the height to spare stacks the end time under the start, keeping both;
 *   - a short one (under two lines' worth) falls back to the start time, which is what orients the
 *     manager, and then drops the draft badge;
 *
 * and the full range stays in the title tooltip and the accessible name throughout.
 */
@container (max-width: 80px) and (min-height: 32px) {
    /*
     * A block box starts its own line while the inherited nowrap keeps each time whole. Letting the
     * range wrap normally instead broke after the dash and cost a third line.
     */
    .planner-block-to {
        display: block;
    }
}

@container (max-width: 80px) and (max-height: 31.99px) {
    .planner-block-to {
        display: none;
    }
}

@container (max-width: 44px) {
    .planner-block-badge {
        display: none;
    }
}

.planner-block-overnight {
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    font-size: 10px;
}

.planner-block-resize {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 6px;
    cursor: ns-resize;
}

/* Horizontal scroll affordance. The strip has no height of its own and sticks to the top of the
   viewport, so the arrows stay level with the day headers however far down the 24-hour column the
   page is scrolled. */
.planner-scroll {
    position: relative;
}

.planner-scroll-nav {
    position: sticky;
    top: 0;
    height: 0;
    z-index: 20;
}

.planner-scroll-arrow {
    position: absolute;
    top: calc(var(--planner-head-height) / 2);
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--tblr-border-color);
    border-radius: 50%;
    background: var(--tblr-bg-surface);
    color: var(--tblr-body-color);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
    cursor: pointer;
}

.planner-scroll-arrow:hover {
    background: var(--tblr-bg-surface-secondary, var(--tblr-bg-surface));
}

.planner-scroll-arrow-prev {
    /* Clear of the hour axis, which is frozen over the left edge of the grid. */
    left: 60px;
}

.planner-scroll-arrow-next {
    right: 4px;
}

/* The rubber band drawn while selecting a range of shifts. It is a child of the scroll container
   and positioned in its content coordinates, so it stays over the same shifts when the grid scrolls
   under it. */
.planner-marquee {
    position: absolute;
    border: 1px solid var(--tblr-primary);
    background-color: color-mix(in srgb, var(--tblr-primary) 15%, transparent);
    border-radius: 2px;
    pointer-events: none;
    z-index: 7;
}

/* Dragging a band across the grid must not start a text selection instead. */
.planner.is-marqueeing,
.planner.is-marqueeing .planner-day-body {
    user-select: none;
}

/* The transient block drawn while painting a new shift. */
.planner-paint-preview {
    position: absolute;
    left: 3px;
    right: 3px;
    border-radius: 4px;
    background-color: color-mix(in srgb, var(--tblr-blue) 28%, transparent);
    border: 1px dashed var(--tblr-blue);
    pointer-events: none;
    z-index: 6;
}

.planner-legend-swatch {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 3px;
    vertical-align: -1px;
}

.planner-legend-swatch.planner-audience-public {
    background-color: var(--tblr-blue);
}

.planner-legend-swatch.planner-audience-staff {
    background-color: var(--tblr-purple);
}

.planner-legend-swatch.planner-phase-setup {
    background-color: color-mix(in srgb, var(--tblr-warning) 40%, transparent);
}

/* Advanced Matrix Planner. Wide table, scrolls horizontally; the
   first "When" column and the header stay put while scrolling. */
.matrix-scroll {
    overflow: auto;
    max-height: 70vh;
}

.matrix-table th,
.matrix-table td {
    vertical-align: middle;
}

.matrix-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--tblr-bg-surface);
}

.matrix-group {
    background: color-mix(in srgb, var(--tblr-primary) 10%, var(--tblr-bg-surface)) !important;
}

.matrix-position {
    font-size: 12px;
    white-space: nowrap;
    min-width: 6rem;
}

.matrix-sticky-col {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--tblr-bg-surface);
}

.matrix-cell {
    min-width: 6rem;
}

.matrix-cell-open {
    background: color-mix(in srgb, var(--tblr-warning) 8%, transparent);
}

.matrix-cell-filled {
    background: color-mix(in srgb, var(--tblr-blue) 6%, transparent);
}

/*
 * The cell's control is a real <button> so it can be reached by keyboard, but it must fill the cell
 * and read as part of the table rather than as a button.
 */
.matrix-cell-button {
    display: block;
    width: 100%;
    min-height: 2.25rem;
    padding: 0.25rem;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.matrix-cell-button:hover {
    background: color-mix(in srgb, var(--tblr-primary) 10%, transparent);
}

.matrix-cell-button:focus-visible {
    outline: 2px solid var(--tblr-primary);
    outline-offset: -2px;
}

/* The affordance for a position that is not enabled on this shift: visible only on hover/focus. */
.matrix-cell-empty {
    color: var(--tblr-border-color);
}

.matrix-cell-button:hover .matrix-cell-empty,
.matrix-cell-button:focus-visible .matrix-cell-empty {
    color: var(--tblr-primary);
}

.matrix-note {
    font-size: 10px;
    color: var(--tblr-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 8rem;
}

/* Planning Availability grid. Value colours read in both themes and
   are paired with the value label, never relying on colour alone. */
.avail-swatch-preferred { --avail-color: var(--tblr-green); }
.avail-swatch-available { --avail-color: var(--tblr-blue); }
.avail-swatch-avoid { --avail-color: var(--tblr-orange); }
.avail-swatch-unavailable { --avail-color: var(--tblr-red); }

label.avail-swatch-preferred.active,
label.avail-swatch-available.active,
label.avail-swatch-avoid.active,
label.avail-swatch-unavailable.active,
.btn-check:checked + label[class*="avail-swatch-"] {
    background-color: color-mix(in srgb, var(--avail-color) 25%, transparent);
    border-color: var(--avail-color);
}

.avail-block {
    position: absolute;
    left: 2px;
    right: 2px;
    border-radius: 3px;
    background-color: color-mix(in srgb, var(--avail-color) 40%, transparent);
    border: 1px solid var(--avail-color);
    pointer-events: none;
}

.avail-overlay {
    position: absolute;
    left: 2px;
    right: 2px;
    border-radius: 3px;
    background-image: repeating-linear-gradient(
        45deg,
        color-mix(in srgb, var(--tblr-secondary) 30%, transparent) 0,
        color-mix(in srgb, var(--tblr-secondary) 30%, transparent) 6px,
        transparent 6px,
        transparent 12px
    );
    border: 1px solid var(--tblr-secondary);
    font-size: 9px;
    color: var(--tblr-secondary);
    padding: 1px 3px;
    overflow: hidden;
    z-index: 3;
}

/* Staff schedule: a column per person, a row per half hour. The time column and the name row are
   frozen, because a block in the middle of a wide schedule says nothing once the name or the hour
   has scrolled away. */
.schedule-scroll {
    max-height: 78vh;
    overflow: auto;
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
}

.schedule-table {
    margin-bottom: 0;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 12px;
}

.schedule-table > thead > tr > th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--tblr-bg-surface);
    border-bottom: 1px solid var(--tblr-border-color);
    text-align: center;
    vertical-align: bottom;
}

.schedule-corner {
    left: 0;
    z-index: 4 !important;
    min-width: 7rem;
}

.schedule-when {
    position: sticky;
    left: 0;
    z-index: 1;
    min-width: 7rem;
    background: var(--tblr-bg-surface);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.schedule-day {
    display: block;
    font-size: 11px;
    color: var(--tblr-primary);
}

.schedule-name {
    min-width: 6rem;
    max-width: 9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The half hours of one day read as a block, with a line where the next day begins. */
.schedule-day-start > th,
.schedule-day-start > td {
    border-top: 2px solid var(--tblr-border-color);
}

/* Tabler paints every cell with an inset shadow the width of the cell, which covers a plain
   background and left only the border showing. Setting the state variable is what actually fills
   the cell, and the fill is the point: it is how an occupied half hour reads at a glance. */
.schedule-busy {
    --tblr-table-bg-state: color-mix(in srgb, var(--tblr-blue) 22%, var(--tblr-bg-surface));
}

/* Only the slot a shift starts in carries its detail; the rest are the same block continuing, and
   the line is what separates two shifts that run back to back. */
.schedule-busy.is-start {
    border-top: 2px solid var(--tblr-blue);
}

.schedule-hours {
    display: block;
    font-weight: 600;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.schedule-title {
    display: block;
    font-size: 11px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.schedule-location {
    display: block;
    font-size: 10px;
    color: var(--tblr-secondary);
    white-space: nowrap;
}

/* A staff shift with nobody on it belongs to no column, so it takes a row of its own and has to be
   impossible to scroll past. */
.schedule-missing > td {
    --tblr-table-bg-state: color-mix(in srgb, var(--tblr-red) 25%, var(--tblr-bg-surface));
    color: var(--tblr-red-fg, #7a1414);
    font-weight: 600;
}

.schedule-missing > th {
    --tblr-table-bg-state: color-mix(in srgb, var(--tblr-red) 14%, var(--tblr-bg-surface));
    background: color-mix(in srgb, var(--tblr-red) 14%, var(--tblr-bg-surface));
}

/* Info Desk chat. Simple, theme-aware bubbles. */
.chat-thread { max-height: 55vh; overflow-y: auto; }
.chat-bubble { max-width: 80%; margin-bottom: 8px; padding: 6px 10px; border-radius: 8px;
    background: color-mix(in srgb, var(--tblr-secondary) 12%, transparent); }
.chat-bubble-mine { margin-left: auto; background: color-mix(in srgb, var(--tblr-primary) 16%, transparent); }
.chat-bubble-internal { background: color-mix(in srgb, var(--tblr-warning) 16%, transparent); font-style: italic; }
.chat-bubble-meta { display: flex; gap: 6px; align-items: baseline; font-size: 11px; }
.chat-bubble-body { white-space: pre-wrap; word-break: break-word; }
.chat-bubble-image { max-width: 240px; border-radius: 6px; margin-top: 4px; }
.chat-typing { padding: 4px 10px; }

.planner.is-painting .planner-day-body,
.planner.is-painting .planner-block,
.planner.is-painting .planner-block-resize {
    cursor: crosshair;
}
