/* Calendar surfaces — DayCalendarVertical, WeekCalendarVertical,
   AppointmentContextMenu, the reschedule confirm and the drag affordances
   (calendar-drag.js). ONE home (#1571): every app that mounts the RCL
   calendar (Admin, Therapist) links this file after its own app.css, so
   the calendar looks the same everywhere and no app keeps a copy.

   Written in the type and spacing scale (--text-*, --space-*): each app's
   :root declares the steps used here with Admin's values. Admin used to
   carry its own, newer copy of these rules in app.css («keep both in
   sync»); the copies drifted and Admin's week view lost its «Κλειστά» and
   holiday day tags. CalendarStylesheetIsLinkedTests pins the link and
   that every calendar class the components render is styled. */

/* --- DayCalendarVertical -------------------------------------------- */

/* The room grid is intentionally wider than a phone; it scrolls inside
   this box. `overscroll-behavior-x: contain` keeps that swipe from turning
   into a browser back-gesture on iOS/Android. */
.day-cal-v { position: relative; overflow: auto; overscroll-behavior-x: contain; max-width: 100%; }
.day-cal-v-empty { padding: var(--space-4); }
.day-cal-v-grid {
    display: grid;
    grid-auto-rows: minmax(2rem, auto);
    position: relative;
    /* 🔴 #921 — «make the columns in all views wider to fit at least 20
       chars». The floor is stated in CHARACTERS, not rem, for the same
       reason `--field-chars` states an input's ceiling that way (see the
       "Field width tokens" block in Admin's app.css): a rem value is only
       "20 characters" at one font size, so the guarantee lapses silently
       the day anyone retunes the calendar type scale, while the stylesheet
       still reads as though it held.

       The arithmetic lives here once and the components consume
       `--cal-col-min` inside their inline `grid-template-columns` — that
       template has to be inline because the column count is data-driven,
       but the WIDTH does not, so the number that encodes the promise stays
       in CSS. The padding term covers the column head's own 0.55rem
       horizontal padding plus the block padding inside the column, so the
       20ch is 20 characters of TEXT rather than 20 characters minus the
       chrome. `ch` resolves against this element's font (inherited, 1rem)
       while the head and blocks render smaller, which leaves the floor
       comfortably above the stated minimum rather than under it.

       The narrowest supported viewport is handled by the existing
       horizontal scroll on the wrapper, not by shrinking the floor: a
       column narrower than its content is exactly what the ticket asks us
       to stop doing.

       --cal-head-h: 🔴 this was the literal `2.75rem` in SIX places — this
       rule, its copy in the other stylesheet, and four inline
       `top: calc(2.75rem + …)` now-line offsets across DayCalendarVertical
       and WeekCalendarVertical. Two-line therapist names make the head
       taller, and a single one of those six left behind would put the
       «ΤΩΡΑ» line at the wrong time of day — wrong quietly, not visibly.
       It is one token now, and CalendarColumnWidthAndNameLinesTests fails
       if the literal comes back. Sized for two name rows plus the
       occupancy bar. */
    --cal-col-chars: 20;
    --cal-col-min: calc(var(--cal-col-chars) * 1ch + 1.6rem);
    --cal-head-h: 3.5rem;
    /* Header row + N hour rows. The :nth row is computed via inline
       grid-row spans so the column wrappers can absolutely position
       blocks without breaking the implicit row track. */
    grid-template-rows: var(--cal-head-h) repeat(var(--hour-rows, 9), var(--row-h, 4.5rem));
    background: var(--card);
}
.day-cal-v-corner {
    position: sticky; left: 0; top: 0; z-index: 3;
    background: var(--card);
    border-bottom: 1px solid var(--line);
    border-right: 1px solid var(--line);
}
.day-cal-v-colhead {
    border-bottom: 1px solid var(--line);
    border-right: 1px solid var(--line);
    padding: 0.4rem 0.55rem;
    background: var(--card);
    position: sticky; top: 0; z-index: 2;
}
.day-cal-v-colhead-row {
    display: flex; align-items: center; gap: var(--space-2);
    font-size: var(--text-base);
}
.day-cal-v-room-badge {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.4rem; height: 1.4rem; border-radius: 4px;
    background: var(--teal-50); color: var(--ink-soft);
    font-weight: 700; font-size: var(--text-xs);
}
/* #921 — «use two rows (name surname) for therapists names». The name is
   a COLUMN of lines rather than one nowrap string: each line ellipsises on
   its own, so a long surname truncates without stealing the given name's
   row. A room head supplies one line and is unchanged visually. The split
   is done in C# from the stored first/last fields — never by wrapping,
   which would break at whatever character the box width lands on. */
.day-cal-v-room-name {
    font-weight: 600; flex: 1; min-width: 0;
    display: flex; flex-direction: column; line-height: 1.15;
}
.day-cal-v-name-line { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* #996 — the ειδικότητα badges beside the therapist's name. Quieter than the
   name on purpose: the name is what the reader scans for, the badges are what
   they check once. Inherits the line's ellipsis, so a therapist with several
   truncates rather than widening every column in the grid. */
.day-cal-v-spec { color: var(--text-muted); font-size: var(--text-xs); font-weight: 400; }
.day-cal-v-room-occ { color: var(--ink-soft); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }
.day-cal-v-occ-bar {
    margin-top: var(--space-1);
    height: 3px;
    background: var(--teal-50);
    border-radius: 999px;
    overflow: hidden;
}
.day-cal-v-occ-bar > span {
    display: block; height: 100%;
    background: var(--lime-500);
}
.day-cal-v-occ-bar { display: flex; }

.day-cal-v-time-axis {
    grid-column: 1;
    position: relative;
    border-right: 1px solid var(--line);
    background: var(--card);
}
.day-cal-v-time-label {
    position: absolute;
    left: 0; right: 0;
    text-align: right;
    padding-right: var(--space-2);
    font-size: var(--text-xs);
    color: var(--ink-soft);
    transform: translateY(-0.5em);
}

.day-cal-v-col {
    position: relative;
    border-right: 1px solid var(--line);
    background: var(--card);
}
.day-cal-v-row-bg, .day-cal-v-row-half {
    position: absolute; left: 0; right: 0;
    pointer-events: none;
}
.day-cal-v-row-bg { border-top: 1px solid var(--line); height: 0; }
.day-cal-v-row-half {
    border-top: 1px dashed var(--line);
    height: 0;
    opacity: 0.45;
}
/* REQ-F04-009 / -010 — out-of-hours shading. */
.day-cal-v-ooh {
    position: absolute; left: 0; right: 0;
    background: color-mix(in oklch, var(--ink) 12%, transparent);
    pointer-events: none;
    z-index: 0;
}
.day-cal-v-holiday-banner {
    margin: 0 0 var(--space-2) 0;
    padding: var(--space-2) var(--space-3);
    border: 1px solid oklch(0.78 0.13 80);
    background: oklch(0.96 0.06 90);
    color: oklch(0.32 0.07 60);
    border-radius: 0.375rem;
    font-size: var(--text-lg);
}

.day-cal-v-block {
    position: absolute;
    left: 0.25rem; right: 0.25rem;
    /* Above the empty-slot "+" links (z-index 1) so blocks reliably
       receive click / right-click / drag. Without this, sparse views
       (e.g. Αίθουσες) stack the empty links over blocks and the
       context menu never opens. */
    z-index: 2;
    border-radius: var(--radius-sm);
    border: 1px solid #B9DDC4;
    background: #EAF6EE;
    padding: var(--space-1) var(--space-2);
    /* Pinned dark ink — block backgrounds are pastel-light in both
       light and dark mode, so the text colour can't follow --ink
       (which flips light in dark mode and disappears against the
       pastel surface). */
    color: oklch(0.22 0.03 215);
    text-decoration: none;
    overflow: hidden;
    display: flex; flex-direction: column; gap: 0;
    font-size: var(--text-sm);
    box-shadow: 0 1px 0 oklch(1 0 0 / 0.6) inset;
}
.day-cal-v-block:hover { filter: brightness(0.97); }
.day-cal-v-block.is-live {
    border-color: oklch(0.62 0.16 145);
    background: #DAF1E1;
}
.day-cal-v-block.is-noshow {
    background: #FBE0D6;
    border-color: #DA8062;
}
.day-cal-v-block.is-cancelled {
    opacity: 0.5; text-decoration: line-through;
}
.day-cal-v-block.is-completed {
    background: oklch(0.96 0.025 145);
    border-color: #B9DDC4;
}

/* #1021/#1027 — a Συνάντηση the day view holds that NO session covers: a rule
   with nobody enrolled yet, or one that claims this slot from a later date.

   🔴 It is a TILE, not a badge. Elias, 2026-08-29, with a claim and a real
   session side by side: «it should look like a "normal" with a special border
   if it is in the future». v0.202 drew a sparse box with a start time and a
   specialty; three of them read as empty containers. The claim now carries the
   session block's own class — .day-cal-v-block — and this rule adds only what
   makes it a CLAIM: the border, and a ground that recedes.

   ⚠️ So this selector must stay narrow. Anything here that restyles layout
   fights .day-cal-v-block and the two drift; the whole point is that they
   cannot look like different species. */
.day-cal-v-proj {
    z-index: 3;
    border-style: dashed;
    border-color: oklch(0.66 0.09 255);
    background: oklch(0.97 0.02 255 / 0.92);
    color: oklch(0.32 0.06 255);
}
.day-cal-v-proj:hover { background: oklch(0.95 0.04 255 / 0.98); }
.day-cal-v-proj:focus-visible { outline: 2px solid oklch(0.55 0.14 255); outline-offset: 1px; }

/* In force NOW and holding an empty room — a booked hour, not a forecast.
   Amber rather than blue: it is a thing to act on (put somebody in it, or
   release it), where an upcoming claim is only a thing to know. */
.day-cal-v-proj.is-today {
    border-color: oklch(0.72 0.13 75);
    background: oklch(0.97 0.04 85 / 0.92);
    color: oklch(0.34 0.07 60);
}
.day-cal-v-proj.is-today:hover { background: oklch(0.95 0.07 85 / 0.98); }

/* «Χωρίς παιδιά» in the child's row — the case the whole feature exists for,
   said in the place a name would be rather than left blank. */
.day-cal-v-proj-empty { font-style: italic; opacity: 0.75; }

/* 🔴 Several claims on one band: ONE scrolling list of single lines.
   Elias: «when more than one overlap show an abbreviated list… scrolled inside
   the slot». Not lanes — at a third of a column width there is no room for a
   name, which is how v0.202 produced the emptiness it was criticised for. And
   not capped with a «+N»: nothing hides behind hover, which carries the full
   detail of a LINE.

   ⚠️ overscroll-behavior is load-bearing. The grid itself scrolls; without
   this, reaching the end of a list chains the scroll to the grid and the page
   jumps while the operator believes they are reading a slot. */
.day-cal-v-proj-list {
    position: absolute;
    left: 0.25rem; right: 0.25rem;
    z-index: 3;
    overflow-y: auto;
    overscroll-behavior: contain;
    border: 1px dashed oklch(0.66 0.09 255);
    border-radius: var(--radius-sm);
    background: oklch(0.98 0.01 255 / 0.96);
    display: flex; flex-direction: column;
}
.day-cal-v-proj-list:focus-visible { outline: 2px solid oklch(0.55 0.14 255); outline-offset: 1px; }

.day-cal-v-proj-row {
    display: flex;
    align-items: baseline;
    gap: var(--space-1);
    padding: var(--space-05) var(--space-1);
    text-decoration: none;
    color: oklch(0.32 0.06 255);
    font-size: var(--text-xs);
    line-height: 1.25;
    white-space: nowrap;
    border-bottom: 1px solid oklch(0.92 0.01 255);
}
.day-cal-v-proj-row:last-child { border-bottom: none; }
.day-cal-v-proj-row:hover { background: oklch(0.95 0.03 255); }
.day-cal-v-proj-row.is-today { color: oklch(0.34 0.07 60); }
.day-cal-v-proj-row .time { font-weight: 600; font-variant-numeric: tabular-nums; }

/* The only part allowed to shrink — the names run out of room first, and an
   ellipsised name with the time and the «από» still readable is the right
   trade on a line this narrow. */
.day-cal-v-proj-who {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    flex: 1 1 auto;
}

/* The count of rules the server could not expand. Not an error banner — the
   day it describes is otherwise correct — but it must not be missable either,
   because what it warns about is a slot that reads as free and is not. */
.day-cal-v-unreadable {
    margin: 0 0 var(--space-2);
    padding: var(--space-2);
    border: 1px solid oklch(0.78 0.13 80);
    border-radius: var(--radius-sm);
    background: oklch(0.97 0.04 90);
    color: oklch(0.32 0.07 60);
    font-size: var(--text-sm);
}

/* 🔴 #1588 — a box opened AT the pointer (the ⋯ menus, the hover card),
   kept inside the viewport: PointerAnchor.Style sets --at-x / --at-y, the
   box starts there and `translate` pulls it back by however much it would
   overrun the right or bottom edge (a percentage in `translate` is the
   box's own size, so it follows the box as it grows — the «Αλλαγή σε
   «…»;» question), never past the left or top edge. At 390 px a press in
   the middle of a block opened the menu at x 231 and cut the question. */
.pointer-anchored {
    --at-edge: 0.5rem;
    left: var(--at-x, 0px);
    top: var(--at-y, 0px);
    max-width: calc(100vw - 2 * var(--at-edge));
    translate:
        max(calc(var(--at-edge) - var(--at-x, 0px)), min(0px, calc(100vw - var(--at-x, 0px) - 100% - var(--at-edge))))
        max(calc(var(--at-edge) - var(--at-y, 0px)), min(0px, calc(100dvh - var(--at-y, 0px) - 100% - var(--at-edge))));
}

/* AppointmentContextMenu — right-click menu on calendar blocks.
   The backdrop is invisible but covers the viewport so any click
   outside the menu closes it; the menu itself is positioned with
   client coordinates from the MouseEventArgs (.pointer-anchored). */
.appt-ctx-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1100;
    background: transparent;
}
.appt-ctx-menu {
    position: absolute;
    min-width: 14rem;
    background: var(--surface, #fff);
    border: 1px solid var(--border, #cfd8dc);
    border-radius: var(--radius-sm, 4px);
    box-shadow: 0 4px 16px oklch(0 0 0 / 0.18);
    padding: var(--space-1) 0;
    display: flex;
    flex-direction: column;
}
.appt-ctx-item {
    background: none;
    border: 0;
    text-align: left;
    padding: var(--space-2) 0.85rem;
    font: inherit;
    color: var(--ink, #1c2a33);
    cursor: pointer;
}
.appt-ctx-item:hover:not(:disabled),
.appt-ctx-item:focus-visible {
    background: var(--teal-100, #e6f3f0);
    outline: none;
}
.appt-ctx-item:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}
.appt-ctx-error {
    margin: var(--space-1) 0.85rem var(--space-2);
    color: var(--danger, #b3261e);
    font-size: var(--text-sm);
}
/* Elias, 2026-11-06 — «Αλλαγή σε «…»;» before a switch of a recorded mark. */
.appt-ctx-confirm {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) 0.85rem;
    max-width: 22rem;
}
/* Zero-specificity fallback for an app without the office's .btn-* (the
   Therapist app's calendar hosts this menu too); the office's classes win.
   🔴 #1588 — the WHOLE selector inside :where(), so it is 0,0,0 like the yes
   rule below and the yes rule wins by order: with only the class inside,
   `button` made it 0,0,1 and the Therapist app's «Αλλαγή» was white. */
:where(.appt-ctx-confirm button) {
    font: inherit;
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--border, #cfd8dc);
    border-radius: var(--radius-sm, 4px);
    background: var(--surface, #fff);
    color: var(--ink, #1c2a33);
    cursor: pointer;
}
:where(.appt-ctx-confirm .appt-ctx-confirm-yes) {
    border-color: var(--danger, #b3261e);
    background: var(--danger, #b3261e);
    color: #fff;
}
/* 🔴 #1435 — the server's reason a tap is not open yet (SessionDto.Actions). */
.appt-ctx-refusal {
    margin: var(--space-1) var(--space-3) var(--space-2);
    color: var(--muted, #64748b);
    font-size: var(--text-sm);
    max-width: 22rem;
}
.day-cal-v-block-head {
    display: flex; align-items: baseline; gap: var(--space-1);
    /* #1573 — ONE line: «· ΣΕ ΕΞΕΛΙΞΗ» wrapped inside its own span on a
       phone, the head grew a line and the foot was cut by 10 px. What does
       not fit is «από …», which ellipsizes (its title keeps the full date). */
    white-space: nowrap;
    color: oklch(0.42 0.10 145);
    font-weight: 700;
    font-size: var(--text-xs);
}
/* #977 — «από 15/09»: when the ΣΕΙΡΑ this tile belongs to began.
   Deliberately quieter than the time: the time is what the tile is FOR,
   the series start is context. Normal weight against the head's 700 and a
   muted colour keep it from competing with the thing being read first.
   Wraps to nothing on a narrow tile because the head is a flex row and
   this is the item that may shrink. */
.cal-series-from {
    font-weight: 400;
    font-variant-numeric: tabular-nums;
    /* 0.50 not 0.55: 4.33:1 on the default #EAF6EE tile and 3.83:1 on
       no-show failed WCAG AA (v0.270 gate, axe home); 0.50 is ≥4.74:1 on
       every tile state. */
    color: oklch(0.50 0.02 145);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
/* 🔴 #1353 — which dates belong to ONE Συνάντηση, without a second colour
   (colour is the therapist's). SynantisiTags draws «↻ 3/12», «Σ-142»,
   «↺ για 13/10», «→ αναπλ. 17/10» on a line of their own under the name;
   SynantisiKinHighlight writes WHICH blocks to outline and dim, these
   tokens say how. A block is a size container (cal-block) so a narrow one
   keeps the icons and drops the words — each tag's title still says it in
   full. */
.day-cal-v {
    --cal-kin-dim: 0.4;
    --cal-kin-outline: 2px dashed oklch(0.30 0.05 215);
    --cal-kin-offset: 1px;
}
.day-cal-v-block { container: cal-block / inline-size; transition: opacity 120ms ease; }
/* The tags' own look (.cal-tags, .cal-tag*) is SynantisiTags.razor.css —
   ONE place for every app (#1358); the block above is their container. */
/* #979 — the occurrence-vs-series choice inside the drag confirm. The
   disabled option stays VISIBLE and dimmed: the operator asked for it, and
   a missing option reads as a product that never considered it. */
.day-cal-v-confirm-scope {
    border: 1px solid var(--border, oklch(0.88 0.01 145));
    border-radius: 0.4rem;
    padding: var(--space-2) var(--space-2);
    margin: var(--space-1) 0 var(--space-3);
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.day-cal-v-confirm-scope legend {
    font-size: var(--text-xs);
    font-weight: 700;
    padding: 0 var(--space-1);
}
.day-cal-v-confirm-scope .csp-check {
    display: flex;
    align-items: flex-start;
    gap: var(--space-1);
}
.day-cal-v-confirm-scope .csp-check.is-disabled { opacity: 0.55; }
.day-cal-v-confirm-scope small {
    display: block;
    font-size: var(--text-xs);
    line-height: var(--leading-tight);
}
.day-cal-v-tag-live { flex-shrink: 0; color: oklch(0.42 0.10 145); font-size: var(--text-xs); letter-spacing: 0.06em; }
/* gap-analysis #9: subtle marker on a deliberately concurrent (opt-in
   double-booked) session. Amber tint so it reads as "noteworthy, not an
   error" and sits quietly at the end of the block head. */
.day-cal-v-block-concurrent {
    margin-left: auto;
    color: oklch(0.62 0.14 75);
    font-size: var(--text-xs);
    font-weight: 700;
    line-height: 1;
}
/* User report (page /): completed / paid status icons on session tiles.
   Compact, colour-coded round badges pinned to the right of the block
   head; green «✓» for completed, teal «€» for paid, each aria-labelled. */
.day-cal-v-status-badges {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    line-height: 1;
}
.day-cal-v-status-badges.has-concurrent {
    margin-left: var(--space-1);
}
.day-cal-v-status-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.05rem;
    height: 1.05rem;
    border-radius: 999px;
    font-size: var(--text-xs);
    font-weight: 700;
    line-height: 1;
    border: 1px solid transparent;
}
.day-cal-v-status-badge.is-completed {
    background: var(--success);
    border-color: oklch(0.52 0.130 145);
    color: #fff;
}
.day-cal-v-status-badge.is-paid {
    background: var(--teal-800);
    border-color: oklch(0.44 0.10 212);
    color: #fff;
}
.day-cal-v-status-badge.is-owed {
    /* User report (page /): «money owed» badge — warm red-orange (hue 25,
       matching the «εκκρεμές υπόλοιπο» pill) so it warns and stays apart
       from the teal paid badge. Never co-occurs with is-paid. */
    background: oklch(0.55 0.16 25);
    border-color: oklch(0.44 0.16 25);
    color: #fff;
}
/* #986 — the three-letter ειδικότητα code. Fixed-width chip: a code that
   changes width per specialty makes the tile's first row jump between
   neighbouring blocks, which is what the badge is meant to stop. */
.day-cal-v-spec-badge {
    /* #986 — a PREFIX inside the therapist pill, not a chip of its own. It
       carries weight and letter-spacing so it reads as a code, and no
       background, because a chip inside a pill is two boxes saying one thing. */
    font-weight: 700;
    letter-spacing: 0.05em;
    margin-right: var(--space-1);
    opacity: 0.75;
    white-space: nowrap;
}
/* #988 — the Συνάντηση's standing notes, at the foot of the hover card.
   Wraps rather than truncating: a note the reader cannot finish is worse
   than a taller card, and the card is transient anyway. */
.day-cal-v-hovercard-notes {
    margin-top: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px solid oklch(0.88 0.01 145);
    font-size: var(--text-xs);
    line-height: var(--leading-normal);
    color: oklch(0.42 0.02 145);
    white-space: pre-wrap;
    max-width: 22rem;
}

/* 🔴 Elias, 2026-11-06 (drill v0.309 T1-D01-Q1) — the child's name takes at
   most TWO lines and ends in «…», so every other line of a 45′ block stays
   (a 41-character name wrapped to 3 and pushed the therapist row out). Beside
   its own surname row (#982) the given name keeps one; alone, two. A long
   single word breaks rather than running out of the block. The block's
   label and hover card say the name in full. */
.day-cal-v-block-child {
    font-weight: 700;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
}
.day-cal-v-block:has(.day-cal-v-block-surname) .day-cal-v-block-child { -webkit-line-clamp: 1; line-clamp: 1; }

/* #982 — the child's SURNAME, on its own row under the given name. Same
   weight as the given name because it is the same fact; a lighter surname
   read as secondary information when it is half the identity. */
.day-cal-v-block-surname {
    font-weight: 700;
    line-height: var(--leading-tight);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.day-cal-v-block-type {
    /* Pinned soft ink — see .day-cal-v-block above for the rationale. */
    color: oklch(0.44 0.04 215);
    font-size: var(--text-xs);
}
/* #1573 — a 45′ block holds its worst case only with every line at its own
   height and nothing between them: no gap, tight lines, compact pills, the
   type label in the foot row (it wraps to its own row on a narrow block).
   flex-shrink 0 — the surname (#982) has overflow:hidden for its ellipsis,
   which let it shrink to 0 and vanish; what does not fit is now cut at the
   bottom, where the block's label and hover card still say it. Measured in
   DayCalendarVertical.razor (RowHeightRem). Keep both copies in sync. */
.day-cal-v-block > * { flex-shrink: 0; line-height: var(--leading-tight); }
.day-cal-v-block-foot .day-cal-v-block-type { margin-right: auto; }
/* #1573 — the pills share ONE row whatever the width: the group shrinks to
   the block, the name pill's text ends in «…», the amount keeps its size. */
.day-cal-v-block-pills { display: flex; gap: var(--space-1); min-width: 0; max-width: 100%; }
.day-cal-v-block-pills > .day-cal-v-pill { min-width: 0; }
.day-cal-v-block-pills > .day-cal-v-pill-amount { flex-shrink: 0; }
.day-cal-v-pill-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.day-cal-v-block-foot { display: flex; align-items: center; gap: 0 var(--space-1); flex-wrap: wrap; margin-top: auto; }
.day-cal-v-pill {
    display: inline-flex; align-items: center;
    padding: 0 var(--space-1);
    border-radius: 999px;
    font-size: var(--text-xs);
    border: 1px solid transparent;
    line-height: var(--leading-tight);
}
.day-cal-v-pill-therapist {
    background: oklch(0.92 0.07 290);
    border-color: oklch(0.78 0.10 290);
    color: oklch(0.34 0.12 290);
}

.day-cal-v-now-line {
    position: absolute;
    height: 0;
    border-top: 2px solid var(--lime-500);
    pointer-events: none;
    z-index: 2;
}
.day-cal-v-now-label {
    position: absolute;
    left: 0;
    background: var(--lime-500);
    color: var(--ink);
    padding: var(--space-05) var(--space-2);
    border-radius: 4px;
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: 0.04em;
    transform: translateY(-50%);
    z-index: 3;
    pointer-events: none;
    grid-column: 1;
    width: 4.5rem;
    text-align: center;
}

/* Phase D — extra pill flavours on calendar blocks. The block sits on
   a pinned-light background that doesn't flip in dark mode, so pill
   text colours are also pinned (var(--ink-soft) flips light and
   disappears against the pill background). */
.day-cal-v-pill-amount {
    background: oklch(0.95 0.04 25);
    border-color: oklch(0.55 0.16 25);
    color: oklch(0.40 0.16 25);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Phase C1 — therapist-column mode pills + adjusted column-header
   badge so the inline colour-hint stays readable on dark accents.
   Pinned colours, for the reason given above .day-cal-v-pill-amount. */
.day-cal-v-pill-room {
    background: oklch(0.94 0.02 210);
    border-color: oklch(0.88 0.025 210);
    color: oklch(0.44 0.04 215);
}
.day-cal-v-colhead-row .day-cal-v-room-badge[style*="background"] {
    /* Inline-style takes over background+colour for therapist mode. */
    border: 1px solid oklch(0.30 0.03 210 / 0.4);
}

/* Phase C2 — week-view tweaks. Day columns get a subtle "today" tint
   on the column header + body, anchored against the same grid the
   day calendar uses. */
.week-cal-v .day-cal-v-colhead.is-today {
    background: oklch(0.97 0.04 130);
    border-bottom-color: var(--lime-500);
}
.week-cal-v .day-cal-v-col.is-today {
    background: oklch(0.99 0.015 130);
}
.week-cal-v-day-label {
    font-weight: 700;
    color: var(--teal-900);
    font-size: var(--text-base);
}
.week-cal-v-date-label {
    margin-left: var(--space-2);
    color: var(--ink-soft);
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    flex: 1;
}

/* REQ-F04-009 / -010 — non-working day columns in the week view. The
   header carries a small tag (holiday name or «Κλειστά») and the whole
   column body is shaded with the same .day-cal-v-ooh fill the day view
   uses, so a closed day / Sunday / public holiday reads distinctly. */
.week-cal-v .day-cal-v-colhead.is-nonworking {
    background: color-mix(in oklch, var(--ink) 6%, var(--card));
}
.week-cal-v-daytag {
    display: block;
    margin-top: var(--space-1);
    padding: var(--space-05) var(--space-2);
    border-radius: 999px;
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Holiday — amber, same palette as the day-view holiday banner. */
.week-cal-v-daytag.is-holiday {
    border: 1px solid oklch(0.78 0.13 80);
    background: oklch(0.96 0.06 90);
    color: oklch(0.32 0.07 60);
}
/* Plain closed / non-operating day (e.g. Sunday). */
.week-cal-v-daytag.is-closed {
    border: 1px solid var(--line);
    background: color-mix(in oklch, var(--ink) 8%, transparent);
    color: var(--ink-soft);
}

/* Phase C3 — keyboard focus ring on calendar session blocks. The
   block is rendered as <a> so :focus-visible already triggers the
   browser default; we lift it slightly so it sits above the
   neighbouring blocks at column boundaries. */
.day-cal-v-block:focus-visible {
    outline: 2px solid var(--lime-500);
    outline-offset: 2px;
    z-index: 4;
}

/* Phase C4 — click-empty-to-create overlay. Empty half-hour slots
   render a faint "+" that fades in on column hover; the link is
   absolutely positioned so it sits above the row dividers but below
   any session block. */
.day-cal-v-empty {
    position: absolute;
    left: 0.25rem;
    right: 0.25rem;
    border-radius: var(--radius-sm);
    color: var(--ink-soft);
    text-decoration: none;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: var(--text-lg);
    opacity: 0;
    transition: opacity var(--transition-fast), background var(--transition-fast);
    z-index: 1;
}
.day-cal-v-col:hover .day-cal-v-empty {
    opacity: 0.5;
}
.day-cal-v-empty:hover,
.day-cal-v-empty:focus-visible {
    opacity: 1 !important;
    background: var(--teal-50);
    outline: 2px dashed var(--lime-500);
    outline-offset: -2px;
}
@media (hover: none) {
    /* Mobile: no hover layer, render at half opacity always so users
       can still see the affordance. */
    .day-cal-v-empty { opacity: 0.4; }
}

/* Phase C5 — drag/resize affordances on calendar session blocks. */
.day-cal-v-block { cursor: grab; touch-action: none; }
/* #724 — a `data-` attribute, not a class: Blazor renders `class` on this
   element (BlockClass) and clears only what it rendered, so a class written
   from JS is a second writer of an attribute the renderer owns. It never
   renders `data-dragging`, so the two cannot reach each other. */
.day-cal-v-block[data-dragging] {
    cursor: grabbing;
    opacity: 0.8;
    outline: 2px solid var(--lime-500);
    outline-offset: 0;
    z-index: 5;
}

/* Drop-target indicator — owner report R-20260731-07 (#573 §6, iPad):
   «When I drag and drop on different date I don't see the square. Eg the
   target date slot.» A move repositions the block inside its OWN column,
   so a cross-column (in week view: cross-DATE) drag gave no feedback in
   the column being dropped on. calendar-drag.js now tints the column
   under the pointer and appends a ghost sized to the dragged block at the
   snapped drop position. Presentational only — the ghost is aria-hidden
   and takes no pointer events, so it can never intercept the drop. */
/* #724 — attribute, not class, for the same reason as [data-dragging]
   above; in the week view the column's class is not even constant
   (`is-today` / `is-nonworking`), so JS and Blazor were writing the same
   string from both ends. */
.day-cal-v-col[data-drop-target] {
    background: var(--teal-50);
}

/* #724 — the ghost is no longer a foreign child of the Blazor-managed
   column. calendar-drag.js creates this layer and parents it to
   `document.body`, which Blazor never diffs (the app roots at #app), and
   positions the ghost `fixed` from the column's own bounding rect.

   z-index sits below `.app-header` (30) and every modal, so an overlay the
   user cannot dismiss can never cover the chrome. It is above the calendar,
   which is a deliberate change from the old in-column z-index 4: a body
   child cannot be pushed underneath page content. In practice the ghost only
   overlaps the dragged block during a SAME-column move, where the drop
   position is already obvious; the cross-column case the ghost exists for
   (R-20260731-07) never overlaps it at all. */
.cal-drop-layer {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 25;
}
.cal-drop-ghost {
    position: fixed;
    border: 2px dashed var(--lime-500);
    border-radius: var(--radius-sm);
    background: color-mix(in oklch, var(--lime-500) 14%, transparent);
    pointer-events: none;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

/* #832, back in v0.279 3-B — the target says «not here» WHILE the pointer is
   over it. The busy windows are the SERVER's answer (busy-windows); the
   browser copy of the overlap rule #1435 removed stays gone.

   REQ-F29-007: THE COLOUR IS NEVER THE ONLY MESSAGE. Three things change at
   once and only one of them is hue — the border goes solid, the ghost carries
   the word «Κατειλημμένο», and the cursor says no. */
.cal-drop-ghost[data-blocked] {
    border-style: solid;
    border-color: #b91c1c;
    background: color-mix(in oklch, #b91c1c 16%, transparent);
    cursor: not-allowed;
}
.cal-drop-ghost__label {
    font-size: var(--text-xs);
    font-weight: 700;
    line-height: 1.1;
    text-align: center;
    padding: 0 var(--space-1);
    color: #7f1d1d;
    background: #fff;
    border-radius: var(--radius-sm);
}
/* Empty when the target is free — no stray chip on an ordinary drag. */
.cal-drop-ghost__label:empty {
    display: none;
}
.day-cal-v-col[data-drop-blocked],
.week-cal-v .day-cal-v-col.is-today[data-drop-blocked] {
    background: #fef2f2;
}
/* The «today» tint would otherwise win over the drop tint in week view
   (same specificity, defined later in the file). */
.week-cal-v .day-cal-v-col.is-today[data-drop-target] {
    background: var(--teal-50);
}
.day-cal-v-resize-handle {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 6px;
    cursor: ns-resize;
    opacity: 0;
    background: linear-gradient(to bottom, transparent, oklch(0.30 0.04 210 / 0.25));
    border-bottom-left-radius: var(--radius-sm);
    border-bottom-right-radius: var(--radius-sm);
    transition: opacity var(--transition-fast);
    touch-action: none;
}
.day-cal-v-block:hover .day-cal-v-resize-handle,
.day-cal-v-block:focus-within .day-cal-v-resize-handle {
    opacity: 0.9;
}

/* Rich hover preview + drag-reschedule confirmation. */
.day-cal-v-hovercard {
    position: fixed;
    z-index: 1200;
    pointer-events: none;
    max-width: 20rem;
    background: var(--card, #fff);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: 0 8px 24px oklch(0.2 0.03 215 / 0.22);
    padding: 0.6rem var(--space-3);
    font-size: var(--text-sm);
}
.day-cal-v-hovercard-time {
    display: flex; justify-content: space-between; align-items: baseline;
    gap: var(--space-3); font-weight: 700;
}
.day-cal-v-hovercard-status {
    font-weight: 600; color: var(--ink-soft); font-size: var(--text-xs);
}
.day-cal-v-hovercard-child {
    font-size: var(--text-lg); font-weight: 700; margin: var(--space-05) 0 var(--space-2);
}
.day-cal-v-hovercard-rows { margin: 0; display: grid; gap: var(--space-05); }
.day-cal-v-hovercard-rows > div { display: flex; gap: var(--space-2); }
.day-cal-v-hovercard-rows dt {
    color: var(--ink-soft); min-width: 5.5rem; margin: 0;
}
.day-cal-v-hovercard-rows dd { margin: 0; font-weight: 600; }
.day-cal-v-hovercard-owed {
    display: flex; justify-content: space-between; align-items: baseline;
    margin-top: var(--space-2); padding-top: var(--space-2);
    border-top: 1px solid var(--line);
}
.day-cal-v-hovercard-owed strong {
    font-size: var(--text-xl); color: oklch(0.55 0.18 25);
}

.day-cal-v-confirm-backdrop {
    position: fixed; inset: 0; z-index: 1300;
    background: oklch(0.2 0.03 215 / 0.35);
    display: flex; align-items: center; justify-content: center;
}
.day-cal-v-confirm {
    background: var(--card, #fff); color: var(--ink);
    border-radius: var(--radius);
    box-shadow: 0 12px 40px oklch(0.2 0.03 215 / 0.35);
    padding: var(--space-5); width: min(24rem, calc(100vw - 2rem));
}
.day-cal-v-confirm h2 { margin: 0 0 var(--space-1); font-size: var(--text-xl); }
.day-cal-v-confirm-child { margin: 0 0 var(--space-3); font-weight: 700; }
.day-cal-v-confirm-rows { margin: 0 0 var(--space-4); display: grid; gap: var(--space-1); }
.day-cal-v-confirm-rows > div { display: flex; gap: var(--space-3); }
.day-cal-v-confirm-rows dt {
    color: var(--ink-soft); min-width: 3rem; margin: 0;
}
.day-cal-v-confirm-rows dd { margin: 0; font-weight: 600; }
.day-cal-v-confirm-actions {
    display: flex; gap: var(--space-2); justify-content: flex-end;
}
