/* Inter — variable subset woff2 files, served from this RCL. Two faces split by
   unicode-range let the browser fetch only the script(s) actually used on the page.
   Paths are RCL-relative (no leading slash); they resolve to
   /_content/Repetitum.Cabinet.Shared/fonts/… in both the WASM host and MAUI. */
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('fonts/Inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('fonts/Inter-cyrillic.woff2') format('woff2');
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116; }

:root {
    /* --------------------------------------------------------------------
       Brand-blue tokens — derived from the РЕПЕТИТУМ logo wordmark, used by
       the rp-* rules in bp.css and the calendar/dashboard blocks below.
       Dark-mode flips live in `body.rp-dark` at the bottom of this file.
       -------------------------------------------------------------------- */
    --brand-primary:      #1D73B3;
    --brand-primary-tint: #E6F2FB;

    /* Slate neutrals (Tailwind palette) — the design system anchors everything
       on these so cabinet + landing visually agree. */
    --slate-50:   #F8FAFC;
    --slate-100:  #F1F5F9;
    --slate-200:  #E2E8F0;
    --slate-300:  #CBD5E1;
    --slate-400:  #94A3B8;
    --slate-500:  #64748B;

    --border-hairline: var(--slate-200);

    /* Validation state colors (used by .invalid / .valid.modified outlines
       and .validation-message — Blazor's own EditForm classes, not MudBlazor).
       Tokenized so the dark-mode block can brighten them for contrast on
       slate-900 backgrounds. */
    --state-error:    #E50000;
    --state-success:  #26B050;

    /* Calendar group-lesson tone (purple) — the only calendar hue not already in the ramp above.
       1:1 lessons use --brand-*, slots/past use --slate-*, reservations reuse --alert-warning-*. */
    --cal-group-bg: #EEEDFE;
    --cal-group-fg: #534AB7;
}

/* Typography (font family, sizes, weights, tracking) comes from Blueprint's own base
   styles (blazorblueprint.css) plus the @font-face above, which stays in app.css because
   it has to be present at first paint. */

/* ------------------------------------------------------------------------
   Form validation (Blazor's built-in EditForm classes). The design system
   uses #e50000 for invalid and #26b050 for valid-modified to match the
   landing's existing colors. Kept verbatim — these classes are emitted by
   InputBase<T>, not by our markup.
   ------------------------------------------------------------------------ */
.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--state-success);
}

.invalid {
    outline: 1px solid var(--state-error);
}

.validation-message {
    color: var(--state-error);
}

h1:focus {
    outline: none;
}

/* ========================================================================
   Dark mode — token overrides triggered by the `rp-dark` class on <body>.
   ThemeController.SetAsync / InitializeAsync calls into theme.js's
   `repetitumTheme.apply()` which toggles the class — our own switch for our
   own --brand-* / --slate-* / --alert-* / --state-* tokens (tokens.css's
   shadcn-style vars flip on the parallel `.dark` class on <html> instead).

   The slate ramp inverts — slate-50 (lightest in light) maps to the
   darkest dark in this block — so any rule reading `var(--slate-50)` in
   dark mode renders the dark canvas, and so on up the ramp.
   ======================================================================== */
body.rp-dark {
    --brand-primary:      #2990DC;
    --brand-primary-tint: rgba(41, 144, 220, 0.18);

    /* Dark canvas ramp — ported from the design bundle's `[data-reader="dim"]`
       block. Background #14181F is warmer/less blue than slate-900; the
       slate-* tokens flip so any `var(--slate-50)` in dark mode renders the
       dark canvas, and so on up the ramp. */
    --slate-50:   #14181F;  /* canvas / page bg — design's --read-bg */
    --slate-100:  #1B2029;  /* one step up — MudPaper surface in dark */
    --slate-200:  #232A35;  /* hairline — design's --read-rule */
    --slate-300:  #3A4250;  /* input border (LinesInputs) */
    --slate-400:  #64748B;
    --slate-500:  #94A3B8;  /* muted text — design's --read-fg-muted */

    --border-hairline: #232A35;

    --state-error:    #F87171;
    --state-success:  #5EE5A8;

    --cal-group-bg: rgba(127, 119, 221, 0.20);
    --cal-group-fg: #AFA9EC;
}

/* The AppBar uses MudBlazor's `Color="Color.Surface"` which resolves to
   Palette.Surface — that flips automatically through PaletteDark.Surface.
   But the inline `border-bottom: 1px solid var(--border-hairline)` in
   MainLayout.razor reads the token directly and follows the block above. */

/* ------------------------------------------------------------------------
   Обратный флип: сцена видеокомнаты (доска + слайд) — единственная поверхность,
   которая НЕ темнеет (--wb-paper/--wb-ink, tokens.css), потому что палитра
   маркеров захардкожена под светлое. Значит внутри неё слейт-рампа должна
   вернуться к светлым значениям: разметка слайда рендерится как .rp-markdown,
   а та читает --slate-100 (фон code/pre), --slate-300 (линейка цитаты),
   --slate-500 (текст цитаты) и --brand-primary (ссылки) — с тёмными значениями
   это дало бы тёмный код-блок и светло-серую цитату на белом листе.

   Специфичность тут не спорит с `body.rp-dark`: это РАЗНЫЕ элементы, поэтому
   собственное объявление квадрата выигрывает для него и его поддерева, а не
   сравнивается с объявлением на <body>. Значения — копия светлого `:root`
   выше (литералы неизбежны: сослаться на затенённое значение переменной
   нельзя). Правишь рампу в `:root` — правь и здесь.
   ------------------------------------------------------------------------ */
.rp-room-square {
    --slate-100:     #F1F5F9;
    --slate-300:     #CBD5E1;
    --slate-500:     #64748B;
    --brand-primary: #1D73B3;
}

/* ------------------------------------------------------------------------
   Onboarding checklist — the "Начало работы" card on TutorHome. Done items
   stay tappable (the tutor may want to revisit the page) but their label
   reads as past-tense via line-through + muted opacity.
   ------------------------------------------------------------------------ */
.rp-onboarding-done {
    text-decoration: line-through;
    opacity: 0.65;
}

/* ------------------------------------------------------------------------
   Blazor error boundary — yellow exclamation-mark banner shown when an
   unhandled exception bubbles up. Stock styling kept; the embedded SVG is
   the standard ASP.NET Core "error" icon.
   ------------------------------------------------------------------------ */
.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

.blazor-error-boundary::after {
    content: "Произошла ошибка."
}

/* ========================================================================
   Calendar (Components/Calendar/*) — shared time-grid surface used by the
   tutor schedule and the student "Мои уроки" page. All hues come from the
   design tokens above (--brand-*, --slate-*, --alert-warning-*, --cal-group-*)
   so the whole calendar flips with dark mode without a separate block.
   Kept global (not scoped .razor.css) because the calendar is split across
   several child components and Blazor scoped CSS does not cross component
   boundaries — matching the rp-tree / rp-block convention above.
   ======================================================================== */
/* ----- time grid (week / day) ----- */
.rp-cal-grid { border: 1px solid var(--border-hairline); border-radius: 12px; overflow: hidden; }
.rp-cal-grid-head { display: flex; border-bottom: 1px solid var(--border-hairline); }
.rp-cal-gutter-head { flex: 0 0 52px; }
.rp-cal-dayhead { flex: 1 1 0; text-align: center; padding: 6px 2px; border-left: 1px solid var(--border-hairline);
                  display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.rp-cal-dayhead--today { background: var(--brand-primary-tint); }
/* Same wash as the column below it, so a spent day reads as one block rather than a grey body under a
   live-looking label. Can never collide with --today: a past day is STRICTLY before today.
   The wash carries the whole signal and the label keeps its colours — deliberately: lightening the day
   number to --slate-400 on top of the wash measured 2.5:1, under the 3:1 floor, for a 15px label that is
   the one thing you navigate a week by. Past is not a reason to make a date hard to read. */
.rp-cal-dayhead--past { background: color-mix(in srgb, var(--slate-500) 10%, transparent); }
.rp-cal-dow { font-size: 11px; text-transform: uppercase; color: var(--slate-500); }
.rp-cal-daynum { font-size: 15px; font-weight: 500; }

/* No inner scroll: the page already scrolls, and an inner scrollbar would shrink the columns relative to
   the (unscrolled) header row, throwing the vertical gridlines out of alignment. The grid grows naturally. */
.rp-cal-grid-body { display: flex; }
.rp-cal-gutter { flex: 0 0 52px; position: relative; }
/* Hour labels sit just BELOW their own gridline (no vertical centring), so the top label isn't clipped
   above the grid and each number reads as the start of the hour block beneath it. */
.rp-cal-hour { position: absolute; right: 6px; margin-top: 2px; font-size: 10px; color: var(--slate-400); }
.rp-cal-cols { display: flex; flex: 1 1 0; }
.rp-cal-daycol { flex: 1 1 0; border-left: 1px solid var(--border-hairline); min-width: 0; }
.rp-cal-day-body { position: relative; width: 100%; }

.rp-cal-cell { position: absolute; left: 0; right: 0; cursor: pointer; }
.rp-cal-cell:hover { background: var(--brand-primary-tint); }

/* Availability-window tint (SP1 Task 10) — the tutor's published availability rule windows,
   computed by SchedulePage (AvailabilitySlots.WindowsForDate) and positioned by DayColumn with the
   same Px() math the event blocks use. Non-interactive and kept below both the now-line and the
   event blocks (z-index), so it never intercepts a click meant for the empty-cell layer beneath it. */
.rp-cal-shade { position: absolute; left: 0; right: 0; pointer-events: none; z-index: 1;
                background: color-mix(in srgb, var(--primary) 8%, transparent); }

/* Everything already behind us, painted as a band instead of leaning on the now-line alone: the line
   answers "where is now", which is a different question from "what is already spent" — and on a week
   whose earlier days are entirely past, the line answers nothing at all because it isn't in those
   columns. Stacked ABOVE the availability shade (z-index 1) so a spent availability window reads as
   spent, and BELOW the event blocks (3) so no chip is washed by it; `pointer-events: none` keeps the
   empty cells underneath clickable, so a click on a past slot still reaches the page's own
   «Это время уже прошло» toast rather than hitting a dead area.
   Colour is mixed from --slate-500, which is a mid grey in BOTH themes (the slate ramp inverts under
   body.rp-dark), so the same rule darkens a light grid and lightens a dark one. A flat rgba black would
   be invisible in dark mode. */
.rp-cal-past { position: absolute; left: 0; right: 0; top: 0; pointer-events: none; z-index: 2;
               background: color-mix(in srgb, var(--slate-500) 10%, transparent); }

.rp-cal-now { position: absolute; left: 0; right: 0; height: 0; border-top: 2px solid var(--state-error); z-index: 5; }
.rp-cal-now::before { content: ''; position: absolute; left: 0; top: -4px; width: 7px; height: 7px;
                      border-radius: 50%; background: var(--state-error); }

/* ----- event blocks ----- */
.rp-cal-ev { position: absolute; border-radius: 6px; padding: 2px 6px; overflow: hidden; cursor: pointer;
             font-size: 12px; line-height: 1.25; box-sizing: border-box; z-index: 3; }
.rp-cal-ev[draggable="true"] { cursor: grab; }
.rp-cal-ev[draggable="true"]:active { cursor: grabbing; }
.rp-cal-ev-title { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rp-cal-ev-sub { font-size: 11px; opacity: 0.85; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Spec 11.5: «Курс „X" · Поток „Y"» — third chip line, truncated by CSS ellipsis (never a string cut). */
.rp-cal-ev-ctx { font-size: 11px; opacity: 0.7; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rp-cal-ev-badge { font-size: 11px; font-weight: 500; }

.rp-cal-ev--lesson { background: var(--brand-primary-tint); border-left: 3px solid var(--brand-primary); color: var(--brand-primary); }
.rp-cal-ev--group  { background: var(--cal-group-bg); border-left: 3px solid var(--cal-group-fg); color: var(--cal-group-fg); }
.rp-cal-ev--open   { background: transparent; border: 1px dashed var(--slate-300); color: var(--slate-500); }
.rp-cal-ev--past   { background: var(--slate-100); border-left: 3px solid var(--slate-300); color: var(--slate-500); }
/* Scheduled lesson running right now — green «идёт», so a live lesson reads apart from a plain upcoming
   one even before the now-line reaches it. */
.rp-cal-ev--live   { background: color-mix(in srgb, var(--state-success) 16%, transparent); border-left: 3px solid var(--state-success); color: var(--state-success); }

/* Cancelled lesson. Kept distinct from --past (conducted): to a tutor scanning the week those two read
   as opposites — work done vs. work that never happened — and only this one is normally rendered on the
   rail. The main-lane rule exists for any surface that opts out of the rail; the strikethrough is what
   carries the meaning at any width. */
/* A spent block: the colour pull comes off, so the eye lands on what is still ahead. The COMPONENT stays
   tone-agnostic — it adds --dim purely from the clock — and the stylesheet, which is where tone appearance
   already lives, decides what fading means for each tone.
   The muted tones opt straight back out. They are already at the visual bottom (a conducted lesson is
   grey ON grey), so fading them buys no extra "past" signal — the band beneath already says it — while
   costing real legibility: measured over the past band, --past at 0.62 fell to 2.27:1, under even the
   3:1 large-text floor. Only the saturated tones, which genuinely still pull the eye when spent, fade. */
.rp-cal-ev--dim { opacity: 0.75; }
.rp-cal-ev--past.rp-cal-ev--dim,
.rp-cal-ev--cancelled.rp-cal-ev--dim,
.rp-cal-ev--open.rp-cal-ev--dim { opacity: 1; }

.rp-cal-ev--cancelled { background: var(--slate-100); border-left: 3px solid var(--slate-300); color: var(--slate-500); }
.rp-cal-ev--cancelled .rp-cal-ev-title { text-decoration: line-through; }

/* ----- rail: inert lessons pushed out of the packed lane -----
   A cancelled lesson holds no time (the server's overlap check ignores it), so it must not sit on the
   slot as a full-width block: that covered the empty-cell click layer and halved the width of any
   replacement lesson booked into the same hour. It becomes a hatched strip pinned to the column's right
   edge instead — still visible, so the gap in the day stays explained, but out of the way.

   Width lives HERE and never in the inline style, because :hover widens it: an inline width would win on
   specificity and the expansion would silently never happen. The strip grows leftward from its pinned
   right edge, so it never escapes the column. */
.rp-cal-rail { position: absolute; width: 8px; border-radius: 3px; overflow: hidden; cursor: pointer;
               z-index: 4; box-sizing: border-box; color: var(--slate-500); font-size: 11px;
               line-height: 1.25; padding: 0; white-space: nowrap;
               background-image: repeating-linear-gradient(45deg,
                   var(--slate-300) 0, var(--slate-300) 2px, transparent 2px, transparent 5px);
               background-color: var(--slate-100);
               transition: width 120ms ease, padding 120ms ease; }
.rp-cal-rail:hover, .rp-cal-rail:focus-visible { width: 148px; max-width: 100%; padding: 2px 6px;
               background-image: none; text-decoration: line-through; }
.rp-cal-rail-label { display: block; overflow: hidden; text-overflow: ellipsis; }

/* Touch: a tap goes straight to the lesson dialog, so the expansion has nothing left to reveal — and a
   sticky :hover after the tap would leave the strip stuck open over the block beside it. Neutralise the
   expansion rather than the width: the width is mirrored by DayColumn's RailStripPx to size the gutter
   the main lane yields, and that C# constant cannot see the media state. */
@media (hover: none) {
  .rp-cal-rail:hover { width: 8px; padding: 0; text-decoration: none;
                       background-image: repeating-linear-gradient(45deg,
                       var(--slate-300) 0, var(--slate-300) 2px, transparent 2px, transparent 5px); }
}

/* ----- tone dots (agenda / month) ----- */
.rp-cal-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.rp-cal-dot--lesson { background: var(--brand-primary); }
.rp-cal-dot--group { background: var(--cal-group-fg); }
.rp-cal-dot--open { background: var(--slate-400); }
.rp-cal-dot--past { background: var(--slate-300); }
/* Hollow, not filled — a cancelled day-chip should read as an absence next to the solid dots. */
.rp-cal-dot--cancelled { background: transparent; box-shadow: inset 0 0 0 2px var(--slate-300); }

/* ----- agenda (list) ----- */
.rp-cal-agenda-day { margin-bottom: 16px; }
.rp-cal-agenda-date { font-size: 13px; font-weight: 500; color: var(--slate-500); margin-bottom: 6px; }
/* Только первая буква («Воскресенье, 26 июля») — capitalize писал русский месяц с большой. */
.rp-cal-agenda-date::first-letter { text-transform: uppercase; }
.rp-cal-agenda-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border-hairline);
                     border-radius: 8px; margin-bottom: 6px; cursor: pointer; }
.rp-cal-agenda-row:hover { background: var(--brand-primary-tint); }
/* The list has no grid to wash, so past/future lives entirely in the rows — and that is enough: when a
   day is behind us EVERY one of its rows is dimmed, which is itself the day-level signal, so the date
   heading needs no second treatment. (It got one at first: --slate-400 measured 2.6:1 against the page,
   too weak for the label you scan the list by.) Hover lifts a spent row back to full strength — it
   stays as clickable as any other. */
.rp-cal-agenda-row--dim { opacity: 0.75; }
.rp-cal-agenda-row--dim:hover { opacity: 1; }
.rp-cal-agenda-time { font-weight: 500; font-size: 13px; flex: 0 0 42px; }
.rp-cal-agenda-main { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.rp-cal-agenda-title { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rp-cal-agenda-sub { font-size: 12px; color: var(--slate-500); }
/* Spec 11.5: «Курс „X" · Поток „Y"» — third agenda-row line. */
.rp-cal-agenda-ctx { font-size: 11px; opacity: 0.7; color: var(--slate-500); }
.rp-cal-agenda-badge { flex: 0 0 auto; }

/* ----- month ----- */
.rp-cal-month { border: 1px solid var(--border-hairline); border-radius: 12px; overflow: hidden; }
.rp-cal-month-head { display: grid; grid-template-columns: repeat(7, 1fr); border-bottom: 1px solid var(--border-hairline); }
.rp-cal-month-dow { text-align: center; padding: 6px 0; font-size: 11px; text-transform: uppercase; color: var(--slate-500); }
.rp-cal-month-body { display: grid; grid-template-columns: repeat(7, 1fr); }
.rp-cal-month-cell { min-height: 94px; border-left: 1px solid var(--border-hairline); border-top: 1px solid var(--border-hairline);
                     padding: 4px; cursor: pointer; overflow: hidden; }
.rp-cal-month-cell--out { background: var(--slate-50); }
.rp-cal-month-cell--out .rp-cal-month-num { color: var(--slate-400); }
/* Applied only to in-month days (MonthGrid.IsPastCell) so it never has to out-order --out's background.
   Wash only, no lightened day number — same reason as .rp-cal-dayhead--past above. */
.rp-cal-month-cell--past { background: color-mix(in srgb, var(--slate-500) 8%, transparent); }
.rp-cal-month-num { font-size: 12px; font-weight: 500; display: inline-flex; align-items: center; justify-content: center;
                    min-width: 22px; height: 22px; }
.rp-cal-month-cell--today .rp-cal-month-num { background: var(--brand-primary); color: #fff; border-radius: 50%; }
.rp-cal-chip { display: flex; align-items: center; gap: 4px; font-size: 11px; padding: 1px 3px; border-radius: 4px;
               cursor: pointer; white-space: nowrap; overflow: hidden; }
.rp-cal-chip:hover { background: var(--brand-primary-tint); }
/* Spent chip. A month cell for TODAY carries no past band of its own (the cell is a day, not a
   timeline), so on today this is the only thing separating the morning's lessons from the evening's.
   Kept lighter than it looks like it should be: the chip's text is body-coloured, so it starts from a
   high contrast the fade eats into directly — unlike the event blocks, whose muted tones opt out above. */
.rp-cal-chip--dim { opacity: 0.75; }
.rp-cal-chip-text { overflow: hidden; text-overflow: ellipsis; }
/* The month cell has no rail (its chips are a list, not geometry), so the strikethrough is what tells a
   cancelled lesson apart there. On mobile the rule below hides the chip text entirely, which is why the
   hollow --cancelled DOT carries the same meaning on its own. */
.rp-cal-chip--cancelled .rp-cal-chip-text { text-decoration: line-through; color: var(--slate-500); }
.rp-cal-month-more { font-size: 10px; color: var(--slate-500); padding-left: 3px; }

/* Mobile month cells are too narrow for the "HH:mm Title" chip — collapse each event to its tone dot
   (a wrapping row under the day number), so the day never clips its label to a single glyph. */
@media (max-width: 768px) {
  .rp-cal-month-cell { min-height: 52px; padding: 3px 2px; }
  .rp-cal-month-events { display: flex; flex-wrap: wrap; align-items: center; gap: 3px; margin-top: 2px; }
  .rp-cal-chip { padding: 0; }
  .rp-cal-chip:hover { background: transparent; }
  .rp-cal-chip-text { display: none; }
  .rp-cal-month-more { padding-left: 0; line-height: 1; }
}

/* ==================== Home pages ==================== */
/* Home page top-level rhythm: a 20px flex column. Children carry their own mb-* utilities
   (used on other pages too), so neutralize bottom margin on direct children — the gap owns
   vertical spacing. */
.rp-home { display: flex; flex-direction: column; gap: 20px; }
.rp-home > * { margin-bottom: 0 !important; }

/* ==================== Whiteboard surface (wb-*) ====================
   wb-surface/wb-canvas back WhiteboardSurface.razor's canvas mount — width/height:100% of their
   `.rp-room-square` ancestor (bp.css) so the board always fills the stage in every rp-room-* layout
   (desktop, mobile/tablet portrait, phone landscape). Внутри комнаты фон тут перебивается на
   transparent (bp.css) — бумагу рисует сам квадрат; --wb-paper остаётся как фолбэк для монтирования
   вне .rp-room-square и держит один и тот же светлый лист в обеих темах. */
.wb-surface { position: relative; width: 100%; height: 100%; background: var(--wb-paper); }
.wb-canvas { width: 100%; height: 100%; touch-action: none; cursor: crosshair; display: block; }
/* Image blocks in lesson content (content page + reader). */
.rp-lesson-image { margin: 0 0 1rem; text-align: center; }
.rp-lesson-image img { max-width: 100%; height: auto; border-radius: 8px; display: block; margin: 0 auto; }
.rp-lesson-image figcaption { margin-top: 0.4rem; font-size: 0.85em; color: var(--muted-foreground); }
/* Lesson cover banners (content page + editor thumbnail + reader). */
.rp-lesson-cover { margin: 0 0 1rem; }
.rp-lesson-cover img { width: 100%; max-height: 220px; object-fit: cover; border-radius: 10px; display: block; }
.rp-reader-cover { margin: 0 0 1.5rem; }
.rp-reader-cover img { width: 100%; max-height: 240px; object-fit: cover; border-radius: 10px; display: block; }

/* ==================== Blazor error UI ====================
   Moved from Layout/MainLayout.razor.css when the layout lost its scoped CSS
   (Blueprint shell rewrite): the default display:none is what keeps the
   reload bar hidden until blazor.web.js flips it on an unhandled exception. */
#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}
