/* ============================================================
   Calendar (DS-03): session-aware week grid + responsive session
   surface (sticky rail / tablet slide-over / phone bottom sheet)
   ============================================================ */

/* ---- Layout ---- */
.calendar-container {
    display: flex;
    gap: 15px;
    align-items: flex-start;
}

.calendar-main {
    flex: 1;
    min-width: 0;
}

.calendar-header-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

.calendar-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 10px;
}

/* The loading_placeholder() initial child is a plain flex block, not a day
   column - without this it would sit squeezed into the grid's first
   auto-fit track instead of spanning the whole grid while the real columns
   are still loading. */
.calendar-grid > .cg-loading-block {
    grid-column: 1 / -1;
}

.calendar-day-column {
    display: flex;
    flex-direction: column;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 10px;
    padding: 10px;
    min-height: 220px;
}

.calendar-day-column.calendar-day-today {
    border-color: rgba(6, 182, 212, 0.6);
    box-shadow: 0 0 0 1px rgba(6, 182, 212, 0.25);
}

.calendar-day-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 8px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--glass-border);
}

.calendar-day-name {
    font-weight: 700;
    font-size: 0.8rem;
    color: var(--accent-secondary);
}

.calendar-note-toggle {
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 0.8rem;
    padding: 2px 6px;
    border-radius: 5px;
}
.calendar-note-toggle:hover {
    background: rgba(255, 255, 255, 0.08);
    color: var(--text-main);
}
.calendar-note-toggle.has-note {
    color: var(--accent-warning, #f59e0b);
}

/* The day note is the shared rich-text box (pages/components/note_editor.py) in
   compact mode; only the column-specific sizing lives here. */
.calendar-day-note-editor {
    margin-bottom: 10px;
}
.calendar-day-note-editor .note-textarea {
    border-style: dashed !important;
    font-size: 0.75rem !important;
    min-height: 56px !important;
    padding: 6px !important;
}

/* The day column is narrow, so its note is clipped to a few lines at rest.
   Clipped by CSS rather than truncated in Python: a "..." cut in the source
   would land mid-URL and destroy the link. Padding and font size match the
   column's textarea override above, and the box reserves the textarea's height
   inline (build_note_editor), so the column does not reflow on click. The
   dashed border marks the day note as the column's own scratch area rather
   than another event card. */
.calendar-day-note-editor .note-display {
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    display: -webkit-box;
    border-style: dashed;
    font-size: 0.75rem;
    line-height: 1.35;
    overflow: hidden;
    padding: 6px;
}
.calendar-day-note-editor .note-display p {
    margin-bottom: 0.2rem;
}

.calendar-events-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    flex: 1;
}

/* ---- Event cards ---- */
.calendar-event-card {
    background: linear-gradient(135deg, rgba(139, 92, 246, 0.13), rgba(139, 92, 246, 0.05));
    border: 1px solid rgba(139, 92, 246, 0.4);
    border-radius: 8px;
    padding: 7px 9px;
    font-size: 0.8rem;
    cursor: pointer;
    transition: filter 0.15s, transform 0.15s;
    display: flex;
    flex-direction: column;
    /* The containing block for the corner pencil. A card on the time grid is
       already positioned by its own inline style, which serves the same end. */
    position: relative;
}
.calendar-event-card:hover {
    filter: brightness(1.25);
    transform: translateY(-1px);
}

.calendar-event-appt {
    background: linear-gradient(135deg, rgba(26, 54, 93, 0.18), rgba(26, 54, 93, 0.05));
    border-color: rgba(26, 54, 93, 0.4);
}

.calendar-event-external {
    background: rgba(148, 163, 184, 0.08);
    border-color: rgba(148, 163, 184, 0.3);
    cursor: default;
}
.calendar-event-external:hover {
    filter: none;
    transform: none;
}

.calendar-event-time {
    font-weight: 700;
    font-size: 0.68rem;
    color: var(--text-muted);
    margin-bottom: 2px;
}
.calendar-event-title {
    font-weight: 600;
    color: var(--text-main);
}
.calendar-event-class {
    font-size: 0.7rem;
    color: var(--accent-secondary);
    margin-top: 3px;
}
/* The class code and the subject share one line under the time: two separate
   lines cost a card more height than the pair is worth, and height is exactly
   what a short lesson does not have. */
.calendar-event-idline {
    align-items: baseline;
    display: flex;
    gap: 6px;
    margin-top: 3px;
}
.calendar-event-idline .calendar-event-class,
.calendar-event-idline .calendar-event-title {
    margin-top: 0;
}

/* The class reads as part of the same phrase as the subject ("1A - German"),
   so it takes the subject's own type and is joined to it by a centred dot
   rather than by spacing alone. It keeps the secondary accent colour: that is
   what still tells the two halves apart at a glance, once the size and weight
   no longer do. The dot is generated rather than a component of its own - it
   is punctuation, and one more span per card buys nothing. */
.calendar-event-idline .calendar-event-class,
.calendar-event-compact .calendar-event-class {
    font-size: inherit;
    font-weight: 600;
}
.calendar-event-idline .calendar-event-class::after,
.calendar-event-compact .calendar-event-class::after {
    color: var(--text-muted);
    /* A vertical bar, not a middle dot: the card's font renders U+00B7 as a
       missing glyph here, and a separator a teacher sees as a broken character
       is worse than no separator at all. */
    content: "|";
    font-weight: 400;
    /* Matches the flex gap on the other side, so the dot sits centred between
       the two words rather than hanging off the class. */
    margin-left: 6px;
}

.session-lesson-notes {
    margin-bottom: 10px;
}
/* One line, and it stays one line. A card is clipped to its lesson's height,
   so a wrapped second row of chips is a row the teacher never sees - which is
   how the Record button used to disappear on an ordinary hundred-pixel column.
   The chips ellipsize instead (see .cal-chip), and the Record button does not
   give way at all (see .cal-record-btn). */
.calendar-event-meta {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: nowrap;
    min-width: 0;
    margin-top: 5px;
}

/* ---- Chips ---- */
/* A chip never wraps its own label: the compact card runs as a single
   non-wrapping row, and a two-line chip inside it breaks that row's height
   rather than merely crowding it. */
/* A status chip is what gives way when the column is too narrow for the whole
   row: it shortens with an ellipsis rather than being cut off mid-letter by the
   card's own overflow. */
.cal-chip {
    white-space: nowrap;
    font-size: 0.58rem;
    font-weight: 700;
    padding: 1px 8px;
    border-radius: 99px;
    letter-spacing: 0.04em;
    display: inline-block;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
}
.cal-chip-recorded {
    /* Filled green so it stays readable on light cards (a faint green chip
       with light-green text was low-contrast in light mode). */
    background: #10b981;
    color: #052e16;
    border: 1px solid #059669;
}
.cal-chip-open {
    /* Filled amber so it stays readable on light cards (a faint yellow chip
       with light-yellow text was unreadable in light mode). */
    background: #f59e0b;
    color: #422006;
    border: 1px solid #d97706;
}
.cal-chip-external {
    background: rgba(148, 163, 184, 0.13);
    color: var(--text-muted);
    border: 1px solid rgba(148, 163, 184, 0.35);
}
.cal-chip-plan {
    /* Filled violet so it stays readable on light cards. */
    background: #8b5cf6;
    color: #fff;
    border: 1px solid #7c3aed;
    text-decoration: none;
}
.cal-chip-plan:hover {
    background: #7c3aed;
    color: #fff;
}
/* The session chip IS the Record control - one chip rather than a status chip
   with a button beside it, which said the same thing twice and never fit a
   hundred-pixel column. It carries .cal-chip-open's amber, and it is the one
   item in the row that never shrinks: the plan chip beside it gives way. */
.cal-chip-action {
    border: 1px solid #d97706;
    cursor: pointer;
    font-family: inherit;
    flex-shrink: 0;
}

/* The route to the event window on a card whose body records instead. It sits
   in the card's corner rather than in the chip row, so a narrow column can
   never squeeze it out - and the body reserves the space so the time and
   subject do not run underneath it. */
.cal-edit-btn {
    position: absolute;
    top: 2px;
    right: 3px;
    z-index: 2;
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: 0.6rem;
    line-height: 1;
    padding: 2px 3px;
    cursor: pointer;
}
.cal-edit-btn:hover {
    color: var(--text-main);
}
.calendar-event-editable .calendar-event-body {
    padding-right: 15px;
}

/* ---- Session drawer (desktop sticky rail) ---- */
.calendar-session-drawer {
    width: 320px;
    flex: 0 0 320px;
    display: flex;
    flex-direction: column;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 10px;
    padding: 15px;
    position: sticky;
    top: 16px;
}

.calendar-drawer-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
}

.session-student-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 55vh;
    overflow-y: auto;
    padding-right: 4px;
}

.session-student-row {
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid var(--glass-border);
    border-radius: 8px;
    padding: 8px 10px;
    display: flex;
    flex-direction: column;
    gap: 5px;
}
.session-student-name {
    font-weight: 600;
    font-size: 0.85rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.session-inputs-row {
    display: flex;
    gap: 6px;
    align-items: center;
}
.session-note-input {
    background: rgba(0, 0, 0, 0.2) !important;
    border: 1px solid var(--glass-border) !important;
    color: var(--text-main) !important;
    font-size: 0.75rem !important;
    border-radius: 4px !important;
    padding: 2px 6px !important;
    flex: 1 1 auto;
}
.session-note-input:focus,
.session-grade-input:focus {
    border-color: var(--accent-primary) !important;
    outline: none;
}
.session-grade-input {
    background: rgba(0, 0, 0, 0.2) !important;
    border: 1px solid var(--glass-border) !important;
    color: var(--text-main) !important;
    font-size: 0.75rem !important;
    border-radius: 4px !important;
    padding: 2px 6px !important;
    width: 64px;
    flex: 0 0 auto;
    text-align: center;
}

/* One grade cell per Participation part marked "recorded every lesson". The
   inputs are the gradebook's own build_cell_render() output, so each part
   honours its own entry mode; these rules only size them for the drawer. */
.session-grade-cell {
    flex: 0 0 auto;
    width: 78px;
}
.session-grade-cell input,
.session-grade-cell select {
    font-size: 0.75rem !important;
    padding: 2px 4px !important;
    text-align: center;
    width: 100%;
}
/* The grade control is built with the gradebook grid's own cell classes, and
   .results-grade-input.gb-cell-input (assets/06_gradebook.css) pins a cell to
   width: 28px !important - correct for a 40-column table, far too narrow here,
   where it left the drawer showing a tiny unreadable box with no room for a
   dropdown arrow. The plain "width: 100%" above cannot win against an
   !important, so this repeats the compound selector with one of its own. */
.session-grade-cell .results-grade-input.gb-cell-input {
    width: 100% !important;
    max-width: none !important;
    height: 26px !important;
    font-size: 0.75rem !important;
    padding: 2px 4px !important;
}

/* Column headings, rendered only when a class records more than one thing per
   lesson - a single-channel class must look exactly as it always did. */
.session-channel-header {
    display: flex;
    gap: 6px;
    align-items: flex-end;
    padding: 0 10px;
}
.session-channel-header .session-student-name {
    flex: 1 1 auto;
}
.session-channel-heading {
    color: var(--text-muted);
    flex: 0 0 auto;
    font-size: 0.65rem;
    overflow: hidden;
    text-align: center;
    text-overflow: ellipsis;
    white-space: nowrap;
    width: 68px;
}

/* ---- Phone today-list (bottom-sheet) ---- */
.session-today-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.session-list-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    background: rgba(139, 92, 246, 0.1);
    border: 1px solid rgba(139, 92, 246, 0.35);
    border-radius: 8px;
    padding: 9px 11px;
    cursor: pointer;
}
.session-list-item:hover {
    filter: brightness(1.2);
}
.session-list-manual {
    background: transparent;
    border-style: dashed;
}
.session-list-title {
    font-size: 0.8rem;
    font-weight: 600;
}
.session-list-sub {
    font-size: 0.68rem;
    color: var(--text-muted);
}

/* ---- Phone today-bar and tablet edge handle (hidden on desktop) ---- */
.calendar-today-bar {
    display: none;
}
.calendar-edge-handle {
    display: none;
}

/* ============================================================
   Responsive
   ============================================================ */

/* Desktop: the drawer is a permanent sticky rail; its back/close controls
   (only meaningful for the slide-over / bottom-sheet) stay hidden even though
   the callback sets an inline display for smaller screens. */
@media (min-width: 992px) {
    #session-back-btn,
    #session-close-btn {
        display: none !important;
    }
}

/* Tablet: session drawer becomes a right slide-over with an edge handle */
@media (max-width: 991.98px) and (min-width: 641px) {
    .calendar-session-drawer {
        position: fixed;
        inset: 0 0 0 auto;
        width: min(360px, 70vw);
        border-radius: 12px 0 0 12px;
        transform: translateX(105%);
        transition: transform 0.25s;
        z-index: 1050;
        max-height: 100vh;
        overflow-y: auto;
        /* Solid surface: as a fixed overlay the translucent glass bg would let
           the grid show through. */
        background: var(--bg-color);
        box-shadow: -8px 0 24px rgba(0, 0, 0, 0.4);
    }
    .calendar-session-drawer.open {
        transform: translateX(0);
    }
    .calendar-edge-handle {
        display: flex;
        align-items: center;
        position: fixed;
        right: 0;
        top: 38%;
        z-index: 1040;
        writing-mode: vertical-rl;
        padding: 14px 7px;
        background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
        color: #fff;
        border: none;
        border-radius: 9px 0 0 9px;
        font-weight: 700;
        font-size: 0.68rem;
        letter-spacing: 0.1em;
        cursor: pointer;
    }
}

/* Phone: bottom sheet + persistent today-bar */
@media (max-width: 640px) {
    body {
        padding-bottom: 70px;
    }
    .calendar-session-drawer {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        top: auto;
        width: 100%;
        max-height: 85vh;
        overflow-y: auto;
        border-radius: 14px 14px 0 0;
        transform: translateY(108%);
        transition: transform 0.25s;
        z-index: 1050;
        /* Solid surface (see tablet note above). */
        background: var(--bg-color);
        box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.4);
    }
    .calendar-session-drawer.open {
        transform: translateY(0);
    }
    .calendar-today-bar {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 8px;
        position: fixed;
        left: 12px;
        right: 12px;
        bottom: 12px;
        z-index: 1040;
        background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
        color: #fff;
        border: none;
        border-radius: 99px;
        padding: 12px 18px;
        font-size: 0.82rem;
        font-weight: 700;
        cursor: pointer;
    }
}

/* When a dropdown inside the session drawer is open, lift the drawer above the
   week grid and let the open menu overflow the drawer's scroll area instead of
   being clipped by it (react-select renders the menu inline). Mirrors the
   card-level :has() pattern in 01_general.css. */
.calendar-session-drawer:has(.Select.is-open) {
    overflow: visible;
    z-index: 10060;
}

/* ---- Lessons that are not happening (cancelled, or a school closure) ----
   The card stays in the column on purpose: it is where the teacher takes the
   cancellation back, and its note has to remain readable. What it loses is the
   Record button and the session chip, both dropped server-side. */
.calendar-event-card.calendar-event-cancelled {
    opacity: 0.55;
    filter: grayscale(0.6);
}
.calendar-event-cancelled .calendar-event-title {
    text-decoration: line-through;
}
.calendar-event-cancelled:hover {
    filter: grayscale(0.6) brightness(1.1);
}

.cal-chip-cancelled {
    background: rgba(148, 163, 184, 0.18);
    border: 1px solid rgba(148, 163, 184, 0.45);
    color: var(--text-muted);
}

/* A closed day names its holiday once in the column header area rather than
   repeating it on every lesson below. */
.calendar-day-closed-banner {
    background: rgba(148, 163, 184, 0.12);
    border: 1px dashed rgba(148, 163, 184, 0.45);
    border-radius: 8px;
    color: var(--text-muted);
    font-size: 0.72rem;
    font-weight: 700;
    margin-bottom: 8px;
    padding: 4px 8px;
    text-align: center;
}
.calendar-day-column.calendar-day-closed {
    background: rgba(148, 163, 184, 0.06);
}

/* ============================================================
   Time grid (wide screens): the week on a real hour axis.
   Cards are positioned and sized by their own start/end time in
   pages/components/calendar_time_grid.py, so a single period is
   visibly half of a double one. Both rows below carry the same
   inline grid-template-columns, which is what keeps the ruler
   aligned with the days beside it.
   ============================================================ */
.calendar-grid-timed {
    display: block;
}

.calendar-timegrid-head,
.calendar-timegrid-body {
    display: grid;
    gap: 10px;
}

.calendar-timegrid-head {
    align-items: end;
    margin-bottom: 8px;
}

.calendar-timegrid-head-cell {
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 10px 10px 0 0;
    border-bottom: none;
    padding: 10px 10px 6px;
}

.calendar-timegrid-allday {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 6px;
}

/* The grid can run 07:00-18:00, so it scrolls inside the page rather than
   pushing the topbar and the session rail off-screen. */
.calendar-timegrid-scroll {
    max-height: 70vh;
    overflow-y: auto;
    overflow-x: hidden;
}

.calendar-timeruler {
    position: relative;
}
.calendar-timeruler-mark {
    position: absolute;
    right: 6px;
    transform: translateY(-50%);
    font-size: 0.68rem;
    font-weight: 700;
    color: var(--text-muted);
    white-space: nowrap;
}

.calendar-timeday {
    position: relative;
    background-color: var(--glass-bg);
    /* One hour line per hour, drawn rather than built: the same picture as a
       row of divs for none of the components. background-size is set inline
       from the same minutes-to-pixels factor the cards use, and
       background-position moves the tiling down to the first whole hour -
       the axis itself starts half an hour before the first lesson. */
    background-image: repeating-linear-gradient(
        to bottom,
        var(--glass-border) 0,
        var(--glass-border) 1px,
        transparent 1px,
        transparent 100%
    );
    background-repeat: repeat-y;
    border: 1px solid var(--glass-border);
    border-radius: 0 0 10px 10px;
    border-top: none;
}

.calendar-timeday.calendar-timeday-today {
    border-color: rgba(6, 182, 212, 0.6);
    box-shadow: 0 0 0 1px rgba(6, 182, 212, 0.25);
}

/* A positioned card fills its slot exactly, so its content is clipped rather
   than allowed to push past the lesson's real end time. */
.calendar-timeday .calendar-event-card {
    overflow: hidden;
    margin: 0 2px;
    /* Deterministic height: the card's block is an inline px height, and the
       three-row layout used to fill a 50-minute (80px) block to 79.4px under
       Bootstrap's 1.5 line-height - it fit on Windows and clipped the Record
       chip on macOS, where the same rows render a fraction taller. A fixed
       line-height and tighter padding bring the budget to ~65px; the number is
       mirrored by ROOMY_CARD_PX in utils/calendar_geometry.py. */
    padding: 5px 8px;
    line-height: 1.2;
}
.calendar-timeday .calendar-event-meta {
    margin-top: 4px;
}
.calendar-timeday .cal-chip {
    line-height: 1.2;
}

/* A card with too little height for the stacked layout runs as ONE row
   instead: time, class, subject and any chips side by side. Which layout a
   card gets is decided in Python from the event's own duration
   (utils/calendar_geometry.py::is_compact_card) - the card's height is an
   inline style, so no CSS media or container query can see it. The chips stay
   siblings of the body rather than moving inside it: the body is the click
   target that opens the event window, and a Record button inside it would open
   that window instead of recording. */
.calendar-event-compact.calendar-event-card,
.calendar-timeday .calendar-event-compact.calendar-event-card {
    align-items: center;
    flex-direction: row;
    flex-wrap: nowrap;
    gap: 6px;
    padding: 3px 9px;
    /* The chips are the card's own flex children here (unlike the roomy
       layout, where they sit in a meta row below the body) and would
       otherwise run flush to the card's right edge - exactly where the
       corner pencil (.cal-edit-btn, position: absolute, z-index: 2) sits.
       The pencil's higher stacking then ate every click meant for the
       Record chip on a short lesson, since the chip rendered underneath it
       in that corner. This reserves the same width the pencil already
       carves out of the roomy body via its own padding-right, so the last
       chip never reaches under it. */
    padding-right: 22px;
}
.calendar-event-compact .calendar-event-body {
    align-items: baseline;
    display: flex;
    gap: 6px;
    min-width: 0;
}
.calendar-event-compact .calendar-event-time {
    margin-bottom: 0;
    white-space: nowrap;
}
.calendar-event-compact .calendar-event-class {
    margin-top: 0;
}
/* The subject is the one part that may be long, so it is the one that
   truncates - the time and the class stay whole. */
.calendar-event-compact .calendar-event-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.calendar-timeday-closed {
    position: absolute;
    left: 4px;
    right: 4px;
    top: 4px;
    z-index: 1;
}

/* The holiday calendar file drop box: a quiet dashed target, matched to the
   address field above it rather than styled as another primary button. */
.closures-upload {
    border: 1px dashed var(--glass-border);
    border-radius: 8px;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 0.8rem;
    margin-bottom: 8px;
    padding: 10px;
    text-align: center;
    transition: border-color 0.15s, color 0.15s;
}
.closures-upload:hover {
    border-color: var(--accent-primary);
    color: var(--text-main);
}
