/* ==========================================================================
   OpenTutorCRM — дизайн-система
   Нативный CSS: @layer + CSS Nesting + CSS-переменные. Без сборщиков.
   Секции: tokens → base → components → utilities.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Шрифты: локальные файлы вместо CDN (правило AGENTS.md п. 8).

   Кабинет работает на своём сервере и не должен зависеть от доступности
   fonts.googleapis.com: недоступный CDN — это подмена гарнитуры на
   системную и лишний внешний запрос на каждой странице. Файлы вендорные,
   как `htmx.min.js`/`alpine.min.js`.

   Каждый файл — вариативный (`wght 100–900`), поэтому `font-weight`
   объявлен диапазоном: один файл закрывает все начертания из макета
   (400/500/600/700) вместо четырёх копий.

   `unicode-range` разделяет подмножества: браузер скачивает кириллицу
   и латиницу отдельно и только те, что реально встретились на странице.
   `latin-ext` нужен для знака рубля (U+20BD) — он не входит в `latin`. */
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/geist-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/geist-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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}

@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/geist-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/geist-mono-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/geist-mono-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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}

@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/geist-mono-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Lexend";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/lexend-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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}

@font-face {
  font-family: "Lexend";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/lexend-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@layer tokens {
  /* --------------------------------------------------------------------------
     Токены (light — база)
     -------------------------------------------------------------------------- */
  :root {
    color-scheme: light;

    /* Цвета */
    --background: #FFFFFF;              /* фон страницы */
    --foreground: #17171B;              /* основной текст */
    --surface: #F7F7F5;                 /* фон сайдбара / surface */
    --card: #FFFFFF;                    /* фон карточек */
    --card-foreground: #17171B;
    --popover: #FFFFFF;                 /* поповеры, дропдауны */
    --popover-foreground: #17171B;
    --muted: #EFEFEC;                   /* второй фон: chips, tablist */
    /* Вторичный текст: заголовки таблиц, подписи, неактивные табы.
       Значение подобрано под фон `--muted` (самый тёмный из светлых):
       #71717A давал там 4.19:1 — ниже AA для мелкого текста. */
    --muted-foreground: #63636B;        /* вторичный текст (5.17:1 на --muted) */
    --accent: #EFEFEC;                  /* hover-фон ghost-кнопок */
    --accent-foreground: #17171B;
    /* Тёплый терракотовый акцент. Затемнён против исходного #A85B34:
       он же служит цветом ТЕКСТА (ссылки, суммы, аватар на --brand-soft)
       и там давал 4.26:1 — ниже AA. #9E5330 читается на всех светлых
       фонах системы и остаётся тем же тоном. */
    --brand: #9E5330;                   /* 5.62:1 на белом, 4.80:1 на --brand-soft */
    --brand-dark: #8E4A28;              /* hover тёмный */
    --brand-soft: #F6EBE4;              /* мягкий фон акцента */
    /* Подпись на заливке --brand. Отдельный токен, а не белый литерал:
       в тёмной теме бренд светлее (#C97A4F), и белый на нём даёт 3.29:1 —
       ниже AA. Тёмная тема переопределяет токен на чернильный (5.44:1). */
    --brand-foreground: #FFFFFF;        /* 5.62:1 на --brand */
    --primary: #1F1F24;                 /* primary-кнопки, лого */
    --primary-foreground: #FFFFFF;
    --secondary-foreground: #3F3F46;    /* not specified — chosen: тёплый тёмно-серый */
    /* Семантика денег и ошибок. Оба токена несут смысл цветом, поэтому
       обязаны проходить AA и как текст (суммы, метки), и как заливка
       кнопки с белой подписью. Исходные #DE3B3D / #1E8E5A давали 4.38 и
       4.14 — ниже нормы. */
    --destructive: #C62F31;             /* 5.45:1 на белом */
    --destructive-foreground: #FFFFFF;  /* not specified — chosen: белый */
    --success: #16794C;                 /* 5.42:1 на белом */
    --success-foreground: #FFFFFF;      /* not specified — chosen: белый */
    --overlay: rgba(23, 23, 27, 0.45);  /* not specified — chosen: тёмный veil для off-canvas */

    /* Предупреждающий баннер (напоминание о не отмеченных уроках).
       finance-banner ссылается на эти же токены с fallback. */
    --warning-soft: #FBF2D7;            /* тёплый жёлтый фон плашки */
    --warning-border: #EFDFAF;
    --warning-fg: #7A5514;              /* не указано — выбран: тёмный оттенок тона */

    /* Границы, фокус */
    --border: #E4E3DF;
    --input: #E4E3DF;
    --ring: #9E5330;                    /* = --brand: кольцо фокуса того же тона */

    /* Сайдбар */
    --sidebar: #F7F7F5;
    --sidebar-foreground: #17171B;
    --sidebar-accent: #EFEFEC;
    --sidebar-accent-foreground: #17171B;
    --sidebar-border: #E4E3DF;

    /* Статусы уроков */
    --status-cancelled: #F8DDDB;        /* отменён */
    --status-unplanned: #C9EEE5;        /* незапланирован */
    --status-rescheduled: #F7E6C3;      /* перенесён */
    --status-regular: #DBE9FF;          /* обычный */
    --status-cancelled-fg: #8C2F2F;     /* not specified — chosen: тёмный оттенок тона */
    --status-unplanned-fg: #166B58;
    --status-rescheduled-fg: #7A5514;
    --status-regular-fg: #26457F;

    /* Дашборд главной: тинты иконок стат-карточек (свой набор, не статусы
       уроков — семантика unrelated). Фиолетового в палитре статусов нет. */
    --stat-blue: #DBE9FF;
    --stat-blue-fg: #26457F;
    --stat-green: #C9EEE5;
    --stat-green-fg: #166B58;
    --stat-orange: #F7E6C3;
    --stat-orange-fg: #7A5514;
    --stat-purple: #E8DFF6;
    --stat-purple-fg: #5B3A8E;

    /* Графики аналитики финансов: заливки рядов и палитра категорий трат.
       Отдельные токены, а не --success/--destructive: яркость подбиралась
       под заливки диаграмм по референсу, а не под семантику кнопок и сумм.
       Индексы категорий закреплены за списком EXPENSE_CATEGORIES модели
       (analytics.CATEGORY_COLOR_INDEX); средняя светлота читается и в тёмной
       теме, поэтому переопределений там нет. */
    --chart-income: #16A34A;
    --chart-earned: #8B5CF6;
    --chart-expense: #EF4444;
    --chart-cat-1: #A85B34;
    --chart-cat-2: #3A7CA5;
    --chart-cat-3: #1E8E5A;
    --chart-cat-4: #DE3B3D;
    --chart-cat-5: #5B3A8E;
    --chart-cat-6: #C77D1E;
    --chart-cat-7: #0F766E;
    --chart-cat-8: #B0367C;
    --chart-cat-9: #64748B;
    --chart-cat-10: #8AA61D;

    /* Карточка слота (спека slot-booking): собственный цвет, не цвета учеников */
    --cal-slot-accent: #3A7CA5;

    /* Шрифты */
    --font-sans: "Geist", system-ui, Arial, sans-serif;
    --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, monospace;
    --font-display: "Lexend", "Geist", system-ui, sans-serif;
    /* Веса: без этих определений `font-weight: var(--font-medium)`
       молча не работает (токен использовался, но не был задан). */
    --font-medium: 500;
    --font-semibold: 600;

    /* Размеры текста: 12 / 14 / 16 / 20 / 24 / 30 / 36 */
    --text-xs: 0.75rem;    /* 12 */
    --text-sm: 0.875rem;   /* 14 */
    --text-base: 1rem;     /* 16 */
    --text-lg: 1.25rem;    /* 20 */
    --text-xl: 1.5rem;     /* 24 */
    --text-2xl: 1.875rem;  /* 30 */

    /* Геометрия */
    --space: 0.25rem;                 /* базовая сетка 4px */
    --radius-sm: 0.5rem;              /* 8px — nav-item, кнопки малые */
    --radius-md: 0.75rem;             /* 12px — кнопки, инпуты */
    --radius-lg: 1.125rem;            /* 18px — карточки */
    --radius-full: 9999px;            /* пилюли, аватары */

    /* Тени. Литералы rgb(...) жили прямо в правилах карточек брони и меню
       задач — три разных оттенка одного и того же тона. Токены держат их
       рядом и дают тёмной теме переопределить: на тёмном фоне тень
       невидима, её заменяет рамка. */
    --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.06);
    --shadow-soft: 0 1px 2px rgba(23, 23, 27, 0.04);
    --shadow-hover: 0 1px 3px rgba(23, 23, 27, 0.06);
    --shadow-popover: 0 8px 24px rgba(23, 23, 27, 0.12);

    /* Анимации */
    --duration-fast: 150ms;
    --duration-base: 200ms;
    --duration-slow: 300ms;
    --ease: ease-in-out;

    /* Размеры layout */
    --sidebar-width: 260px;
    --sidebar-width-collapsed: 72px;
    --mobile-header-h: 64px;
    --mobile-tabbar-h: 56px;

    /* Скроллбар */
    --scrollbar-thumb: #D8D7D2;       /* not specified — chosen: тёплый серый */
    --scrollbar-thumb-hover: #BFBEBA;
  }

  /* --------------------------------------------------------------------------
     Тёмная тема — только переопределение токенов. Компоненты не меняются.
     Тёплая, не инверсия: фон #0E0E11, текст #EDEDEF, терракотовый акцент.
     -------------------------------------------------------------------------- */
  .dark {
    color-scheme: dark;

    --background: #0E0E11;
    --foreground: #EDEDEF;
    --surface: #1C1C20;               /* not specified — chosen: #1C1C20 (surface-light) */
    --card: #17171A;
    --card-foreground: #EDEDEF;
    --popover: #17171A;
    --popover-foreground: #EDEDEF;
    --muted: #232327;
    --muted-foreground: #8A8F98;
    --accent: #232327;
    --accent-foreground: #EDEDEF;
    --brand: #C97A4F;                 /* осветлённый терракот для контраста */
    --brand-dark: #A85B34;
    --brand-soft: #2A1D16;
    /* На осветлённом бренде белая подпись даёт 3.29:1 — ниже AA.
       Чернильный цвет читается на том же фоне с 5.44:1. */
    --brand-foreground: #17171A;      /* 5.44:1 на --brand */
    --primary: #EDEDEF;               /* инверсная primary-кнопка */
    --primary-foreground: #17171A;
    --secondary-foreground: #D4D4D8;
    --destructive: #F2555A;
    --destructive-foreground: #17171A;
    --success: #3FB37A;               /* подтверждение сохранения: светлее для тёмного фона */
    --success-foreground: #0E0E11;
    --overlay: rgba(0, 0, 0, 0.6);

    /* Предупреждающий баннер: та же схема пастели, что у статусов. */
    --warning-soft: rgba(247, 230, 195, 0.12);
    --warning-border: rgba(247, 230, 195, 0.35);
    --warning-fg: #E7C88A;

    --border: #26262B;
    --input: #26262B;
    --ring: #C97A4F;

    --sidebar: #131316;
    --sidebar-foreground: #EDEDEF;
    --sidebar-accent: #232327;
    --sidebar-accent-foreground: #EDEDEF;
    --sidebar-border: #26262B;

    /* Статусы: приглушённая пастель — 16% от светлых тонов, светлый текст */
    --status-cancelled: rgba(248, 221, 219, 0.16);
    --status-unplanned: rgba(201, 238, 229, 0.16);
    --status-rescheduled: rgba(247, 230, 195, 0.16);
    --status-regular: rgba(219, 233, 255, 0.16);
    --status-cancelled-fg: #F0A9A6;
    --status-unplanned-fg: #86DCC8;
    --status-rescheduled-fg: #E7C88A;
    --status-regular-fg: #A9C6F5;

    /* Дашборд главной: приглушённая пастель — та же схема, что у статусов. */
    --stat-blue: rgba(219, 233, 255, 0.16);
    --stat-blue-fg: #A9C6F5;
    --stat-green: rgba(201, 238, 229, 0.16);
    --stat-green-fg: #86DCC8;
    --stat-orange: rgba(247, 230, 195, 0.16);
    --stat-orange-fg: #E7C88A;
    --stat-purple: rgba(232, 223, 246, 0.16);
    --stat-purple-fg: #CBB4EE;

    --cal-slot-accent: #7FB3D4;         /* слот: осветлённый стальной для контраста */

    --shadow-card: 0 0 0 1px rgba(255, 255, 255, 0.05); /* тени почти не видны — компенсируем границей */
    /* На тёмном фоне мягкая тень не читается: её роль берёт рамка,
       поэтому все три «светлых» оттенка становятся тонким контуром. */
    --shadow-soft: 0 0 0 1px rgba(255, 255, 255, 0.04);
    --shadow-hover: 0 0 0 1px rgba(255, 255, 255, 0.07);
    --shadow-popover: 0 8px 24px rgba(0, 0, 0, 0.5);

    --scrollbar-thumb: #3A3A40;
    --scrollbar-thumb-hover: #4A4A52;
  }
}

@layer base {
  /* --------------------------------------------------------------------------
     Reset / база
     -------------------------------------------------------------------------- */
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  html {
    -webkit-text-size-adjust: 100%;
    tab-size: 4;
  }

  body {
    margin: 0;
    background: var(--background);
    color: var(--foreground);
    font-family: var(--font-sans);
    font-size: var(--text-base);
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }

  h1, h2, h3, h4, h5, h6 {
    margin: 0;
    line-height: 1.25;
  }

  h1 {
    font-size: var(--text-xl);
    font-weight: 700;
  }

  h2 {
    font-size: var(--text-lg);
    font-weight: 600;
  }

  h3 {
    font-size: var(--text-base);
    font-weight: 600;
  }

  p { margin: 0; }
  ul, ol { margin: 0; padding: 0; list-style: none; }

  a {
    color: var(--brand);
    text-decoration: none;
    transition: color var(--duration-fast) var(--ease);
    &:hover { color: var(--brand-dark); }
  }

  button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    touch-action: manipulation;
  }

  img, svg { display: block; }

  /* Lucide-иконки: stroke-width 2 по умолчанию задаётся в JS-разметке */
  [data-lucide] {
    width: 20px;
    height: 20px;
    flex: none;
  }

  /* Фокус виден всегда через :focus-visible */
  :focus-visible {
    outline: 3px solid var(--ring);
    outline-offset: 2px;
  }

  /* Числа, ID — моно */
  code, kbd, samp, [data-mono] {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
  }

  /* Тонкий скроллбар */
  ::-webkit-scrollbar {
    width: 6px;
    height: 6px;
  }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb);
    border-radius: var(--radius-full);
  }
  ::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }
  * { scrollbar-width: thin; scrollbar-color: var(--scrollbar-thumb) transparent; }
}

@layer components {
  /* ==========================================================================
     Сайдбар (fixed, off-canvas на мобиле)
     ========================================================================== */
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: var(--sidebar-width);
    z-index: 40;
    display: flex;
    flex-direction: column;
    background: var(--sidebar);
    color: var(--sidebar-foreground);
    border-right: 1px solid var(--sidebar-border);
    transition: transform var(--duration-slow) var(--ease),
                width var(--duration-slow) var(--ease);

    & .nav-divider {
      border-top: 1px solid var(--sidebar-border);
      margin: calc(var(--space)) 0;       /* 4px */
    }
  }

  /* Элементы сайдбара: плоские BEM-селекторы (в нативном CSS нельзя
     конкатенировать селекторы через &, поэтому &__x не работает). */
  .sidebar__header {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);        /* 12px */
    height: 56px;
    padding: 0 calc(var(--space) * 3);
    border-bottom: 1px solid var(--sidebar-border);
    flex: none;
  }

  .sidebar__logo {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 8px;
    flex: none;
  }

  /* Логотип-иконка (SVG, градиент внутри) тянется на размер контейнера */
  .brand-logo {
    display: block;
    width: 100%;
    height: 100%;
  }

  .sidebar__brand {
    font-size: var(--text-base);
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--foreground);
    white-space: nowrap;
    overflow: hidden;
  }

  .sidebar__nav {
    flex: 1;
    overflow-y: auto;
    padding: calc(var(--space) * 2);
  }

  /* Секция ребёнка в сайдбаре кабинета родителя (change parent-cabinet):
     имя-заголовок и две его страницы. Классы навигации — те же `.nav-item`. */
  .parent-nav__section {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
    margin-bottom: calc(var(--space) * 2);
  }

  .parent-nav__heading {
    padding: calc(var(--space) * 2) calc(var(--space) * 3) calc(var(--space) * 1);
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    font-weight: var(--font-medium);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .sidebar__footer {
    flex: none;
    margin-top: auto;
    border-top: 1px solid var(--sidebar-border);
    padding: calc(var(--space) * 3);
  }

  /* --------------------------------------------------------------------------
     Переключатель темы (светлая / тёмная) в футере сайдбара.
     Активный сегмент красит CSS по классу .dark на <html>: подсветка верна
     ещё до выполнения app.js и не «прыгает» при загрузке страницы.
     -------------------------------------------------------------------------- */
  .sidebar__theme {
    margin-bottom: calc(var(--space) * 2); /* 8px до строки пользователя */
  }

  .theme-switch {
    display: flex;
    gap: 2px;
    padding: 3px;
    border-radius: var(--radius-sm);
    background: var(--sidebar-accent);
  }

  .theme-switch__btn {
    display: flex;
    flex: 1;
    align-items: center;
    justify-content: center;
    gap: calc(var(--space) * 2);          /* 8px */
    min-height: 32px;
    padding: 0 calc(var(--space) * 2);
    border-radius: 6px;
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--muted-foreground);
    transition: background-color var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);

    & [data-lucide] { width: 16px; height: 16px; }

    &:hover { color: var(--foreground); }

    & span {
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }
  }

  /* Активный сегмент: светлая тема — «Светлая», тёмная — «Тёмная» */
  html:not(.dark) .theme-switch__btn[data-theme-toggle="light"],
  html.dark .theme-switch__btn[data-theme-toggle="dark"] {
    background: var(--background);
    color: var(--foreground);
    box-shadow: var(--shadow-card);
  }

  /* Пункт меню */
  .nav-item {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);          /* 12px */
    min-height: 40px;                     /* тач-таргет ≥ 40px */
    border-radius: 8px;
    padding: calc(var(--space) * 2) calc(var(--space) * 3); /* 8px 12px */
    font-size: var(--text-sm);
    font-weight: 500;
    line-height: 1.25;
    color: var(--muted-foreground);
    background: transparent;
    text-decoration: none;
    overflow: hidden;
    transition: background-color var(--duration-base) var(--ease),
                color var(--duration-base) var(--ease);

    &:hover {
      background: color-mix(in srgb, var(--sidebar-accent) 60%, transparent);
      color: var(--foreground);
    }

    & span {
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }
  }

  .nav-item--active {
    background: var(--sidebar-accent);
    color: var(--sidebar-accent-foreground);
    font-weight: 600;

    &:hover { background: var(--sidebar-accent); }
  }

  /* Кружок счётчика в сайдбаре: необработанные заявки («Брони»),
     просроченные задачи («Задачи»). Партиал (`/tutor/<модуль>/count`)
     наполняет контейнер; пустой (`:empty`) кружка не показывает —
     ноль не сигнал. */
  .nav-item__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    margin-left: auto;                  /* прижат к правому краю пункта */
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--destructive);
    color: var(--destructive-foreground);
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1;
    overflow: visible;
  }

  .nav-item__badge:empty { display: none; }

  /* Нижний блок сайдбара (Настройки) */
  .sidebar__section {
    flex: none;
    border-top: 1px solid var(--sidebar-border);
    padding: calc(var(--space) * 3);
  }

  /* Кнопка-«язычок» сворачивания */
  .sidebar__toggle {
    position: absolute;
    right: -12px;
    top: 80px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 1px solid var(--sidebar-border);
    background: var(--sidebar);
    box-shadow: var(--shadow-card);
    color: var(--muted-foreground);

    &:hover { color: var(--foreground); }

    & [data-lucide] { width: 14px; height: 14px; }
  }

  /* ==========================================================================
     Свёрнутый сайдбар: полоса иконок 72px.
     Источник истины — класс sidebar-collapsed на <html> (его ставит бутстрап-
     скрипт в base.html ещё до отрисовки, чтобы не было «прыжка»).
     Только десктоп: на мобиле сайдбар работает как off-canvas.
     ========================================================================== */
  @media (min-width: 1024px) {
    html.sidebar-collapsed .sidebar {
      width: var(--sidebar-width-collapsed);

      & .sidebar__brand,
      & .nav-item span,
      & .user-row__meta,
      & .nav-divider {
        display: none;
      }

      /* Кружок счётчика остаётся видимым в полосе иконок: правило
         `.nav-item span` выше прячет и его, поэтому здесь явное
         исключение с бо́льшей специфичностью (пустой контейнер — без кружка). */
      & .nav-item .nav-item__badge:not(:empty) {
        display: inline-flex;
      }

      /* Иконки по центру полосы */
      & .sidebar__header {
        justify-content: center;
        padding: 0;
        gap: 0;
      }

      & .sidebar__nav,
      & .sidebar__section,
      & .sidebar__footer {
        padding-left: calc(var(--space) * 2);
        padding-right: calc(var(--space) * 2);
      }

      & .nav-item {
        justify-content: center;
        padding-left: 0;
        padding-right: 0;
      }

      & .user-row-wrap {
        flex-direction: column;
        gap: calc(var(--space) * 2);
      }

      & .user-row {
        justify-content: center;
        padding: calc(var(--space) * 2) 0;
      }

      /* Переключатель темы в свёрнутом сайдбаре — только иконки:
         подписи не влезают в полосу 72px. */
      & .theme-switch__btn {
        padding: 0;
      }

      & .theme-switch__btn span {
        display: none;
      }

      /* «Язычок» разворачивает обратно */
      & .sidebar__toggle [data-lucide] {
        transform: rotate(180deg);
      }
    }
  }

  /* Строка пользователя в футере сайдбара */
  .user-row {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);
    border-radius: 8px;
    padding: calc(var(--space) * 2);
    min-height: 40px;
    text-decoration: none;
    color: inherit;
    transition: background-color var(--duration-base) var(--ease);

    &:hover { background: var(--muted); }
  }

  .nav-item--grow { flex: 1; min-width: 0; }
  .user-row--grow { flex: 1; min-width: 0; }

  .user-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--foreground);
    font-size: var(--text-sm);
    font-weight: 600;
    flex: none;
  }

  .user-row__meta {
    flex: 1;
    min-width: 0;
  }

  .user-row__name {
    display: block;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--foreground);
  }

  .user-row__role {
    display: block;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .user-row__name,
  .user-row__role {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* ==========================================================================
     Overlay (мобильный off-canvas)
     ========================================================================== */
  .overlay {
    position: fixed;
    inset: 0;
    z-index: 30;
    background: var(--overlay);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--duration-slow) var(--ease);

    &.is-visible {
      opacity: 1;
      pointer-events: auto;
    }
  }

  /* ==========================================================================
     Кнопки
     ========================================================================== */
  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: calc(var(--space) * 2);
    min-height: 36px;
    min-width: 36px;
    padding: 0 calc(var(--space) * 4);    /* 0 16px */
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    font-weight: 500;
    border: 1px solid transparent;
    transition: background-color var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);

    & [data-lucide] { width: 16px; height: 16px; } /* 16px в кнопках */

    &:disabled {
      opacity: 0.5;
      cursor: not-allowed;
      pointer-events: none; /* без hover-эффектов в неактивном состоянии */
    }
  }

  .btn--primary {
    background: var(--primary);
    color: var(--primary-foreground);

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

  .btn--ghost {
    background: transparent;
    color: var(--foreground);

    &:hover { background: var(--accent); }
  }

  .btn--brand {
    background: var(--brand);
    color: var(--brand-foreground);

    &:hover { background: var(--brand-dark); }
  }

  .btn--destructive {
    background: var(--destructive);
    color: var(--destructive-foreground);

    &:hover { background: color-mix(in srgb, var(--destructive) 90%, transparent); }
  }

  /* Подтверждение сохранения (ДЗ, design D8): кнопка на пару секунд
     становится зелёной и неактивной, затем lessons.js возвращает ей
     обычный вид. Заливка остаётся плотной в disabled-состоянии. */
  .btn--success {
    background: var(--success);
    color: var(--success-foreground);

    &:disabled {
      opacity: 1; /* иначе общая disabled-прозрачность гасит зелёный */
      cursor: default;
    }
  }

  .btn--icon {
    width: 32px;
    height: 32px;
    padding: 0;
    border-radius: 8px;
  }

  .btn--block {
    width: 100%;
  }

  /* Вторичная кнопка с рамкой: действие без заливки */
  .btn--outline {
    border-color: var(--border);
    background: var(--background);
    color: var(--foreground);

    &:hover { background: var(--accent); }

    &.is-active {
      border-color: var(--primary);
      background: var(--primary);
      color: var(--primary-foreground);
    }
  }

  .btn--sm {
    min-height: 32px;
    min-width: 32px;
    padding: 0 calc(var(--space) * 3);
    font-size: var(--text-xs);

    & [data-lucide] { width: 14px; height: 14px; }
  }

  /* ==========================================================================
     Карточки
     ========================================================================== */
  .card {
    background: var(--card);
    color: var(--card-foreground);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: calc(var(--space) * 6);      /* 24px */

  }

  .card__title {
    font-size: var(--text-base);
    font-weight: 600;
  }

  .card__desc {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  /* Сетка «ключ — значение» внутри карточки профиля */
  .profile-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  /* ==========================================================================
     Настройки репетитора (/tutor/profile): табы, карточки, переключатели
     ========================================================================== */

  /* Ссылка «Назад» над заголовком */
  .settings-back {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin-bottom: calc(var(--space) * 3);
    font-size: var(--text-sm);
    color: var(--muted-foreground);

    & [data-lucide] {
      width: 16px;
      height: 16px;
    }

    &:hover { color: var(--foreground); }
  }

  /* Табы настроек: строка на всю ширину, на узких экранах переносятся */
  .settings-tabs {
    display: flex;
    flex-wrap: wrap;
    width: 100%;
    margin-bottom: calc(var(--space) * 4);

    & .tab { flex: 1 1 auto; }
  }

  /* Карточка вкладки; между карточками — вертикальный зазор */
  .settings-card {
    margin-bottom: calc(var(--space) * 4);
  }

  /* Двухколоночная сетка полей вкладки (с телефона — одна колонка) */
  .form-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);
    margin-top: calc(var(--space) * 4);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  /* Поле на всю ширину сетки («О себе») */
  .form-grid__full {
    grid-column: 1 / -1;
  }

  /* Футер формы вкладки: действие справа (--start — слева, как у
     карточки «Безопасность» референса) */
  .settings-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: calc(var(--space) * 5);
  }

  .settings-actions--start {
    justify-content: flex-start;
  }

  /* Список строк-переключателей (вкладка «Уведомления») */
  .settings-list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    margin-top: calc(var(--space) * 4);
  }

  /* Строка настройки: текст слева, переключатель справа */
  .switch-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: calc(var(--space) * 4);
    padding: calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
  }

  .switch-row__main {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
    min-width: 0;
  }

  .switch-row__title {
    font-weight: 500;
  }

  .switch-row__desc {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  .switch-row__params {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: calc(var(--space) * 3);
    margin-top: calc(var(--space) * 2);
  }

  .switch-row__param-label {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Секция мессенджера (вкладка «Мессенджеры»): шапка «название + статус»,
     ниже — поле токена и подсказка */
  .messenger {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
  }

  .messenger__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
  }

  .messenger__name {
    font-weight: 500;
  }

  /* Статус-пилюля: базовый стиль нейтральный, модификаторы красят текст */
  .messenger__status {
    flex: none;
    padding: calc(var(--space) * 1) calc(var(--space) * 2);
    border-radius: var(--radius-full);
    font-size: var(--text-sm);
    background: var(--muted);
    color: var(--muted-foreground);
  }

  .messenger__status--on {
    color: var(--success);
  }

  .messenger__status--off {
    color: var(--destructive);
  }

  .messenger__status--soon {
    color: var(--muted-foreground);
  }

  .messenger__hint {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  /* Карточка канала уведомлений (вкладка «Уведомления»): строка
     «иконка — название — действие». Состояние красит модификатор:
     привязан — зелёный фон, ждёт и не подключён — мягкий акцент. */
  .channels-list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    margin-bottom: calc(var(--space) * 4);
  }

  .channel-card {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--brand-soft);
  }

  .channel-card--connected {
    background: color-mix(in srgb, var(--success) 8%, var(--background));
    border-color: color-mix(in srgb, var(--success) 30%, var(--border));
  }

  .channel-card--pending {
    background: color-mix(in srgb, var(--brand) 6%, var(--background));
  }

  .channel-card__icon {
    flex: none;
    color: var(--brand);
  }

  .channel-card__icon--on {
    color: var(--success);
  }

  .channel-card__main {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
    min-width: 0;
  }

  .channel-card__title {
    font-weight: 500;
  }

  .channel-card__desc {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  .channel-card__link {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 1);
    color: var(--brand);
    font-size: var(--text-sm);
  }

  .channel-card__action {
    flex: none;
    margin-left: auto;
  }

  /* «Отвязать» — текстовая красная кнопка со ссылкой-иконкой */
  .channel-card__unlink {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 1);
    padding: 0;
    border: none;
    background: none;
    color: var(--destructive);
    font-size: var(--text-sm);
    cursor: pointer;
  }

  .channel-card__unlink:hover {
    text-decoration: underline;
  }

  /* Приглашение ученика в мессенджеры (вкладка «Настройки» карточки):
     карточки состояний — те же `.channel-card*`, своё — только
     оформление длинного диплинка и статуса копирования. */
  .invite-list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  .invite-link {
    align-self: flex-start;
    max-width: 100%;
    color: var(--brand);
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    word-break: break-all;
  }

  .invite-actions {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
  }

  .invite-status {
    color: var(--success);
    font-size: var(--text-sm);
  }

  .invite-status--warn {
    color: var(--muted-foreground);
  }

  /* Блок «Личный кабинет» (вкладка «Настройки» формы ученика): плашка
     привязанного входа — по образцу `.access-note`, заметка и строка
     действий. Ссылка приглашения — те же `.invite-link`/`.invite-status`,
     что у блока мессенджеров. */
  .cabinet-access {
    display: flex;
    align-items: flex-start;
    gap: calc(var(--space) * 2);
    margin-bottom: calc(var(--space) * 3);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--muted);
    font-size: var(--text-sm);
  }

  .cabinet-access__icon {
    width: 16px;
    height: 16px;
    flex: none;
    margin-top: 2px;
    color: var(--success);
  }

  .cabinet-access__main {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
  }

  .cabinet-access__title {
    font-weight: var(--font-medium);
  }

  .cabinet-access__desc {
    color: var(--muted-foreground);
    word-break: break-all;
  }

  .cabinet-note {
    margin: 0 0 calc(var(--space) * 3);
    font-size: var(--text-sm);
    line-height: 1.5;
    color: var(--muted-foreground);
  }

  .cabinet-actions {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
    margin-top: calc(var(--space) * 3);
  }

  /* Переключатель: чекбокс со стилизованным треком. Состояние —
     соседним селектором `input:checked + .switch__track`. */
  .switch {
    display: inline-flex;
    flex: none;
    cursor: pointer;
  }

  .switch__track {
    position: relative;
    width: 44px;
    height: 24px;
    border-radius: var(--radius-full);
    background: var(--muted);
    border: 1px solid var(--border);
    transition: background-color var(--duration-fast) var(--ease),
                border-color var(--duration-fast) var(--ease);
  }

  .switch__track::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 18px;
    height: 18px;
    border-radius: var(--radius-full);
    background: var(--background);
    box-shadow: var(--shadow-card);
    transition: transform var(--duration-fast) var(--ease);
  }

  .switch input:checked + .switch__track {
    background: var(--primary);
    border-color: var(--primary);
  }

  .switch input:checked + .switch__track::after {
    transform: translateX(20px);
    background: var(--primary-foreground);
  }

  .switch input:focus-visible + .switch__track {
    outline: 3px solid var(--ring);
    outline-offset: 2px;
  }

  /* ==========================================================================
     Дашборд главной (/tutor/): стат-карточки и два списка дня
     ========================================================================== */
  .stat-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);
    margin-bottom: calc(var(--space) * 5);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    @media (min-width: 1024px) {
      grid-template-columns: repeat(4, minmax(0, 1fr));
    }
  }

  .stat-card {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    background: var(--card);
    color: var(--card-foreground);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: calc(var(--space) * 5);
  }

  .stat-card__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  .stat-card__label {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  .stat-card__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);

    & [data-lucide] { width: 18px; height: 18px; }
  }

  .stat-card__icon--blue   { background: var(--stat-blue);   color: var(--stat-blue-fg); }
  .stat-card__icon--green  { background: var(--stat-green);  color: var(--stat-green-fg); }
  .stat-card__icon--orange { background: var(--stat-orange); color: var(--stat-orange-fg); }
  .stat-card__icon--purple { background: var(--stat-purple); color: var(--stat-purple-fg); }

  .stat-card__value {
    font-size: var(--text-2xl);
    font-weight: 700;
    letter-spacing: -0.01em;
    font-variant-numeric: tabular-nums;
  }

  .stat-card__caption {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  .home-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);

    @media (min-width: 1024px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  .home-panel {
    background: var(--card);
    color: var(--card-foreground);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: calc(var(--space) * 5);
  }

  .home-panel__title {
    font-size: var(--text-base);
    font-weight: 600;
    margin-bottom: calc(var(--space) * 3);
  }

  .home-panel__list {
    display: flex;
    flex-direction: column;
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .home-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 3) 0;
    border-top: 1px solid var(--border);

    &:first-child { border-top: 0; }
  }

  .home-row__main {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space));
    min-width: 0;
  }

  .home-row__name {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .home-row__meta {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  .home-row__time {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    white-space: nowrap;
  }

  /* Сумма платежа — всегда «+»: в блок только оплаченные платежи. */
  .home-row__amount {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--success);
    white-space: nowrap;
  }

  .home-empty {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
    padding: calc(var(--space) * 4) 0;
  }


  /* ==========================================================================
     Табы (segmented control)
     ========================================================================== */
  .tabs {
    display: inline-flex;
    background: var(--muted);
    border-radius: 8px;
    padding: 3px;
    gap: 2px;
  }

  .tab {
    min-height: 32px;
    padding: 0 calc(var(--space) * 3);
    border: 0;
    border-radius: 6px;
    background: none;
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--muted-foreground);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);

    &:hover { color: var(--foreground); }
  }

  .tab--active {
    background: var(--background);
    color: var(--foreground);
    box-shadow: var(--shadow-card);
  }

  /* Табы модалки ученика: на всю ширину, на мобильном переносятся. */
  .modal-tabs {
    display: flex;
    flex-wrap: wrap;
    width: 100%;
  }

  .modal-tabs .tab {
    flex: 1 1 auto;
  }

  /* Панель вкладки — колонка секций; скрывается Alpine через x-show. */
  .tab-panel {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);
  }

  .tab-panel__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 8) calc(var(--space) * 4);
    border: 1px dashed var(--border);
    border-radius: var(--radius-md);
    color: var(--muted-foreground);
    font-size: var(--text-sm);
    text-align: center;
  }

  /* ==========================================================================
     Формы
     ========================================================================== */
  .field {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  /* Поле формы на обе колонки сетки `form-section` (секция — двухколоночная
     сетка, по умолчанию поле занимает одну колонку). */
  .field--full {
    grid-column: 1 / -1;
  }

  .label {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--foreground);
  }

  .input {
    height: 36px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--input);
    background: var(--background);
    color: var(--foreground);
    padding: 0 calc(var(--space) * 3);    /* 0 12px */
    font-size: var(--text-sm);
    transition: border-color var(--duration-fast) var(--ease),
                box-shadow var(--duration-fast) var(--ease);

    &::placeholder { color: var(--muted-foreground); }

    &:focus-visible {
      outline: none;
      border-color: var(--ring);
      box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 50%, transparent);
    }

    &:disabled {
      background: var(--muted);
      color: var(--muted-foreground);
      cursor: not-allowed;
    }

    /* Поле, которое не пропустило отправку. Ставит `form-feedback.js`,
       когда браузер останавливает запрос: рамка держится до первой правки,
       поэтому видно, что именно чинить, даже если подсказка браузера уже
       погасла. */
    &[aria-invalid="true"] {
      border-color: var(--destructive);
      box-shadow: 0 0 0 3px color-mix(in srgb, var(--destructive) 18%, transparent);
    }
  }

  /* Текстовые поля: высоту задаёт `rows`, поэтому фиксированные 36px
     от `.input` им не подходят. */
  textarea.input {
    height: auto;
    min-height: 72px;
    padding: calc(var(--space) * 2) calc(var(--space) * 3);
  }

  /* ==========================================================================
     Селект с поиском (combobox)
     ========================================================================== */
  /* Корень селекта — система координат для накладного списка (фолбэк,
     пока `dropdown.js` не перевёл список в `fixed`). */
  .combobox {
    position: relative;
  }

  .combobox__control {
    position: relative;
    display: flex;
    align-items: center;
  }

  .combobox__control .input {
    flex: 1;
    min-width: 0;
    padding-right: calc(var(--space) * 9);  /* место под кнопку очистки */
  }

  .combobox__clear {
    position: absolute;
    right: calc(var(--space) * 2);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-full);
    background: transparent;
    color: var(--muted-foreground);
    font-size: var(--text-base);
    line-height: 1;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);

    &:hover {
      background: var(--accent);
      color: var(--foreground);
    }
  }

  .combobox__list {
    /* Накладной список: раскрытие не сдвигает соседние поля, а накрывает
       их. В рабочем режиме `dropdown.js` ставит его `fixed` под полем —
       абсолютный потомок обрезался бы прокручиваемым `.modal-body`.
       `absolute` ниже — фолбэк до инициализации и без JS. */
    position: absolute;
    top: calc(100% + var(--space) * 2);
    left: 0;
    right: 0;
    z-index: 30;
    max-height: 260px;
    overflow-y: auto;
    padding: var(--space);
    list-style: none;
    background: var(--popover);
    color: var(--popover-foreground);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
  }

  .combobox__option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    min-height: 32px;
    padding: 0 calc(var(--space) * 2);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease);

    &:hover { background: var(--accent); }
  }

  .combobox__option--active {
    background: var(--accent);
    color: var(--accent-foreground);
  }

  .combobox__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .combobox__offset {
    flex: none;
    color: var(--muted-foreground);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
  }

  .combobox__option--active .combobox__offset {
    color: var(--accent-foreground);
  }

  .combobox__note {
    padding: calc(var(--space) * 2);
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  /* ==========================================================================
     Chips / статусы уроков
     ========================================================================== */
  .chip {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space));
    min-height: 24px;
    padding: 0 calc(var(--space) * 2.5);
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--secondary-foreground);
    font-size: var(--text-xs);
    font-weight: 500;
  }

  .chip--cancelled     { background: var(--status-cancelled);     color: var(--status-cancelled-fg); }
  .chip--unplanned     { background: var(--status-unplanned);     color: var(--status-unplanned-fg); }
  .chip--rescheduled   { background: var(--status-rescheduled);   color: var(--status-rescheduled-fg); }
  .chip--regular       { background: var(--status-regular);       color: var(--status-regular-fg); }

  /* Статусы ученика */
  .chip--new      { background: var(--status-regular);     color: var(--status-regular-fg); }
  .chip--active   { background: var(--status-unplanned);   color: var(--status-unplanned-fg); }
  .chip--archived { background: var(--muted);              color: var(--muted-foreground); }

  /* Типы урока (спека lesson-management) */
  .chip--individual { background: var(--status-regular);     color: var(--status-regular-fg); }
  .chip--group      { background: var(--status-rescheduled); color: var(--status-rescheduled-fg); }
  .chip--trial      { background: var(--status-cancelled);   color: var(--status-cancelled-fg); }
  .chip--practice   { background: var(--muted);              color: var(--muted-foreground); }

  /* Статусы урока: «Проведён» — зелёным (спека), «Запланирован» — нейтрально */
  .chip--planned   { background: var(--muted);            color: var(--muted-foreground); }
  .chip--completed { background: var(--status-unplanned); color: var(--status-unplanned-fg); }

  /* Статусы брони (спека booking-management): «Отменена» переиспользует
     красный `.chip--cancelled` выше. */
  .chip--pending   { background: var(--status-rescheduled); color: var(--status-rescheduled-fg); }
  .chip--confirmed { background: var(--status-unplanned);   color: var(--status-unplanned-fg); }
  .chip--rejected  { background: var(--status-cancelled);   color: var(--status-cancelled-fg); }

  /* ==========================================================================
     Уроки: чипы фильтра периода («Все / Сегодня / На этой неделе»)
     ========================================================================== */
  .period-chips {
    display: inline-flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
  }

  .period-chip {
    position: relative;
    display: inline-flex;
  }

  .period-chip__input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
  }

  .period-chip__view {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    padding: 0 calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--card);
    color: var(--secondary-foreground);
    font-size: var(--text-sm);
    font-weight: 500;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease),
                border-color var(--duration-fast) var(--ease);
  }

  .period-chip__input:hover + .period-chip__view {
    background: var(--brand-soft);
  }

  .period-chip__input:checked + .period-chip__view {
    background: var(--brand);
    border-color: transparent;
    color: var(--brand-foreground);
  }

  .period-chip__input:focus-visible + .period-chip__view {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
  }

  /* ==========================================================================
     Уроки: модальная форма — вкладки, панель расписания, оценка-звёзды,
     подсказки и предупреждение о пересчёте списания
     ========================================================================== */
  .tab--disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .schedule-panel {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--muted);

    /* Панель занимает обе колонки секции формы урока. */
    grid-column: 1 / -1;
  }

  .schedule-panel__title {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
  }

  .schedule-slot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 2);
    width: 100%;
  }

  .schedule-slot__weekday {
    width: auto;
    min-width: 96px;
  }

  .schedule-slot__time {
    width: auto;
    min-width: 124px;
  }

  .schedule-slot__dash {
    color: var(--muted-foreground);
  }

  /* Интервал и дата окончания — в одну строку, на всю ширину панели. */
  .schedule-panel__row {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 3);
    width: 100%;

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  .schedule-panel__interval,
  .schedule-panel__until {
    min-width: 160px;
  }

  /* Быстрые пресеты «До даты» (значения — из настроек календаря
     репетитора): строка чипов под полем «Дата окончания». */
  .until-presets {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 1);
  }

  .until-presets__chip {
    padding: 0 calc(var(--space) * 2);
    min-height: 26px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    font: inherit;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);

    &:hover {
      border-color: var(--brand);
      color: var(--brand);
    }
  }

  /* Блок «Присутствие» формы группового урока (спека lesson-attendance):
     шапка со счётчиком и быстрыми кнопками, список участников с чекбоксами,
     у отсутствовавшего — селект причины. */
  .attendance__bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  .attendance__counter {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
  }

  .attendance__quick {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 1);
  }

  .attendance__chip {
    padding: 0 calc(var(--space) * 2);
    min-height: 26px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    font: inherit;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);

    &:hover {
      border-color: var(--brand);
      color: var(--brand);
    }
  }

  .attendance__list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  .attendance__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  /* Имя участника с чекбоксом: занимает всё место слева, кликабельно
     целиком, как остальные чекбоксы формы. */
  .attendance__present {
    flex: 1;
    min-width: 0;
  }

  .attendance__reason {
    width: auto;
    min-width: 180px;
  }

  .rating-stars {
    display: inline-flex;
    gap: calc(var(--space) * 0.5);
  }

  .rating-star {
    display: inline-flex;
    cursor: pointer;
  }

  .rating-star__view {
    font-size: 1.2rem;
    line-height: 1;
    color: var(--muted-foreground);
    transition: color var(--duration-fast) var(--ease);
  }

  .rating-star__view--clear {
    font-size: var(--text-sm);
  }

  /* Заливка «от начала»: активна выбранная звезда и все до неё.
     `+` видит только соседа справа, поэтому «предыдущие» звёзды ловим
     через `:has(~ …)`: метка, у которой правее есть метка с отмеченным
     radio. Кнопка сброса («—», value="") никого не заливает — иначе
     отметили бы все звёзды, стоящие левее неё. */
  .rating-star:has(> input:checked) .rating-star__view,
  .rating-star:has(~ .rating-star > input:checked:not([value=""])) .rating-star__view {
    color: var(--brand);
  }

  .rating-star input:focus-visible + .rating-star__view {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
  }

  .form-hint {
    margin-top: calc(var(--space));
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .form-hint--warning {
    color: var(--status-cancelled);
    font-weight: 500;
  }

  /* Баннер формы: предупреждение, которое НЕ блокирует сохранение
     (design.md D5, п. 3). Отличается от `.error` и видом, и ролью:
     `.error` — «не сохранено», баннер — «сохранится, если подтвердите». */
  .form-banner {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    margin-bottom: calc(var(--space) * 3);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--status-cancelled);
    border-radius: var(--radius-md);
    background: color-mix(in srgb, var(--status-cancelled) 12%, transparent);
    color: var(--foreground);
    font-size: var(--text-sm);
    line-height: 1.5;
  }

  .form-banner__text {
    margin: 0;
    font-weight: 500;
  }

  /* Предупреждение о недоступности — не ошибка: сохранение не блокируется,
     достаточно подтвердить. Поэтому плашка тёплая, как баннер неотмеченных
     уроков, а не красная, как «не сохранено» (design.md D5, п. 3). */
  .form-banner--warning {
    border-color: var(--warning-border);
    background: var(--warning-soft);
    color: var(--warning-fg);
  }

  /* ==========================================================================
     Страница учеников: сетка карточек, фильтры, вид «список»
     ========================================================================== */
  .students-card {
    padding: calc(var(--space) * 5);
  }

  .card-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    margin-bottom: calc(var(--space) * 4);
  }

  .filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 2);
    flex: 1;
    min-width: 0;
  }

  .filters__search {
    flex: 1;
    min-width: 200px;
  }

  .filters__status {
    width: auto;
    min-width: 170px;
  }

  .view-toggle {
    display: inline-flex;
    gap: calc(var(--space));
  }

  .students-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    @media (min-width: 1024px) {
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }
  }

  /* Вид «список»: таблица зеброй вместо карточек.

     Сервер отдаёт обе разметки одним партиалом (`_grid.html`) и не знает о
     выбранном виде, поэтому переключение — чистый CSS поверх Alpine-класса
     на контейнере: в «списке» карточки скрываются и показывается таблица. */
  .students-grid--list {
    grid-template-columns: 1fr;

    & .student-card {
      display: none;
    }

    & .students-table-wrap {
      display: block;
    }
  }

  .students-table-wrap {
    display: none;
    overflow-x: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--card);
    color: var(--card-foreground);
    box-shadow: var(--shadow-card);
  }

  .students-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
  }

  .students-table__head {
    background: var(--muted);
  }

  .students-table__th {
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    text-align: left;
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted-foreground);
    white-space: nowrap;
  }

  .students-table__th--status {
    text-align: right;
  }

  .students-table__row {
    cursor: pointer;
    border-bottom: 1px solid var(--border);

    &:last-child {
      border-bottom: none;
    }

    /* Зебра: чётные строки на втором фоне */
    &:nth-child(even) {
      background: var(--muted);
    }

    &:hover,
    &:focus-visible {
      background: var(--brand-soft);
      outline: none;
    }
  }

  .students-table__cell {
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    vertical-align: middle;
  }

  .students-table__cell--status {
    text-align: right;
    white-space: nowrap;
  }

  .students-table__name {
    font-weight: 600;
    color: var(--foreground);
  }

  .students-table__meta {
    display: block;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .students-table__contact {
    display: block;
    color: var(--muted-foreground);

    & + & {
      margin-top: 2px;
    }
  }

  .students-table__contact--empty {
    color: var(--muted-foreground);
  }

  /* Колонки «Уроки»/«Баланс»: числа выровнены по правому краю колонки */
  .students-table__th--num {
    text-align: right;
  }

  .students-table__cell--num {
    text-align: right;
    white-space: nowrap;
  }

  .students-table__person {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
  }

  /* Тулбар списка: заголовок с переключателем вида, фильтры и «Удалённые» */
  .students-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 3);
    margin-bottom: calc(var(--space) * 4);
  }

  .students-toolbar__main {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);
    margin-right: auto;
  }

  .students-toolbar__title {
    font-size: var(--text-base);
    font-weight: 600;
  }

  .students-toolbar__deleted {
    flex: none;
  }

  /* Переключатель вида — две иконки; активная подсвечена (.btn--outline
     уже красит .is-active, здесь только форма квадратной кнопки). */
  .view-toggle__btn {
    border-radius: var(--radius-md);
  }

  .filters__sort {
    width: auto;
    min-width: 150px;
  }

  /* ==========================================================================
     Ученики: панель долга, деньги, модалки «Уроки», «Отчёт», «Доступ»
     ========================================================================== */

  /* Панель долга над списком (по референсу): мягкий красный фон из токенов */
  .students-debt {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 4);
    margin-bottom: calc(var(--space) * 4);
    padding: calc(var(--space) * 4) calc(var(--space) * 5);
    border: 1px solid color-mix(in srgb, var(--destructive) 30%, transparent);
    border-radius: var(--radius-lg);
    background: color-mix(in srgb, var(--destructive) 8%, transparent);
  }

  .students-debt__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    flex: none;
    border-radius: var(--radius-full);
    background: color-mix(in srgb, var(--destructive) 14%, transparent);
    color: var(--destructive);

    & [data-lucide] {
      width: 20px;
      height: 20px;
    }
  }

  .students-debt__info {
    display: flex;
    flex-direction: column;
    gap: 2px;
  }

  .students-debt__label {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--destructive);
  }

  .students-debt__amount {
    font-size: var(--text-xl);
    font-weight: 700;
    color: var(--destructive);
  }

  .students-debt__side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: calc(var(--space));
    margin-left: auto;
  }

  .students-debt__count {
    font-size: var(--text-sm);
    color: var(--destructive);

    & span {
      display: inline-block;
      min-width: 1.5em;
      text-align: center;
      font-weight: 700;
    }
  }

  .students-debt__toggle {
    border: 0;
    padding: 0;
    background: none;
    font: inherit;
    font-size: var(--text-sm);
    color: var(--foreground);
    text-decoration: underline;
    cursor: pointer;
  }

  /* Деньги с окраской: долг — красным, плюс — зелёным (только токены) */
  .money-value {
    font-weight: 600;
    color: var(--foreground);
    white-space: nowrap;
  }

  .money-value--debt {
    color: var(--destructive);
  }

  .money-value--positive {
    color: var(--success);
  }

  /* Метрики карточки: «Проведено», следующий урок, «Осталось» */
  .student-stats {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  .student-stats__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
    margin: 0;
    font-size: var(--text-sm);
    color: var(--foreground);
  }

  .student-stats__label {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    min-width: 0;

    & [data-lucide] {
      width: 16px;
      height: 16px;
      flex: none;
      color: var(--muted-foreground);
    }
  }

  .student-stats__label--empty {
    color: var(--muted-foreground);
  }

  /* Окно «Уроки» ученика: фиксированная высота тела. Смена чипа фильтра
     меняет число строк, и модалка под их размер «прыгала»; теперь окно
     постоянное — короткий список оставляет пустое место внизу, длинный
     скроллится внутри (тело уже `overflow-y: auto`). `scrollbar-gutter`
     держит место под скроллбар: его появление/скрытие не сдвигает контент. */
  .modal-content--list {
    height: min(30rem, 85dvh);

    & .modal-body {
      scrollbar-gutter: stable;
    }
  }

  /* Модалка «Уроки»: строки занятий (по образцу вкладки группы) */
  .student-lessons {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);
  }

  .student-lessons__list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  .student-lesson {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--card);
    color: var(--card-foreground);
    font: inherit;
    text-align: left;
    cursor: pointer;

    &:hover {
      background: var(--accent);
    }
  }

  .student-lesson__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  .student-lesson__time {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    font-weight: 600;

    & [data-lucide] {
      width: 16px;
      height: 16px;
      color: var(--muted-foreground);
    }
  }

  .student-lesson__meta {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Модалка «Отчёт»: навигация по месяцам и карточки-итоги */
  .report {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);
  }

  .report-month {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 2) calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--card);
  }

  .report-month__label {
    font-weight: 600;
  }

  .report-month__nav {
    border-radius: var(--radius-md);
  }

  .report-cards {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: calc(var(--space) * 3);

    @media (min-width: 640px) {
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }
  }

  .report-card {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space));
    padding: calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--card);
  }

  .report-card__title {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin: 0;
    font-size: var(--text-sm);
    color: var(--muted-foreground);

    & [data-lucide] {
      width: 16px;
      height: 16px;
      flex: none;
    }
  }

  .report-card__value {
    margin: 0;
    font-size: var(--text-xl);
    font-weight: 700;
  }

  .report-card__note {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  /* Модалка «Доступ»: поясняющая плашка, пароль с генератором,
     экран «данные один раз» */
  .access-note {
    display: flex;
    align-items: flex-start;
    gap: calc(var(--space) * 2);
    margin-bottom: calc(var(--space) * 4);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--muted);
    font-size: var(--text-sm);
    line-height: 1.5;

    & p {
      margin: 0;
    }

    & [data-lucide] {
      width: 16px;
      height: 16px;
      flex: none;
      margin-top: 2px;
      color: var(--muted-foreground);
    }
  }

  /* Экран «данные показываются один раз»: предупреждение заметнее плашки */
  .access-note--warn {
    border-color: color-mix(in srgb, var(--warning-fg) 40%, transparent);
    background: var(--warning-soft);
  }

  .access-password {
    display: flex;
    gap: calc(var(--space) * 2);

    & .input {
      flex: 1;
      min-width: 0;
    }
  }

  .access-password__generate {
    flex: none;
    white-space: nowrap;
  }

  .access-credentials {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  .access-credentials__value {
    margin: 0;
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--muted);
    font-family: var(--font-mono);
    font-weight: 600;
  }

  /* На телефоне таблица теснеет: компактные отступы и шрифт. */
  @media (max-width: 639.98px) {
    .students-table {
      font-size: var(--text-xs);
    }

    .students-table__th,
    .students-table__cell {
      padding: calc(var(--space) * 2) calc(var(--space) * 3);
    }
  }

  /* ==========================================================================
     Уроки: таблица списка (спека lesson-management)
     ========================================================================== */
  .lessons-card {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);
  }

  .lessons-table-wrap {
    /* Прокручиваемая область, а не только горизонтальная лента: `thead`
       залипает внутри неё (change lessons-table-scale). Без ограничения
       высоты `position: sticky` у шапки не к чему прилипать — она уезжает
       вместе со строками. */
    overflow: auto;
    max-height: min(70vh, 720px);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--card);
    color: var(--card-foreground);
    box-shadow: var(--shadow-card);
    /* Фокус на области прокрутки: с клавиатуры её тоже нужно листать. */
    scrollbar-gutter: stable;
  }

  .lessons-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
  }

  .lessons-table__head {
    background: var(--muted);
  }

  /* Шапка остаётся на месте при прокрутке строк. `border-collapse:
     collapse` съедает рамку залипшей ячейки, поэтому линия низа нарисована
     тенью — она не исчезает при скролле. */
  .lessons-table__th {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    background: var(--muted);
    box-shadow: inset 0 -1px 0 var(--border);
    text-align: left;
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted-foreground);
    white-space: nowrap;
  }

  /* Заголовок-переключатель сортировки: ссылка на весь размер ячейки,
     чтобы промах по стрелке не оставлял клик без действия.

     `vertical-align: middle` обязателен: `inline-flex` по умолчанию
     выравнивается по базовой линии, и под ней остаётся место под
     descender — шапка вырастала с 42 до 60px на десктопе (проверено
     в браузере). С ним высоту задаёт margin-box ссылки. */
  .lessons-table__sort {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space));
    margin: calc(var(--space) * -1) calc(var(--space) * -2);
    padding: calc(var(--space)) calc(var(--space) * 2);
    border-radius: var(--radius-sm);
    color: inherit;
    text-decoration: none;
    text-transform: inherit;
    letter-spacing: inherit;
    vertical-align: middle;

    &:hover {
      background: var(--accent);
      color: var(--foreground);
    }

    &:focus-visible {
      outline: 2px solid var(--ring);
      outline-offset: 1px;
    }
  }

  .lessons-table__sort--active {
    color: var(--foreground);
  }

  .lessons-table__sort-icon {
    display: inline-flex;

    & [data-lucide] { width: 12px; height: 12px; }
  }

  /* Колонка выбора: узкая и без лишнего отступа. */
  .lessons-table__th--check,
  .lessons-table__cell--check {
    width: 1%;
    padding-right: 0;
  }

  /* Чекбокс выбора строки. Обёртка — `<label>`: клик по её краю
     переключает чекбокс, а не открывает модалку строки (фильтр триггера
     строки смотрит на `.lessons-select-hit`).

     По умолчанию цель компактная (плотность десктопа важнее): 44×44
     включается только в мобильной раскладке — иначе шапка росла с 42 до
     60px из-за одной ячейки выбора (проверено в браузере). */
  .lessons-select-hit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
  }

  .lessons-select {
    width: 16px;
    height: 16px;
    margin: 0;
    cursor: pointer;

    &:disabled { cursor: default; }
  }

  .lessons-select-hit:has(.lessons-select:disabled) {
    cursor: default;
  }

  .lessons-table__th--status {
    text-align: right;
  }

  .lessons-table__row {
    /* Клик по строке открывает модалку редактирования (задачи 6.1–6.3). */
    cursor: pointer;
    border-bottom: 1px solid var(--border);

    &:last-child {
      border-bottom: none;
    }

    /* Зебра: чётные строки на втором фоне */
    &:nth-child(even) {
      background: var(--muted);
    }
  }

  .lessons-table__cell {
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    vertical-align: middle;
  }

  .lessons-table__cell--status {
    text-align: right;
    white-space: nowrap;
  }

  .lessons-table__target {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: calc(var(--space) * 1.5);
  }

  .lessons-table__name {
    font-weight: 600;
    color: var(--foreground);
  }

  .lessons-table__rating {
    color: var(--muted-foreground);
    white-space: nowrap;
  }

  /* На телефоне таблица теснеет: компактные отступы и шрифт. */
  @media (max-width: 639.98px) {
    .lessons-table {
      font-size: var(--text-xs);
    }

    .lessons-table__th,
    .lessons-table__cell {
      padding: calc(var(--space) * 2) calc(var(--space) * 3);
    }
  }

  /* Панель массовой отметки: действие и подпись под таблицей.
     Кнопка выключена, пока ничего не выбрано, — подпись объясняет, что
     делать (change lessons-table-scale). */
  .lessons-bulk {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  .lessons-bulk__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 3);
  }

  .lessons-bulk__hint {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Пагинация: своя секция, а не общий класс статистики — таблица уроков
     живёт в другом модуле, и общий класс связал бы их правки. */
  .lessons-pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .lessons-pagination__nav {
    display: flex;
    gap: calc(var(--space) * 2);
  }

  /* Счётчик страниц: цифры моноширинные, иначе «страница 9 из 10» дёргает
     соседнюю кнопку при перелистывании. */
  .lessons-pagination__info {
    font-variant-numeric: tabular-nums;
  }

  .lessons-pagination__ghost {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    padding: 0 calc(var(--space) * 3);
    color: var(--muted-foreground);
    opacity: 0.6;
  }

  .student-card {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 5);
    background: var(--card);
    color: var(--card-foreground);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
  }

  .student-head {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);
  }

  .avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    flex: none;
    border-radius: var(--radius-full);
    background: var(--brand-soft);
    color: var(--brand);
    font-size: var(--text-sm);
    font-weight: 600;
  }

  .student-id {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1;
    min-width: 0;
  }

  .student-name {
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--foreground);

    &:hover { color: var(--brand); }
  }

  .student-meta {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .student-contacts {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space));
  }

  .student-contact {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
    min-width: 0;

    & [data-lucide] {
      width: 16px;
      height: 16px;
      color: var(--muted-foreground);
    }
  }

  .student-contact__meta {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .student-payer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 2);
    font-size: var(--text-sm);
    color: var(--foreground);
  }

  .student-actions {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
    margin-top: auto;
  }

  .empty-state {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 10) calc(var(--space) * 4);
    text-align: center;
    color: var(--muted-foreground);

    & [data-lucide] {
      width: 32px;
      height: 32px;
    }
  }

  .empty-state__title {
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--foreground);
  }

  .empty-state__text {
    font-size: var(--text-sm);
  }

  /* ==========================================================================
     Группы: сетка карточек и состав формы
     ========================================================================== */
  .groups-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    @media (min-width: 1024px) {
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }
  }

  .group-card {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 5);
    background: var(--card);
    color: var(--card-foreground);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
  }

  .group-card__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  .group-card__id {
    display: flex;
    flex-direction: column;
    gap: var(--space);
    min-width: 0;
  }

  .group-card__name {
    font-size: var(--text-base);
    font-weight: 600;
  }

  .group-card__desc {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  /* Меню «...»: кнопка и выпадающий список действий */
  .group-card__menu {
    position: relative;
    flex: none;
  }

  .group-card__menu-list {
    position: absolute;
    top: calc(100% + var(--space));
    right: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    min-width: 160px;
    padding: var(--space);
    background: var(--popover);
    color: var(--popover-foreground);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
  }

  .group-card__menu-item {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    min-height: 32px;
    padding: 0 calc(var(--space) * 2);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: inherit;
    font: inherit;
    font-size: var(--text-sm);
    text-align: left;
    cursor: pointer;

    &:hover { background: var(--accent); }

    & [data-lucide] {
      width: 14px;
      height: 14px;
    }
  }

  .group-card__menu-item--danger {
    color: var(--destructive);
  }

  .group-card__metrics {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2) calc(var(--space) * 4);
  }

  .group-card__metric {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 1.5);
    font-size: var(--text-sm);
    color: var(--muted-foreground);

    & [data-lucide] {
      width: 14px;
      height: 14px;
    }
  }

  .group-card__actions {
    display: flex;
    gap: calc(var(--space) * 2);
    margin-top: auto;
  }

  /* Заголовок секции с подписью справа («Ученики» / «Макс: 10») */
  .label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
    width: 100%;
  }

  .label-row__note {
    font-size: var(--text-xs);
    font-weight: 400;
    color: var(--muted-foreground);
  }

  .modal-subtitle {
    margin-top: var(--space);
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  /* Состав формы группы */
  .group-members {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  .group-members__empty {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .group-members__list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .group-members__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 2);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--muted);
  }

  .group-members__main {
    display: flex;
    flex-direction: column;
    flex: 1 1 140px;
    min-width: 0;
  }

  .group-members__name {
    font-size: var(--text-sm);
    font-weight: 500;
  }

  .group-members__meta {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .group-members__field {
    flex: 0 1 150px;
  }

  .group-members__add {
    max-width: 100%;
  }

  /* Разделитель и пункт «Добавить ученика» в конце списка выбора */
  .combobox__divider {
    height: 1px;
    margin: calc(var(--space) * 2) 0;
    background: var(--border);
    list-style: none;
  }

  .combobox__option--action {
    font-weight: 500;
    color: var(--brand);

    & .combobox__name {
      display: inline-flex;
      align-items: center;
      gap: calc(var(--space) * 2);
    }

    & [data-lucide] {
      width: 14px;
      height: 14px;
    }
  }

  /* Мини-форма создания ученика внутри формы группы (D9) */
  .group-mini {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--background);
  }

  .group-mini__title {
    font-size: var(--text-sm);
    font-weight: 600;
  }

  .group-mini__fields {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 3);

    @media (min-width: 640px) {
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }
  }

  .group-mini__actions {
    display: flex;
    justify-content: flex-end;
    gap: calc(var(--space) * 2);
  }

  .input--sm {
    height: 32px;
    font-size: var(--text-xs);
  }

  /* Невалидное поле: ошибка видна и без отправки формы */
  .input--invalid {
    border-color: var(--destructive);
  }

  /* Вкладка «Общее»: сводка группы и состав */
  .group-summary {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    margin: 0;
    padding: calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--muted);
  }

  .group-summary__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
  }

  .group-summary__label {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .group-summary__value {
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 500;
  }

  .group-roster {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .group-roster__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 2) 0;
    border-bottom: 1px solid var(--border);

    &:last-child { border-bottom: none; }
  }

  .group-roster__name {
    font-size: var(--text-sm);
    font-weight: 500;
  }

  .group-roster__meta {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .group-roster__empty {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .group-notes__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 3);
  }

  .group-notes__saved {
    font-size: var(--text-xs);
    color: var(--status-unplanned-fg);
  }

  /* ==========================================================================
     Модальное окно и секции формы
     ========================================================================== */
  .modal-root {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: calc(var(--space) * 4);
  }

  /* Второй слой модалок (design D10): диалоги счёта открываются поверх
     карточки ученика, которая остаётся в `#modal` со своими
     несохранёнными полями. Отдельный z-index — чтобы оверлей верхнего
     слоя перекрывал нижний, а не наоборот. */
  .modal-root--stack {
    z-index: 60;
  }

  .modal-overlay {
    position: absolute;
    inset: 0;
    background: var(--overlay);
  }

  .modal-content {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 720px;
    max-height: 90vh;
    max-height: 90dvh;
    /* Скроллится только тело (`.modal-body`): шапка и футер с кнопками
       остаются на месте при любом объёме контента. */
    overflow: hidden;
    display: flex;
    flex-direction: column;
    background: var(--card);
    color: var(--card-foreground);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
  }

  .modal-header {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 6) calc(var(--space) * 6) 0;
  }

  /* Форма занимает остаток модалки: скроллящееся тело + липкий футер. */
  .modal-form {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  /* Обёртка вкладок карточки ученика (design D9): три соседние панели
     делят одно место под таббаром. Без неё `flex: 1` пришлось бы вешать
     на каждую панель, и они растягивали бы модалку втрое. */
  .tab-shell {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  /* Панель вкладки: своё скроллящееся тело и свой футер. `min-height: 0`
     обязателен — иначе `overflow-y` у `.modal-body` не включится внутри
     flex-родителя. Панели — соседи, поэтому `<form>` в них не вложены
     (вложенные формы браузер выбрасывает). */
  .tab-form {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  /* Форма полей окна внутри `.modal-body`: панель брони и футер лежат
     рядом с ней, а не внутри (вложенные <form> браузер выбрасывает, и
     «Подтвердить» отправляла форму окна). Собственного оформления не
     требует — поля уже разложены `.form-section`; `min-width: 0` не даёт
     длинному значению поля распереть модалку. */
  .slot-form {
    min-width: 0;
  }

  .modal-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);
    padding: calc(var(--space) * 4) calc(var(--space) * 6);
  }

  .modal-actions {
    flex-shrink: 0;
    display: flex;
    justify-content: flex-end;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 4) calc(var(--space) * 6) calc(var(--space) * 6);
    border-top: 1px solid var(--border);
    background: var(--card);
  }

  /* Левая группа футера модалки урока (удаление урока/серии): прижата
     к левому краю, правые кнопки («Закрыть», сохранение) остаются справа. */
  .modal-actions__group {
    margin-right: auto;
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
  }

  /* ==========================================================================
     Счёт ученика (модуль finance)
     ========================================================================== */
  /* Суммы «Сумма платежа / Доступно к возврату» в диалоге возврата:
     числа посчитаны сервисом, шаблон их только выводит. Моноширинный
     шрифт — чтобы цифры сравнивались глазом, как в остальных суммах. */
  .refund-summary__amount,
  .refund-summary__available {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    color: var(--foreground);
  }

  /* Панель платежей во вкладке карточки ученика: действия, баланс, списки.
     Панель не форма — кнопки самостоятельные, поэтому сетка колонки. */
  .finance-panel {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);
  }

  .finance-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
  }

  .finance-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--warning-border, var(--border));
    border-radius: var(--radius-md);
    background: var(--warning-soft, var(--muted));
    font-size: var(--text-sm);
  }

  .finance-banner__text {
    flex: 1 1 240px;
    color: var(--foreground);
  }

  .finance-balance {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
    padding: calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface);
  }

  .finance-balance__label {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .finance-balance__value {
    font-family: var(--font-mono);
    font-size: var(--text-xl, 1.5rem);
    font-weight: 600;
    color: var(--foreground);
  }

  .finance-balance__hint {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .finance-section {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  .finance-section__title {
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--muted-foreground);
  }

  .finance-list {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    overflow: hidden;
  }

  .finance-row {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    background: var(--card);

    & + & {
      border-top: 1px solid var(--border);
    }
  }

  .finance-row__main {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
    min-width: 0;
  }

  .finance-row__title {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--foreground);
  }

  .finance-row__status {
    font-size: var(--text-xs, 0.75rem);
    font-weight: 400;
    color: var(--muted-foreground);
  }

  .finance-row__meta {
    font-size: var(--text-xs, 0.75rem);
    color: var(--muted-foreground);
    overflow-wrap: anywhere;
  }

  .finance-row__amount {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--foreground);
  }

  .finance-row__amount--in {
    color: var(--success, var(--foreground));
  }

  .finance-row__amount--out {
    color: var(--destructive, var(--foreground));
  }

  .finance-row__actions {
    display: flex;
    gap: calc(var(--space) * 1);
  }

  .finance-empty {
    margin: 0;
    padding: calc(var(--space) * 4);
    border: 1px dashed var(--border);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Итоги счёта за период: строки «стоимость / баланс / к оплате».
     «К оплате» выделено — это то число, ради которого открыт диалог. */
  .invoice-summary {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface);
  }

  .invoice-summary__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .invoice-summary__row--total {
    padding-top: calc(var(--space) * 2);
    border-top: 1px solid var(--border);
    font-weight: 600;
    color: var(--foreground);
  }

  .invoice-summary__amount {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    color: var(--foreground);
  }

  /* ==========================================================================
     Счёт группы: вкладка «Оплаты» модалки управления (модуль finance)
     ========================================================================== */
  /* Панель — не форма: чипы периода, три сводки и строки участников.
     Чипы переиспользуют `.period-chips` из дизайн-системы. */
  .group-payments {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);
  }

  .group-payments__stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: calc(var(--space) * 2);

    @media (max-width: 560px) {
      grid-template-columns: 1fr;
    }
  }

  .group-payments-stat {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface);
  }

  .group-payments-stat__label {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .group-payments-stat__value {
    font-family: var(--font-mono);
    font-size: var(--text-lg);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--foreground);
  }

  /* Долг участников — предупреждение, а не просто число (референс). */
  .group-payments-stat__value--danger {
    color: var(--destructive);
  }

  .group-payments__list {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    overflow: hidden;
  }

  .group-payments-member {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    background: var(--card);

    & + & {
      border-top: 1px solid var(--border);
    }
  }

  .group-payments-member__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
  }

  .group-payments-member__name {
    font-weight: 500;
    color: var(--brand);
    text-decoration: none;

    &:hover { text-decoration: underline; }
  }

  .group-payments-member__balance {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--foreground);
  }

  .group-payments-member__balance--danger {
    color: var(--destructive);
  }

  .group-payments-member__meta {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .group-payments-member__charged {
    font-variant-numeric: tabular-nums;
  }

  .group-payments-member__debt {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  .group-payments-member__debt--danger {
    color: var(--destructive);
  }

  .group-payments__empty {
    margin: 0;
    padding: calc(var(--space) * 4);
    border: 1px dashed var(--border);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .group-payments__note {
    margin: 0;
    font-size: var(--text-xs, 0.75rem);
    color: var(--muted-foreground);
  }

  /* ==========================================================================
     Занятия группы: вкладка «Занятия» модалки управления (модуль lesson)
     ========================================================================== */
  /* Панель — не форма: чипы статуса и список занятий-карточек.
     Чипы переиспользуют `.period-chips` из дизайн-системы. */
  .group-lessons {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);
  }

  .group-lessons__list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  /* Строка-карточка — кнопка: клик открывает модалку урока во втором
     слое (`#modal-lesson`), клавиатура получает фокус бесплатно. */
  .group-lesson {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1.5);
    width: 100%;
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--card);
    color: var(--card-foreground);
    text-align: left;
    cursor: pointer;

    &:hover {
      border-color: var(--brand);
    }

    &:focus-visible {
      outline: 2px solid var(--brand);
      outline-offset: 1px;
    }
  }

  .group-lesson__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
  }

  /* Дата и интервал — моноширинные: цифры выровнены по разрядам,
     как суммы в панели оплат. */
  .group-lesson__time {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
  }

  .group-lesson__meta {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2) calc(var(--space) * 4);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .group-lesson__subject,
  .group-lesson__hw {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 1.5);

    & [data-lucide] {
      width: 14px;
      height: 14px;
    }
  }

  .form-section {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 3);
    margin: 0;
    padding: calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  .form-section__title {
    padding: 0 calc(var(--space) * 2);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--muted-foreground);
  }

  .checkbox {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    font-size: var(--text-sm);
    color: var(--foreground);

    &:has(input:disabled) {
      opacity: 0.5;
      cursor: not-allowed;
    }
  }

  /* Контакты в форме ученика: аккордеон привязанных строк и поле добавления
     с поиском по базе. */
  .contact-rows {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    margin: 0 0 calc(var(--space) * 4);
    padding: 0;
    list-style: none;
    grid-column: 1 / -1;
  }

  .contact-rows__empty {
    padding: calc(var(--space) * 3);
    border: 1px dashed var(--border);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .contact-row {
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface);
  }

  .contact-row__head {
    display: flex;
    align-items: center;
    gap: var(--space);
  }

  .contact-row__toggle {
    flex: 1;
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);
    min-width: 0;
    padding: calc(var(--space) * 3);
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
  }

  .contact-row__arrow {
    flex: none;
    display: inline-flex;
    color: var(--muted-foreground);
    transition: transform var(--duration-fast) var(--ease);
  }

  .contact-row__arrow--open {
    transform: rotate(90deg);
  }

  .contact-row__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
  }

  .contact-row__role {
    flex: none;
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .contact-row__role:empty,
  .contact-row__phone:empty {
    display: none;
  }

  .contact-row__phone {
    margin-left: auto;
    flex: none;
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .contact-row__remove {
    flex: none;
    margin-right: calc(var(--space) * 2);
  }

  .contact-row__form {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 4);
    border-top: 1px solid var(--border);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  .contact-row__reminders {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 2);
  }

  /* Бейдж «Личный кабинет» на миникарточке контакта (change parent-cabinet):
     состояние входа и кнопка приглашения. Живёт в раскрытой форме строки
     и заменяется htmx-ответом целиком — остальная форма не перерисовывается. */
  .contact-cabinet {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--muted);
    font-size: var(--text-sm);
  }

  .contact-cabinet__title {
    font-weight: var(--font-medium);
  }

  .contact-cabinet__access {
    display: flex;
    align-items: flex-start;
    gap: calc(var(--space) * 2);
    margin: 0;
    color: var(--success);
    word-break: break-all;
  }

  .contact-cabinet__note {
    margin: 0;
    color: var(--muted-foreground);
    line-height: 1.5;
  }

  .contact-cabinet__term {
    margin: 0;
    color: var(--muted-foreground);
  }

  .contact-cabinet__link {
    align-self: flex-start;
    max-width: 100%;
    color: var(--brand);
    font-family: var(--font-mono);
    word-break: break-all;
  }

  .contact-cabinet__statuses {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 2);
  }

  .contact-cabinet__status {
    color: var(--success);
  }

  .contact-cabinet__status--warn {
    color: var(--muted-foreground);
  }

  .contact-cabinet__actions {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
    margin-top: calc(var(--space) * 1);
  }

  /* Поле выбора человека (ученика или контакта) — общий виджет:
     `_person_picker.html` + `picker.js`. Внешний вид одинаков в форме
     ученика и в форме группы. */
  .person-picker {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    grid-column: 1 / -1;
  }

  /* Селект цели урока — тот же виджет, но со своим именем блока: варианты
     списка приходят с сервера (htmx), и общий `person-picker` не должен
     отвечать за их разметку. */
  .lesson-target {
    position: relative;
  }

  /* Контейнер вариантов подменяется htmx целиком: пока ответа нет, он
     пуст, но место не схлопывается — иначе список «прыгал» бы по высоте. */
  .lesson-target__options {
    display: block;
    min-height: 0;
  }

  /* Список в форме урока раскрывается над нижними полями (модалка
     прокручивается), поэтому высоту ограничивает он сам, а не форма. */
  .lesson-target__list {
    max-height: 260px;
  }

  /* Мини-форма создания цели внутри селекта — общий `group-mini`, только
     без внутренней рамки: она стоит внутри поля, а не рядом с ним. */
  .lesson-target__mini {
    margin-top: calc(var(--space) * 2);
    background: var(--card);
  }

  .contact-suggestion {
    grid-column: 1 / -1;
  }

  .contact-hint {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .contact-suggestion__body {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 3);
    border-radius: var(--radius-md);
    background: var(--brand-soft);
  }

  /* Модалка и панель фильтров на мобильном: форма на весь экран,
     фильтры скрыты до нажатия кнопки «Фильтры». */
  @media (max-width: 639.98px) {
    .modal-root {
      padding: 0;
      align-items: stretch;
    }

    .modal-content {
      max-width: none;
      max-height: none;
      border-radius: 0;
    }

    .filters {
      display: none;

      &.is-open { display: flex; }
    }

    .filters__search,
    .filters__status {
      flex: 1 1 100%;
      min-width: 0;
      width: 100%;
    }
  }


  .auth {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: calc(var(--space) * 4);
    background: var(--surface);
  }

  .auth__card {
    width: 100%;
    max-width: 400px;               /* not specified — chosen: узкая колонка формы */
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 5);    /* 20px */
    padding: calc(var(--space) * 8);/* 32px */
  }

  .auth__brand {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);
    text-decoration: none;
    color: var(--foreground);
    align-self: center;
  }

  .auth__brand:hover { color: var(--foreground); }

  .auth__title {
    font-size: var(--text-lg);
    font-weight: 700;
    letter-spacing: -0.01em;
    text-align: center;
  }

  .auth__links {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(var(--space) * 3);
    font-size: var(--text-sm);
  }

  .auth__links a { color: var(--muted-foreground); }
  .auth__links a:hover { color: var(--brand); }

  /* Вертикальная форма-стек */
  .form-stack {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);      /* 16px */
  }

  /* Уведомления (notice — успех, error — ошибка) */
  .notice,
  .error {
    font-size: var(--text-sm);
    line-height: 1.5;
    border-radius: var(--radius-md);
    padding: calc(var(--space) * 3) calc(var(--space) * 4); /* 12px 16px */
  }

  .notice {
    background: color-mix(in srgb, var(--status-unplanned) 70%, transparent);
    color: var(--status-unplanned-fg);
  }

  .error {
    background: color-mix(in srgb, var(--status-cancelled) 70%, transparent);
    color: var(--status-cancelled-fg);
  }

  /* Ссылка сброса пароля (long URL — переносимо) */
  .reset-link-wrap {
    margin-top: calc(var(--space) * 4);
    font-size: var(--text-xs);
    word-break: break-all;
  }

  /* ==========================================================================
     404 — страница ошибки
     ========================================================================== */
  .not-found {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: calc(var(--space) * 4);
    text-align: center;
    padding: calc(var(--space) * 6) calc(var(--space) * 4);
  }

  /* Иллюстрация: ограничена и по ширине, и по высоте, чтобы вся страница
     (арт + код + заголовок + текст + кнопки) умещалась на один экран. */
  .not-found__art {
    width: auto;
    height: clamp(96px, 22vh, 200px);    /* fallback: без поддержки dvh */
    height: clamp(96px, 22dvh, 200px);
    max-width: min(280px, 60vw);
    flex: none;
    color: var(--brand);
  }

  .not-found__code {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1;
    color: var(--foreground);
  }

  .not-found__title {
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--foreground);
  }

  .not-found__text {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
    max-width: 36rem;
  }

  .not-found__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: calc(var(--space) * 2);
  }

  /* Низкие экраны (ноутбуки, ландшафт телефона): ещё компактнее. */
  @media (max-height: 640px) {
    .not-found {
      gap: calc(var(--space) * 3);
      padding: calc(var(--space) * 4) calc(var(--space) * 4);
    }

    .not-found__art {
      height: clamp(72px, 18vh, 140px);   /* fallback: без поддержки dvh */
      height: clamp(72px, 18dvh, 140px);
    }
    .not-found__code { font-size: var(--text-xl); }
    .not-found__title { font-size: var(--text-base); }
  }

  /* ==========================================================================
     Календарь недели (.cal-*)

     Сетка: колонка времени слева + семь колонок дней. Высота часа —
     CSS-переменная `--cal-hour-h`: позиция/высота карточек задаются в часах
     от 6:00 (см. services/presentation.py), поэтому сетка масштабируется
     без пересчёта на сервере. Тёмная тема — только через токены.
     ======================================================================= */
  .cal-page {
    display: flex;
    flex-direction: column;
    height: 100dvh;
  }

  .cal-card {
    --cal-hour-h: 48px;             /* высота одного часового интервала */
    --cal-time-w: 64px;             /* ширина колонки времени */
    --cal-line: var(--border);      /* линии сетки */
    --cal-line-soft: color-mix(in srgb, var(--border) 55%, transparent);
    /* Тон недоступности: единственный способ «выключить» день в сетке.
       Выходных «по календарю» нет — суббота и воскресенье выглядят как
       будни, пока репетитор не занёс отметку в таблицу. */
    --cal-off: color-mix(in srgb, var(--status-cancelled) 30%, transparent);

    flex: 1;
    min-height: 0;                  /* иначе flex-колонка не даёт скролл */
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
  }

  /* Область подмены HTMX (#calendar-swap): растягивается на всю карточку,
     иначе flex-ребёнок с min-height:auto тянется на высоту контента,
     `.cal-scroll` перестаёт быть скролл-контейнером и сетка обрезается
     карточкой вместо прокрутки (спека «Отображение текущей недели»:
     закрепление панели и шапки при вертикальной прокрутке). */
  #calendar-swap {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  /* Панель управления: закреплена сверху карточки */
  .cal-toolbar {
    flex: none;                      /* не сжимается, когда сетке тесно */
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border-bottom: 1px solid var(--cal-line);
  }

  .cal-toolbar__title {
    font-size: var(--text-lg);
    font-weight: 700;
    letter-spacing: -0.01em;
    margin-left: calc(var(--space) * 2);
  }

  .cal-toolbar__spacer { flex: 1; }

  /* Группа кнопок действий («Цвет», «Добавить», «⋮»): один flex-элемент,
     поэтому перенос строки панели не отрывает «⋮» от соседей. Иначе на
     средней ширине кнопка «⋮» уезжала на вторую строку к левому краю, и её
     меню (прижатое к правому краю кнопки) обрезалось `overflow: hidden`
     карточки — пункт «Добавить слот» становился недоступен (найдено в
     браузере при прогоне сценария). */
  .cal-toolbar__actions {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    /* Перенос строки прижимает группу к правому краю: она продолжает ряд
       кнопок панели, а не начинается заново от левого края. */
    margin-left: auto;
  }

  /* Бейдж часового пояса из профиля репетитора */
  .cal-tz {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space));
    padding: 2px calc(var(--space) * 2);
    border: 1px solid var(--cal-line);
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    white-space: nowrap;
  }

  .cal-tz [data-lucide] { width: 12px; height: 12px; }

  /* Пояснение пустой недели */
  .cal-empty {
    padding: calc(var(--space) * 2) calc(var(--space) * 4);
    color: var(--muted-foreground);
    font-size: var(--text-sm);
    border-bottom: 1px solid var(--cal-line-soft);
  }

  /* Область прокрутки сетки: вертикальная и горизонтальная */
  .cal-scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;
  }

  .cal-grid {
    display: grid;
    grid-template-columns: var(--cal-time-w) repeat(7, minmax(120px, 1fr));
    min-width: max-content;
    position: relative;               /* якорь для чипа времени четверти */
  }

  /* Угол сетки с месяцем/годом: sticky по обеим осям */
  .cal-corner {
    position: sticky;
    top: 0;
    left: 0;
    z-index: 3;
    display: flex;
    align-items: flex-end;
    padding: calc(var(--space) * 2);
    background: var(--card);
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    border-right: 1px solid var(--cal-line);
    border-bottom: 1px solid var(--cal-line);
  }

  /* Шапка дней: закреплена сверху при прокрутке сетки */
  .cal-dayhead {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: calc(var(--space) * 2) calc(var(--space));
    background: var(--card);
    border-bottom: 1px solid var(--cal-line);
    border-left: 1px solid var(--cal-line-soft);
    text-align: center;
  }

  .cal-dayhead__name {
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    text-transform: lowercase;
  }

  .cal-dayhead__num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 30px;
    height: 30px;
    padding: 0 calc(var(--space));
    border-radius: var(--radius-full);
    font-size: var(--text-base);
    font-weight: 700;
  }

  /* Текущий день — залитый круг */
  .cal-dayhead__num--today {
    background: var(--primary);
    color: var(--primary-foreground);
  }

  .cal-dayhead__count {
    color: var(--muted-foreground);
    font-size: var(--text-xs);
  }

  /* Плашка «Выходной» в шапке сб/вс */
  .cal-dayhead__off {
    padding: 1px calc(var(--space) * 2);
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.02em;
  }

  /* Причина отметки в шапке дня (design.md D4): заменяет плашку
     «Выходной» и подпись числа занятий, поэтому несёт тон причины —
     тот же `--cal-off`, что и штриховка тела дня. */
  .cal-dayhead__off--unavailable {
    background: var(--cal-off);
    color: var(--foreground);
    font-weight: 600;
    text-transform: none;
  }

  .cal-dayhead__dots {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 3px;
    min-height: 8px;
  }

  .cal-dot {
    width: 6px;
    height: 6px;
    border-radius: var(--radius-full);
    background: var(--dot, var(--muted-foreground));
  }

  /* Колонка времени: час крупно, половина часа мелко */
  .cal-times {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--card);
    border-right: 1px solid var(--cal-line);
  }

  .cal-times__hour {
    position: relative;
    height: var(--cal-hour-h);
  }

  .cal-times__h {
    position: absolute;
    top: -0.6em;
    right: calc(var(--space) * 2);
    font-size: var(--text-sm);
    font-weight: 600;
    font-family: var(--font-mono);
    color: var(--foreground);
  }

  .cal-times__m {
    position: absolute;
    top: calc(50% - 0.6em);
    right: calc(var(--space) * 2);
    font-size: var(--text-xs);
    font-family: var(--font-mono);
    color: var(--muted-foreground);
  }

  .cal-times__end {
    position: relative;
    height: 0;
  }

  /* Колонка дня: слоты четвертей и поверх — карточки событий */
  .cal-day {
    position: relative;
    border-left: 1px solid var(--cal-line-soft);
  }

  /* Штриховка недоступности: единственный способ «выключить» день в сетке.
     Выходных «по календарю» нет — суббота и воскресенье выглядят как будни,
     пока репетитор не занёс отметку в таблицу `tutor_unavailability`
     (спека calendar, «Выходные задаёт репетитор отметками»). */
  .cal-day--unavailable {
    background-image: repeating-linear-gradient(
      45deg,
      transparent 0 4px,
      var(--cal-off) 4px 6px
    );
    background-color: color-mix(in srgb, var(--cal-off) 40%, transparent);
  }

  .cal-hour {
    height: var(--cal-hour-h);
    border-top: 1px solid var(--cal-line-soft);
  }

  .cal-hour:first-child { border-top: 1px solid var(--cal-line); }

  /* Четверть часа: цель наведения и клика (подсветка — в calendar.js) */
  .cal-slot {
    height: calc(var(--cal-hour-h) / 4);
    border-top: 1px solid var(--cal-line-soft);
  }

  .cal-slot:hover {
    background: color-mix(in srgb, var(--brand) 10%, transparent);
  }

  /* Чип времени наведённой четверти (позиционирует calendar.js):
     абсолютное позиционирование выносит элемент из потока грида. */
  .cal-hover {
    position: absolute;
    z-index: 4;
    padding: 0 calc(var(--space));
    border-radius: var(--radius-full);
    background: var(--foreground);
    color: var(--background);
    font-family: var(--font-mono);
    font-size: 10px;
    line-height: 16px;
    pointer-events: none;             /* не мешает наведению и drag'у */
    transform: translateY(-4px);
  }

  /* Карточка занятия: позиция и размер — в часах от 6:00, наложение — в %.
     `container-type: size` превращает карточку в контейнер запросов: от её
     высоты зависит, какие строки помещаются (см. .cal-event__time ниже).
     Высота у карточки задана явно, поэтому size-контейнмент безопасен. */
  .cal-event {
    position: absolute;
    top: calc(var(--top) * var(--cal-hour-h));
    height: calc(var(--dur) * var(--cal-hour-h));
    left: var(--left);
    width: var(--width);
    box-sizing: border-box;
    container-type: size;
    display: flex;
    flex-direction: column;
    gap: 0;
    padding: 1px calc(var(--space) * 2);
    border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--accent) 12%, var(--card));
    color: var(--card-foreground);
    font-size: var(--text-xs);
    line-height: 1.15;                /* две строки влезают в 40-минутный урок */
    overflow: hidden;
    cursor: grab;
    touch-action: none;               /* drag без конфликта со скроллом */
    transition: box-shadow var(--duration-fast) var(--ease),
                transform var(--duration-fast) var(--ease);
  }

  .cal-event:hover {
    transform: translateY(-1px);
    box-shadow: var(--shadow-card);
    z-index: 1;
  }

  /* Прошедшее занятие приглушено с пунктирной рамкой */
  .cal-event--past {
    opacity: 0.55;
    border-style: dashed;
    border-left-style: solid;
    cursor: default;
  }

  /* Просроченный урок: запланирован, но время прошло (design.md D5) —
     акцент остаётся цветом цели, красной становится только рамка.
     Цвет — токен `--destructive`, а не литерал: в тёмной теме он
     осветлён, иначе рамка терялась бы на тёмной карточке. */
  .cal-event--overdue {
    border-color: color-mix(in srgb, var(--destructive) 55%, transparent);
  }

  /* Карточка в процессе перетаскивания: transform двигает calendar.js;
     сам hit-тест по сетке делает calendar.js (elementUnder), поэтому
     pointer-events здесь не трогаем — они нужны для pointer capture. */
  .cal-event--dragging {
    z-index: 5;
    opacity: 0.9;
    cursor: grabbing;
    transition: none;
    box-shadow: var(--shadow-card);
  }

  /* Подсветка целевой четверти при drag */
  .cal-slot--drop {
    background: color-mix(in srgb, var(--brand) 22%, transparent);
    border-radius: var(--radius-sm);
  }

  .cal-event__strip { display: none; }  /* полоса — через border-left */

  /* Имя цели (ученик или группа) — главная строка карточки: она не сжимается
     и не исчезает даже в самом коротком уроке. */
  .cal-event__title {
    flex: 0 0 auto;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Вторая строка: цена и интервал времени. Обрезается первой, когда
     высоты карточки не хватает (см. @container ниже). */
  .cal-event__meta {
    flex: 0 0 auto;
    display: flex;
    gap: var(--space);
    white-space: nowrap;
    overflow: hidden;
  }

  .cal-event__price {
    color: var(--muted-foreground);
  }

  .cal-event__time {
    flex: 0 0 auto;
    font-family: var(--font-mono);
    color: var(--muted-foreground);
  }

  /* Короткий урок (30 минут, карточка 24 px): вторая строка целиком не
     помещается — остаётся только имя. Иначе сжимались обе строки и карточка
     читалась как «одно время» без ученика. Полный текст всегда доступен во
     всплывающей подсказке `title`. Порог — по content-box контейнера:
     30 минут ≈ 20 px, 40 минут ≈ 28 px. */
  @container (max-height: 24px) {
    .cal-event__meta {
      display: none;
    }
  }

  /* Пометка «Группа» — третья строка: она появляется только там, где есть
     запас высоты (урок от 50 минут). Название группы говорит о ней и так. */
  @container (max-height: 38px) {
    .cal-event__tag {
      display: none;
    }
  }

  .cal-event__tag {
    flex: 0 0 auto;
    align-self: flex-start;
    padding: 0 calc(var(--space));
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);
    font-size: 10px;
  }

  /* Карточка окна для записи (спека calendar, «Окна отображаются в
     календаре во всех режимах»): собственный цвет `--cal-slot-accent`
     вместо цветов учеников, курсор указателя — окно не перетаскивается.
     Тёмная тема задаёт только токен (см. `.dark`), компонентные правила
     тему не дублируют.

     Состояние окна задаёт отдельный класс (`cal-event--slot-free`,
     `--slot-pending`, `--slot-booking`, `--slot-lesson`, `--slot-past`):
     подпись состояния считается на сервере, а вид карточки — из этого
     класса, поэтому новое состояние нельзя показать, забыв стиль. */
  .cal-event--slot {
    border-width: 1px;
    border-style: dashed;
    border-color: color-mix(in srgb, var(--cal-slot-accent) 50%, transparent);
    border-left-width: 3px;
    border-left-style: solid;
    border-left-color: var(--cal-slot-accent);
    background: color-mix(in srgb, var(--cal-slot-accent) 10%, var(--card));
    cursor: pointer;
    touch-action: auto;                /* без драга — не мешает скроллу */
  }

  /* Свободно: пунктирная рамка — «место, которое можно занять» */
  .cal-event--slot-free {
    border-style: dashed;
  }

  /* Ожидает подтверждения: сплошная рамка, штриховка фона — заявка есть,
     но время ещё не закреплено за учеником (design D9). */
  .cal-event--slot-pending {
    border-style: solid;
    background-image: repeating-linear-gradient(
      135deg,
      transparent 0 5px,
      color-mix(in srgb, var(--cal-slot-accent) 14%, transparent) 5px 10px
    );
  }

  /* Занято записью и занято уроком: сплошная рамка и залитый фон —
     время ушло, окно больше не предлагается ученику. */
  .cal-event--slot-booking,
  .cal-event--slot-lesson {
    border-style: solid;
    background: color-mix(in srgb, var(--cal-slot-accent) 22%, var(--card));
  }

  /* Время прошло: рамка гаснет вместе с подписью (приглушение — ниже) */
  .cal-event--slot-past {
    border-style: dotted;
    background: color-mix(in srgb, var(--cal-slot-accent) 6%, var(--card));
  }

  /* Приглушение занятых и прошедших окон: карточка уходит на второй план,
     но остаётся читаемой — подпись состояния важнее декора. */
  .cal-event--muted {
    opacity: 0.6;
    cursor: default;
  }

  /* Подпись состояния окна («Свободно», «Занято уроком», …).
     Как и имя цели у урока, она не сжимается: без `flex: 0 0 auto`
     подпись схлопывалась до нуля, и окно читалось как одно время. */
  .cal-event--slot .cal-event__title {
    flex: 0 0 auto;
  }

  /* Окно ниже 48 px (40 минут ≈ 32 px): три строки не помещаются —
     убираем интервал времени, оставляя подпись состояния, то есть то,
     по чему окно и опознаётся. */
  @container (max-height: 47px) {
    .cal-event--slot .cal-event__meta {
      display: none;
    }
  }

  /* Выпадающий список «Цвет» (заглушка без битых ссылок) */
  .cal-color {
    position: relative;
  }

  /* Контейнер меню быстрого добавления «⋮» (спека vacation-creation):
     та же геометрия, что у .cal-color; пункты меню и позиционирование
     выпадающего списка переиспользуют .cal-color__menu (design.md D1),
     поэтому темы на токенах действуют и для нового меню. */
  .cal-more {
    position: relative;
  }

  .cal-color__menu {
    position: absolute;
    top: calc(100% + var(--space));
    right: 0;
    z-index: 30;
    min-width: 10rem;
    margin: 0;
    padding: var(--space);
    list-style: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--card);
    box-shadow: var(--shadow-card);
  }

  .cal-color__menu button {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space);
    width: 100%;
    padding: calc(var(--space)) calc(var(--space) * 2);
    border: none;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--card-foreground);
    font-size: var(--text-sm);
    text-align: left;
    cursor: pointer;
  }

  .cal-color__menu button:hover {
    background: var(--muted);
  }

  /* Активный режим раскраски в меню «Цвет» (design.md D5) */
  .cal-color__menu button.is-active {
    background: var(--muted);
    font-weight: 600;
  }

  /* Поле показа публичной ссылки, когда буфер обмена недоступен (спека
     calendar, «Буфер обмена недоступен»): ссылку можно выделить и
     скопировать вручную. Ширина ограничена, чтобы панель не разъезжалась
     на узких экранах, а поле прокручивало длинный адрес. */
  .cal-link-fallback {
    flex: 1 1 100%;
    display: flex;
    gap: calc(var(--space) * 2);
  }

  .cal-link-fallback__field {
    flex: 1 1 auto;
    min-width: 0;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
  }

  /* Тост подтверждений действий панели (копирование ссылки) */
  .cal-toast {
    position: fixed;
    left: 50%;
    bottom: calc(var(--space) * 6);
    z-index: 70;
    transform: translateX(-50%);
    padding: calc(var(--space) * 2) calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--foreground);
    color: var(--background);
    font-size: var(--text-sm);
    box-shadow: var(--shadow-card);
    pointer-events: none;
  }

  /* Модалки календаря удалены вместе с демо-слоем: диалог окна приходит
     серверным фрагментом `slot/_dialog.html` и использует общие
     `.modal-*` из дизайн-системы. */

  /* Плавающая кнопка «+» (узкие экраны): добавление урока без FAB-вида
     «День», который отложен в следующий change */
  .cal-fab {
    display: none;
  }

  @media (max-width: 639.98px) {
    .cal-fab {
      position: fixed;
      right: calc(var(--space) * 4);
      bottom: calc(var(--space) * 4);
      z-index: 50;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 52px;
      height: 52px;
      border: none;
      border-radius: var(--radius-full);
      background: var(--primary);
      color: var(--primary-foreground);
      box-shadow: var(--shadow-card);
      cursor: pointer;
    }

    .cal-fab [data-lucide] { width: 22px; height: 22px; }
  }

  /* Узкие экраны: сетка скроллится горизонтально */
  @media (max-width: 639.98px) {
    .cal-toolbar { padding: calc(var(--space) * 2) calc(var(--space) * 3); }
    .cal-toolbar__title { font-size: var(--text-base); width: 100%; margin-left: 0; }
  }

  /* ==========================================================================
     Панель «Отпуск и выходные» (.un-*)

     Форма новой отметки и список «Уже отмечено» в одной панели модалки
     (design.md D8/D11). Цвета — только токены темы: панель живёт и в
     светлой, и в тёмной теме.
     ======================================================================= */

  /* Корень панели: своя ширина — список отметок уже формы урока */
  .un-panel .modal-content {
    max-width: 640px;
  }

  /* Переключатель режима: «Период» / «Постоянный выходной» */
  .un-modes {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
  }

  .un-modes__option {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space));
    padding: calc(var(--space)) calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--background);
    color: var(--foreground);
    font-size: var(--text-sm);
    cursor: pointer;
  }

  /* Выбранный режим: акцентная рамка и фон — как у активного таба */
  .un-modes__option:has(input:checked) {
    border-color: var(--primary);
    background: color-mix(in srgb, var(--primary) 10%, transparent);
    color: var(--primary);
    font-weight: 600;
  }

  .un-modes__option input {
    accent-color: var(--primary);
  }

  /* Поля выбранного режима: `template x-if` вставляет их одной группой,
     поэтому им нужна своя двухколоночная сетка внутри `form-section`. */
  .un-fields {
    display: contents;
  }

  /* Список «Уже отмечено» */
  .un-marks {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  .un-marks__title {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
  }

  .un-marks__empty {
    padding: calc(var(--space) * 3);
    border: 1px dashed var(--border);
    border-radius: var(--radius-md);
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  .un-list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  /* Строка отметки: причина и срок слева, действия справа */
  .un-mark {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--card);
  }

  /* Прошедшая отметка приглушена: она уже не влияет на будущие недели */
  .un-mark--past {
    opacity: 0.72;
  }

  .un-mark__main {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 8rem;
  }

  .un-mark__reason {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
  }

  .un-mark__span {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
    font-variant-numeric: tabular-nums;
  }

  .un-mark__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space));
  }

  .un-mark__field {
    display: inline-flex;
    align-items: center;
  }

  /* Свёрнутый блок прошедших отметок (design.md D11) */
  .un-past {
    margin-top: calc(var(--space));
  }

  .un-past__summary {
    padding: calc(var(--space)) 0;
    color: var(--muted-foreground);
    font-size: var(--text-sm);
    cursor: pointer;
  }

  /* ==========================================================================
     Окно для записи: диалог и предпросмотр пачки (спека slot-management)
     ========================================================================== */

  /* Корень диалога: чуть шире формы урока — в режиме пачки две колонки
     полей и блок предпросмотра. */
  .slot-dialog .modal-content {
    max-width: 40rem;
  }

  /* Блок ошибок в диалоге: отдельная полоса над формой, чтобы сообщение о
     конфликте времени не смешивалось с полями. */
  .slot-dialog__errors {
    padding-bottom: 0;
  }

  /* Переключатель режима: «Одно окно» / «Несколько дней» — та же геометрия,
     что у режимов панели недоступности. */
  .slot-modes {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
    margin-bottom: calc(var(--space) * 3);
  }

  .slot-modes__option {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space));
    padding: calc(var(--space)) calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--background);
    color: var(--foreground);
    font-size: var(--text-sm);
    cursor: pointer;
  }

  .slot-modes__option:has(input:checked) {
    border-color: var(--primary);
    background: color-mix(in srgb, var(--primary) 10%, transparent);
    color: var(--primary);
    font-weight: 600;
  }

  .slot-modes__option input {
    accent-color: var(--primary);
  }

  /* Дни недели пачки: компактные пилюли, выбранный день — акцентный. */
  .slot-weekdays {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
  }

  .slot-weekdays__option {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.75rem;
    padding: calc(var(--space)) calc(var(--space) * 2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--background);
    font-size: var(--text-sm);
    cursor: pointer;
  }

  .slot-weekdays__option:has(input:checked) {
    border-color: var(--primary);
    background: color-mix(in srgb, var(--primary) 12%, transparent);
    color: var(--primary);
    font-weight: 600;
  }

  /* Чекбокс скрыт визуально, но остаётся доступным для клавиатуры и
     скринридера: подпись целиком — цель клика. */
  .slot-weekdays__option input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* Блок предпросмотра пачки: отдельная секция с рамкой — число окон не
     должно теряться среди полей формы. */
  .slot-preview-box {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface);
  }

  .slot-preview-box__title {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
  }

  .slot-preview {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  /* Число окон — главная строка отчёта: крупнее остальных подписей. */
  .slot-preview__count {
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--foreground);
  }

  .slot-preview__empty {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  /* Времена одного дня: подпись дня и чипы времени в одну строку с переносом. */
  .slot-preview__times {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space));
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  .slot-preview__day {
    font-weight: 600;
    color: var(--foreground);
  }

  .slot-preview__time {
    padding: 0 calc(var(--space) * 2);
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
  }

  .slot-preview__skips {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space));
  }

  .slot-preview__skips-title {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--muted-foreground);
  }

  .slot-skip-list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) / 2);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  /* Строка пропуска: когда и почему — причина справа, серым. */
  .slot-skip {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
    font-size: var(--text-sm);
  }

  .slot-skip__when {
    font-family: var(--font-mono);
    color: var(--foreground);
  }

  .slot-skip__reason {
    color: var(--muted-foreground);
  }

  /* Карточка заявки в диалоге окна: имя, контакт и статус брони. */
  .slot-booking {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) / 2);
    margin-top: calc(var(--space) * 3);
    padding: calc(var(--space) * 3);
    border: 1px solid color-mix(in srgb, var(--cal-slot-accent) 45%, var(--border));
    border-radius: var(--radius-md);
    background: color-mix(in srgb, var(--cal-slot-accent) 8%, var(--card));
  }

  .slot-booking__title {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
  }

  .slot-booking__person {
    font-weight: 600;
    color: var(--foreground);
  }

  .slot-booking__contact {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Комментарий заявителя: свободный текст из публичной формы, поэтому
     перенос строк сохраняется и шрифт не моно — это не реквизит. */
  .slot-booking__comment {
    padding: calc(var(--space) * 2) calc(var(--space) * 3);
    border-radius: var(--radius-md);
    background: var(--muted);
    font-size: var(--text-sm);
    color: var(--foreground);
    white-space: pre-line;
    overflow-wrap: anywhere;
  }

  .slot-booking__status {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Форма действия с бронью: своя, а не часть формы окна (вложенные <form>
     в HTML запрещены). Внутри — поля выбора ученика и кнопки действия. */
  .slot-booking__form {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) / 2);
    margin-top: calc(var(--space) * 2);
  }

  /* Кнопки действия: слева «Отклонить»/«Отменить», справа «Подтвердить».
     Перенос разрешён — на узком экране кнопки встают в столбец. */
  .slot-booking__actions {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) / 2);
    justify-content: flex-end;
  }

  /* ==========================================================================
     Публичная страница записи (спека public-booking)
     ========================================================================== */

  /* Страница без сайдбара и навигации CRM: ученик приходит по ссылке из
     мессенджера и не должен видеть интерфейс кабинета. Фон — мягкий
     голубой градиент из макета, контент — узкая колонка по центру. */
  .public-page {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    justify-content: center;
    padding: calc(var(--space) * 6) calc(var(--space) * 4);
    background:
      radial-gradient(
        120% 80% at 50% 0%,
        color-mix(in srgb, var(--status-regular) 55%, var(--card)) 0%,
        var(--surface) 60%
      );
  }

  .public-shell {
    width: 100%;
    max-width: 72rem;
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 5);
  }

  /* Шапка: заголовок по центру и подпись педагога — как на макете. */
  .public-head {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(var(--space) / 2);
    text-align: center;
  }

  .public-head__title {
    font-family: var(--font-display);
    font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem);
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--foreground);
  }

  .public-head__tutor {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Карточка «Как записаться»: три шага, по центру, до выбора окна. */
  .public-steps {
    width: 100%;
    max-width: 34rem;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 4) calc(var(--space) * 5);
  }

  .public-steps__title {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
  }

  .public-steps__list {
    margin: 0;
    padding-left: calc(var(--space) * 5);
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) / 2);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .public-steps__item {
    line-height: 1.5;
  }

  /* Календарь недели: карточка с навигацией, баннером пояса и сеткой. */
  .public-calendar {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 4);
  }

  /* Переключение недель: стрелки по краям, подпись диапазона по центру. */
  .public-week {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: calc(var(--space) * 3);
  }

  .public-week__nav {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border: 1px solid var(--border);
    border-radius: 50%;
    color: var(--muted-foreground);
    text-decoration: none;
  }

  .public-week__nav:hover {
    color: var(--foreground);
    background: var(--muted);
  }

  .public-week__label {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
    white-space: nowrap;
  }

  /* Баннер о поясе: предупреждение, а не украшение — поэтому тёплая плашка
     с рамкой (токены warning из дизайн-системы, спека public-booking). */
  .public-zone {
    margin: 0;
    padding: calc(var(--space) * 2) calc(var(--space) * 3);
    border: 1px solid var(--warning-border);
    border-left-width: 3px;
    border-radius: var(--radius-md);
    background: var(--warning-soft);
    font-size: var(--text-sm);
    line-height: 1.5;
    color: var(--warning-fg);
  }

  .public-calendar__empty {
    padding: calc(var(--space) * 6) 0;
    text-align: center;
    color: var(--muted-foreground);
  }

  /* Сетка недели: семь равных столбцов. На узком экране сетка прокручивается
     по горизонтали, а не ломается на две строки — иначе дни перемешиваются
     и выбор времени становится нечитаемым. */
  .public-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(6.5rem, 1fr));
    gap: calc(var(--space) * 2);
    overflow-x: auto;
    padding-bottom: calc(var(--space) * 2);
  }

  .public-day {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    min-width: 0;
  }

  .public-day__head {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: calc(var(--space) * 2) calc(var(--space) / 2);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    background: var(--muted);
    text-align: center;
  }

  .public-day__weekday {
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--foreground);
  }

  .public-day__date {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .public-day__body {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    min-height: 8rem;
  }

  /* День без окон: подпись вместо пустоты, чтобы столбец не «проваливался». */
  .public-day__empty {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: calc(var(--space) * 2);
    border-radius: var(--radius-md);
    background: var(--status-cancelled);
    font-size: var(--text-xs);
    color: var(--status-cancelled-fg);
    text-align: center;
  }

  /* Карточка окна: время, конец и состояние. Общая база, состояние — модификатор. */
  .public-slot {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: calc(var(--space) * 2) calc(var(--space) / 2);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    text-align: center;
    text-decoration: none;
  }

  .public-slot__time {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-weight: 600;
    line-height: 1.2;
  }

  .public-slot__end {
    font-size: var(--text-xs);
    opacity: 0.75;
  }

  .public-slot__state {
    margin-top: 2px;
    font-size: var(--text-xs);
  }

  /* Свободное окно: зелёная карточка, кликабельная целиком (design D2). */
  .public-slot--free {
    background: color-mix(in srgb, var(--success) 10%, var(--card));
    border-color: color-mix(in srgb, var(--success) 25%, var(--card));
    color: var(--success);
    cursor: pointer;
  }

  .public-slot--free:hover {
    background: color-mix(in srgb, var(--success) 18%, var(--card));
  }

  .public-slot--free:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
  }

  /* Занятое окно: красная карточка без ссылки и без курсора-указателя.
     Кем занято — не показывается, поэтому подпись одна на все случаи. */
  .public-slot--busy {
    background: var(--status-cancelled);
    color: var(--status-cancelled-fg);
  }

  /* Выбранное окно: синий акцент, как в макете. */
  .public-slot--selected {
    background: var(--status-regular);
    border-color: color-mix(in srgb, var(--status-regular-fg) 30%, var(--card));
    color: var(--status-regular-fg);
  }

  .public-slot--selected:hover {
    background: var(--status-regular);
  }

  /* Карточка формы заявки: одна на страницу, под сеткой. */
  .public-form {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 5);
  }

  .public-form__title {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--foreground);
  }

  .public-form__title i {
    width: 1.125rem;
    height: 1.125rem;
    color: var(--muted-foreground);
  }

  .public-form__selected {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Плашка ошибок над сеткой: причина отказа видна и тогда, когда
     выбранного окна в сетке уже нет (окно заняли между выбором и отправкой). */
  .public-alert {
    margin: 0;
  }

  .public-form__form {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  .public-form__row {
    display: grid;
    gap: calc(var(--space) * 3);
  }

  .public-form__field {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) / 2);
  }

  .public-form__submit {
    width: 100%;
  }

  /* На широком экране имя и телефон встают в две колонки. */
  @media (min-width: 640px) {
    .public-form__row {
      grid-template-columns: 1fr 1fr;
    }
  }

  /* Экран успеха: одна карточка по центру, без сетки и формы (design D3). */
  .public-success {
    width: 100%;
    max-width: 32rem;
    margin: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 8) calc(var(--space) * 6);
    text-align: center;
  }

  .public-success__check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.5rem;
    height: 3.5rem;
    border-radius: 50%;
    background: color-mix(in srgb, var(--success) 14%, var(--card));
    color: var(--success);
  }

  .public-success__check i {
    width: 1.75rem;
    height: 1.75rem;
  }

  .public-success__title {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: 700;
    color: var(--foreground);
  }

  .public-success__text {
    color: var(--muted-foreground);
  }

  .public-success__zone {
    font-size: var(--text-xs);
    color: var(--warning-fg);
  }

  .public-success__time {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) / 2);
    padding: calc(var(--space) * 3);
    border-radius: var(--radius-md);
    background: var(--muted);
  }

  .public-success__time-label {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .public-success__time-value {
    font-size: var(--text-base);
    color: var(--foreground);
  }

  /* ==========================================================================
     Основной layout
     ========================================================================== */
  .main {
    padding-left: 0;
    transition: padding-left var(--duration-slow) var(--ease);
  }

  /* Страница без сайдбара (/): отступ не нужен ни на десктопе, ни на мобиле */
  .main--no-sidebar {
    padding-left: 0;
  }

  .main__container {
    max-width: 80rem;
    margin: 0 auto;
    padding: calc(var(--space) * 4) calc(var(--space) * 4); /* 32px 16px */
  }

  /* Ссылка «Назад» над заголовком страницы (по референсу списка учеников) */
  .back-link {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space));
    margin-bottom: calc(var(--space) * 2);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
    text-decoration: none;

    & [data-lucide] {
      width: 16px;
      height: 16px;
    }

    &:hover { color: var(--foreground); }
  }

  .page-header {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space));
    margin-bottom: calc(var(--space) * 5);

    & h1 {
      font-size: var(--text-xl);
      font-weight: 700;
      letter-spacing: -0.01em;
    }

    & p {
      color: var(--muted-foreground);
      font-size: var(--text-sm);
    }
  }

  /* Заголовок страницы с действиями справа (список учеников, карточка) */
  .page-header--row {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
  }

  /* ==========================================================================
     Мобильная шапка и нижний таб-бар
     ========================================================================== */
  .mobile-header {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);
    height: var(--mobile-header-h);
    padding: 0 calc(var(--space) * 4);
    background: var(--background);
    border-bottom: 1px solid var(--border);
  }

  .bottom-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 20;
    display: flex;
    height: var(--mobile-tabbar-h);
    background: var(--background);
    border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom);
  }

  .bottom-bar__item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    color: var(--muted-foreground);
    text-decoration: none;
    font-size: 10px;
    min-height: 40px;
    transition: color var(--duration-fast) var(--ease);
  }

  .bottom-bar__item:hover { color: var(--foreground); }

  .bottom-bar__item--active { color: var(--brand); }

  /* Десктоп: сайдбар всегда виден, контент со сдвигом */
  @media (min-width: 1024px) {
    .sidebar {
      transform: translateX(0);
    }

    .main {
      padding-left: var(--sidebar-width);
    }

    /* Свёрнутый сайдбар — контент сдвигается меньше.
       :not(.main--no-sidebar) — иначе на публичной / появился бы отступ. */
    .sidebar-collapsed .main:not(.main--no-sidebar) {
      padding-left: var(--sidebar-width-collapsed);
    }

    .main--no-sidebar { padding-left: 0; }

    .main__container {
      padding: calc(var(--space) * 8) calc(var(--space) * 8); /* 32px 32px */
    }

    .mobile-header,
    .bottom-bar,
    .overlay {
      display: none;
    }
  }

  /* Мобилка: off-canvas + отступ под нижний таб-бар */
  @media (max-width: 1023.98px) {
    .sidebar {
      transform: translateX(-100%);

      &.is-open { transform: translateX(0); }
    }

    .main {
      padding-bottom: calc(var(--mobile-tabbar-h) + calc(var(--space) * 4));
    }
  }

  /* ==========================================================================
     Напоминание о не отмеченных уроках (.notice-banner): предупреждающая
     плашка вверху контента любой страницы кабинета. Разметку подгружает
     партиал /tutor/notices в контейнер _notice_banner.html (change
     unmarked-lessons); закрытие — Alpine, видимость управляется x-show.
     ========================================================================== */
  .notice-banner {
    display: flex;
    align-items: flex-start;
    gap: calc(var(--space) * 3);
    margin-bottom: calc(var(--space) * 5);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--warning-border);
    border-radius: var(--radius-md);
    background: var(--warning-soft);
    color: var(--warning-fg);
    font-size: var(--text-sm);
    line-height: 1.5;
  }

  .notice-banner__icon {
    display: inline-flex;
    flex-shrink: 0;

    & [data-lucide] { width: 16px; height: 16px; }
  }

  .notice-banner__body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
  }

  .notice-banner__text {
    font-weight: 500;
  }

  .notice-banner__dot {
    margin: 0 calc(var(--space) * 1);
    opacity: 0.7;
  }

  .notice-banner__link {
    color: inherit;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
    margin-left: calc(var(--space) * 2);
  }

  .notice-banner__link:hover {
    text-decoration-thickness: 2px;
  }

  .notice-banner__hint {
    opacity: 0.85;
  }

  .notice-banner__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    padding: 0;
    border: none;
    border-radius: var(--radius-sm);
    background: none;
    color: inherit;
    cursor: pointer;

    & [data-lucide] { width: 14px; height: 14px; }

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

  /* ==========================================================================
     Статистика (/tutor/stats): табы, KPI, риск, таблицы, периоды
     ========================================================================== */
  .stats-tabs {
    margin-bottom: calc(var(--space) * 5);

    /* Таб-чипы — ссылки, а не кнопки: сбрасываем оформление <a>. */
    & .tab {
      display: inline-flex;
      align-items: center;
      text-decoration: none;
    }
  }

  .stats-toolbar {
    display: flex;
    margin-bottom: calc(var(--space) * 5);
  }

  .stats-forecast {
    margin-bottom: calc(var(--space) * 5);
  }

  .stats-forecast__title {
    margin: 0 0 calc(var(--space) * 3);
    font-size: var(--text-base);
    font-weight: 600;
  }

  .stats-duo {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 3);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, 1fr);
    }
  }

  .stats-duo__item {
    padding: calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--card);
  }

  .stats-duo__label {
    margin: 0 0 calc(var(--space) * 2);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .stats-duo__value {
    margin: 0 0 calc(var(--space) * 2);
    font-size: var(--text-xl);
    font-weight: 700;
  }

  .stats-duo__sub {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .stats-kpis {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);
    margin-bottom: calc(var(--space) * 5);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, 1fr);
    }

    @media (min-width: 1024px) {
      grid-template-columns: repeat(4, 1fr);
    }
  }

  .stats-kpi {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  .stats-kpi__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  .stats-kpi__title {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--foreground);
  }

  .stats-kpi__icon {
    display: inline-flex;
    color: var(--muted-foreground);

    & [data-lucide] { width: 18px; height: 18px; }
  }

  .stats-kpi__icon--up { color: var(--success); }
  .stats-kpi__icon--info { color: var(--stat-blue-fg); }
  .stats-kpi__icon--danger { color: var(--destructive); }

  .stats-kpi__value {
    margin: calc(var(--space) * 2) 0 0;
    font-size: var(--text-xl);
    font-weight: 700;
  }

  .stats-kpi__sub {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .stats-kpi__link {
    margin-top: auto;
    padding-top: calc(var(--space) * 2);
    font-size: var(--text-xs);
    color: var(--brand);
    text-decoration: none;

    &:hover { text-decoration: underline; }
  }

  .stats-risk {
    border: 1px solid var(--warning-border);
  }

  .stats-risk__head {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin: 0 0 calc(var(--space) * 3);
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--warning-fg);

    & [data-lucide] { width: 18px; height: 18px; }
  }

  .stats-risk__text {
    margin: 0 0 calc(var(--space) * 3);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .stats-risk__list {
    margin: 0;
    padding-left: calc(var(--space) * 5);
    font-size: var(--text-sm);
    color: var(--foreground);

    & li { margin-bottom: calc(var(--space)); }
  }

  .stats-risk__ok {
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--success);
    text-align: center;
  }

  .stats-card__header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    margin-bottom: calc(var(--space) * 4);
  }

  .stats-card__title {
    margin: 0;
    font-size: var(--text-base);
    font-weight: 600;
  }

  .stats-filters {
    flex: none;
    margin-bottom: calc(var(--space) * 3);
  }

  .stats-filters__search {
    flex: 1;
    min-width: 180px;
  }

  .stats-check {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    font-size: var(--text-sm);
    color: var(--foreground);
  }

  .stats-hint {
    margin: 0 0 calc(var(--space) * 4);
    font-size: var(--text-xs);
    line-height: 1.5;
    color: var(--muted-foreground);
  }

  .stats-table-wrap {
    overflow-x: auto;
  }

  .stats-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
  }

  .stats-table__th {
    padding: calc(var(--space) * 2) calc(var(--space) * 3);
    border-bottom: 1px solid var(--border);
    text-align: left;
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--muted-foreground);
    white-space: nowrap;
  }

  .stats-table__th--num,
  .stats-table__cell--num { text-align: right; }

  .stats-table__row {
    border-bottom: 1px solid var(--border);

    &:last-child { border-bottom: none; }
  }

  .stats-table__cell {
    padding: calc(var(--space) * 3);
    vertical-align: middle;
  }

  .stats-state {
    display: inline-flex;
    align-items: center;
    margin-top: calc(var(--space));
    padding: 0 calc(var(--space) * 2);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    font-size: var(--text-xs);
    font-weight: 500;
    background: var(--card);
    color: var(--muted-foreground);
  }

  .stats-state--active {
    border-color: var(--status-unplanned);
    background: var(--status-unplanned);
    color: var(--status-unplanned-fg);
  }

  .stats-state--risk {
    border-color: var(--status-rescheduled);
    background: var(--status-rescheduled);
    color: var(--status-rescheduled-fg);
  }

  .stats-state--lost { color: var(--muted-foreground); }

  .stats-pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    margin-top: calc(var(--space) * 4);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .stats-pagination__nav {
    display: flex;
    gap: calc(var(--space) * 2);
  }

  .stats-pagination__info {
    color: var(--muted-foreground);
  }

  .stats-pagination__ghost {
    display: inline-flex;
    align-items: center;
    color: var(--muted-foreground);
    opacity: 0.6;
  }

  .stats-period {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 3);
    margin-bottom: calc(var(--space) * 5);
  }

  .stats-period__caption {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .stats-period__custom {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 2);
  }

  .stats-panel {
    margin-bottom: calc(var(--space) * 5);
  }

  .stats-panel__title {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin: 0 0 calc(var(--space) * 3);
    font-size: var(--text-base);
    font-weight: 600;

    & [data-lucide] { width: 18px; height: 18px; }
  }

  .stats-num {
    font-size: var(--text-sm);

    & summary {
      cursor: pointer;
      font-weight: 500;
      color: var(--brand);
    }
  }

  .stats-num__list {
    margin: calc(var(--space) * 2) 0 0;
    padding-left: calc(var(--space) * 4);
    font-weight: 400;
    text-align: left;
    color: var(--muted-foreground);

    & li { margin-bottom: calc(var(--space)); }
  }

  .stats-reconcile {
    margin: calc(var(--space) * 4) 0 0;
    font-size: var(--text-sm);
    color: var(--foreground);
  }

  /* ==========================================================================
     Страница финансов (/tutor/finance): тулбар периода, карточки-сводки,
     сравнение «заработано/получено», чип выбранного ученика в диалоге.
     ========================================================================== */
  .fin-toolbar {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin-bottom: calc(var(--space) * 5);
  }

  .fin-toolbar__period {
    max-width: 260px;
  }

  /* Сводка — два яруса, а не восемь карточек одного веса. Главные
     (доход, чистая прибыль, долги) крупнее: это ответ на вопрос, с которым
     открывают «Финансы». Расшифровка (возвраты, чистый доход, расходы,
     заработано, переплата) мельче, но на месте — иерархия без скрытия
     данных, поэтому обновление блока по htmx ничего не сбрасывает. */
  .fin-kpis {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);
  }

  .fin-kpis--lead {
    margin-bottom: calc(var(--space) * 4);

    @media (min-width: 640px) {
      grid-template-columns: repeat(3, 1fr);
    }
  }

  /* Компактный ярус на мобиле — две колонки: пять карточек в столбик
     растягивали сводку на лишний экран, а числа здесь короткие. */
  .fin-kpis--secondary {
    grid-template-columns: repeat(2, 1fr);
    margin-bottom: calc(var(--space) * 5);

    @media (min-width: 1024px) {
      grid-template-columns: repeat(5, 1fr);
    }
  }

  .fin-kpi {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  .fin-kpi--lead .fin-kpi__value {
    font-size: var(--text-2xl);
  }

  /* Компактная карточка: меньше воздуха и мельче число — вес карточки
     показывает её роль в сводке, а не важность данных. */
  .fin-kpi--compact {
    gap: calc(var(--space) * 1);
    padding: calc(var(--space) * 4);
  }

  .fin-kpi--compact .fin-kpi__value {
    font-size: var(--text-lg);
  }

  .fin-kpi--compact .fin-kpi__sub {
    line-height: 1.4;
  }

  .fin-kpi__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  .fin-kpi__title {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--foreground);
  }

  .fin-kpi__icon {
    display: inline-flex;
    color: var(--muted-foreground);

    & [data-lucide] { width: 18px; height: 18px; }
  }

  /* Тинты дашборда главной: те же пары, что у стат-карточек главной. */
  .fin-kpi__icon--green { color: var(--stat-green-fg); }
  .fin-kpi__icon--red { color: var(--destructive); }
  .fin-kpi__icon--violet { color: var(--stat-purple-fg); }

  .fin-kpi__value {
    margin: calc(var(--space) * 2) 0 0;
    font-size: var(--text-xl);
    font-weight: 700;
    color: var(--foreground);
  }

  .fin-kpi__value--green { color: var(--success); }
  .fin-kpi__value--red { color: var(--destructive); }
  .fin-kpi__value--violet { color: var(--stat-purple-fg); }

  .fin-kpi__sub {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .fin-compare {
    padding: calc(var(--space) * 4);
  }

  .fin-compare__title {
    margin: 0 0 calc(var(--space) * 3);
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--foreground);
  }

  .fin-compare__grid {
    display: grid;
    grid-template-columns: 1fr;

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, 1fr);
    }

    @media (min-width: 1024px) {
      grid-template-columns: repeat(4, 1fr);
    }

    gap: calc(var(--space) * 4);
  }

  .fin-compare__item {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
  }

  .fin-compare__label {
    margin: 0 0 calc(var(--space) * 2);
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .fin-compare__value {
    margin: 0;
    font-size: var(--text-xl);
    font-weight: 700;
    color: var(--foreground);
  }

  .fin-compare__value--green { color: var(--success); }

  /* Минус-строка «− Расходы» и её сумма — красные: расход вычитается
     из арифметики блока (референс страницы финансов). */
  .fin-compare__value--red { color: var(--destructive); }

  .fin-compare__label--minus { color: var(--destructive); }

  .fin-compare__note {
    margin: calc(var(--space) * 4) 0 0;
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Корень сводки — обновляемый контейнер: после сохранения платежа блок
     приходит заново целиком (design D6 finance-page). */
  .fin-summary {
    margin-bottom: calc(var(--space) * 5);
  }

  /* Чип выбранного ученика в диалоге «Доход»: появляется под полем выбора,
     клик по крестику сбрасывает выбор (payments.js). */
  .fin-student-chip {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin-top: calc(var(--space) * 2);
    padding: calc(var(--space) * 1) calc(var(--space) * 2);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    font-size: var(--text-sm);
    color: var(--foreground);
    background: var(--card);

    & [data-lucide] { width: 16px; height: 16px; color: var(--muted-foreground); }
  }

  /* ==========================================================================
     Секция операций страницы финансов: табы, таблица операций и карточка
     «Балансы учеников» (справа от табов, по референсу).
     ========================================================================== */

  /* Двухколоночная раскладка: слева табы со списком, справа балансы.
     minmax(0, 1fr) не даёт длинным именам растягивать колонку. */
  .fin-operations__layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);
    margin-bottom: calc(var(--space) * 5);

    @media (min-width: 1024px) {
      grid-template-columns: minmax(0, 1fr) 300px;
      align-items: start;
    }
  }

  .fin-operations__main {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }

  /* Вкладки и кнопка «Добавить» в одну строку: кнопка прижата вправо,
     на узких экранах переносится под вкладки, а не сжимает их. */
  .fin-operations__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
    flex-wrap: wrap;
    margin-bottom: calc(var(--space) * 3);
  }

  /* Пять вкладок сегмента переносятся на узких экранах, а не сжимаются
     в нечитаемые пилюли. */
  .fin-operations__tabs {
    display: flex;
    flex-wrap: wrap;
  }

  /* Кнопке «Добавить» не нужен отступ слева: gap головы держит дистанцию
     до вкладок, а сам класс остаётся точкой расширения под стили кнопки. */
  .fin-operations__add {
    flex-shrink: 0;
  }

  .fin-ops {
    padding: calc(var(--space) * 2) calc(var(--space) * 4);
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  /* Чипсы фильтра «Все операций» над таблицей: ссылка, как вкладки —
     состояние видно в адресе. Активный — тёмный, как у пилюль горизонта. */
  .fin-ops__filters {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
  }

  .fin-ops__chip {
    display: inline-flex;
    align-items: center;
    padding: calc(var(--space) * 1) calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--card);
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
    transition:
      background var(--duration-fast) var(--ease),
      color var(--duration-fast) var(--ease);
  }

  .fin-ops__chip:hover {
    background: var(--muted);
    color: var(--foreground);
  }

  .fin-ops__chip--active,
  .fin-ops__chip--active:hover {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--primary-foreground);
  }

  /* Карточки итогов ленты: строка из четырёх значений над таблицей,
     на телефоне — сетка 2×2, чтобы цифры не сжимались в строку. */
  .fin-ops__totals {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: calc(var(--space) * 2);
  }

  @media (min-width: 768px) {
    .fin-ops__totals {
      grid-template-columns: repeat(4, minmax(0, 1fr));
    }
  }

  .fin-ops__total {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
    padding: calc(var(--space) * 2) calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card);
  }

  .fin-ops__total-label {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .fin-ops__total-value {
    font-size: var(--text-sm);
    font-weight: 600;
    white-space: nowrap;
  }

  .fin-ops__total-value--green { color: var(--success); }

  .fin-ops__total-value--red { color: var(--destructive); }

  .fin-ops__table {
    width: 100%;
    border-collapse: collapse;
  }

  .fin-ops__head .fin-ops__th {
    padding: calc(var(--space) * 2);
    border-bottom: 1px solid var(--border);
    text-align: left;
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--muted-foreground);
    white-space: nowrap;
  }

  /* Дата/тип прячутся на телефоне: описание и сумма важнее,
     горизонтальный скролл таблицы хуже неполной строки. */
  @media (max-width: 639px) {
    .fin-ops__th--date,
    .fin-ops__th--type,
    .fin-ops__cell--date,
    .fin-ops__cell--type {
      display: none;
    }
  }

  .fin-ops__th--amount,
  .fin-ops__cell--amount {
    text-align: right;
  }

  .fin-ops__row .fin-ops__cell {
    padding: calc(var(--space) * 2);
    border-bottom: 1px solid var(--border);
    font-size: var(--text-sm);
    color: var(--foreground);
    vertical-align: top;
  }

  .fin-ops__row:last-child .fin-ops__cell {
    border-bottom: 0;
  }

  .fin-ops__cell--desc {
    display: flex;
    align-items: flex-start;
    gap: calc(var(--space) * 2);
    min-width: 0;
  }

  .fin-ops__cell--date,
  .fin-ops__cell--type {
    white-space: nowrap;
    color: var(--muted-foreground);
  }

  .fin-ops__icon {
    display: inline-flex;
    padding: calc(var(--space) * 1);
    border-radius: var(--radius-full);
    background: var(--muted);

    & [data-lucide] { width: 16px; height: 16px; }
  }

  .fin-ops__icon .fin-ops__amount--green { color: var(--success); }
  .fin-ops__icon .fin-ops__amount--red { color: var(--destructive); }

  .fin-ops__title {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-weight: 500;
    min-width: 0;
  }

  .fin-ops__comment {
    font-size: var(--text-xs);
    font-weight: 400;
    color: var(--muted-foreground);
    overflow-wrap: anywhere;
  }

  /* Бейдж статуса платежа: пара «тинт фона + цвет текста», как у
     статусов карточки ученика. */
  .fin-ops__status {
    display: inline-flex;
    padding: calc(var(--space) * 0.5) calc(var(--space) * 2);
    border-radius: var(--radius-full);
    font-size: var(--text-xs);
    white-space: nowrap;
  }

  .fin-ops__status--paid {
    background: var(--stat-green);
    color: var(--stat-green-fg);
  }

  .fin-ops__status--pending {
    background: var(--muted);
    color: var(--muted-foreground);
  }

  .fin-ops__status--cancelled {
    background: var(--muted);
    color: var(--muted-foreground);
    text-decoration: line-through;
  }

  .fin-ops__amount--green { color: var(--success); }

  .fin-ops__amount--red { color: var(--destructive); }

  /* Пустой период: сообщение вместо таблицы. */
  .fin-ops__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 8) calc(var(--space) * 4);
    color: var(--muted-foreground);
    font-size: var(--text-sm);

    & p { margin: 0; }

    & [data-lucide] { width: 28px; height: 28px; }
  }

  /* ==========================================================================
     Вкладка «Аналитика»: пилюли горизонта (6/12 мес) и карточки графиков.
     Вся геометрия SVG приходит с сервера (services/analytics.py) — здесь
     только заливки, подписи осей и раскладка карточек.
     ========================================================================== */

  .fin-analytics {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  .fin-analytics__pills {
    display: flex;
    gap: calc(var(--space) * 2);
  }

  /* Пилюля горизонта — ссылка, а не кнопка: состояние видно в адресе
     (паттерн вкладок). Активная — тёмная, как primary-кнопка референса. */
  .fin-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 76px;
    padding: calc(var(--space) * 1.5) calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--card);
    color: var(--muted-foreground);
    font-size: var(--text-sm);
    font-weight: 500;
    text-decoration: none;
    transition:
      background var(--duration-fast) var(--ease),
      color var(--duration-fast) var(--ease);
  }

  .fin-pill:hover {
    background: var(--muted);
    color: var(--foreground);
  }

  .fin-pill--active,
  .fin-pill--active:hover {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--primary-foreground);
  }

  /* Карточка графика: заголовок и легенда в одну строку, график ниже. */
  .fin-chart {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 4);
  }

  .fin-chart__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  .fin-chart__title {
    margin: 0;
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--foreground);
  }

  .fin-chart__legend {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2) calc(var(--space) * 4);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .fin-chart__legend-item {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 1.5);
  }

  .fin-chart__dot {
    width: 10px;
    height: 10px;
    border-radius: 3px;
  }

  .fin-chart__dot--income { background: var(--chart-income); }
  .fin-chart__dot--earned { background: var(--chart-earned); }
  .fin-chart__dot--expenses { background: var(--chart-expense); }

  /* viewBox + width:100% растягивают серверный SVG без пересчёта
     координат; пирог ограничен — большой круг растягивал бы карточку. */
  .fin-chart__svg {
    display: block;
    width: 100%;
    height: auto;
  }

  .fin-chart__svg--pie {
    max-width: 280px;
    margin-inline: auto;
  }

  .fin-chart__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 8) calc(var(--space) * 4);
    color: var(--muted-foreground);
    font-size: var(--text-sm);

    & p { margin: 0; }

    & [data-lucide] { width: 28px; height: 28px; }
  }

  /* Ось столбчатого графика: сетка пунктиром, база — сплошной, подписи
     приглушённые — ось не спорит с данными (как на референсе). */
  .fin-grid {
    stroke: var(--border);
    stroke-dasharray: 4 4;
  }

  .fin-axis {
    stroke: var(--border);
  }

  .fin-tick {
    fill: var(--muted-foreground);
    font-size: 11px;
    text-anchor: end;
  }

  .fin-month {
    fill: var(--muted-foreground);
    font-size: 11px;
    text-anchor: middle;
  }

  .fin-bar--income { fill: var(--chart-income); }
  .fin-bar--earned { fill: var(--chart-earned); }
  .fin-bar--expenses { fill: var(--chart-expense); }

  /* Пирог категорий: сектора с белой разделительной каймой, проценты —
     снаружи кольца (координаты и показ подписей решает сервис). */
  .fin-slice {
    stroke: var(--card);
    stroke-width: 2;
  }

  .fin-slice--1 { fill: var(--chart-cat-1); }
  .fin-slice--2 { fill: var(--chart-cat-2); }
  .fin-slice--3 { fill: var(--chart-cat-3); }
  .fin-slice--4 { fill: var(--chart-cat-4); }
  .fin-slice--5 { fill: var(--chart-cat-5); }
  .fin-slice--6 { fill: var(--chart-cat-6); }
  .fin-slice--7 { fill: var(--chart-cat-7); }
  .fin-slice--8 { fill: var(--chart-cat-8); }
  .fin-slice--9 { fill: var(--chart-cat-9); }
  .fin-slice--10 { fill: var(--chart-cat-10); }

  .fin-pie-label {
    fill: var(--muted-foreground);
    font-size: 12px;
    font-weight: 600;
  }

  /* Легенда категорий под пирогом: колонки резиновые — от узкой карточки
     до широкого экрана список сам выбирает число колонок. */
  .fin-cat-legend {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: calc(var(--space) * 2) calc(var(--space) * 4);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .fin-cat-legend__item {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    min-width: 0;
  }

  .fin-cat-legend__dot {
    flex: none;
    width: 10px;
    height: 10px;
    border-radius: 3px;
  }

  .fin-cat-legend__dot--1 { background: var(--chart-cat-1); }
  .fin-cat-legend__dot--2 { background: var(--chart-cat-2); }
  .fin-cat-legend__dot--3 { background: var(--chart-cat-3); }
  .fin-cat-legend__dot--4 { background: var(--chart-cat-4); }
  .fin-cat-legend__dot--5 { background: var(--chart-cat-5); }
  .fin-cat-legend__dot--6 { background: var(--chart-cat-6); }
  .fin-cat-legend__dot--7 { background: var(--chart-cat-7); }
  .fin-cat-legend__dot--8 { background: var(--chart-cat-8); }
  .fin-cat-legend__dot--9 { background: var(--chart-cat-9); }
  .fin-cat-legend__dot--10 { background: var(--chart-cat-10); }

  .fin-cat-legend__name {
    color: var(--foreground);
    font-size: var(--text-sm);
    overflow-wrap: anywhere;
  }

  .fin-cat-legend__percent {
    margin-left: auto;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
  }

  .fin-cat-legend__amount {
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    white-space: nowrap;
  }

  /* ==========================================================================
     Вкладка «Прогноз на месяц»: месяц и действия, сетка карточек-метрик,
     блок предупреждений, расшифровки и карточка зафиксированного снимка.
     Числа приходят готовыми из services/forecast.py — здесь только вид.
     ========================================================================== */

  .fin-forecast {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  /* Панель вкладки: слева селектор месяца, справа «Скачать CSV» и
     «Зафиксировать прогноз» — как на референсе. */
  .fin-forecast__toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2) calc(var(--space) * 4);
  }

  .fin-forecast__month {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin: 0;
  }

  .fin-forecast__select {
    width: auto;
    min-width: 200px;
  }

  .fin-forecast__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 2);

    & form { margin: 0; }
  }

  /* Карточки-метрики: две колонки на широком экране, одна на телефоне.
     Сетка, а не flex-wrap: карточки выравниваются по строкам. */
  .fin-forecast__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 3);

    @media (min-width: 768px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  .fin-metric {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 4);
  }

  .fin-metric__label {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 1.5);
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--muted-foreground);
  }

  /* Подсказка «ⓘ» — иконка с title и скрытым текстом для скринридера:
     пояснение не занимает места в карточке. */
  .fin-metric__hint {
    display: inline-flex;
    color: var(--muted-foreground);
    cursor: help;

    & [data-lucide] { width: 14px; height: 14px; }
  }

  .fin-metric__value {
    margin: 0;
    font-size: var(--text-xl);
    font-weight: 700;
    line-height: 1.2;
    color: var(--foreground);
  }

  .fin-metric__note {
    margin: 0;
    font-size: var(--text-xs);
    line-height: 1.5;
    color: var(--muted-foreground);
  }

  /* «Не в расписании» — ожидание, а не состоявшиеся деньги: значение
     приглушено, чтобы не читалось как факт. */
  .fin-metric--unscheduled .fin-metric__value {
    color: var(--muted-foreground);
  }

  /* Блок предупреждений: мягкая жёлтая плашка из токенов баннера. */
  .fin-warning {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--warning-border);
    border-radius: var(--radius-lg);
    background: var(--warning-soft);
    color: var(--warning-fg);
  }

  .fin-warning__title {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 600;

    & [data-lucide] { width: 16px; height: 16px; }
  }

  .fin-warning__list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1.5);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--text-sm);
    line-height: 1.5;
  }

  .fin-warning__item {
    margin: 0;
  }

  /* Расшифровки: «Не в расписании» и «По ученикам — календарь». */
  .fin-forecast__detail {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 4);
  }

  .fin-forecast__detail-title {
    margin: 0;
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--foreground);
  }

  .fin-forecast__rows {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .fin-forecast__row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 2) 0;
    border-bottom: 1px solid var(--border);

    &:last-child { border-bottom: none; }
  }

  .fin-forecast__row-main {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 0.5);
    min-width: 0;
  }

  .fin-forecast__row-name {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--foreground);
    overflow-wrap: anywhere;
  }

  .fin-forecast__row-detail {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .fin-forecast__row-amount {
    flex: none;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
    white-space: nowrap;
  }

  /* Карточка «Прогноз vs факт»: снимок прогноза и отклонение факта. */
  .fin-snapshot {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 4);
  }

  .fin-snapshot__head {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
  }

  .fin-snapshot__title {
    margin: 0;
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--foreground);
  }

  .fin-snapshot__meta {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .fin-snapshot__row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: calc(var(--space) * 2);
    margin: 0;
    font-size: var(--text-sm);
  }

  .fin-snapshot__label {
    font-weight: 500;
    color: var(--foreground);
  }

  .fin-snapshot__value {
    color: var(--muted-foreground);
  }

  /* Бейдж отклонения: цвет говорит, в какую сторону факт ушёл от прогноза. */
  .fin-snapshot__badge {
    margin-left: calc(var(--space) * 1);
    font-weight: 600;
  }

  .fin-snapshot__badge--below { color: var(--destructive); }
  .fin-snapshot__badge--above { color: var(--success); }
  .fin-snapshot__badge--inrange { color: var(--muted-foreground); }

  /* Карточка «Балансы учеников»: должники и переплатчики с суммами сторон.
     Список ограничен по высоте: при десятках учеников карточка не должна
     вытягивать страницу, скролл внутри. */
  .fin-balances {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 4);
  }

  .fin-balances__title {
    margin: 0;
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--foreground);
  }

  .fin-balances__section {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  .fin-balances__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--foreground);
  }

  .fin-balances__sum {
    font-size: var(--text-xs);
    white-space: nowrap;
  }

  .fin-balances__sum--red { color: var(--destructive); }
  .fin-balances__sum--green { color: var(--success); }

  .fin-balances__list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    margin: 0;
    padding: 0;
    list-style: none;
    max-height: 320px;
    overflow-y: auto;
  }

  .fin-balances__row {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    min-width: 0;
  }

  .fin-balances__avatar {
    width: 32px;
    height: 32px;
    font-size: var(--text-xs);
  }

  .fin-balances__name {
    flex: 1;
    min-width: 0;
    font-size: var(--text-sm);
    color: var(--foreground);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .fin-balances__amount {
    font-size: var(--text-sm);
    font-weight: 600;
    white-space: nowrap;
  }

  .fin-balances__amount--red { color: var(--destructive); }
  .fin-balances__amount--green { color: var(--success); }

  .fin-balances__empty {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* ==========================================================================
     Брони и заявки (спека booking-management, страница /tutor/bookings)
     ========================================================================== */
  .bookings-pending,
  .bookings-history {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    margin-bottom: calc(var(--space) * 8);
  }

  .bookings-title {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin: 0;
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--foreground);
  }

  .bookings-title [data-lucide] {
    width: 20px;
    height: 20px;
    color: var(--muted-foreground);
  }

  .bookings-title__count {
    display: inline-flex;
    align-items: center;
    min-height: 22px;
    padding: 0 calc(var(--space) * 2);
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    font-weight: 500;
  }

  .bookings-empty {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Карточка ожидающей заявки: тёплый акцент слева отделяет «ждёт решения»
     от остального интерфейса; действия прижаты вправо. */
  .booking-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: calc(var(--space) * 4);
    padding: calc(var(--space) * 5) calc(var(--space) * 5)
             calc(var(--space) * 5) calc(var(--space) * 5 + 3px);
    border: 1px solid var(--border);
    border-left: 4px solid var(--brand);
    border-radius: var(--radius-lg);
    background: var(--card);
    box-shadow: var(--shadow-soft);
  }

  .booking-card__body {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    min-width: 0;
  }

  .booking-card__time {
    margin: 0 0 calc(var(--space) * 2);
    font-size: var(--text-xl);
    font-weight: 600;
    color: var(--brand);
    font-variant-numeric: tabular-nums;
  }

  .booking-card__details {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1.5);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .booking-card__field {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    font-size: var(--text-sm);
    color: var(--foreground);
  }

  .booking-card__field [data-lucide] {
    width: 16px;
    height: 16px;
    color: var(--muted-foreground);
    flex-shrink: 0;
  }

  .booking-card__actions {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
  }

  /* Строка истории: чип статуса, кто и когда, справа — дата решения. */
  .booking-history__row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 3) calc(var(--space) * 2);
    border-bottom: 1px solid var(--border);
  }

  .booking-history__row:last-of-type {
    border-bottom: none;
  }

  .booking-history__main {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
    min-width: 0;
  }

  .booking-history__name {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--foreground);
  }

  .booking-history__meta {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .booking-history__cancel {
    margin-left: auto;
  }

  .booking-history__date {
    margin-left: auto;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
    white-space: nowrap;
  }

  .booking-history__cancel + .booking-history__date {
    margin-left: 0;
  }

  /* ==========================================================================
     Задачи (спека task-management, страница /tutor/tasks)
     ========================================================================== */

  /* Панель действий в заголовке страницы: вид, фильтр и «Новая задача». */
  .tasks-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 2);
  }

  .tasks-toolbar .filters {
    width: auto;
  }

  /* Доска: четыре колонки по этапам; на телефоне — одна. */
  .task-board {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    @media (min-width: 1100px) {
      grid-template-columns: repeat(4, minmax(0, 1fr));
    }
  }

  .task-column {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
  }

  .task-column__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  .task-column__title {
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
  }

  .task-column__count {
    display: inline-flex;
    align-items: center;
    min-height: 22px;
    padding: 0 calc(var(--space) * 2);
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
  }

  .task-column__body {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  /* Колонка под курсором при перетаскивании карточки: пунктир в
     фирменном тоне — видно, куда упадёт карточка. */
  .task-column__body--drop {
    outline: 2px dashed color-mix(in srgb, var(--brand) 55%, transparent);
    outline-offset: 2px;
    border-radius: var(--radius-md);
    background: color-mix(in srgb, var(--brand) 6%, transparent);
  }

  /* Пустая колонка: пунктирная рамка и иконка — видно, что доска жива,
     а не сломалась. */
  .task-column__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: calc(var(--space));
    min-height: 96px;
    border: 1px dashed var(--border);
    border-radius: var(--radius-md);
    color: var(--muted-foreground);
    font-size: var(--text-sm);

    & [data-lucide] {
      width: 20px;
      height: 20px;
    }
  }

  /* Список: блоки «Сегодня», «Предстоящие», «Без срока», «Завершено». */
  .task-list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 6);
  }

  .task-group {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  .task-group__head {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
  }

  .task-group__title {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--foreground);
  }

  .task-group__count {
    display: inline-flex;
    align-items: center;
    min-height: 22px;
    padding: 0 calc(var(--space) * 2);
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
  }

  .task-group__body {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
  }

  .task-group__empty {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Карточка задачи: кликабельна целиком (открывает диалог правки). */
  .task-card {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--card);
    color: var(--card-foreground);
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease),
                box-shadow var(--duration-fast) var(--ease);

    &:hover {
      border-color: color-mix(in srgb, var(--brand) 40%, var(--border));
      box-shadow: var(--shadow-hover);
    }

    &:focus-visible {
      outline: 2px solid var(--ring);
      outline-offset: 2px;
    }
  }

  /* Просроченная задача: левая полоса красного тона. */
  .task-card--overdue {
    border-left: 3px solid var(--destructive);
  }

  /* Карточку можно схватить мышью — это перенос на другой этап. */
  .task-card[draggable="true"] {
    cursor: grab;
  }

  /* Тащим сейчас: приглушена, чтобы не закрывать цель переноса. */
  .task-card--dragging {
    opacity: 0.45;
  }

  .task-card__top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  .task-card__title {
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--foreground);
    overflow-wrap: anywhere;
  }

  .task-card__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--space) * 2);
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .task-card__priority {
    display: inline-flex;
    align-items: center;
    min-height: 20px;
    padding: 0 calc(var(--space) * 1.5);
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);
    font-weight: 500;
  }

  .task-card__priority--low {
    background: var(--stat-green);
    color: var(--stat-green-fg);
  }

  .task-card__priority--medium {
    background: var(--stat-orange);
    color: var(--stat-orange-fg);
  }

  .task-card__priority--high {
    background: var(--status-cancelled);
    color: var(--status-cancelled-fg);
  }

  .task-card__due,
  .task-card__students {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space));
    min-width: 0;

    & [data-lucide] {
      width: 13px;
      height: 13px;
      flex-shrink: 0;
    }
  }

  .task-card__due--overdue {
    color: var(--destructive);
    font-weight: 500;
  }

  .task-card__students {
    overflow-wrap: anywhere;
  }

  /* Меню «⋯» карточки: кнопка, список и опасный пункт (паттерн
     `.group-card__menu`). */
  .task-card__menu {
    position: relative;
    flex-shrink: 0;
  }

  .task-card__menu-list {
    position: absolute;
    top: calc(100% + var(--space));
    right: 0;
    z-index: 5;
    display: flex;
    flex-direction: column;
    min-width: 168px;
    padding: calc(var(--space));
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--popover);
    color: var(--popover-foreground);
    box-shadow: var(--shadow-popover);
  }

  .task-card__menu-item {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 2);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    font: inherit;
    font-size: var(--text-sm);
    color: inherit;
    text-align: left;
    cursor: pointer;

    &:hover { background: var(--accent); }

    & [data-lucide] {
      width: 15px;
      height: 15px;
      color: var(--muted-foreground);
    }
  }

  .task-card__menu-item--danger {
    color: var(--destructive);

    & [data-lucide] { color: currentColor; }
  }

  /* Диалог задачи: «Приоритет» и «Срок» в одну строку, на узком — столбиком. */
  .task-form__row {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 3);

    @media (min-width: 480px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  /* Блок учеников в форме — на всю ширину секции. */
  .form-section--tasks {
    grid-column: 1 / -1;
  }

  .task-students {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 2);
    grid-column: 1 / -1;
  }

  .task-students__empty {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .task-students__list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1.5);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .task-students__row {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 1.5) calc(var(--space) * 2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
  }

  .task-students__name {
    flex: 1;
    min-width: 0;
    font-size: var(--text-sm);
    overflow-wrap: anywhere;
  }

  /* ==========================================================================
     Тап-таргеты на мобиле (WCAG 2.5.5 — цель 44×44)
     ==========================================================================
     Палец крупнее курсора: 36px-кнопки и 32px-пилюли на телефоне — это
     промах, а промах по крестику баннера вместо «Открыть» закрывает
     напоминание о списании. На десктопе плотность важнее размера цели,
     поэтому поднимаем только в мобильной раскладке — по той же границе
     (1023.98px), по которой сайдбар уезжает в off-canvas. */
  @media (max-width: 1023.98px) {
    /* `.btn--icon` и `.btn--sm` задают фиксированные 32px, но `min-*`
       сильнее конкретной ширины/высоты: цель растёт до квадрата. */
    .btn,
    .btn--sm,
    .tab,
    .group-card__menu-item,
    .task-card__menu-item {
      min-height: 44px;
      min-width: 44px;
    }

    /* Пункты сайдбара: 40px хватало курсору, но палец в off-canvas меню
       попадает между пунктами и открывает соседний раздел. */
    .nav-item {
      min-height: 44px;
    }

    /* Строка пользователя в футере сайдбара — тоже цель: тап по аватару
       ведёт в профиль, промах — на «Выйти» рядом. */
    .user-row {
      min-height: 44px;
    }

    /* Селекты и текстовые вводы — тот же палец; `textarea.input` не
       трогаем: его высоту задает `rows`, а правило специфичнее. */
    .input:not(textarea) {
      height: 44px;
    }

    .back-link {
      min-height: 44px;
    }

    /* Заголовок-сортировка: 26px высоты — промах по стрелке. Ссылка
       растягивается по ячейке, но не ниже 44px; отступы ячейки при этом
       не схлопываются — шапка на мобиле выше, зато попадание верное. */
    .lessons-table__sort {
      min-height: 44px;
    }

    /* Чекбокс выбора строки: сам квадрат 16px, но цель пальца — 44×44.
       На десктопе цель компактная: иначе шапка таблицы вырастала с 42 до
       60px из-за одной ячейки выбора. */
    .lessons-select-hit {
      width: 44px;
      height: 44px;
      margin: calc(var(--space) * -1);
    }

    /* Крестик баннера: цель 44×44, но визуально плашка не растёт —
       отрицательные отступы забирают добавленные пиксели обратно. */
    .notice-banner__close {
      width: 44px;
      height: 44px;
      margin: calc(var(--space) * -2) calc(var(--space) * -2) calc(var(--space) * -2) 0;
    }

    /* Кружок сворачивания сайдбара вылезает за край панели на 12px.
       Скрытая панель оставляла бы на экране 11px этой цели — слепок
       без действия; увеличиваем цель и прячем её вместе с панелью. */
    .sidebar__toggle {
      width: 44px;
      height: 44px;
      right: -22px;
    }

    .sidebar:not(.is-open) .sidebar__toggle {
      display: none;
    }
  }

  /* ==================================================================
     Рассылки (/tutor/mailings): вкладки, список получателей, композер,
     модалки шаблонов, история бота.
     ================================================================== */

  /* Вкладки-пилюли (как view-toggle задач): ссылка без JS, htmx с JS. */
  .mailing-tabs {
    display: inline-flex;
    gap: calc(var(--space));
    margin-bottom: calc(var(--space) * 4);
  }

  .mailing-tabs__item {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space));
    min-height: 36px;
    padding: 0 calc(var(--space) * 3);
    border-radius: var(--radius-full);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--muted-foreground);
    text-decoration: none;
    transition: background-color var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);

    &:hover { color: var(--foreground); }
  }

  .mailing-tabs__item--active {
    background: var(--background);
    color: var(--foreground);
    box-shadow: var(--shadow-card);
  }

  /* Сетка «список + композер»: на мобиле колонки в стек. */
  .mailing-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 4);
    align-items: start;

    @media (min-width: 1024px) {
      grid-template-columns: minmax(0, 1.6fr) minmax(320px, 1fr);
    }
  }

  /* Корень партиала списка: сам себя перерисовывает (фильтры, отправка),
     поэтому идёт прямой колонкой сетки формы. */
  .mailing-recipients {
    display: grid;
    gap: calc(var(--space) * 4);
    min-width: 0;
  }

  /* Фильтры получателей. */
  .mailing-filters {
    display: grid;
    gap: calc(var(--space) * 3);
  }

  .mailing-filters__title {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    font-size: var(--text-base);
    font-weight: 600;

    & svg { width: 18px; height: 18px; }
  }

  .mailing-filters__search { width: 100%; }

  .mailing-filters__segments {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
  }

  .mailing-chip {
    display: inline-flex;
    align-items: center;
    min-height: 30px;
    padding: 0 calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    font-size: var(--text-sm);
    color: var(--secondary-foreground);
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease),
                border-color var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);

    &:hover {
      border-color: var(--brand);
      color: var(--brand);
    }
  }

  .mailing-chip--active {
    background: var(--brand-soft);
    border-color: var(--brand);
    color: var(--brand);
  }

  /* Таблица получателей. */
  .mailing-list__header {
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border-bottom: 1px solid var(--border);
  }

  .mailing-list__counter {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--foreground);
  }

  .mailing-list__empty { padding: calc(var(--space) * 6) calc(var(--space) * 4); }

  .mailing-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
  }

  .mailing-table th {
    padding: calc(var(--space) * 2) calc(var(--space) * 4);
    text-align: left;
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--muted-foreground);
    border-bottom: 1px solid var(--border);
  }

  .mailing-table td {
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    vertical-align: middle;
    border-bottom: 1px solid var(--border);
  }

  .mailing-table tbody tr:last-child td { border-bottom: 0; }

  .mailing-table__check {
    width: 44px;
    text-align: center;
  }

  /* Ученик без каналов: строка приглушена — отправить ему всё равно некуда. */
  .mailing-table__row--disabled {
    color: var(--muted-foreground);
    opacity: 0.6;
  }

  .mailing-table__name { font-weight: 500; }

  .mailing-table__next {
    margin-top: calc(var(--space));
    font-size: var(--text-xs);
    color: var(--success);
  }

  /* Метки каналов: подключён — акцентная, нет — глухая. */
  .mailing-channels {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space));
  }

  .mailing-channels__pill {
    display: inline-flex;
    align-items: center;
    min-height: 24px;
    padding: 0 calc(var(--space) * 2.5);
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    font-weight: 500;
  }

  .mailing-channels__pill--on {
    background: var(--brand-soft);
    color: var(--brand);
  }

  /* Панель «Текст сообщения». */
  .mailing-composer {
    display: grid;
    gap: calc(var(--space) * 3);
  }

  .mailing-composer__title {
    font-size: var(--text-base);
    font-weight: 600;
  }

  .mailing-composer__counter {
    margin-top: calc(var(--space) * -1);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .mailing-composer__summary {
    padding: calc(var(--space) * 2) calc(var(--space) * 3);
    border-radius: var(--radius-md, 8px);
    background: var(--brand-soft);
    color: var(--brand);
    font-size: var(--text-sm);
  }

  .mailing-composer__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
  }

  .mailing-composer__template { max-width: 60%; }

  .mailing-composer__save {
    flex-shrink: 0;
    white-space: nowrap;
  }

  .mailing-composer__templates { justify-self: start; }

  .mailing-composer__text {
    width: 100%;
    resize: vertical;
    font-family: inherit;
  }

  .mailing-composer__hint {
    font-size: var(--text-xs);
    line-height: 1.6;
    color: var(--muted-foreground);
  }

  /* Кнопки-облочки подстановок под полем текста. */
  .mailing-tokens {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space));
    margin-top: calc(var(--space) * -1);
  }

  .mailing-tokens__btn {
    min-height: 26px;
    padding: 0 calc(var(--space) * 2);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--secondary-foreground);
    font-family: "Geist Mono", monospace;
    font-size: var(--text-xs);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease),
                border-color var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);

    &:hover {
      border-color: var(--brand);
      color: var(--brand);
    }
  }

  .mailing-composer__submit { width: 100%; }

  /* Модалка «Шаблоны сообщений». */
  .mailing-templates__list {
    display: grid;
    gap: calc(var(--space) * 3);
  }

  .mailing-templates__item {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 3);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg, 12px);
  }

  .mailing-templates__meta {
    min-width: 0;
  }

  .mailing-templates__name {
    font-size: var(--text-sm);
    font-weight: 600;
  }

  .mailing-templates__text {
    margin-top: calc(var(--space));
    font-size: var(--text-sm);
    color: var(--muted-foreground);
    overflow-wrap: anywhere;
  }

  .mailing-templates__actions {
    display: flex;
    gap: calc(var(--space));
    flex-shrink: 0;
  }

  .mailing-templates__delete { color: var(--destructive); }

  .mailing-templates__empty {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  /* Вкладка «История». */
  .mailing-history { padding: 0; }

  .mailing-history__header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: calc(var(--space) * 2);
    padding: calc(var(--space) * 4);
    border-bottom: 1px solid var(--border);
  }

  .mailing-history__title {
    font-size: var(--text-base);
    font-weight: 600;
  }

  .mailing-history__note {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .mailing-history__scroll { overflow-x: auto; }

  .mailing-history__table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
  }

  .mailing-history__table th {
    padding: calc(var(--space) * 2) calc(var(--space) * 4);
    text-align: left;
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--muted-foreground);
    border-bottom: 1px solid var(--border);
  }

  .mailing-history__table td {
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    vertical-align: top;
    border-bottom: 1px solid var(--border);
  }

  .mailing-history__table tbody tr:last-child td { border-bottom: 0; }

  .mailing-history__row { background: none; }

  .mailing-history__date {
    white-space: nowrap;
    color: var(--muted-foreground);
  }

  .mailing-history__text {
    max-width: 420px;
    overflow-wrap: anywhere;
    white-space: pre-line;
  }

  .mailing-history__status {
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--success);
  }

  .mailing-history__status--error { color: var(--destructive); }

  .mailing-history__error {
    margin-top: calc(var(--space));
    font-size: var(--text-xs);
    color: var(--destructive);
  }

  .mailing-history__empty { padding: calc(var(--space) * 6) calc(var(--space) * 4); }
}

  /* Личный кабинет ученика.
     Страницы входа и регистрации — центрированная карточка (как auth у
     репетитора); сам кабинет после входа построен на CRM-каркасе:
     `cabinet/_sidebar.html` переиспользует классы `.sidebar*`/`.nav-item`,
     контент — `.main`/`.page-header`/`.card`. Своё — только таблица
     «Моих уроков», плашка ближайшего занятия и заглушки меню. */
  .cabinet {
    min-height: 100vh;
    max-width: 480px;
    margin: 0 auto;
    padding: calc(var(--space) * 10) calc(var(--space) * 4) calc(var(--space) * 12);
  }

  .cabinet-auth__header {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);
    margin-bottom: calc(var(--space) * 6);
    color: var(--muted-foreground);
  }

  .cabinet__card {
    padding: calc(var(--space) * 6) calc(var(--space) * 6) calc(var(--space) * 8);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--card);
  }

  .cabinet__title {
    margin: 0 0 calc(var(--space) * 5);
    font-size: var(--text-xl);
    font-weight: var(--font-semibold);
  }

  .cabinet__form {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);
  }

  .cabinet__hint {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  /* Ближайшее занятие: карточка с парой «подпись — дата и время». */
  .cabinet-nearest {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
    margin-bottom: calc(var(--space) * 5);
  }

  .cabinet-nearest__label {
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .cabinet-nearest__when {
    font-size: var(--text-lg);
    font-weight: var(--font-medium);
    font-variant-numeric: tabular-nums;
  }

  /* Таблица «Моих уроков»: как таблицы CRM — компактные строки,
     цифры табличные, тип занятия приглушён. */
  .cabinet-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
  }

  .cabinet-table th {
    padding: calc(var(--space) * 2) calc(var(--space) * 3);
    border-bottom: 1px solid var(--border);
    text-align: left;
    font-size: var(--text-xs);
    font-weight: var(--font-medium);
    color: var(--muted-foreground);
  }

  .cabinet-table td {
    padding: calc(var(--space) * 3);
    border-bottom: 1px solid var(--border);
  }

  .cabinet-table tbody tr:last-child td {
    border-bottom: none;
  }

  .cabinet-table__date {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  .cabinet-table__time {
    color: var(--muted-foreground);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  .cabinet-table__type {
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    text-align: right;
  }

  .cabinet-empty {
    margin: calc(var(--space) * 2) 0;
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  /* ---- Кабинет родителя (change parent-cabinet): те же токены, что у
     CRM; сайдбар переиспользует `.sidebar*`, таблицы — `.cabinet-table`. ---- */

  /* Главная страница: карточки-секции по детям и общий пункт. */
  .parent-overview {
    display: grid;
    grid-template-columns: 1fr;
    gap: calc(var(--space) * 3);

    @media (min-width: 640px) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  .parent-overview__child {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  .parent-overview__name {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: var(--font-medium);
  }

  .parent-overview__links {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--space) * 2);
  }

  /* Вкладка «Финансы»: общий итог сверху и метрики по ребёнку. */
  .parent-finance__total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    margin-bottom: calc(var(--space) * 4);
  }

  .parent-finance__total-label {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  .parent-finance__total-value {
    font-size: var(--text-xl);
    font-weight: var(--font-semibold);
  }

  .parent-finance__child {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 4);
    margin-bottom: calc(var(--space) * 4);
  }

  .parent-finance__name {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: var(--font-medium);
  }

  .parent-finance__cards {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: calc(var(--space) * 3);
  }

  .parent-finance__metric {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 1);
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--muted);
  }

  .parent-finance__metric-label {
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  .parent-finance__metric-value {
    font-weight: var(--font-medium);
  }

  /* Вкладка «Профиль»: просмотр данных контакта (design D9). */
  .parent-profile__list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    margin: 0 0 calc(var(--space) * 3);
  }

  .parent-profile__row {
    display: flex;
    align-items: baseline;
    gap: calc(var(--space) * 3);
  }

  .parent-profile__term {
    flex: none;
    width: 9rem;
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  .parent-profile__value {
    margin: 0;
    word-break: break-word;
  }

  /* Заглушка пункта меню кабинета (разделы будущих change'ов): видна,
     но не интерактивна — без ховера и курсора ссылки. */
  .nav-item--disabled {
    cursor: default;
    opacity: 0.55;
  }

  .nav-item--disabled:hover {
    background: transparent;
    color: var(--muted-foreground);
  }

  /* Вкладка «Домашние задания» кабинета (change cabinet-homework):
     список карточек на нативном `<details>` — раскрытие работает и без
     JS, chevron поворачивается на [open]. Форма ответа — HTMX-фрагмент,
     целящийся в свою карточку; цвета только через токены темы. */
  .hw-list {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  .hw-card {
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--card);
    color: var(--card-foreground);
  }

  /* Раскрытая карточка выделена акцентом: видно, какую работу сдают. */
  .hw-card[open] {
    border-color: var(--brand);
  }

  .hw-card__summary {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 4) calc(var(--space) * 5);
    cursor: pointer;
    list-style: none;
  }

  .hw-card__summary::-webkit-details-marker { display: none; }

  .hw-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-full);
    background: var(--muted);
    color: var(--muted-foreground);

    & svg { width: 18px; height: 18px; }
  }

  .hw-card__title {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: var(--font-medium);
  }

  .hw-card__side {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    flex-shrink: 0;
  }

  .hw-card__chevron {
    width: 16px;
    height: 16px;
    color: var(--muted-foreground);
    transition: transform var(--duration-fast) var(--ease);
  }

  .hw-card[open] .hw-card__chevron { transform: rotate(180deg); }

  /* Шапка карточки без раскрытия (кабинет родителя): та же строка, что
     у summary вкладки ученика, но без курсора и chevron — родитель
     смотрит, а не управляет (design D7 change'а parent-cabinet). */
  .hw-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--space) * 3);
    padding: calc(var(--space) * 4) calc(var(--space) * 5) calc(var(--space) * 3);
  }

  .hw-card__head .hw-card__title {
    overflow: visible;
    white-space: normal;
  }

  .hw-card__body {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
    padding: 0 calc(var(--space) * 5) calc(var(--space) * 5);
  }

  /* Задание пишут в textarea: переносы строки — часть текста. */
  .hw-card__assignment {
    margin: 0;
    white-space: pre-line;
  }

  .hw-card__due {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin: 0;
    color: var(--muted-foreground);
    font-size: var(--text-sm);

    & svg { width: 16px; height: 16px; }
  }

  .hw-card__recording {
    margin: 0;
    font-size: var(--text-sm);
  }

  .hw-card__comment {
    margin: 0;
    padding: calc(var(--space) * 3) calc(var(--space) * 4);
    border-radius: var(--radius-md);
    background: var(--muted);
    font-size: var(--text-sm);
    white-space: pre-line;
  }

  .hw-card__comment-label { font-weight: var(--font-medium); }

  .hw-card__divider {
    border: none;
    border-top: 1px solid var(--border);
    margin: calc(var(--space) * 1) 0;
  }

  .hw-card__actions {
    display: flex;
    justify-content: flex-end;
    margin-top: calc(var(--space) * 2);
  }

  .hw-answer {
    display: flex;
    flex-direction: column;
    gap: calc(var(--space) * 3);
  }

  .hw-answer--readonly {
    gap: calc(var(--space) * 2);
  }

  .hw-answer__head {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin: 0;
    font-weight: var(--font-medium);

    & svg { width: 16px; height: 16px; color: var(--muted-foreground); }
  }

  .hw-answer__done {
    margin: 0;
    color: var(--success);
    font-size: var(--text-sm);
  }

  .hw-answer__label {
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .hw-answer__text {
    margin: 0;
    font-size: var(--text-sm);
    white-space: pre-line;
  }

  .hw-answer__empty {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }

  .hw-links {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: calc(var(--space) * 2);
  }

  /* Поля ссылок на всю ширину карточки, кнопка — по содержимому. */
  .hw-links .input { width: 100%; }

  .hw-links__head {
    display: flex;
    align-items: center;
    gap: calc(var(--space) * 2);
    margin: 0;
    font-weight: var(--font-medium);

    & svg { width: 16px; height: 16px; color: var(--muted-foreground); }
  }

  .hw-links__hint {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .hw-links__add { margin-top: calc(var(--space) * 1); }

  .hw-links__list {
    margin: 0;
    padding-left: calc(var(--space) * 5);
    font-size: var(--text-sm);
    word-break: break-all;
  }

  /* Статусы ДЗ в кабинете: «Задано» — нейтральное, «Сдано» — работа
     ушла репетитору, «Проверено» — финальное зелёное, «Не выполнено» —
     тревожное красное. */
  .chip--hw-assigned {
    background: var(--muted);
    color: var(--secondary-foreground);
  }

  .chip--hw-submitted {
    background: var(--status-unplanned);
    color: var(--status-unplanned-fg);
  }

  .chip--hw-checked {
    background: var(--success);
    color: var(--success-foreground);
  }

  .chip--hw-not-done {
    background: var(--status-cancelled);
    color: var(--status-cancelled-fg);
  }

@layer utilities {
  /* Утилиты — минимальный набор */
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  .truncate {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .flex { display: flex; }
  .items-center { align-items: center; }
  .flex-wrap { flex-wrap: wrap; }
  .gap-2 { gap: calc(var(--space) * 2); }
  .mt-4 { margin-top: calc(var(--space) * 4); }
  .mt-5 { margin-top: calc(var(--space) * 5); }
  .hidden { display: none; }

  /* Дополнительные утилиты раскладки */
  .grow { flex-grow: 1; }
  .flex-1 { flex: 1; min-width: 0; }
  .justify-between { justify-content: space-between; }
  .span-2 { grid-column: span 2; }

  /* Скрыть на телефоне: второстепенные действия не занимают экран */
  @media (max-width: 639.98px) {
    .hide-sm { display: none; }
  }

  /* Показать только на телефоне: кнопка раскрытия панели фильтров.
     На десктопе панель видна всегда, и кнопка была бы пустым действием. */
  .show-sm { display: none; }

  @media (max-width: 639.98px) {
    .show-sm { display: inline-flex; }
  }
}

/* Alpine: скрыть до инициализации, иначе модалка мигнёт при загрузке.
   Правило вне @layer: это не утилита и не компонент, а служебная директива. */
[x-cloak] { display: none !important; }
