/* Chat page styles. CSS requires @import before any other rule (including
   @font-face below), so this stays at the very top. Loading it here means both
   hosts (WASM + MAUI) that already pull app.css get chat.css for free — no need
   to edit two host shells. */
@import "chat.css";

/* 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 and
       used by the component overrides below. MudTheme.PaletteLight in
       CabinetTheme.cs holds the same colour as `--brand-primary` (plus
       Secondary/Tertiary for MudBlazor's own pipeline). 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). MudBlazor's text and border defaults
       are close but not identical; 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;
    --slate-700:  #334155;
    --slate-900:  #0F172A;

    --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;

    /* Alert tints, one triplet per severity (bg / ink / msg). `ink` drives
       the icon colour (via currentColor) and the 1px border (via color-mix);
       `msg` is the body-text ink. Each color appears in exactly one place —
       flip them once in the dark block below and every MudAlert tracks. */
    --alert-success-bg:  #E7F8EF;
    --alert-success-ink: #2BB673;
    --alert-success-msg: #0F5132;
    --alert-warning-bg:  #FEF6DA;
    --alert-warning-ink: #E89505;
    --alert-warning-msg: #6E430A;
    --alert-error-bg:    #FDEAEA;
    --alert-error-ink:   #DC2626;
    --alert-error-msg:   #8B1818;
    --alert-info-bg:     #E6F3FB;
    --alert-info-ink:    #2990DC;
    --alert-info-msg:    #14598E;

    /* 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) is configured via
   MudTheme.Typography in Themes/CabinetTheme.cs — MudThemeProvider injects a
   runtime <style> tag that overrides any :root rule we set here, so the theme
   is the only source of truth that wins. The @font-face above stays in app.css
   because it has to be present at first paint, before MudThemeProvider runs. */

/* ------------------------------------------------------------------------
   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;
}

/* ========================================================================
   MudBlazor 9.4 component overrides — bring the cabinet visually onto the
   РЕПЕТИТУМ design system (see Claude Design bundle, ui_kits/cabinet/).
   Each block targets a specific MudBlazor class set; the design tokens
   come from --brand-*, --slate-* and --border-hairline above.
   ======================================================================== */

/* ---------- Buttons ----------
   Sentence case via text-transform: none (MudBlazor defaults to uppercase),
   weight 500. Radius comes from CabinetTheme.LayoutProperties.DefaultBorderRadius
   (10px) and is shared with inputs so forms read as one shape language.
   Filled gets a subtle ink-tone shadow; outlined uses a 1.5px inner ring
   instead of a thin border so it doesn't shift layout on hover. */
.mud-button-root {
    text-transform: none;
    font-weight: 500;
    letter-spacing: 0;
    transition: background-color 120ms, color 120ms, box-shadow 120ms;
}

/* Default-size buttons match outlined-input height (~48px) so forms with
   stacked inputs + a submit button look balanced. Excludes size-small /
   size-large variants (which keep their MudBlazor defaults) and icon
   buttons (whose visual is the icon, not the chrome). */
.mud-button-root:not(.mud-icon-button):not([class*="-size-small"]):not([class*="-size-large"]) {
    min-height: 48px;
}

/* AppBar buttons (signed-out "Войти", future contextual actions) stay at
   their natural MudBlazor size — a 48px chrome button overpowers the avatar
   next to it and looks oversized inside a 64px AppBar. Scoping by
   `.mud-appbar` avoids the discipline trap of "remember Size.Small for
   every AppBar button". */
.mud-appbar .mud-button-root {
    min-height: unset;
}

.mud-button-root.mud-button-filled {
    box-shadow: 0 1px 2px rgba(20, 89, 142, 0.25);
}

.mud-button-root.mud-button-filled:hover {
    box-shadow: 0 2px 4px rgba(20, 89, 142, 0.3);
}

.mud-button-root.mud-button-outlined {
    border-width: 1.5px;
}

.mud-button-root.mud-button-outlined:hover {
    background-color: var(--brand-primary-tint);
}

.mud-button-root.mud-button-text:hover {
    background-color: var(--brand-primary-tint);
}

/* MudForm renders as `display: flex; flex-direction: column; align-items:
   stretch;` — which makes its direct children (text fields, alerts, buttons)
   fill the form width. Inputs DO want to be wide, but buttons should sit
   inline at their content width. Align direct-child buttons to the start so
   they don't stretch. !important wins over MudButton's own align-self rules
   from MudBlazor.min.css. If a future case (e.g., a row of chips) needs the
   same treatment, add a SEPARATE rule for that selector — do not widen this
   one to cover layout containers like <MudStack>, which legitimately want to
   fill the form width when they hold a row of inputs. */
.mud-form > .mud-button-root {
    align-self: flex-start !important;
}

/* ---------- Paper / cards ----------
   The design system uses 12px for outlined paper (admin stat cards, profile
   shell) and the same hairline as the AppBar border. Default elevation-0
   paper (sign-in card body) stays uncluttered — no border, just radius. */
.mud-paper {
    border-radius: 12px;
}

.mud-paper-outlined {
    border-radius: 12px;
    border: 1px solid var(--border-hairline);
    box-shadow: none;
}

/* Inputs (MudTextField, MudSelect, MudAutocomplete) are intentionally left
   to MudBlazor + the CabinetTheme defaults. Earlier attempts to re-skin
   the border / focus / shrunk-label scale / notched-legend width poked
   through to inner elements that MudBlazor lays out around the fieldset
   and produced visible artifacts (sharp inner corners past the rounded
   border, asymmetric caption padding). The theme gives us what matters:
       - Corner radius from LayoutProperties.DefaultBorderRadius = 10px
       - Focus / primary tint from PaletteLight.Primary (brand-blue)
       - Hover / surface tones from PaletteLight automatically
       - Inter font from Typography

   The safe tweaks here are: pseudo-element content removal (no layout
   impact), label colour (a property MudBlazor does not animate), and the
   shrunk-label scale paired with a matching legend font-size (notch width
   has to grow with label width or the right edge sits on the border). We
   deliberately do NOT touch border-color, focus shadow, or border
   transitions — those re-introduced visible inner-corner artifacts in
   earlier attempts. */

/* Hide the required asterisk — the design system communicates required-ness
   via the validation message below the field, not a "*" on the label. */
.mud-input-control.mud-input-required > .mud-input-control-input-container > .mud-input-label::after {
    content: none;
}

/* Caption colour matches the field border (slate-400 in light mode, slate-600
   in dark) so the label visually anchors to the outline rather than reading
   as separate body text. PaletteLight.LinesInputs / PaletteDark.LinesInputs
   in CabinetTheme.cs drives both — change it there and the label follows.
   Three states to cover:
     1. Empty + unfocused — base rule below.
     2. Filled + unfocused (label shrunk to the border) — MudBlazor's own
        `.mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol`
        rule re-colours the shrunk label to text-primary (slate-900). We
        match that selector and override it back to lines-inputs so the
        caption keeps its colour the moment the field gets a value.
     3. Focused (with or without value) — MudBlazor tints the label to
        Primary; we re-state the focus rule AFTER (2) so it wins by source
        order even when both selectors match a focused-and-filled field. */
.mud-input-control > .mud-input-control-input-container > .mud-input-label-inputcontrol {
    color: var(--mud-palette-lines-inputs);
}

.mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol {
    color: var(--mud-palette-lines-inputs);
}

.mud-input:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol {
    color: var(--mud-palette-primary);
}

/* Bump the shrunk floating label from MudBlazor's default scale(0.75) (≈12px)
   to scale(0.875) (≈14px) so the caption-on-the-border is easier to read.
   Translate Y nudged -6 → -7 to keep the larger glyph vertically centred on
   the border line; max-width re-uses MudBlazor's formula with the new scale.
   The paired legend font-size below sizes the notch so both edges of the
   label have the same 5px padding to the border. */
.mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined,
.mud-input:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined {
    transform: translate(14px, -7px) scale(0.875);
    max-width: calc((100% - 14px) / 0.875);
}

.mud-input.mud-input-outlined > .mud-input-outlined-border legend {
    font-size: 0.875rem;
}

/* ---------- Numeric field spin buttons (RpNumericField) ----------
   MudNumericField stacks the up/down buttons in an absolutely-positioned column pinned to
   right:0 / top:0 / bottom:0 of the (position:relative) .mud-input box, with
   justify-content:space-between. Against the cabinet's rounded outlined border (10px radius
   + a 1–2px border line) the square buttons sit ON the border and their corners poke past the
   rounded corners — the "buttons outside the frame" artefact. Pull the column a few px off the
   right edge and centre it vertically so the buttons clear both the border line and the corner
   arcs; shrink the glyphs so the pair fits comfortably even in a dense field. Values are small
   nudges — adjust right/font-size if a future field needs more room. */
.mud-input-control.mud-input-number-control .mud-input-numeric-spin {
    /* Inset the column a few px inside the box on all sides so it never sits on the border line or
       pokes past the rounded corners (the "buttons outside the frame" artefact), then let the two
       buttons split that inset height for a large click target. ~3px top/bottom clears the 10px
       corner radius at the ~4px right inset. */
    top: 3px;
    bottom: 3px;
    right: 4px;
    justify-content: stretch;
    gap: 0;
}
.mud-input-control.mud-input-number-control .mud-input-numeric-spin button {
    /* Each button fills HALF the inset column (a tall hit area), instead of MudButton's 48px box that
       overflowed the field. !important beats MudButton's own min-height. A little horizontal padding
       widens the target while staying inside the 24px right-padding MudBlazor reserves for the spin. */
    flex: 1 1 0;
    padding: 0 3px;
    line-height: 1;
    min-height: 0 !important;
    height: auto !important;
}
.mud-input-control.mud-input-number-control .mud-input-numeric-spin .mud-icon-root {
    font-size: 1rem;
}

/* ---------- Checkbox ----------
   When checked, the default-color checkbox should fill with brand-blue instead
   of slate-700 (the default text color). MudCheckBox uses the wrapping
   .mud-button-root span's color to tint the SVG icon; .mud-checkbox-true marks
   the checked state. Color="Color.Primary" on the markup would also work, but
   doing it once here keeps the entire cabinet consistent without forcing every
   future MudCheckBox to remember the Color parameter. */
.mud-checkbox .mud-checkbox-true.mud-default-text {
    color: var(--brand-primary);
}
.mud-checkbox .mud-checkbox-true.mud-default-text:hover {
    background-color: var(--brand-primary-tint);
}

/* ---------- Alerts ----------
   A flat, professional banner: tinted background + a 1px severity-toned
   border + a plain severity-coloured icon (no accent stripe, no filled
   "puck"). MudAlert's structure: .mud-alert wraps .mud-alert-icon-left
   (icon) + .mud-alert-message. We strip MudBlazor's own icon padding and
   line-height so the glyph sits flush against the FIRST text line
   (align-items: flex-start) — that's why a multi-line alert no longer has
   its icon floating up off-centre. The tinted background + ink-tone text
   matches the design system's success/warning/error/info tokens; the
   border-color is derived from the same ink via color-mix so it follows
   dark-mode token overrides automatically. */
.mud-alert {
    border-radius: 8px;
    padding: 11px 14px;
    align-items: flex-start;
    gap: 10px;
    border: 1px solid transparent;
}

.mud-alert .mud-alert-icon-left {
    background: transparent;
    border-radius: 0;
    width: auto;
    height: auto;
    min-width: 0;
    padding: 0;
    margin: 0;
    display: flex;
    align-items: center;
    opacity: 1;
}

.mud-alert .mud-alert-icon-left .mud-icon-root {
    color: currentColor;
    font-size: 20px;
    line-height: 1.45;
}

/* Kill MudBlazor's default .mud-alert-message{padding:9px 0} so the message
   baseline lines up with the icon under flex-start. */
.mud-alert .mud-alert-message {
    padding: 0;
    line-height: 1.45;
}

/* Severity tints — text inherits the ink tone from a wrapper so ::before
   and .mud-alert-icon-left's `currentColor` pick the same brand hue. The
   message itself gets a darker ink via .mud-alert-message. */
.mud-alert.mud-alert-text-success,
.mud-alert.mud-alert-outlined-success,
.mud-alert.mud-alert-filled-success {
    background: var(--alert-success-bg);
    color: var(--alert-success-ink);
    border-color: color-mix(in srgb, var(--alert-success-ink) 28%, transparent);
}
.mud-alert.mud-alert-text-success .mud-alert-message,
.mud-alert.mud-alert-outlined-success .mud-alert-message,
.mud-alert.mud-alert-filled-success .mud-alert-message {
    color: var(--alert-success-msg);
}

.mud-alert.mud-alert-text-warning,
.mud-alert.mud-alert-outlined-warning,
.mud-alert.mud-alert-filled-warning {
    background: var(--alert-warning-bg);
    color: var(--alert-warning-ink);
    border-color: color-mix(in srgb, var(--alert-warning-ink) 32%, transparent);
}
.mud-alert.mud-alert-text-warning .mud-alert-message,
.mud-alert.mud-alert-outlined-warning .mud-alert-message,
.mud-alert.mud-alert-filled-warning .mud-alert-message {
    color: var(--alert-warning-msg);
}

.mud-alert.mud-alert-text-error,
.mud-alert.mud-alert-outlined-error,
.mud-alert.mud-alert-filled-error {
    background: var(--alert-error-bg);
    color: var(--alert-error-ink);
    border-color: color-mix(in srgb, var(--alert-error-ink) 28%, transparent);
}
.mud-alert.mud-alert-text-error .mud-alert-message,
.mud-alert.mud-alert-outlined-error .mud-alert-message,
.mud-alert.mud-alert-filled-error .mud-alert-message {
    color: var(--alert-error-msg);
}

.mud-alert.mud-alert-text-info,
.mud-alert.mud-alert-outlined-info,
.mud-alert.mud-alert-filled-info {
    background: var(--alert-info-bg);
    color: var(--alert-info-ink);
    border-color: color-mix(in srgb, var(--alert-info-ink) 28%, transparent);
}
.mud-alert.mud-alert-text-info .mud-alert-message,
.mud-alert.mud-alert-outlined-info .mud-alert-message,
.mud-alert.mud-alert-filled-info .mud-alert-message {
    color: var(--alert-info-msg);
}

/* ========================================================================
   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. MudBlazor 9.4's
   MudThemeProvider does NOT add a body class on dark mode — it rewrites
   :root --mud-palette-* via a runtime <style>, which doesn't help us
   because our --brand-* / --slate-* / --alert-* / --state-* tokens are
   ours, not MudBlazor's. Hence this separate switch.

   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 to slate-900 ≈ body text colour. */
    --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 */
    --slate-700:  #CBD5E1;
    --slate-900:  #E8EAEC;  /* body text — design's --read-fg */

    --border-hairline: #232A35;

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

    --alert-success-bg:  rgba(43, 182, 115, 0.18);
    --alert-success-ink: #5EE5A8;
    --alert-success-msg: #A7F3D0;
    --alert-warning-bg:  rgba(232, 149, 5, 0.18);
    --alert-warning-ink: #FBBF24;
    --alert-warning-msg: #FDE68A;
    --alert-error-bg:    rgba(220, 38, 38, 0.18);
    --alert-error-ink:   #F87171;
    --alert-error-msg:   #FECACA;
    --alert-info-bg:     rgba(41, 144, 220, 0.18);
    --alert-info-ink:    #60A5FA;
    --alert-info-msg:    #BFDBFE;

    --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. */

/* ------------------------------------------------------------------------
   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: "Произошла ошибка."
}

/* Typing indicator — shown below the message list in direct rooms only.
   --slate-500 is the design system's muted-text neutral (#64748B light, #94A3B8 dark). */
.typing-indicator {
    padding: 2px 20px 6px;
    font-size: 12px;
    font-style: italic;
    color: var(--slate-500, #64748B);
}

/* RpEmptyState — shared empty-list block. Tokens flip in body.rp-dark automatically. */
.rp-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 40px 16px;
}
.rp-empty-icon { color: var(--slate-300); margin-bottom: 8px; }
.rp-empty-icon.mud-icon-root { width: 40px; height: 40px; font-size: 40px; }
.rp-empty-title { font-size: 15px; font-weight: 600; color: var(--slate-700); margin-bottom: 4px; }
.rp-empty-desc { font-size: 13px; color: var(--slate-500); max-width: 380px; }
.rp-empty-action { margin-top: 16px; }

/* Course lesson-canvas block cards */
.rp-block-card { transition: box-shadow 120ms ease; }
.rp-block-card:hover { box-shadow: var(--mud-elevation-2); }
/* Small, muted per-block save-status dot — only rendered for non-saved states (see BlockCard). */
.rp-block-status-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; vertical-align: middle; }

/* Course structure tree (left editor pane) */
.rp-tree-module { border-radius: 8px; padding: 4px 6px; }
.rp-tree-title { cursor: default; }
.rp-tree-lesson { padding: 4px 8px 4px 20px; border-radius: 6px; cursor: pointer; }
.rp-tree-lesson:hover { background: var(--mud-palette-action-default-hover); }
.rp-tree-lesson-active { background: var(--mud-palette-primary-hover); font-weight: 600; }
/* Row action buttons (↑↓ / add / delete) stay hidden until the row is hovered or keyboard-focused,
   so a deep tree isn't cluttered with always-on icons. focus-within keeps them reachable for keyboard. */
.rp-tree-actions { opacity: 0; transition: opacity 120ms ease; }
.rp-tree-modhead:hover .rp-tree-actions,
.rp-tree-modhead:focus-within .rp-tree-actions,
.rp-tree-lesson:hover .rp-tree-actions,
.rp-tree-lesson:focus-within .rp-tree-actions { opacity: 1; }

/* Course outline (single-column document editor: CourseOutline → ModuleSection → LessonSection).
   Kept global (not scoped .razor.css) because the document spans several child components and Blazor
   scoped CSS does not cross component boundaries — same reason as rp-tree / rp-cal. All hues are design
   tokens so dark mode flips for free. */
.rp-outline-module { border: 1px solid var(--mud-palette-lines-default); border-radius: 10px; margin-bottom: 12px; overflow: hidden; }
.rp-outline-row { display: flex; align-items: center; gap: 4px; padding: 6px 8px; }
.rp-outline-module > .rp-outline-row { background: var(--mud-palette-background-grey); }
/* min-width:0 + overflow-wrap lets a long title shrink and wrap (even an unbroken token) instead of
   pushing the count chip / ⋮ kebab off-screen — on mobile the kebab is the only rename/move/delete path. */
.rp-outline-title { font-size: 0.95rem; font-weight: 600; line-height: 2.2; cursor: text; min-width: 0; overflow-wrap: anywhere; }
.rp-outline-lesson { border-top: 1px solid var(--mud-palette-lines-default); }
.rp-outline-lesson:first-child { border-top: none; }
.rp-outline-lesson .rp-outline-title { font-weight: 500; }
.rp-outline-lesson-body { padding: 4px 12px 12px 40px; }
.rp-outline-add { padding: 6px 8px 6px 40px; }
.rp-outline-count { white-space: nowrap; }

/* ========================================================================
   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.
   ======================================================================== */
.rp-cal { display: flex; flex-direction: column; gap: 12px; }

.rp-cal-header { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.rp-cal-nav { display: flex; align-items: center; gap: 4px; }
.rp-cal-title { font-size: 1.05rem; font-weight: 500; margin-left: 8px; }
.rp-cal-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ----- 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); }
.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); }

.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; }
.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--slot   { background: transparent; border: 1px dashed var(--slate-300); color: var(--slate-500); }
.rp-cal-ev--reservation { background: var(--alert-warning-bg); border: 1px solid var(--alert-warning-ink); color: var(--alert-warning-msg); }
.rp-cal-ev--past   { background: var(--slate-100); border-left: 3px solid var(--slate-300); color: var(--slate-500); }

/* ----- 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--slot { background: var(--slate-400); }
.rp-cal-dot--reservation { background: var(--alert-warning-ink); }
.rp-cal-dot--past { background: 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; text-transform: capitalize; }
.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); }
.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); }
.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); }
.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); }
.rp-cal-chip-text { overflow: hidden; text-overflow: ellipsis; }
.rp-cal-month-more { font-size: 10px; color: var(--slate-500); padding-left: 3px; }

/* ---------- Tests gradebook matrix ----------
   Sticky first column (test names) so the grid scrolls horizontally under it.
   Cell tone follows the result state; all on design tokens so dark mode flips. */
.rp-matrix-wrap { overflow-x: auto; }
.rp-matrix { border-collapse: separate; border-spacing: 0; width: max-content; min-width: 100%; }
.rp-matrix th, .rp-matrix td { border: 1px solid var(--mud-palette-lines-default); padding: 6px 10px; white-space: nowrap; text-align: center; }
.rp-matrix thead th { background: var(--mud-palette-background-grey); position: sticky; top: 0; z-index: 2; }
.rp-matrix .rp-matrix-rowhead { position: sticky; left: 0; z-index: 1; background: var(--mud-palette-surface); text-align: left; white-space: normal; min-width: 200px; max-width: 280px; }
.rp-matrix thead th.rp-matrix-corner { left: 0; z-index: 3; }
.rp-matrix-cell { cursor: pointer; min-width: 64px; }
.rp-matrix-cell--none { color: var(--mud-palette-text-disabled); }
.rp-matrix-cell--assigned { color: var(--mud-palette-info-text); background: var(--mud-palette-info-hover); }
.rp-matrix-cell--scored { background: var(--mud-palette-background-grey); }
.rp-matrix-cell--passed { color: var(--mud-palette-success-text); background: var(--mud-palette-success-hover); }
.rp-matrix-cell--failed { color: var(--mud-palette-error-text); background: var(--mud-palette-error-hover); }
/* Deactivated test row: dimmed so it reads as retired but stays reachable to reactivate. */
.rp-matrix-row--inactive { opacity: .55; }
.rp-matrix-row--inactive .rp-matrix-rowhead { font-style: italic; }

/* ==================== Dashboard (home pages) ==================== */
/* Home page top-level rhythm: a flex column with the same 20px cadence as .rp-dash
   columns. Children carry their own legacy 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; }

.rp-dash { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; }
.rp-dash-main { flex: 3 1 520px; min-width: 0; display: flex; flex-direction: column; gap: 20px; }
.rp-dash-rail { flex: 1 1 300px; min-width: 280px; max-width: 360px; display: flex; flex-direction: column; gap: 20px; }
.rp-dash-stack { display: flex; flex-direction: column; gap: 20px; }

.rp-dash-stats { display: flex; gap: 12px; flex-wrap: wrap; }
.rp-dash-tile { flex: 1 1 170px; background: var(--mud-palette-surface); border: 1px solid var(--border-hairline);
    border-radius: 12px; padding: 14px 16px; }
.rp-dash-tile--click { cursor: pointer; }
.rp-dash-tile--click:hover { border-color: var(--brand-primary); }
.rp-dash-tile-head { display: flex; align-items: center; gap: 8px; color: var(--slate-500); font-size: 12.5px; margin-bottom: 8px; }
.rp-dash-tile-value { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; }
.rp-dash-tile-note { font-size: 12px; color: var(--slate-500); }

.rp-dash-card { padding: 0; border: 1px solid var(--border-hairline); border-radius: 12px; overflow: hidden; }
.rp-dash-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 18px 12px; }
.rp-dash-card-title { font-size: 16px; font-weight: 600; }
.rp-dash-card-eyebrow { font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--slate-500); padding: 16px 18px 4px; }
.rp-dash-card-link { font-size: 13px; white-space: nowrap; flex-shrink: 0; }
.rp-dash-card-empty { padding: 14px 18px 18px; color: var(--slate-500); font-size: 13.5px; }
/* Body inset for free-form content inside .rp-dash-card (title + balance + button stacks
   in the rail cards). The eyebrow's own 16/18/4 padding owns the top inset, so this only
   adds horizontal + bottom. Not needed when the card body is a stack of .rp-dash-row's. */
.rp-dash-card-body { padding: 0 18px 16px; }

.rp-dash-row { display: flex; align-items: center; gap: 14px; padding: 13px 18px; border-top: 1px solid var(--border-hairline); }
.rp-dash-row--click { cursor: pointer; }
.rp-dash-row--click:hover { background: var(--mud-palette-action-default-hover); }
.rp-dash-row-body { flex: 1; min-width: 0; }
.rp-dash-row-title { font-size: 14.5px; font-weight: 600; }
.rp-dash-row-sub { font-size: 12.5px; color: var(--slate-500); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rp-dash-row-time { font-size: 13px; color: var(--slate-500); white-space: nowrap; }

.rp-dash-hero { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap;
    background: var(--rp-hero-accent, var(--brand-primary)); color: #fff; border-radius: 14px; padding: 22px 24px; }
.rp-dash-hero-eyebrow { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; opacity: .9; margin-bottom: 8px; }
.rp-dash-hero-title { font-size: 22px; font-weight: 600; letter-spacing: -0.01em; }
.rp-dash-hero-sub { font-size: 14.5px; opacity: .85; margin-top: 2px; }
.rp-dash-hero-meta { display: flex; gap: 18px; margin-top: 14px; font-size: 14px; flex-wrap: wrap; }
.rp-dash-hero-meta span { display: inline-flex; align-items: center; gap: 6px; }
.rp-dash-hero-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.rp-dash-hero-empty { font-size: 14px; opacity: .9; }

.rp-dash-balance-line { display: flex; align-items: baseline; gap: 8px; }
.rp-dash-balance-num { font-size: 30px; font-weight: 700; letter-spacing: -0.02em; }
.rp-dash-balance-exp { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; margin-top: 4px; }
.rp-dash-progress { padding: 6px 18px 14px; }
.rp-dash-progress-head { display: flex; justify-content: space-between; font-size: 13.5px; margin-bottom: 6px; }

/* ==================== Video room (rp-room-*) ====================
   Full-viewport participant grid for live lessons (/room/{lessonId}).
   Uses ReaderLayout (no sidebar/appbar). All colors via var(--...) so
   dark mode flips for free — mirrors the rp-cal-* / rp-tree-* blocks. */
.rp-room { display: flex; flex-direction: column; height: 100dvh; background: var(--mud-palette-background); position: relative; overflow: hidden; }
.rp-room-reconnecting { position: absolute; top: 0; left: 0; right: 0; z-index: 10;
    background: var(--mud-palette-warning); color: #fff; text-align: center; padding: 6px 12px; font-size: 13.5px; }
.rp-room-grid { flex: 1 1 0; overflow: hidden; display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    grid-auto-rows: minmax(180px, 1fr); gap: 4px; padding: 4px; }
.rp-room-tile { position: relative; background: var(--mud-palette-surface);
    border-radius: 10px; overflow: hidden; display: flex; align-items: flex-end; justify-content: center; }
.rp-room-tile video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.rp-room-speaking { outline: 2px solid var(--brand-primary); }
.rp-room-avatar { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-size: 2.5rem; font-weight: 700; letter-spacing: -0.02em;
    color: var(--mud-palette-text-primary); background: var(--mud-palette-action-default-hover); }
/* One's own camera-off tile shows a small hint, not a screen-filling initial. */
.rp-room-avatar-self { font-size: 0.95rem; font-weight: 500; letter-spacing: 0;
    color: var(--mud-palette-text-secondary); }
.rp-room-name { position: relative; z-index: 1; padding: 4px 8px 6px;
    font-size: 13px; font-weight: 500; color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.6);
    background: linear-gradient(transparent, rgba(0,0,0,.45)); width: 100%; text-align: left; }
.rp-room-tile .mud-icon-button { position: absolute; top: 4px; right: 4px; z-index: 2;
    background: rgba(0,0,0,.35); color: #fff; border-radius: 50%; }
.rp-room-hands { position: absolute; top: 8px; right: 8px; z-index: 5;
    background: var(--mud-palette-surface); border: 1px solid var(--border-hairline);
    border-radius: 12px; padding: 10px 14px; min-width: 200px; max-height: 40vh; overflow-y: auto;
    box-shadow: 0 4px 20px rgba(0,0,0,.12); }
.rp-room-hands h4 { margin: 0 0 8px; font-size: 13px; font-weight: 600; color: var(--slate-500); }
.rp-room-hand-row { display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 4px 0; border-bottom: 1px solid var(--border-hairline); }
.rp-room-hand-row:last-child { border-bottom: none; }
.rp-room-toolbar { flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
    gap: 12px; padding: 10px 16px; background: var(--mud-palette-surface);
    border-top: 1px solid var(--border-hairline); }
.rp-room-fallback { display: flex; flex-direction: column; align-items: center; justify-content: center;
    height: 100dvh; gap: 8px; padding: 24px; text-align: center;
    color: var(--mud-palette-text-secondary); font-size: 15px; }
/* Narrow screen: single-column tile grid */
@media (max-width: 600px) {
    .rp-room-grid { grid-template-columns: 1fr; }
    .rp-room-hands { position: static; max-height: none; margin: 4px; border-radius: 8px; }
}

/* ==================== Mobile (≤600px) ====================
   The cabinet had no phone-specific overrides — it leaned entirely on MudBlazor's
   grid. Two phone-only refinements live here:
     1) Give page content more horizontal room — the lg container's 16px gutters plus
        a card's generous pa-6 (24px) ate ~40px per side on a 390px screen. Trim both.
     2) Collapse the notifications matrix (4 narrow columns) into one card per event so
        the Push column can't be squeezed past the viewport edge. */
@media (max-width: 600px) {
    /* (E) Reclaim side space: thinner container gutters + trimmed page-card padding.
       Scoped to .mud-main-content so dialogs/menus/popovers (rendered in providers
       outside the main content) keep their own padding. */
    .mud-main-content .mud-container {
        padding-left: 8px;
        padding-right: 8px;
    }
    .mud-main-content .mud-paper.pa-6 {
        padding: 16px !important;
    }

    /* (B1) Notifications matrix → stacked cards. Hide the header row; each <tr> becomes a
       bordered card with the event label on its own line and the three channel cells in a
       labelled row (the label comes from each cell's data-label via ::before). The class lands
       on MudSimpleTable's wrapper <div> and MudBlazor sets table/thead/tr display via more
       specific selectors, so the structural overrides need !important to win. */
    .rp-notif-table thead { display: none !important; }
    .rp-notif-table table,
    .rp-notif-table tbody,
    .rp-notif-table tr { display: block !important; }
    .rp-notif-table tbody tr {
        border: 1px solid var(--border-hairline);
        border-radius: 10px;
        padding: 10px 12px;
        margin-bottom: 8px;
    }
    .rp-notif-table tbody td {
        border: none !important;
        padding: 0 !important;
        text-align: left !important;
    }
    .rp-notif-table tbody td:first-child {
        font-weight: 600;
        margin-bottom: 8px;
    }
    /* The three channel cells share a wrapping row under the label. */
    .rp-notif-table tbody td:not(:first-child) {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        margin-right: 18px;
    }
    .rp-notif-table tbody td:not(:first-child)::before {
        content: attr(data-label);
        color: var(--slate-500);
        font-size: 12px;
    }

    /* Dashboard: single column, rail under main, on phones. */
    .rp-dash { flex-direction: column; }
    .rp-dash-main, .rp-dash-rail { flex: 1 1 100%; max-width: 100%; }
}

/* ==================== SP2-A whiteboard surface (wb-*) ====================
   Tokens: --mud-palette-background (surface, flips in dark mode),
           --mud-palette-background-grey (toolbar pill bg, flips in dark mode),
           --brand-primary (active-color outline, matches rp-room-* neighbouring rules),
           --mud-palette-text-primary (clear button label). */
.wb-surface { position: relative; width: 100%; height: 100%; background: var(--mud-palette-background, #fff); }
.wb-canvas { width: 100%; height: 100%; touch-action: none; cursor: crosshair; display: block; }
.wb-toolbar { position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%);
    display: flex; gap: 6px; align-items: center; padding: 6px 10px; border-radius: 999px;
    background: var(--mud-palette-background-grey, #f2f2f2); box-shadow: 0 2px 8px rgba(0,0,0,.18); }
.wb-color { width: 22px; height: 22px; border-radius: 50%; border: 2px solid #fff; cursor: pointer; padding: 0; }
.wb-color-active { outline: 2px solid var(--brand-primary, #5b6cff); outline-offset: 1px; }
.wb-color:disabled, .wb-clear:disabled, .wb-restore:disabled { opacity: .4; cursor: default; }
.wb-clear, .wb-restore { border: none; background: transparent; color: var(--mud-palette-text-primary, #1a1a1a); cursor: pointer; font-size: 14px; }

/* SP2-A room layout: whiteboard stage + demoted video.
   The stage MUST be an in-flow flex child (flex:1), NOT position:absolute — it replaced the SP1
   `.rp-room-grid` (which was `flex:1 1 0`). As `position:absolute` it collapsed the flex column:
   `.rp-room-toolbar` (the only remaining in-flow child) floated to the top and was painted-over by the
   stage canvas, and the absolute filmstrip (z-index:4) covered the board toolbar — so mic/cam/leave AND
   the pen/colour/eraser/clear controls were all unclickable. As a flex child the toolbar returns to the
   bottom and `position:relative` makes the stage the containing block for the corner/filmstrip/wb-toolbar. */
.rp-room-stage { flex: 1 1 0; position: relative; min-height: 0; overflow: hidden;
    display: flex; align-items: center; justify-content: center; container-type: size; }
.rp-room-corner { position: absolute; top: 12px; right: 12px; width: 160px; z-index: 5; }
.rp-room-corner-tile { width: 160px; height: 120px; border-radius: 8px; overflow: hidden; }
.rp-room-filmstrip { position: absolute; bottom: 96px; left: 12px; right: 12px; display: flex;
    gap: 8px; overflow-x: auto; z-index: 4; }
.rp-room-film-tile { width: 160px; height: 120px; flex: 0 0 auto; border-radius: 8px; overflow: hidden; position: relative; }
/* z-index:3 lifts the invite/mute actions above the .rp-room-name label (z-index:1), which is
   full-width at the tile's bottom and otherwise covers the «Пригласить» MudButton — making the
   grant unclickable (the mute MudIconButton already had z-index:2 from .rp-room-tile .mud-icon-button,
   so only the invite button was dead). Caught by the live 2-browser smoke. */
.rp-room-tile-actions { position: absolute; bottom: 4px; right: 4px; display: flex; gap: 2px; z-index: 3; }
/* SP2-B: the content stage is a centered 1:1 SQUARE, letterboxed within the room. Both the slide layer
   and the whiteboard canvas live inside it, so normalized 0..1 strokes are aspect-stable on every device
   (fixes the SP2-A cross-aspect distortion) and one deck works on portrait + landscape. The square is sized
   to the smaller of the stage's two dimensions via container-query units (cqmin). */
.rp-room-square { position: relative; width: 100cqmin; height: 100cqmin;
    background: var(--mud-palette-background, #fff); overflow: hidden; }
/* The slide is the background layer; the whiteboard <canvas> overlays it transparently (.wb-surface). */
.rp-room-slide { position: absolute; inset: 0; overflow: auto; padding: 4% 5%;
    background: var(--mud-palette-background, #fff); color: var(--mud-palette-text-primary, #1a1a1a); }
/* Font sizes are PURE container-query units (cqmin) — no px clamp. The clamp's px floor/ceiling made the
   same slide look bigger (floor on small screens) or smaller (ceiling on large screens) relative to the
   square, so a deck rendered differently per device. Pure cqmin makes the slide a uniform zoom of the
   square: identical proportions on every screen. Markdown children inside the body are em-relative, so
   they scale with the body size automatically. */
.rp-room-slide-title { margin: 0 0 0.6em; font-size: 5cqmin; font-weight: 600; }
/* SP2-B-images: an AI image is the slide's focal element — large, centered, contained (never cropped),
   capped so the title + a short body still fit in the square. cqmin keeps it proportional like the text. */
.rp-room-slide-image { display: block; max-width: 100%; max-height: 60cqmin; margin: 0 auto 0.6em;
    object-fit: contain; border-radius: 8px; }
.rp-room-slide-body { font-size: 3.2cqmin; line-height: 1.4; }
/* When a slide is shown the whiteboard surface must be transparent so the slide shows through. */
.rp-room-square .wb-surface { background: transparent; }
.rp-room-slide-counter { font-size: 13px; color: var(--mud-palette-text-primary, #1a1a1a); min-width: 44px; text-align: center; }
/* 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(--mud-palette-text-secondary); }
/* 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-lesson-cover-thumb { width: 32px; height: 32px; object-fit: cover; border-radius: 6px; margin-right: 8px; flex-shrink: 0; }
.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; }
