// =============================================================================
// Appointment calendar
//
// Built on the same px-* primitives as the appointments day list — the two
// screens are one click apart and a clinic switching between them should not
// feel it cross a border. Everything here is either a FullCalendar override or
// a shape the list screen did not already need: the toolbar, the appointment
// card, the slide-over and the legend.
//
// FullCalendar ships its own CSS with its own variables. Overriding through
// those variables where they exist is what keeps this from becoming a wall of
// !important the next minor release breaks.
// =============================================================================

.px-calendar {
    overflow: hidden;
}

// ---- Toolbar ----------------------------------------------------------------

.px-calendar__toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem;
    padding: 1.125rem 1.25rem;
    border-bottom: 1px solid var(--px-border);
}

.px-calendar__nav {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.px-calendar__step {
    display: inline-grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    border: 1px solid var(--px-border);
    border-radius: var(--px-radius-sm);
    background: var(--clinic-surface);
    color: var(--px-muted);
    transition: all var(--px-ease);

    &:hover,
    &:focus-visible {
        color: var(--px-text);
        border-color: color-mix(in srgb, var(--px-primary) 40%, var(--px-border));
        box-shadow: var(--px-shadow-md);
    }
}

.px-calendar__title {
    flex: 1 1 12rem;
    min-width: 0;
    margin: 0;
    text-align: center;
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--px-text);
}

// The view switcher wraps under the title on narrow screens rather than
// squeezing four labels into a strip nobody can hit with a thumb.
.px-calendar__views {
    justify-content: flex-end;
}

@media (max-width: 767.98px) {
    .px-calendar__toolbar {
        justify-content: center;
    }

    .px-calendar__title {
        order: -1;
        flex-basis: 100%;
    }

    .px-calendar__views {
        justify-content: center;
        width: 100%;
    }
}

.px-calendar__body {
    padding: 1rem 1.25rem 1.25rem;
    position: relative;
}

// ---- The grid itself --------------------------------------------------------

.px-calendar .fc {
    --fc-border-color: var(--px-border);
    --fc-page-bg-color: transparent;
    --fc-neutral-bg-color: var(--clinic-surface-alt);
    --fc-today-bg-color: color-mix(in srgb, var(--px-primary) 6%, transparent);
    --fc-now-indicator-color: #ef4444;
    --fc-event-border-color: transparent;
    --fc-small-font-size: 0.75rem;

    font-size: 0.875rem;
}

// Day-of-week headings. Sticky is already on via stickyHeaderDates; this is
// what makes the strip opaque enough to sit over scrolling content.
.px-calendar .fc .fc-col-header-cell {
    padding: 0.625rem 0.25rem;
    background: var(--clinic-surface-alt);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--px-muted);
}

// Saturday and Sunday, tinted so a week reads as a shape before it is read as
// seven words.
.px-calendar .fc .fc-day-sat,
.px-calendar .fc .fc-day-sun {
    background: color-mix(in srgb, var(--clinic-surface-alt) 55%, transparent);
}

.px-calendar .fc .fc-daygrid-day-frame {
    padding: 0.25rem;
    transition: background-color var(--px-ease);
}

.px-calendar .fc .fc-daygrid-day:hover .fc-daygrid-day-frame {
    background: color-mix(in srgb, var(--px-primary) 4%, transparent);
}

.px-calendar .fc .fc-daygrid-day-number {
    padding: 0.375rem 0.5rem;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--px-muted-strong);
}

// Today: a ring rather than a heavy fill, so the appointments inside it stay
// the thing being read.
.px-calendar .fc .fc-day-today {
    box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--px-primary) 45%, transparent);
    border-radius: 0.5rem;
}

.px-calendar .fc .fc-day-today .fc-daygrid-day-number {
    color: var(--px-primary);
    font-weight: 700;
}

.px-calendar .fc .fc-daygrid-day.fc-day-other {
    opacity: 0.55;
}

// How many are booked that day, counted from the event store rather than from
// the three cards the cell has room to draw.
.px-day-count {
    display: inline-grid;
    place-items: center;
    min-width: 1.25rem;
    height: 1.25rem;
    margin: 0.375rem 0 0 0.375rem;
    padding: 0 0.3125rem;
    border-radius: 999px;
    font-size: 0.6875rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--px-primary);
    background: color-mix(in srgb, var(--px-primary) 12%, transparent);
}

.px-calendar .fc .fc-day-today .px-day-count {
    color: #fff;
    background: var(--px-primary);
}

.px-calendar .fc .fc-more-link {
    display: inline-block;
    margin: 0.125rem 0.25rem 0.25rem;
    padding: 0.125rem 0.4375rem;
    border-radius: 999px;
    background: var(--clinic-surface-alt);
    font-size: 0.6875rem;
    font-weight: 600;
    color: var(--px-muted-strong);

    &:hover {
        background: color-mix(in srgb, var(--px-primary) 12%, transparent);
        color: var(--px-primary);
    }
}

.px-calendar .fc .fc-timegrid-slot {
    height: 2.75rem;
}

.px-calendar .fc .fc-list-day-cushion {
    background: var(--clinic-surface-alt);
}

.px-calendar .fc .fc-list-event:hover td {
    background: color-mix(in srgb, var(--px-primary) 6%, transparent);
}

// ---- The appointment card ---------------------------------------------------

.px-calendar .fc .fc-event {
    border: 0;
    background: transparent;
    box-shadow: none;
    margin-bottom: 0.1875rem;
}

.px-calendar .fc .fc-event:focus-visible {
    outline: 2px solid var(--px-primary);
    outline-offset: 2px;
}

.px-appt {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.5rem;
    border-radius: 0.625rem;
    // The status colour arrives as a border, not a fill: a solid block of
    // colour behind 12px text is where contrast goes to die.
    border-inline-start: 3px solid var(--px-appt-tone, var(--px-muted));
    background: var(--clinic-surface);
    box-shadow: var(--px-shadow);
    cursor: pointer;
    overflow: hidden;
    animation: px-appt-in 200ms ease both;
    transition: transform var(--px-ease), box-shadow var(--px-ease);

    &:hover {
        transform: translateY(-1px);
        box-shadow: var(--px-shadow-md);
    }
}

@keyframes px-appt-in {
    from { opacity: 0; transform: translateY(2px); }
    to { opacity: 1; transform: none; }
}

.px-appt__body {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1 1 auto;
    line-height: 1.25;
}

.px-appt__name {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--px-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.px-appt__meta {
    font-size: 0.6875rem;
    color: var(--px-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.px-appt__status {
    flex: 0 0 auto;
    padding: 0.0625rem 0.375rem;
    border-radius: 999px;
    font-size: 0.625rem;
    font-weight: 600;
    color: var(--px-appt-tone, var(--px-muted));
    background: color-mix(in srgb, var(--px-appt-tone, var(--px-muted)) 12%, transparent);
}

// Hidden by a filter. `display: none` rather than opacity, so FullCalendar's
// row heights collapse with it instead of leaving gaps where a card was.
.px-appt--filtered {
    display: none !important;
}

// In the narrow columns of a month grid there is no room for a status pill
// beside a name — the colour on the edge carries it there.
@media (max-width: 1199.98px) {
    .fc-daygrid .px-appt__status {
        display: none;
    }
}

// ---- Status tones -----------------------------------------------------------
//
// One declaration per status, feeding both the card edge and the legend swatch.
// These match the pill colours the day list uses for the same words.

.fc-status-scheduled { --px-appt-tone: #64748b; }
.fc-status-confirmed { --px-appt-tone: #2563eb; }
.fc-status-checked_in { --px-appt-tone: #6d4cff; }
.fc-status-in_progress { --px-appt-tone: #b45309; }
.fc-status-completed { --px-appt-tone: #047857; }
.fc-status-cancelled { --px-appt-tone: #b91c1c; }
.fc-status-no_show { --px-appt-tone: #be123c; }

[data-bs-theme="dark"] {
    .fc-status-scheduled { --px-appt-tone: #94a3b8; }
    .fc-status-confirmed { --px-appt-tone: #93c5fd; }
    .fc-status-checked_in { --px-appt-tone: #c4b5fd; }
    .fc-status-in_progress { --px-appt-tone: #fcd34d; }
    .fc-status-completed { --px-appt-tone: #6ee7b7; }
    .fc-status-cancelled { --px-appt-tone: #fca5a5; }
    .fc-status-no_show { --px-appt-tone: #fda4af; }
}

// ---- Legend -----------------------------------------------------------------

.px-legend {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--px-border);
}

.px-legend__item {
    display: inline-flex;
    align-items: center;
    gap: 0.4375rem;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--px-muted-strong);
}

.px-legend__swatch {
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 0.25rem;
    background: var(--px-appt-tone, var(--px-muted));
}

.px-legend__note {
    flex: 1 1 16rem;
    min-width: 0;
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--px-muted);
}

// ---- Loading ----------------------------------------------------------------

.px-skeleton {
    display: inline-block;
    width: 2.5rem;
    height: 1.25rem;
    border-radius: 0.375rem;
    background: linear-gradient(
        90deg,
        var(--clinic-surface-alt) 25%,
        color-mix(in srgb, var(--px-muted) 12%, var(--clinic-surface-alt)) 37%,
        var(--clinic-surface-alt) 63%
    );
    background-size: 400% 100%;
    animation: px-skeleton 1.4s ease infinite;
}

@keyframes px-skeleton {
    from { background-position: 100% 50%; }
    to { background-position: 0 50%; }
}

// Dimmed rather than blanked while a range loads: the previous month stays
// readable, so paging does not flash an empty grid at somebody mid-sentence
// with a patient. The same choice the filtered listings make.
.px-calendar--busy .px-calendar__body {
    opacity: 0.55;
    pointer-events: none;
    transition: opacity 120ms ease-in-out;
}

// ---- Slide-over -------------------------------------------------------------

.px-drawer {
    position: fixed;
    inset: 0;
    z-index: 1055;
}

.px-drawer__scrim {
    position: absolute;
    inset: 0;
    background: rgb(15 23 42 / 45%);
    opacity: 0;
    transition: opacity var(--px-ease);
}

.px-drawer__panel {
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    width: min(26rem, 100%);
    display: flex;
    flex-direction: column;
    background: var(--clinic-surface);
    box-shadow: var(--px-shadow-lg);
    // Translated by its own width so the transform works out to the same
    // distance whichever side the drawer is pinned to under RTL.
    transform: translateX(100%);
    transition: transform var(--px-ease);
    overflow: hidden;
}

[dir="rtl"] .px-drawer__panel {
    transform: translateX(-100%);
}

.px-drawer--open {
    .px-drawer__scrim { opacity: 1; }
    .px-drawer__panel { transform: none; }
}

.px-drawer__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.25rem;
    border-bottom: 1px solid var(--px-border);
}

.px-drawer__eyebrow {
    margin: 0;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--px-muted);
}

.px-drawer__title {
    margin: 0.125rem 0 0;
    font-size: 1.125rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--px-text);
}

.px-drawer__body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 0.5rem 1.25rem 1.25rem;
}

.px-drawer__note {
    margin: 1rem 0 0;
    padding-top: 1rem;
    border-top: 1px solid var(--px-border);
    font-size: 0.75rem;
    line-height: 1.6;
    color: var(--px-muted);
}

.px-drawer__foot {
    display: flex;
    gap: 0.5rem;
    padding: 1rem 1.25rem;
    border-top: 1px solid var(--px-border);
}

// A drawer that covers the screen on a phone is just a page; giving it the full
// width is what stops it being a letterbox with a scrollbar.
@media (max-width: 575.98px) {
    .px-drawer__panel {
        width: 100%;
    }
}

// ---- Floating action --------------------------------------------------------

.px-fab {
    position: fixed;
    inset-block-end: 1.25rem;
    inset-inline-end: 1.25rem;
    z-index: 1040;
    display: grid;
    place-items: center;
    width: 3.5rem;
    height: 3.5rem;
    border-radius: 50%;
    color: #fff;
    font-size: 1.25rem;
    background: linear-gradient(135deg, var(--px-primary), var(--px-primary-2));
    box-shadow: 0 8px 20px -4px rgb(109 76 255 / 50%);
    transition: transform var(--px-ease), box-shadow var(--px-ease);

    &:hover,
    &:focus-visible {
        color: #fff;
        transform: translateY(-2px);
        box-shadow: 0 12px 26px -6px rgb(109 76 255 / 60%);
    }
}

// ---- Quieter motion ---------------------------------------------------------

@media (prefers-reduced-motion: reduce) {
    .px-appt,
    .px-skeleton {
        animation: none;
    }

    .px-appt,
    .px-drawer__panel,
    .px-drawer__scrim,
    .px-fab,
    .px-calendar__step {
        transition: none;
    }

    .px-appt:hover,
    .px-fab:hover {
        transform: none;
    }
}

// ---- Print ------------------------------------------------------------------
//
// The drawer's Print button prints the page behind it, so the controls that are
// only useful on screen come off the paper.

@media print {
    .px-calendar__toolbar .px-calendar__nav,
    .px-calendar__views,
    .px-fab,
    .px-drawer__scrim,
    [data-clinic-calendar-filters] {
        display: none !important;
    }

    .px-drawer {
        position: static;
    }

    .px-drawer__panel {
        position: static;
        width: 100%;
        transform: none;
        box-shadow: none;
    }
}
