/*
 * Church Service Planner — Application Styles
 *
 * All visual styling lives here. No inline styles in Java views.
 * Design system: Seline Analytics — см. блок токенов ниже и docs/style-guide.md.
 * Светлая и тёмная темы задаются токенами --sl-*; ниже блока токенов
 * не должно быть ни одного hex-значения (проверяется scripts/check-contrast.py).
 *
 * Naming convention:
 *   .view-*       — top-level view containers
 *   .header-*     — page header sections
 *   .form-*       — form elements
 *   .grid-*       — grid customisations
 *   .status-*     — status badges / indicators
 */
/* ═══════════════════════════════════════════════════════════════════════════
   ДИЗАЙН-СИСТЕМА — Seline Analytics, адаптированная под проект
   Источник: https://styles.refero.design/style/7967c6d9-e50c-42b5-b4d1-74003ba41781
   («quiet analyst's desk on warm paper»)

   Один слой токенов --sl-* обслуживает и веб-портал (через --lumo-*),
   и Telegram MiniApp (через --ma-*). Ничего не хардкодим ниже этого блока.

   Три осознанных отклонения от опубликованной Seline — все по WCAG,
   проверяются скриптом scripts/check-contrast.py:

   1. Ash Gray #a8a29e даёт 2.52:1 на белом. У Seline это helper-текст,
      у нас — ТОЛЬКО disabled-состояния (их WCAG не покрывает).
      Helper-текст живёт на --sl-text-body #78716c (4.80:1).
   2. Два токена акцента вместо одного: --sl-accent и --sl-accent-on
      (текст поверх акцентной заливки). В тёмной теме белый на #5B95E5
      даёт 3.05:1 — не проходит, поэтому там подпись тёмная (6.47:1).
   3. Бордер разделён надвое: --sl-border — декоративные разделители
      строк (на них построена вся Seline), --sl-border-control — обводка
      инпутов и чекбоксов, где WCAG 1.4.11 требует 3:1.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Inter, self-host ────────────────────────────────────────────────────────
   Google Fonts CDN НЕ используем: приход в Германии, LG München 3 O 17493/20
   признал динамическую вставку Google Fonts передачей IP в США без согласия.
   Файлы: src/main/resources/META-INF/resources/fonts/, отдаются как /fonts/*.
   Благодаря unicode-range немецкоязычный пользователь грузит только latin.
   ─────────────────────────────────────────────────────────────────────────── */

@font-face {
    font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap;
    src: url('../fonts/inter-cyrillic-ext-400-normal.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap;
    src: url('../fonts/inter-cyrillic-400-normal.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap;
    src: url('../fonts/inter-latin-400-normal.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                   U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
                   U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap;
    src: url('../fonts/inter-cyrillic-ext-500-normal.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap;
    src: url('../fonts/inter-cyrillic-500-normal.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap;
    src: url('../fonts/inter-latin-500-normal.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                   U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
                   U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap;
    src: url('../fonts/inter-cyrillic-ext-600-normal.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
    font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap;
    src: url('../fonts/inter-cyrillic-600-normal.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap;
    src: url('../fonts/inter-latin-600-normal.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                   U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
                   U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ── Токены: светлая тема ─────────────────────────────────────────────────── */

:root {
    /* Поверхности — тёплая stone-шкала Seline */
    --sl-canvas:          #fafaf9;   /* фон страницы */
    --sl-surface:         #ffffff;   /* карточки, гриды, оверлеи */
    --sl-surface-sunken:  #f5f5f4;   /* zebra, вложенные панели */

    /* Границы */
    --sl-border:          #e8e6e5;   /* разделители строк (декор) */
    --sl-border-strong:   #d6d3d1;   /* frozen-колонка, акцентные рамки */
    --sl-border-control:  #948e8a;   /* обводка инпутов — WCAG 1.4.11, 3.10:1 */

    /* Текст */
    --sl-text:            #0c0a09;   /* заголовки и данные — 19.76:1 */
    --sl-text-body:       #78716c;   /* лейблы, helper, secondary — 4.80:1 */
    --sl-text-muted:      #a8a29e;   /* ТОЛЬКО disabled — 2.52:1 */

    /* Акцент — брендовый синий адвентистского символа */
    --sl-accent:          #1B4F99;   /* 8.00:1 на белом */
    --sl-accent-hover:    #163f7a;
    --sl-accent-on:       #ffffff;   /* подпись поверх акцентной заливки */
    --sl-accent-wash:     rgba(27, 79, 153, 0.10);

    /* Семантика */
    --sl-success:         #15803d;   /* 5.02:1 */
    --sl-warning:         #b45309;   /* 5.02:1 */
    --sl-danger:          #b91c1c;   /* 6.47:1 */
    --sl-success-wash:    rgba(21, 128, 61, 0.12);
    --sl-warning-wash:    rgba(180, 83, 9, 0.12);
    --sl-danger-wash:     rgba(185, 28, 28, 0.12);
    --sl-neutral-wash:    rgba(120, 113, 108, 0.12);

    /* Радиусы — Seline */
    --sl-radius-icon:     4px;
    --sl-radius-input:    6px;
    --sl-radius-card:     10px;
    --sl-radius-card-lg:  16px;
    --sl-radius-pill:     9999px;

    /* Тени — Seline. Иерархию несут бордеры, тени только приподнимают карточку */
    --sl-shadow-hairline: rgba(0, 0, 0, 0.05) 0 1px 2px 0;
    --sl-shadow-sm:       rgba(0, 0, 0, 0.10) 0 4px 6px -1px, rgba(0, 0, 0, 0.10) 0 2px 4px -2px;
    --sl-shadow-md:       rgba(0, 0, 0, 0.05) 0 4px 16px 0;
    --sl-shadow-xl:       rgba(17, 12, 46, 0.12) 0 12px 45px 0;

    /* Типографика */
    --sl-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    --sl-tracking-display: -0.021em;   /* Seline: отрицательный трекинг на крупном */
    --sl-tracking-heading: -0.025em;
    --sl-tracking-body:     0.003em;
}

/* ── Токены: тёмная тема ──────────────────────────────────────────────────────
   Seline тёмную ветку не публикует — выведена из её же stone-шкалы.
   Селектор html[theme~="dark"] (0,1,1) перебивает лумовский [theme~="dark"].
   ─────────────────────────────────────────────────────────────────────────── */

html[theme~="dark"] {
    --sl-canvas:          #0c0a09;
    --sl-surface:         #1c1917;
    --sl-surface-sunken:  #292524;

    --sl-border:          rgba(255, 255, 255, 0.09);
    --sl-border-strong:   rgba(255, 255, 255, 0.16);
    --sl-border-control:  #6a6663;   /* 3.08:1 на surface */

    --sl-text:            #e7e5e4;   /* 13.93:1 */
    --sl-text-body:       #a8a29e;   /* 6.93:1 */
    --sl-text-muted:      #78716c;

    --sl-accent:          #5B95E5;   /* 5.73:1 — светлее #1B4F99, иначе тонет */
    --sl-accent-hover:    #7FAEEC;
    --sl-accent-on:       #0c0a09;   /* белый на #5B95E5 = 3.05:1, не проходит */
    --sl-accent-wash:     rgba(91, 149, 229, 0.16);

    --sl-success:         #4ade80;
    --sl-warning:         #fbbf24;
    --sl-danger:          #f87171;
    --sl-success-wash:    rgba(74, 222, 128, 0.16);
    --sl-warning-wash:    rgba(251, 191, 36, 0.16);
    --sl-danger-wash:     rgba(248, 113, 113, 0.16);
    --sl-neutral-wash:    rgba(168, 162, 158, 0.16);

    --sl-shadow-hairline: rgba(0, 0, 0, 0.30) 0 1px 2px 0;
    --sl-shadow-sm:       rgba(0, 0, 0, 0.40) 0 4px 6px -1px, rgba(0, 0, 0, 0.40) 0 2px 4px -2px;
    --sl-shadow-md:       rgba(0, 0, 0, 0.35) 0 4px 16px 0;
    --sl-shadow-xl:       rgba(0, 0, 0, 0.55) 0 12px 45px 0;
}

/* ── Маппинг на Lumo ──────────────────────────────────────────────────────────
   Java-вью не трогаем: они уже написаны на --lumo-*. Переопределяем сам Lumo.
   ─────────────────────────────────────────────────────────────────────────── */

html {
    --lumo-base-color:              var(--sl-surface);
    --lumo-tint:                    var(--sl-surface);
    --lumo-shade:                   var(--sl-text);

    --lumo-contrast-5pct:           rgba(12, 10, 9, 0.04);
    --lumo-contrast-10pct:          rgba(12, 10, 9, 0.09);
    --lumo-contrast-20pct:          rgba(12, 10, 9, 0.16);
    --lumo-contrast-30pct:          rgba(12, 10, 9, 0.26);
    --lumo-contrast-50pct:          rgba(12, 10, 9, 0.44);
    --lumo-contrast-60pct:          rgba(12, 10, 9, 0.54);
    --lumo-contrast-70pct:          rgba(12, 10, 9, 0.64);
    --lumo-contrast-90pct:          rgba(12, 10, 9, 0.88);
    --lumo-contrast:                var(--sl-text);

    --lumo-header-text-color:       var(--sl-text);
    --lumo-body-text-color:         var(--sl-text);
    --lumo-secondary-text-color:    var(--sl-text-body);
    --lumo-tertiary-text-color:     var(--sl-text-muted);
    --lumo-disabled-text-color:     var(--sl-text-muted);

    --lumo-primary-color:           var(--sl-accent);
    --lumo-primary-text-color:      var(--sl-accent);
    --lumo-primary-contrast-color:  var(--sl-accent-on);
    --lumo-primary-color-50pct:     var(--sl-accent-wash);
    --lumo-primary-color-10pct:     var(--sl-accent-wash);

    --lumo-error-color:             var(--sl-danger);
    --lumo-error-text-color:        var(--sl-danger);
    --lumo-error-contrast-color:    var(--sl-surface);
    --lumo-error-color-50pct:       var(--sl-danger-wash);
    --lumo-error-color-10pct:       var(--sl-danger-wash);

    --lumo-success-color:           var(--sl-success);
    --lumo-success-text-color:      var(--sl-success);
    --lumo-success-contrast-color:  var(--sl-surface);
    --lumo-success-color-50pct:     var(--sl-success-wash);
    --lumo-success-color-10pct:     var(--sl-success-wash);

    --lumo-warning-color:           var(--sl-warning);
    --lumo-warning-text-color:      var(--sl-warning);
    --lumo-warning-color-10pct:     var(--sl-warning-wash);

    /* Радиусы: -m остаётся под инпуты (6px), кнопки получают pill отдельно */
    --lumo-border-radius-s:         var(--sl-radius-icon);
    --lumo-border-radius-m:         var(--sl-radius-input);
    --lumo-border-radius-l:         var(--sl-radius-card);

    --lumo-box-shadow-xs:           var(--sl-shadow-hairline);
    --lumo-box-shadow-s:            var(--sl-shadow-sm);
    --lumo-box-shadow-m:            var(--sl-shadow-md);
    --lumo-box-shadow-l:            var(--sl-shadow-md);
    --lumo-box-shadow-xl:           var(--sl-shadow-xl);

    /* Типографика: база 14px — compact density Seline */
    --lumo-font-family:             var(--sl-font);
    --lumo-font-size-xxs:           0.6875rem;   /* 11px */
    --lumo-font-size-xs:            0.75rem;     /* 12px */
    --lumo-font-size-s:             0.8125rem;   /* 13px */
    --lumo-font-size-m:             0.875rem;    /* 14px — тело Seline */
    --lumo-font-size-l:             1rem;        /* 16px */
    --lumo-font-size-xl:            1.25rem;     /* 20px */
    --lumo-font-size-xxl:           1.5rem;      /* 24px */
    --lumo-font-size-xxxl:          2rem;        /* 32px */
    --lumo-line-height-xs:          1.25;
    --lumo-line-height-s:           1.4;
    --lumo-line-height-m:           1.64;        /* Seline body */

    /* Отступы: 4px-сетка, compact */
    --lumo-space-xs:                4px;
    --lumo-space-s:                 8px;
    --lumo-space-m:                 12px;
    --lumo-space-l:                 20px;
    --lumo-space-xl:                32px;
}

/* ── Тёмная ветка ─────────────────────────────────────────────────────────────
   Здесь повторяется ВЕСЬ маппинг из блока html выше, а не только контрастная
   шкала. Причина — специфичность: Vaadin объявляет тёмную палитру правилом
   `:host([theme~="dark"]), [theme~="dark"]` (0,1,0), которое перебивает наш
   `html` (0,0,1). Пока переменные жили только в html, тёмная тема брала из
   Seline один --lumo-font-family, а остальные 25 — из штатного Lumo: фон
   #233348 вместо #1c1917, акцент #0c6ce9 вместо #5B95E5 и, что хуже всего,
   --lumo-primary-contrast-color #fff вместо --sl-accent-on (белое на нашем
   акценте даёт 3.05:1 — ровно та причина, по которой --sl-accent-on и заведён).

   Селектор с удвоенным атрибутом даёт (0,2,0) и выигрывает у Lumo. Удвоение,
   а не `html[theme~="dark"]` (0,1,1), потому что Vaadin ставит theme="dark"
   на ТРИ элемента — html, body и vaadin-app-layout — и правило Lumo
   срабатывает на каждом заново, объявляя палитру локально. Поправка только на
   html чинит html, а body и всё под ним продолжают наследовать лумовское.

   При правке маппинга выше правь и здесь: значения ссылаются на --sl-*,
   которые сами переключаются по теме, поэтому строки идентичны — расходиться
   они не должны. Проверка: сравнить вычисленные --lumo-* с --sl-* в обеих
   темах, должно сойтись и в светлой, и в тёмной.
   ─────────────────────────────────────────────────────────────────────────── */

[theme~="dark"][theme~="dark"] {
    --lumo-base-color:              var(--sl-surface);
    --lumo-tint:                    var(--sl-surface);
    --lumo-shade:                   var(--sl-text);

    --lumo-contrast-5pct:           rgba(231, 229, 228, 0.05);
    --lumo-contrast-10pct:          rgba(231, 229, 228, 0.10);
    --lumo-contrast-20pct:          rgba(231, 229, 228, 0.18);
    --lumo-contrast-30pct:          rgba(231, 229, 228, 0.28);
    --lumo-contrast-50pct:          rgba(231, 229, 228, 0.48);
    --lumo-contrast-60pct:          rgba(231, 229, 228, 0.58);
    --lumo-contrast-70pct:          rgba(231, 229, 228, 0.68);
    --lumo-contrast-90pct:          rgba(231, 229, 228, 0.90);
    --lumo-contrast:                var(--sl-text);

    --lumo-header-text-color:       var(--sl-text);
    --lumo-body-text-color:         var(--sl-text);
    --lumo-secondary-text-color:    var(--sl-text-body);
    --lumo-tertiary-text-color:     var(--sl-text-muted);
    --lumo-disabled-text-color:     var(--sl-text-muted);

    --lumo-primary-color:           var(--sl-accent);
    --lumo-primary-text-color:      var(--sl-accent);
    --lumo-primary-contrast-color:  var(--sl-accent-on);
    --lumo-primary-color-50pct:     var(--sl-accent-wash);
    --lumo-primary-color-10pct:     var(--sl-accent-wash);

    --lumo-error-color:             var(--sl-danger);
    --lumo-error-text-color:        var(--sl-danger);
    --lumo-error-contrast-color:    var(--sl-canvas);
    --lumo-error-color-50pct:       var(--sl-danger-wash);
    --lumo-error-color-10pct:       var(--sl-danger-wash);

    --lumo-success-color:           var(--sl-success);
    --lumo-success-text-color:      var(--sl-success);
    --lumo-success-contrast-color:  var(--sl-canvas);
    --lumo-success-color-50pct:     var(--sl-success-wash);
    --lumo-success-color-10pct:     var(--sl-success-wash);

    --lumo-warning-color:           var(--sl-warning);
    --lumo-warning-text-color:      var(--sl-warning);
    --lumo-warning-color-10pct:     var(--sl-warning-wash);
}

/*
 * То же правило Lumo, кроме переменных, ставит на эти элементы
 * `background-color: var(--lumo-base-color)` — то есть подложку карточек, а не
 * холст. Своё `html, body { background: var(--sl-canvas) }` ниже имеет (0,0,1)
 * и проигрывает, поэтому в тёмной теме фон страницы возвращаем отдельно.
 */
html[theme~="dark"][theme~="dark"],
body[theme~="dark"][theme~="dark"] {
    background: var(--sl-canvas);
}

/* Фон страницы — тёплый холст; карточки остаются белыми (--lumo-base-color) */
html, body {
    background: var(--sl-canvas);
    font-family: var(--sl-font);
    letter-spacing: var(--sl-tracking-body);
}

h1 {
    letter-spacing: var(--sl-tracking-display);
    font-weight: 500;   /* Seline: вес не повышаем ради акцента */
}

h2, h3, h4 {
    letter-spacing: var(--sl-tracking-heading);
    font-weight: 500;   /* Seline: вес не повышаем ради акцента */
}

/* ═══════════════════════════════════════════════════════════════════════════
   КОМПОНЕНТЫ — Seline поверх Lumo
   Иерархию несут hairline-бордеры, а не тени. Из Seline НЕ берём
   highlight-span в заголовках и mascot-стикер: это приёмы лендинга,
   в админке они шум.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Кнопки: pill, заливка только у primary ──────────────────────────────── */

vaadin-button {
    border-radius: var(--sl-radius-pill);
    font-weight: 500;
    letter-spacing: var(--sl-tracking-body);
}

/* Вторичная кнопка — контурная, без заливки (Seline: один залитый акцент) */
vaadin-button:not([theme~="primary"]):not([theme~="tertiary"]):not([theme~="tertiary-inline"]):not([theme~="icon"]):not([theme~="menu-bar-button"]) {
    background: transparent;
    border: 1px solid var(--sl-border-control);
    color: var(--sl-text);
}

vaadin-button[theme~="primary"] {
    background: var(--sl-accent);
    color: var(--sl-accent-on);
}

vaadin-button[theme~="primary"]:hover {
    background: var(--sl-accent-hover);
}

/* ── Поля ввода: радиус 6px, обводка --sl-border-control (WCAG 1.4.11) ───── */

html {
    --vaadin-input-field-border-radius: var(--sl-radius-input);
    --vaadin-input-field-border-width: 1px;
    --vaadin-input-field-border-color: var(--sl-border-control);
    --vaadin-input-field-background: var(--sl-surface);
}

/* ── Грид: разделители-волоски, компактная плотность ─────────────────────── */

vaadin-grid {
    --lumo-size-m: 2.25rem;
    border: 1px solid var(--sl-border);
    border-radius: var(--sl-radius-card);
    overflow: hidden;
    background: var(--sl-surface);
}

vaadin-grid::part(header-cell) {
    background: var(--sl-surface-sunken);
    font-weight: 500;
    color: var(--sl-text-body);
    letter-spacing: var(--sl-tracking-body);
}

vaadin-grid::part(row):hover > [part~="body-cell"] {
    background: var(--sl-accent-wash);
}

/* ── Карточки и оверлеи ──────────────────────────────────────────────────── */

.detail-section-card,
.health-table {
    box-shadow: var(--sl-shadow-md);
}

vaadin-dialog-overlay::part(overlay),
vaadin-select-overlay::part(overlay),
vaadin-combo-box-overlay::part(overlay),
vaadin-menu-bar-overlay::part(overlay),
vaadin-context-menu-overlay::part(overlay) {
    border: 1px solid var(--sl-border);
    border-radius: var(--sl-radius-card);
    box-shadow: var(--sl-shadow-xl);
    background: var(--sl-surface);
}

/* ── Бейджи Lumo ─────────────────────────────────────────────────────────── */

vaadin-tabs {
    box-shadow: inset 0 -1px 0 0 var(--sl-border);
}

/* ═══════════════════════════════════════════════════════════════
   Day-of-week selector (WeeklyScheduleField — /admin/backup,
   /admin/schedules).
   The FlowingCode DayOfWeekSelector addon sizes its round day
   buttons for a single character. We show locale-aware two-letter
   abbreviations (Mo/Di/Mi/…, Пн/Вт/Ср/…) — ASCII pairs like the
   English "Mo"/"Tu" happened to still fit the narrow default width,
   but wider glyphs (German, Cyrillic, …) were clipped with an
   ellipsis. Widen the buttons and let the label render in full.
   ═══════════════════════════════════════════════════════════════ */
.fc-days-of-week-selector-button {
    width: auto !important;
    min-width: 2.75em !important;
    padding: 0 0.4em !important;
    box-sizing: border-box !important;
}

.fc-days-of-week-selector-button::part(label) {
    overflow: visible !important;
    text-overflow: unset !important;
    white-space: nowrap !important;
}

/* ═══════════════════════════════════════════════════════════════
   Admin Layout — AppLayout with sidebar navigation
   Based on Vaadin Business App Starter design patterns.
   ═══════════════════════════════════════════════════════════════ */

/*
 * Drawer background: slightly elevated above page for depth.
 */
vaadin-app-layout::part(drawer) {
    background: var(--lumo-base-color);
    border-right: 1px solid var(--lumo-contrast-10pct);
    display: flex;
    flex-direction: column;
    transition: width 0.25s ease;
}

/*
 * Desktop vs. phone — decided by the POINTER, not by the width (Фаза 84).
 *
 * Phone  = touch pointer (pointer: coarse) AND small screen (≤ 767px wide, or ≤ 500px
 *          high = phone in landscape). Drawer is a closed overlay, the ☰ context menu
 *          in the top bar replaces it (WebLayout.buildMobileNavbar).
 * Everything else (mouse/touchpad at ANY window size and ANY zoom, tablets) = desktop:
 *          the drawer is always visible.
 *
 * Why not width: browser zoom shrinks the CSS viewport — 250 % on a 1512px MacBook is a
 * 605px wide viewport. A width breakpoint turned a zoomed desktop into a "phone" and the
 * sidebar vanished (reported 19.09.2026 at 200 → 250 %).
 *
 * Why not Lumo's default: Lumo switches to overlay at
 *     @media (max-width: 800px), (max-height: 600px)
 * and AppLayout closes the drawer on that switch; with no DrawerToggle the menu was
 * gone on low desktop windows (1366×768 minus browser chrome). Styles of the outer
 * document beat :host rules of the shadow root, so the declarations below override Lumo.
 *
 * The two media queries are exact complements — keep every "phone"/"desktop" block in
 * this file on these two queries:
 *   phone:   (pointer: coarse) and (max-width: 767px), (pointer: coarse) and (max-height: 500px)
 *   desktop: not (pointer: coarse), (min-width: 768px) and (min-height: 501px)
 *
 * Drawer width is pinned too: in its media query Lumo widens the drawer to 20em.
 */
vaadin-app-layout {
    --vaadin-app-layout-drawer-overlay: false;
    --vaadin-app-layout-drawer-width: 16em;
}

@media (pointer: coarse) and (max-width: 767px), (pointer: coarse) and (max-height: 500px) {
    vaadin-app-layout {
        --vaadin-app-layout-drawer-overlay: true;
        --vaadin-app-layout-drawer-width: 20em;
    }
}

/*
 * Low desktop windows (small screen or zoomed in): compact brand and footer so the menu
 * items keep the room and the item list scrolls instead of pushing the footer out.
 * Harmless on a phone — there the drawer is never shown.
 */
@media (max-height: 700px) {
    .web-brand-row {
        padding: var(--lumo-space-xs) 0;
        min-height: 0;
    }

    .web-brand-logo {
        width: 40px;
        height: 40px;
    }

    .web-drawer-footer {
        padding: 0 var(--lumo-space-s);
    }

    .web-drawer-footer-layout {
        padding: var(--lumo-space-xs) !important;
        gap: var(--lumo-space-xs) !important;
    }
}

/*
 * web-brand-row: drawer header — logo centered, close button absolute top-right.
 */
.web-brand-row {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: var(--lumo-space-m) 0;
    border-bottom: 1px solid var(--lumo-contrast-10pct);
    min-height: var(--lumo-size-xl);
}

/*
 * web-brand-logo-wrapper: centers the logo horizontally.
 */
.web-brand-logo-wrapper {
    display: flex;
    justify-content: center;
    width: 100%;
}

/*
 * web-brand-logo: the Adventist symbol image.
 */
.web-brand-logo {
    width: 72px;
    height: 72px;
    display: block;
}

/*
 * web-drawer-collapse-btn: collapse/expand button (◀/▶) pinned to top-right of the brand row.
 * Hidden on phones (drawer is never shown there). On a zoomed-in desktop it is the way to
 * win back horizontal space, so it must NOT depend on the width.
 */
.web-drawer-collapse-btn {
    position: absolute;
    top: var(--lumo-space-s);
    right: var(--lumo-space-s);
}

@media (pointer: coarse) and (max-width: 767px), (pointer: coarse) and (max-height: 500px) {
    .web-drawer-collapse-btn { display: none; }
}

/* ── Collapsible sidebar rail — desktop only (any width, any zoom) ─────── */

@media not (pointer: coarse), (min-width: 768px) and (min-height: 501px) {
    /*
     * Shift main content to match the collapsed drawer width (4.5rem).
     */
    vaadin-app-layout.drawer-collapsed {
        --vaadin-app-layout-drawer-offset-left: 4.5rem;
    }

    /*
     * Shrink the drawer to icon-rail width and clip overflow.
     */
    vaadin-app-layout.drawer-collapsed::part(drawer) {
        width: 4.5rem;
        overflow: hidden;
    }

    /*
     * Hide label text: the label is a raw text node, so font-size: 0 is the only
     * way to collapse it.
     *
     * CAVEAT: Lumo icon sizes are em-based (--lumo-icon-size-m: 1.5em), so
     * font-size: 0 collapses the prefix icons too and the rail renders empty.
     * The icons therefore get an explicit rem size back below.
     */
    vaadin-app-layout.drawer-collapsed vaadin-side-nav-item {
        font-size: 0;
    }

    /*
     * Restore icon size inside the zero-font-size item (иконки в свёрнутом режиме).
     * Direct children only — the prefix icon of this item, not of nested sub-items.
     */
    vaadin-app-layout.drawer-collapsed vaadin-side-nav-item > vaadin-icon,
    vaadin-app-layout.drawer-collapsed vaadin-side-nav-item > [slot="prefix"] {
        font-size: var(--lumo-font-size-m);
        width: 1.5rem;
        height: 1.5rem;
        flex: 0 0 auto;
    }

    /*
     * Center prefix icons in the item row; remove horizontal padding.
     */
    vaadin-app-layout.drawer-collapsed vaadin-side-nav-item::part(link) {
        justify-content: center;
        padding-left: 0;
        padding-right: 0;
    }

    /*
     * Hide expandable-group toggle arrow (нет места для стрелки подменю).
     */
    vaadin-app-layout.drawer-collapsed vaadin-side-nav-item::part(toggle-button) {
        display: none;
    }

    /*
     * Hide footer — language, theme and sign-out don't fit in 4.5rem.
     */
    vaadin-app-layout.drawer-collapsed .web-drawer-footer {
        display: none;
    }

    /*
     * Shrink the brand logo to 36px to fit within 4.5rem rail.
     */
    vaadin-app-layout.drawer-collapsed .web-brand-logo {
        width: 36px;
        height: 36px;
    }

    /*
     * Collapsed brand row: logo wrapper takes natural width (not 100%),
     * both logo and collapse button sit centred side-by-side.
     */
    vaadin-app-layout.drawer-collapsed .web-brand-row {
        justify-content: center;
        gap: var(--lumo-space-s);
    }

    vaadin-app-layout.drawer-collapsed .web-brand-logo-wrapper {
        width: auto;
    }

    /*
     * Release absolute positioning so the button participates in flex layout
     * and centres correctly alongside the miniature logo.
     */
    vaadin-app-layout.drawer-collapsed .web-drawer-collapse-btn {
        position: static;
    }
}

/*
 * web-nav-scroller: fills remaining drawer height so footer stays at bottom.
 */
.web-nav-scroller {
    flex: 1;
    min-height: 0;          /* allow shrinking below content height → the list scrolls */
    padding: var(--lumo-space-s);
}

/*
 * web-sidenav: remove default padding, let scroller handle it.
 */
.web-sidenav {
    width: 100%;
}

/*
 * web-drawer-footer: pinned at the bottom of the drawer.
 */
.web-drawer-footer {
    padding: var(--lumo-space-s) var(--lumo-space-m);
    border-top: 1px solid var(--lumo-contrast-10pct);
}

.web-logout-btn {
    width: 100%;
    justify-content: flex-start;
    color: var(--lumo-secondary-text-color);
}

.web-logout-btn:hover {
    color: var(--lumo-error-text-color);
}

/*
 * web-navbar: phone top bar — page title left, ☰ context menu right (Фаза 84).
 * On desktop the whole navbar part is hidden: the drawer is always there, and the grid
 * keeps the vertical space the bar was removed for (d33d1f5a). A hidden navbar has zero
 * height, so AppLayout's navbar offset collapses to 0 as well.
 */
@media not (pointer: coarse), (min-width: 768px) and (min-height: 501px) {
    vaadin-app-layout::part(navbar) {
        display: none;
    }
}

.web-navbar {
    padding: 0 var(--lumo-space-xs) 0 var(--lumo-space-m);
    gap: var(--lumo-space-s);
}

.web-navbar-title {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--lumo-font-size-l);
    font-weight: 600;
}

/*
 * web-mobile-menu-item: icon + label row inside the mobile ContextMenu.
 * --active marks the current page (and the «Einstellungen» parent of a settings page).
 */
.web-mobile-menu-item {
    display: flex;
    align-items: center;
    gap: var(--lumo-space-s);
}

.web-mobile-menu-item > vaadin-icon {
    width: var(--lumo-icon-size-s);
    height: var(--lumo-icon-size-s);
    flex: 0 0 auto;
    color: var(--lumo-secondary-text-color);
}

.web-mobile-menu-item--active {
    font-weight: 600;
    color: var(--lumo-primary-text-color);
}

.web-mobile-menu-item--active > vaadin-icon {
    color: var(--lumo-primary-text-color);
}

/*
 * web-user-label: signed-in admin name on the right side of the navbar.
 */
.web-user-label {
    white-space: nowrap;
}

/*
 * Dark theme overrides — Lumo dark palette handles most colours automatically.
 * These tweaks ensure the drawer and brand area look polished in dark mode.
 */
[theme~="dark"] vaadin-app-layout::part(drawer) {
    background: var(--lumo-base-color);
    border-right-color: var(--lumo-contrast-10pct);
}

/* ── Form ──────────────────────────────────────────────────── */

/*
 * form-add-btn: primary submit button in add-forms.
 * Aligns vertically with labelled fields when placed in a HorizontalLayout
 * with Alignment.END. Margin matches Lumo field label height.
 */
.form-add-btn {
    margin-top: var(--lumo-space-l);
}

/* ── Schedule grid (EventOccurrenceView) ────────────────────── */

/*
 * Row part "conflict": two non-cancelled occurrences share the same room
 * and have overlapping time on the same date.
 * Uses ::part() to reach inside the Vaadin Grid Shadow DOM.
 */
vaadin-grid::part(row conflict) {
    background-color: var(--lumo-error-color-10pct);
    color: var(--lumo-error-text-color);
}

/*
 * Row part "cancelled": occurrence has been manually cancelled by admin.
 */
vaadin-grid::part(row cancelled) {
    opacity: 0.5;
    text-decoration: line-through;
}

/* Разделители колонок */
vaadin-grid::part(body-cell),
vaadin-grid::part(header-cell) {
    border-right: 1px solid var(--lumo-contrast-10pct);
}

/* Frozen-колонка — усиленный разделитель */
vaadin-grid::part(first-column-cell) {
    border-right: 2px solid var(--sl-border-strong);
}

/* ═══════════════════════════════════════════════════════════════
   Health dashboard (HealthView) — /admin/health
   Compact zebra-striped tables instead of full-width stretched rows.
   ═══════════════════════════════════════════════════════════════ */

/*
 * health-container: caps overall width so the dashboard reads as a
 * compact panel instead of stretching across the whole page.
 */
.health-container {
    max-width: 520px;
    width: 100%;
}

/*
 * health-table: bordered card wrapping a set of .health-row entries.
 */
.health-table {
    border: 1px solid var(--lumo-contrast-10pct);
    border-radius: var(--lumo-border-radius-l);
    overflow: hidden;
    width: 100%;
}

/*
 * health-row: one line — icon, label (flex-grow), value.
 * Even rows get a subtle background for readability (zebra striping).
 */
.health-row {
    gap: var(--lumo-space-m);
    padding: var(--lumo-space-s) var(--lumo-space-m);
}

.health-row:not(:last-child) {
    border-bottom: 1px solid var(--lumo-contrast-10pct);
}

.health-row:nth-child(even) {
    background: var(--lumo-contrast-5pct);
}

.health-row-label {
    font-size: var(--lumo-font-size-s);
    color: var(--lumo-secondary-text-color);
}

.health-row-value {
    font-size: var(--lumo-font-size-s);
    font-weight: 600;
    color: var(--lumo-body-text-color);
    white-space: nowrap;
}

/*
 * health-row--warn: stale/at-risk metric (e.g. backup older than 48h) —
 * value text switches to the error colour.
 */
.health-row--warn .health-row-value {
    color: var(--lumo-error-text-color);
}

/*
 * health-icon: base sizing for the row's leading icon.
 */
.health-icon {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
}

/* Seline держит один хроматический акцент, поэтому «цветные» имена классов
 * (их ставит HealthView) больше не означают буквальный цвет, а означают роль:
 *   blue   — информационная метрика (участники, рассылка)
 *   orange — требует внимания (ожидают подтверждения, напоминания)
 *   green / red — состояние бэкапа и эскалаций
 *   purple / teal — служебные метрики без состояния; их различает глиф
 *                   иконки, а не цвет (DATABASE / TRASH / DOWNLOAD).
 * Прежняя палитра была Google Material и Seline противоречила. */
.health-icon--blue   { color: var(--sl-accent); }
.health-icon--orange { color: var(--sl-warning); }
.health-icon--green  { color: var(--sl-success); }
.health-icon--red    { color: var(--sl-danger); }
.health-icon--purple { color: var(--sl-text-body); }
.health-icon--teal   { color: var(--sl-text-body); }

/* ═══════════════════════════════════════════════════════════════
   MiniApp (Telegram MiniApp) — Phase 19.3 / 19.4
   All .ma-* classes. Uses Telegram CSS vars with dark fallbacks
   so the app looks native inside Telegram and decent in browser.
   ═══════════════════════════════════════════════════════════════ */

/*
 * MiniApp живёт на тех же токенах Seline, что и веб-портал.
 * --tg-theme-* мы намеренно БОЛЬШЕ НЕ НАСЛЕДУЕМ — обоснование
 * в docs/style-guide.md, раздел «Почему мы ушли с --tg-theme-*».
 *
 * Светлая/тёмная ветка выбирается по Telegram.WebApp.colorScheme:
 * TelegramThemeInitializer ставит на <html> тот же атрибут theme="dark",
 * что и Lumo, поэтому переключение --sl-* работает одним механизмом
 * и для админки, и для MiniApp.
 */
.ma-layout {
    --ma-bg:           var(--sl-canvas);
    --ma-secondary-bg: var(--sl-surface);
    --ma-text:         var(--sl-text);
    --ma-hint:         var(--sl-text-body);
    --ma-link:         var(--sl-accent);
    --ma-btn:          var(--sl-accent);
    --ma-btn-text:     var(--sl-accent-on);
    --ma-primary-text: var(--sl-text);
    --ma-radius:       var(--sl-radius-card);
    --ma-nav-height:   68px;
    --ma-border:       var(--sl-border);

    display: flex;
    flex-direction: column;
    height: 100vh;    /* fallback: Huawei WebView < 108 не поддерживает dvh */
    height: 100dvh;   /* modern: учитывает скрывающийся browser chrome */
    background-color: var(--ma-bg);
    color: var(--ma-text);
    box-sizing: border-box;
}

/*
 * ma-content: scrollable area above the bottom nav.
 */
.ma-content {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

/*
 * ma-bottom-nav: static tab bar at the bottom of the flex layout.
 */
.ma-bottom-nav {
    flex-shrink: 0;
    height: var(--ma-nav-height);
    background: var(--ma-secondary-bg);
    display: flex;
    border-top: 1px solid var(--ma-border);
    z-index: 100;
    /* iOS safe-area support */
    padding-bottom: env(safe-area-inset-bottom, 0px);
}

/*
 * ma-nav-tab: individual bottom nav tab (wraps RouterLink anchor).
 */
.ma-nav-tab {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none !important;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

/* iOS-style: inactive tabs are dimmed */
.ma-nav-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border-radius: var(--sl-radius-card-lg);
    background: var(--sl-neutral-wash);
    color: var(--sl-text-body);
    transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
}

.ma-nav-icon svg {
    width: 26px;
    height: 26px;
    flex-shrink: 0;
}

/* Active tab: full opacity + slight pop */
.ma-nav-tab.ma-active .ma-nav-icon {
    background: var(--sl-accent);
    color: var(--sl-accent-on);
    transform: scale(1.06);
}

.ma-nav-tab:hover .ma-nav-icon {
    background: var(--sl-accent-wash);
    color: var(--sl-accent);
}

/*
 * ma-page-header: section title inside a MiniApp view.
 */
.ma-page-header {
    padding: 16px 16px 4px 16px;
    font-size: 20px;
    font-weight: 700;
    color: var(--ma-text);
    margin: 0;
}

/*
 * ma-card: rounded dark card — the main content block.
 */
.ma-card {
    background: var(--ma-secondary-bg);
    border: 1px solid var(--ma-border);
    border-radius: var(--ma-radius);
    padding: 14px 16px;
    margin: 8px 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/*
 * ma-card-row: horizontal row inside a card (label + value, or action row).
 */
.ma-card-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.ma-card-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--ma-text);
}

.ma-card-sub {
    font-size: 13px;
    color: var(--ma-hint);
}

.ma-card-actions {
    display: flex;
    gap: 8px;
    margin-top: 4px;
    flex-wrap: wrap;
}

/*
 * ma-my-duty: left-border accent for occurrences where the member is assigned.
 */
.ma-my-duty {
    border-left: 3px solid var(--ma-btn);
}

/*
 * Status badges.
 */
.ma-badge {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 20px;
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}

.ma-badge-pending {
    background: var(--sl-warning-wash);
    border: 1px solid var(--sl-warning);
    color: var(--sl-text);
}

.ma-badge-confirmed {
    background: var(--sl-success-wash);
    border: 1px solid var(--sl-success);
    color: var(--sl-text);
}

.ma-badge-declined {
    background: var(--sl-danger-wash);
    border: 1px solid var(--sl-danger);
    color: var(--sl-text);
}

.ma-badge-cancelled {
    background: var(--sl-neutral-wash);
    border: 1px solid var(--sl-border-control);
    color: var(--sl-text-body);
}

/*
 * ma-btn-*: compact action buttons inside cards.
 */
.ma-btn {
    border: none;
    border-radius: 8px;
    padding: 7px 14px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: opacity 0.15s;
    -webkit-tap-highlight-color: transparent;
}

.ma-btn:hover { opacity: 0.85; }

.ma-btn-primary {
    background: var(--ma-btn);
    color: var(--ma-btn-text);
}

.ma-btn-danger {
    background: var(--sl-danger-wash);
    border: 1px solid var(--sl-danger);
    color: var(--sl-danger);
}

.ma-btn-ghost {
    background: var(--sl-neutral-wash);
    color: var(--ma-hint);
}

/*
 * ma-empty: empty-state message.
 */
.ma-empty {
    text-align: center;
    color: var(--ma-hint);
    padding: 48px 24px;
    font-size: 14px;
}

/*
 * ma-quarter-header: section label above a group of service cards ("Q2 · 2026").
 */
.ma-quarter-header {
    padding: 20px 16px 8px 16px;
    font-size: 16px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    text-align: center;
    color: var(--ma-primary-text);
}

/*
 * ma-filter-row: toggle buttons for filters (e.g. Upcoming / All).
 */
.ma-filter-row {
    display: flex;
    gap: 8px;
    padding: 8px 12px 0 12px;
}

.ma-filter-btn {
    padding: 6px 14px;
    border-radius: 20px;
    border: 1px solid var(--ma-border);
    background: transparent;
    color: var(--ma-hint);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
    -webkit-tap-highlight-color: transparent;
}

.ma-filter-btn.ma-active {
    background: var(--ma-btn);
    color: var(--ma-btn-text);
    border-color: var(--ma-btn);
}

/*
 * ma-add-btn: floating bottom-anchored add button.
 */
.ma-add-section {
    padding: 12px 12px 4px 12px;
}

/*
 * ma-date-header: day separator between cards.
 */
.ma-date-header {
    padding: 12px 16px 4px 16px;
    font-size: 12px;
    font-weight: 600;
    color: var(--ma-hint);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

/*
 * ── Profile view ──────────────────────────────────────────────────────────────
 */

.ma-profile-section-title {
    font-size: 12px;
    font-weight: 700;
    color: var(--ma-hint);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    display: block;
    margin-bottom: 8px;
}

/*
 * ma-card-section-header: row with section title (left) and small edit button (right).
 * Used in Contacts and Address cards.
 */
.ma-card-section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 4px;
}

/*
 * ma-channel-row: single contact channel row (type + value + optional delete button).
 */
.ma-channel-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 4px 0;
}

/*
 * ma-dialog-form: vertical stack of fields inside a Dialog.
 */
.ma-dialog-form {
    display: flex;
    flex-direction: column;
    gap: var(--lumo-space-s);
}

.ma-profile-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 4px 0;
}

.ma-profile-icon {
    font-size: 15px;
    min-width: 22px;
    text-align: center;
    margin-top: 1px;
}

.ma-profile-text {
    font-size: 14px;
    color: var(--ma-text);
    line-height: 1.4;
}

/*
 * Language toggle buttons row
 */
.ma-lang-btn-row {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.ma-lang-btn {
    padding: 7px 18px;
    border-radius: 20px;
    border: 1px solid var(--ma-border);
    background: transparent;
    color: var(--ma-hint);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: all 0.15s;
}

.ma-lang-btn--active {
    background: var(--ma-btn);
    color: var(--ma-btn-text);
    border-color: var(--ma-btn);
}

/* ── Profile card redesign ───────────────────────────────────────────────── */

/*
 * ma-profile-card-header: avatar circle + info column side by side.
 */
.ma-profile-card-header {
    display: flex;
    align-items: center;
    gap: 14px;
}

/*
 * ma-avatar: circular initials badge.
 */
.ma-avatar {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: var(--ma-btn);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    font-weight: 700;
    color: var(--ma-btn-text);
    flex-shrink: 0;
}

/*
 * ma-profile-info: flex column containing name row + meta row.
 */
.ma-profile-info {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 5px;
    min-width: 0;
}

/*
 * ma-profile-name-row: space-between — name on left, language tag on right.
 */
.ma-profile-name-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.ma-profile-name {
    font-size: 17px;
    font-weight: 600;
    color: var(--ma-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/*
 * ma-badge-role: pill badge for member role (ADMIN, MEMBER, …).
 */
.ma-badge-role {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 20px;
    font-size: 11px;
    font-weight: 700;
    background: var(--sl-accent-wash);
    border: 1px solid var(--sl-accent);
    color: var(--ma-link);
    white-space: nowrap;
}

/*
 * ma-profile-lang: flag emoji + language code, right side of name row.
 */
.ma-profile-lang {
    display: flex;
    align-items: center;
    gap: 3px;
    font-size: 13px;
    font-weight: 500;
    color: var(--ma-hint);
    white-space: nowrap;
    flex-shrink: 0;
}

/*
 * ma-profile-meta-row: role badge + birthday on the same line.
 */
.ma-profile-meta-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.ma-profile-birthday {
    font-size: 13px;
    color: var(--ma-hint);
}

/* ── Unavailability rows ──────────────────────────────────────────────────── */

/*
 * ma-unavail-rows: wrapper div that holds all period rows.
 */
.ma-unavail-rows {
    display: flex;
    flex-direction: column;
}

/*
 * ma-unavail-row: single period row — date on left, status badge on right.
 */
.ma-unavail-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 0;
    border-bottom: 1px solid var(--ma-border);
}

.ma-unavail-row:last-child {
    border-bottom: none;
    padding-bottom: 4px;
}

.ma-unavail-date {
    font-size: 14px;
    color: var(--ma-text);
}

/*
 * ma-badge-bald: upcoming period — green outline pill.
 */
.ma-badge-bald {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 20px;
    font-size: 11px;
    font-weight: 600;
    border: 1px solid var(--sl-success);
    background: transparent;
    color: var(--sl-success);
    white-space: nowrap;
}

/*
 * ma-unavail-past: past period — just hint-coloured text, no pill.
 */
.ma-unavail-past {
    font-size: 13px;
    color: var(--ma-hint);
}

/*
 * Logout button — full-width tertiary button at the bottom of profile
 */
.ma-logout-btn {
    margin: 4px 12px 16px 12px;
    padding: 12px;
    text-align: center;
    font-size: 14px;
    font-weight: 500;
    color: var(--sl-danger);
    background: var(--sl-danger-wash);
    border-radius: var(--ma-radius);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}

.ma-logout-btn:active {
    opacity: 0.7;
}

/*
 * ma-week-nav: week navigation bar (← range →) above the card list.
 */
.ma-week-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 6px 8px 2px 8px;
    gap: 8px;
}

.ma-week-nav-btn {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    font-weight: 300;
    color: var(--ma-btn);
    border-radius: 50%;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.12s;
}

.ma-week-nav-btn:active {
    background: var(--sl-accent-wash);
}

.ma-week-nav-btn--disabled {
    color: var(--ma-border);
    cursor: default;
    pointer-events: none;
}

.ma-week-range {
    flex: 1;
    text-align: center;
    font-size: 14px;
    font-weight: 600;
    color: var(--ma-text);
}

/*
 * ma-chevron: expand/collapse indicator on worship service cards.
 */
.ma-chevron {
    font-size: 11px;
    color: var(--ma-hint);
    margin-left: 4px;
    transition: transform 0.2s;
}

/*
 * ma-card--worship: worship cards get a pointer cursor to hint at tappability.
 */
.ma-card--worship {
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.ma-card--worship:active {
    opacity: 0.85;
}

/*
 * ma-ministry-detail: inline expanded ministry plan inside a worship card.
 */
.ma-ministry-detail {
    margin-top: 10px;
    border-top: 1px solid var(--ma-border);
    padding-top: 8px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/*
 * ma-ministry-section-header: section title (e.g. "1. Stunde", "Dienste").
 */
.ma-ministry-section-header {
    font-size: 11px;
    font-weight: 700;
    color: var(--ma-hint);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 8px 0 3px 0;
}

.ma-ministry-section-header:first-child {
    padding-top: 2px;
}

/*
 * ma-role-row: role name (left) + member name with state (right).
 */
.ma-role-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    border-bottom: 1px solid var(--sl-border);
}

.ma-role-row:last-child {
    border-bottom: none;
}

.ma-role-name {
    font-size: 13px;
    color: var(--ma-hint);
    flex: 1;
}

.ma-role-member {
    font-size: 13px;
    font-weight: 500;
    color: var(--ma-text);
    text-align: right;
}

.ma-role-member--empty {
    font-size: 13px;
    color: var(--ma-border);
    text-align: right;
}

/* ── Splash animation (Phase 20) ───────────────────────────────────────── */

@keyframes ma-splash-ring {
    from { stroke-dashoffset: 302; opacity: 0; }
    to   { stroke-dashoffset: 0;   opacity: 1; }
}
@keyframes ma-splash-cross-v {
    from { transform: scaleY(0); }
    to   { transform: scaleY(1); }
}
@keyframes ma-splash-cross-h {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}
@keyframes ma-splash-pulse {
    from { transform: scale(1);   opacity: 0.15; }
    to   { transform: scale(1.5); opacity: 0;    }
}
@keyframes ma-splash-text {
    from { opacity: 0; letter-spacing: 0.3em; }
    to   { opacity: 1; letter-spacing: 0.1em; }
}
@keyframes ma-splash-exit {
    from { opacity: 1; transform: scale(1);    }
    to   { opacity: 0; transform: scale(0.94); }
}

.ma-splash-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100vh;
    background: var(--ma-bg);
    animation: ma-splash-exit 0.4s ease-in 2.0s both;
}

.ma-splash-ring {
    stroke-dasharray: 302;
    stroke-dashoffset: 302;
    animation: ma-splash-ring 0.5s ease-out 0.1s both;
}

.ma-splash-pulse {
    animation: ma-splash-pulse 0.5s ease-out 1.1s both;
}

.ma-splash-cross-v {
    transform-origin: 60px 60px;
    animation: ma-splash-cross-v 0.3s ease-in-out 0.6s both;
}

.ma-splash-cross-h {
    transform-origin: 60px 60px;
    animation: ma-splash-cross-h 0.3s ease-in-out 0.9s both;
}

.ma-splash-label {
    opacity: 0;
    animation: ma-splash-text 0.4s ease-out 1.6s both;
}

/* ── Service Preferences stepper (ServicePreferencesView) ────────────────────
 * ma-pref-stepper-row: horizontal row with −, value, + controls.
 * ma-pref-btn:         round tap target for − and +.
 * ma-pref-value:       centre label showing the current value or "unbegrenzt".
 */
.ma-pref-stepper-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    margin-top: 10px;
}

.ma-pref-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--ma-btn);
    color: var(--ma-btn-text);
    font-size: 20px;
    font-weight: 600;
    cursor: pointer;
    user-select: none;
    transition: opacity 0.15s;
}

.ma-pref-btn:active {
    opacity: 0.7;
}

.ma-pref-value {
    width: 72px;
    flex-shrink: 0;
    text-align: center;
    font-size: 30px;
    font-weight: 600;
    color: var(--ma-text);
    overflow: hidden;
}

/* ══════════════════════════════════════════════════════════════
   Member detail panel — redesign (Phase 40+)
   ══════════════════════════════════════════════════════════════ */

.detail-header-card {
    background: var(--sl-accent);
    color: var(--sl-accent-on);
    border-radius: var(--lumo-border-radius-l);
    padding: var(--lumo-space-l);
    position: relative;
    overflow: hidden;
    margin-bottom: var(--lumo-space-s);
    box-sizing: border-box;
    width: 100%;
}

.detail-section-card {
    background: var(--lumo-base-color);
    border: 1px solid var(--lumo-contrast-10pct);
    border-radius: var(--lumo-border-radius-l);
    padding: var(--lumo-space-m) var(--lumo-space-l);
    margin-bottom: var(--lumo-space-s);
    box-sizing: border-box;
    width: 100%;
}

.detail-avatar {
    width: 52px;
    height: 52px;
    min-width: 52px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--sl-accent-on) 20%, transparent);
    border: 2px solid color-mix(in srgb, var(--sl-accent-on) 38%, transparent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    font-weight: 600;
    color: white;
}

.detail-section-title {
    font-size: var(--lumo-font-size-xs);
    font-weight: 600;
    color: var(--lumo-secondary-text-color);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    display: block;
    margin-bottom: var(--lumo-space-s);
}

.detail-meta-label {
    font-size: var(--lumo-font-size-xs);
    color: var(--lumo-secondary-text-color);
    display: block;
}

.detail-meta-value {
    font-size: var(--lumo-font-size-s);
    color: var(--lumo-body-text-color);
    display: block;
    margin-bottom: var(--lumo-space-s);
}

.detail-channel-row {
    display: flex;
    align-items: center;
    gap: var(--lumo-space-s);
    padding: var(--lumo-space-xs) 0;
    border-bottom: 1px solid var(--lumo-contrast-5pct);
}

.detail-channel-icon {
    color: var(--sl-accent);
    flex-shrink: 0;
    width: 18px;
    height: 18px;
}

.detail-action-primary {
    background: var(--sl-accent) !important;
    color: var(--sl-accent-on) !important;
    flex: 1;
}

.detail-action-secondary {
    background: var(--sl-accent-wash) !important;
    border: 1px solid var(--sl-accent) !important;
    color: var(--sl-accent) !important;
}

.detail-action-danger {
    background: var(--sl-danger-wash) !important;
    border: 1px solid var(--sl-danger) !important;
    color: var(--sl-danger) !important;
}

/*
 * detail-header-action-btn: icon-only button embedded in the blue header card.
 * White translucent style so the buttons are legible on the dark-blue background.
 */
.detail-header-action-btn {
    color: var(--sl-accent-on) !important;
    opacity: 0.85;
    background: color-mix(in srgb, var(--sl-accent-on) 14%, transparent) !important;
    /* Та же форма и размер, что у .toolbar-icon-btn — круги на всей странице */
    width: 36px;
    min-width: 36px;
    height: 36px;
    padding: 0;
    border-radius: var(--sl-radius-pill) !important;
    transition: background 0.15s, color 0.15s !important;
}

.detail-header-action-btn:hover {
    background: color-mix(in srgb, var(--sl-accent-on) 26%, transparent) !important;
    color: var(--sl-accent-on) !important;
    opacity: 1;
}

/* Delete button — subtle red tint on hover */
.detail-header-action-danger:hover {
    background: var(--sl-danger) !important;
    color: var(--sl-surface) !important;
    opacity: 1;
}

/* ═══════════════════════════════════════════════════════════════════════════
 * AvailabilityInputView — переключатель квартала и строки доступности
 * ═══════════════════════════════════════════════════════════════════════════ */

/*
 * ma-avail-quarter-toggle: две pill-кнопки переключения квартала.
 * Размещаются горизонтально с небольшим отступом.
 */
.ma-avail-quarter-toggle {
    display: flex;
    gap: 0;
    margin: 0 var(--lumo-space-m) var(--lumo-space-m);
    border-radius: var(--lumo-border-radius-l);
    overflow: hidden;
    border: 1px solid var(--ma-btn);
}

.ma-avail-quarter-btn {
    flex: 1;
    text-align: center;
    padding: 8px var(--lumo-space-s);
    font-size: var(--lumo-font-size-s);
    font-weight: 500;
    color: var(--ma-btn);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
    user-select: none;
    -webkit-user-select: none;
}

.ma-avail-quarter-btn:active {
    opacity: 0.75;
}

.ma-avail-quarter-btn--active {
    background: var(--ma-btn);
    color: var(--ma-btn-text);
}

/*
 * ma-avail-row: одна строка с датой и toggle-переключателем.
 */
.ma-avail-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px var(--lumo-space-m);
    border-bottom: 1px solid var(--lumo-contrast-5pct);
    gap: var(--lumo-space-s);
}

.ma-avail-date-label {
    font-size: var(--lumo-font-size-s);
    color: var(--ma-text);
    flex-shrink: 0;
}

/*
 * ma-avail-toggle: горизонтальный переключатель Verfügbar / Verhindert.
 * Две половины с общей границей; активная сторона залита цветом.
 */
.ma-avail-toggle {
    display: flex;
    border-radius: var(--lumo-border-radius-l);
    overflow: hidden;
    border: 1px solid var(--lumo-contrast-20pct);
    flex-shrink: 0;
}

/* FIXED-состояние (из /away): весь toggle приглушён */
.ma-avail-toggle--fixed {
    opacity: 0.45;
    pointer-events: none;
}

.ma-avail-toggle-opt {
    padding: 6px 12px;
    font-size: var(--lumo-font-size-xs);
    font-weight: 500;
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
    user-select: none;
    -webkit-user-select: none;
    white-space: nowrap;
}

.ma-avail-toggle-opt--left {
    border-right: 1px solid var(--lumo-contrast-10pct);
}

/* Неактивная сторона — прозрачный фон, приглушённый текст */
.ma-avail-toggle-opt--inactive {
    background: transparent;
    color: var(--ma-hint);
}

/* Активная сторона: Verfügbar — зелёный */
.ma-avail-toggle-opt--avail-active {
    background: var(--sl-success-wash);
    border: 1px solid var(--sl-success);
    color: var(--sl-text);
}

/* Активная сторона: Verhindert — красный */
.ma-avail-toggle-opt--unavail-active {
    background: var(--sl-danger-wash);
    border: 1px solid var(--sl-danger);
    color: var(--sl-text);
}

.ma-avail-toggle-opt:active:not(.ma-avail-toggle--fixed *) {
    opacity: 0.7;
}

/* ═══════════════════════════════════════════════════════════════
   Status badges (MemberListView и далее)
   Текст — --sl-text на семантической заливке: семантический цвет
   как ТЕКСТ на своей же 12%-заливке даёт 3.9:1 и не проходит AA.
   Роль несут заливка и сплошной бордер.
   ═══════════════════════════════════════════════════════════════ */

.status-badge {
    font-size: var(--lumo-font-size-xs);
    border-radius: var(--sl-radius-pill);
    padding: 2px 10px;
    white-space: nowrap;
}

.status-badge--active {
    background: var(--sl-success-wash);
    border: 1px solid var(--sl-success);
    color: var(--sl-text);
}

.status-badge--pending {
    background: var(--sl-warning-wash);
    border: 1px solid var(--sl-warning);
    color: var(--sl-text);
}

/* ═══════════════════════════════════════════════════════════════════════════
 * Иконки (Lucide, ISC) и единообразные icon-only кнопки
 *
 * Один набор на всё приложение: сетка 24×24, обводка 2px, stroke=currentColor.
 * Файлы — META-INF/resources/icons/*.svg, обёртка — church.sda.common.boundary.AppIcon.
 * Форма кнопок общая (круг 36×36), чтобы панель инструментов не выглядела
 * набором разнородных значков.
 * ═══════════════════════════════════════════════════════════════════════════ */

.app-icon {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
}

/*
 * Заливку задаём и через --vaadin-button-*, и напрямую: вариант кнопки
 * tertiary объявляет `background: transparent !important` внутри shadow DOM,
 * а такие правила побеждают любые document-стили — поэтому кнопки с этим
 * набором вариантов не используются (см. AppIcon.toolbarButton).
 */
.toolbar-icon-btn {
    --vaadin-button-background: var(--sl-neutral-wash);
    --vaadin-button-text-color: var(--sl-text);
    --vaadin-button-border-radius: var(--sl-radius-pill);
    --vaadin-button-height: 36px;
    --vaadin-button-min-width: 36px;
    --vaadin-button-padding: 0;
    width: 36px;
    min-width: 36px;
    height: 36px;
    /* Круг не сжимается: место в тулбаре отдаёт поле фильтра (flex: 1 1 0). */
    flex: 0 0 auto;
    padding: 0;
    border-radius: var(--sl-radius-pill) !important;
    background: var(--sl-neutral-wash) !important;
    color: var(--sl-text) !important;
    transition: background 0.15s, color 0.15s;
}

.toolbar-icon-btn:hover {
    background: var(--sl-accent-wash) !important;
    color: var(--sl-accent) !important;
}

.toolbar-icon-btn--primary {
    --vaadin-button-background: var(--sl-accent);
    --vaadin-button-text-color: var(--sl-accent-on);
    background: var(--sl-accent) !important;
    color: var(--sl-accent-on) !important;
}

.toolbar-icon-btn--primary:hover {
    background: var(--sl-accent-hover) !important;
    color: var(--sl-accent-on) !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
 * Каналы связи — компактные плашки (chips) вместо строк
 * ═══════════════════════════════════════════════════════════════════════════ */

.ma-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--lumo-space-xs);
    width: 100%;
}

.ma-contact-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--lumo-space-xs);
    max-width: 100%;
    padding: 3px 10px;
    border: 1px solid var(--sl-border);
    border-radius: var(--sl-radius-pill);
    background: var(--sl-surface-sunken);
    font-size: var(--lumo-font-size-s);
    line-height: 1.5;
}

.ma-contact-chip__icon {
    color: var(--sl-accent);
    width: 15px;
    height: 15px;
    flex-shrink: 0;
}

.ma-contact-chip__value {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ma-contact-chip__value--mono {
    font-family: monospace;
}

/*
 * Кнопка удаления канала: скрыта, пока плашка не под курсором или не в фокусе.
 * :focus-within оставляет её доступной с клавиатуры (WCAG 2.1.1).
 */
.ma-contact-chip__remove {
    width: 16px;
    min-width: 16px;
    height: 16px;
    max-width: 0;
    padding: 0;
    margin: 0;
    opacity: 0;
    overflow: hidden;
    color: var(--sl-danger) !important;
    background: transparent !important;
    transition: max-width 0.12s ease, opacity 0.12s ease;
}

.ma-contact-chip:hover .ma-contact-chip__remove,
.ma-contact-chip:focus-within .ma-contact-chip__remove {
    max-width: 16px;
    opacity: 1;
}

/* ═══════════════════════════════════════════════════════════════════════════
 * Карточка участника: счётчики служений, строки «ближайшее/последнее»,
 * гистограмма нагрузки по месяцам
 * ═══════════════════════════════════════════════════════════════════════════ */

.ma-card-stack {
    display: flex;
    flex-direction: column;
    gap: var(--lumo-space-s);
}

.ma-stat-row {
    display: flex;
    gap: var(--lumo-space-s);
}

.ma-stat {
    flex: 1 1 0;
    min-width: 0;
    padding: 8px 10px;
    text-align: center;
    background: var(--sl-surface-sunken);
    border: 1px solid var(--sl-border);
    border-radius: var(--sl-radius-card);
}

.ma-stat__value {
    display: block;
    font-size: 20px;
    font-weight: 600;
    color: var(--sl-text);
    letter-spacing: var(--sl-tracking-heading);
}

.ma-stat__label {
    display: block;
    font-size: 11px;
    line-height: 1.3;
    color: var(--sl-text-body);
}

/* Ненулевое «ждут подтверждения» — единственное состояние, требующее действия */
.ma-stat--warn {
    background: var(--sl-warning-wash);
    border-color: var(--sl-warning);
}

.ma-slot__label {
    font-size: var(--lumo-font-size-xs);
    color: var(--sl-text-body);
}

.ma-slot__value {
    font-size: var(--lumo-font-size-s);
    color: var(--sl-text);
}

.detail-empty-hint {
    font-size: var(--lumo-font-size-s);
    color: var(--sl-text-body);
}

/*
 * Гистограмма: одна серия, поэтому один цвет и без легенды — подпись месяца
 * снизу, точное число в тултипе. Месяц без служений остаётся видимым обрубком
 * в цвете границы, иначе пропуск не отличить от края диаграммы.
 * Базовая линия — inset-тень трека, чтобы она шла ровно по низу столбиков,
 * а не под подписями.
 */
.ma-histogram {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    width: 100%;
}

.ma-histogram__col {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
}

.ma-histogram__track {
    width: 100%;
    height: 44px;
    display: flex;
    align-items: flex-end;
    box-shadow: inset 0 -1px 0 var(--sl-border);
}

.ma-histogram__bar {
    width: 100%;
    min-height: 3px;
    background: var(--sl-accent);
    border-radius: 4px 4px 0 0;
}

.ma-histogram__bar--empty {
    height: 2px;
    min-height: 2px;
    background: var(--sl-border-strong);
    border-radius: 0;
}

.ma-histogram__label {
    font-size: 10px;
    line-height: 1;
    color: var(--sl-text-body);
}

/* Месяц до начала периода служения: в счётчик нагрузки уже не входит, но остаётся виден */
.ma-histogram__col--before-window {
    opacity: 0.4;
}

/* Плашка роли, которой участник руководит */
.ma-contact-chip--accent {
    background: var(--sl-accent-wash);
    border-color: var(--sl-accent);
}

.ma-contact-chip__suffix {
    font-size: 11px;
    color: var(--sl-text-body);
    white-space: nowrap;
}

/* Ссылка «Маршрут» под адресом участника */
.ma-route-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: var(--lumo-space-xs);
    font-size: var(--lumo-font-size-s);
    color: var(--sl-accent);
    text-decoration: none;
}

.ma-route-link:hover,
.ma-route-link:focus-visible {
    text-decoration: underline;
}

/* ═══════════════════════════════════════════════════════════════════════════
 * Матрица доступности (/web/availability) — метки в ячейках
 *
 * Раньше каждая ячейка была отдельным деревом компонентов на сервере:
 * Div + vaadin-icon + Tooltip + восемь inline-стилей. На сетке 14 колонок ×
 * ~30 буферизованных строк это давало 417 компонентов vaadin-tooltip и
 * ответ UIDL на 748 КБ (~1.8 КБ на ячейку) — на аплинке Synology 17 секунд.
 *
 * Теперь ячейку рисует LitRenderer: шаблон уходит на клиент один раз, на
 * строку передаётся только имя состояния. Иконка приходит из общего набора
 * Lucide через mask-image (файл кэшируется браузером один раз на всё
 * приложение), цвет — из currentColor, подсказка — нативный title.
 * ═══════════════════════════════════════════════════════════════════════════ */

.av-mark {
    display: block;
    width: 0.85rem;
    height: 0.85rem;
    margin: 0 auto;
    background-color: currentColor;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-size: contain;
            mask-size: contain;
}

/* Доступен — приглушённая галочка: норма не должна перетягивать взгляд */
.av-mark--free {
    color: color-mix(in srgb, var(--sl-success) 55%, var(--sl-text-body));
    -webkit-mask-image: url('/icons/check.svg');
            mask-image: url('/icons/check.svg');
}

/* Недоступен — крестик цветом ошибки: именно это ищут глазами */
.av-mark--busy {
    color: var(--sl-danger);
    -webkit-mask-image: url('/icons/x.svg');
            mask-image: url('/icons/x.svg');
}

/* Дата отменена — служения в этот день нет, доступность не спрашивается */
.av-mark--off {
    display: block;
    color: var(--sl-text-muted);
    text-align: center;
}

/* ═══════════════════════════════════════════════════════════════════════════
 * Квартальный план (/web/quarter-schedule) — ячейки сетки
 *
 * Ячейка здесь встречается сотнями (роли × даты), поэтому всё оформление живёт
 * в классах, а не в inline-стилях и не в серверных компонентах. Инлайн-стиль
 * повторяется в UIDL дословно на каждую ячейку, а Tooltip.forComponent — это
 * вдобавок ещё и отдельный серверный компонент на каждую.
 *
 * Селекторы усилены атрибутом [theme~="badge"] (0,2,0): лумовская тема бейджа
 * объявляет и размер шрифта, и box-shadow, и раньше её перебивал инлайн.
 * Цвет статуса остаётся за темой Lumo (error / success / contrast) — классы
 * его не трогают, только рамки конфликтов поверх.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* Отменённая дата — служения нет */
.qs-off {
    color: var(--sl-text-muted);
}

/* Пустой слот: плюс из общего набора Lucide, кликабельный на всю ячейку */
.qs-add {
    display: block;
    width: 1.15rem;
    height: 1.15rem;
    margin: 0 auto;
    cursor: pointer;
    background-color: var(--sl-danger);
    -webkit-mask-image: url('/icons/plus.svg');
            mask-image: url('/icons/plus.svg');
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-size: contain;
            mask-size: contain;
    opacity: 0.55;
    transition: opacity 0.12s;
}

.qs-add:hover {
    opacity: 1;
}

.qs-badge[theme~="badge"] {
    font-size: var(--lumo-font-size-xs);
    cursor: default;
}

.qs-badge--clickable[theme~="badge"] {
    cursor: pointer;
}

/* Уборка: имена в столбик, колонка узкая */
.qs-badge--cleaning[theme~="badge"] {
    white-space: pre-line;
    line-height: 1.2;
    text-align: center;
}

/* Две независимые категории конфликтов, цвета как в диалоге «Проверить конфликты»:
   двойное назначение — ошибка, назначение вопреки недоступности — предупреждение. */
.qs-badge--conflict[theme~="badge"] {
    box-shadow: 0 0 0 2px var(--sl-danger);
}

.qs-badge--unavailable[theme~="badge"] {
    box-shadow: 0 0 0 2px var(--sl-warning);
}

/* Выбранный первым бейдж в режиме обмена */
.qs-badge--swap[theme~="badge"] {
    outline: 2px solid var(--sl-accent);
}

/* ── Список «служение → человек (N): даты» (Фаза 79) ──────────────────────
   Второй разрез того же квартала. Цвет статуса и рамки конфликтов дают
   прежние .qs-badge*; здесь — раскладка, чередование строк и масштаб. */

/* Колонки раскладывает браузер: на широком экране их две-три, на телефоне одна.
   Ширина колонки в em, поэтому вместе со шрифтом меняется и она. */
.qs-list {
    display: block;
    columns: 26em auto;
    column-gap: 3.5rem;
    column-rule: 1px solid var(--sl-border);
    font-size: var(--lumo-font-size-m);
}

/* Одно служение целиком в одной колонке */
.qs-list__block {
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    page-break-inside: avoid;
    margin: 0 0 1.25rem;
}

.qs-list__role-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    padding-bottom: 0.15rem;
    border-bottom: 1px solid var(--sl-border-strong);
}

.qs-list__role {
    font-weight: 600;
    color: var(--sl-text);
}

/* Счётчики — основной цвет текста (19.76:1). --sl-text-muted здесь был ошибкой:
   по гайду это токен ТОЛЬКО для disabled, 2.52:1, читать его невозможно. */
.qs-list__count {
    color: var(--sl-text);
    font-weight: 600;
    font-size: 0.85em;
    font-variant-numeric: tabular-nums;
}

.qs-list__person {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem;
    padding: 0.2rem 0.4rem;
    border-radius: var(--sl-radius-input);
}

/* Чередование фона: чётные строки на сниженной подложке */
.qs-list__person--alt {
    background: var(--sl-surface-sunken);
}

.qs-list__name {
    min-width: 11em;
    font-weight: 500;
    color: var(--sl-text);
}

@media (max-width: 640px) {
    .qs-list__name {
        min-width: 100%;
    }
}

/* ── Дата в списке — чип-кнопка ───────────────────────────────────────────
   В гриде дата это координата ячейки, а в списке — единственный способ
   открыть назначение, поэтому она должна выглядеть нажимаемой: рамка,
   поверхность, отклик на наведение и нажатие. Цвет отдан статусу: залить
   чип акцентом (как кнопку «Quartal generieren») значило бы закрасить
   зелёный и красный, на которые смотрят в первую очередь.
   Размеры в em и rem × --qs-scale — чип едет вместе с размером шрифта. */
.qs-date {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: calc(1.75rem * var(--qs-scale, 1));
    padding: 0 0.6em;
    border: 1px solid var(--sl-border-control);
    border-radius: var(--sl-radius-pill);
    background: var(--sl-surface);
    color: var(--sl-text);
    font-size: var(--lumo-font-size-xs);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    cursor: default;
    transition: background 0.12s, border-color 0.12s, color 0.12s, transform 0.06s;
}

/* Подтверждено и отказ — как в гриде, но рамкой и лёгкой заливкой,
   а не сплошным цветом: в строке таких дат до тринадцати. */
.qs-date--ok {
    border-color: var(--sl-success);
    background: var(--sl-success-wash);
    color: var(--sl-success);
}

.qs-date--no {
    border-color: var(--sl-danger);
    background: var(--sl-danger-wash);
    color: var(--sl-danger);
}

/* Правится отсюда — значит кнопка */
.qs-date--clickable {
    cursor: pointer;
}

/* Ховер после статусных правил: рамка и фон становятся акцентными, а цвет
   текста остаётся статусным — отклик есть у всех чипов, статус не теряется. */
.qs-date--clickable:hover {
    border-color: var(--sl-accent);
    background: var(--sl-accent-wash);
}

.qs-date--clickable:active {
    transform: translateY(1px);
}

/* Не правится отсюда (связанная роль): пунктир вместо сплошной рамки */
.qs-date--readonly {
    border-style: dashed;
    border-color: var(--sl-border);
    color: var(--sl-text-body);
}

/* Конфликты — те же две категории и те же цвета, что у бейджей грида */
.qs-date--conflict {
    box-shadow: 0 0 0 2px var(--sl-danger);
}

.qs-date--unavailable {
    box-shadow: 0 0 0 2px var(--sl-warning);
}

/* ── Масштаб шрифта плана (Фаза 79) ───────────────────────────────────────
   Грид рисует ячейки в shadow DOM и берёт размеры из --lumo-font-size-*;
   унаследованный font-size туда не доходит, а кастомные свойства — да.
   Значение --qs-scale ставит QuarterScheduleView на контейнер плана. */
.qs-scaled {
    --lumo-font-size-xxs: calc(0.75rem   * var(--qs-scale, 1));
    --lumo-font-size-xs:  calc(0.8125rem * var(--qs-scale, 1));
    --lumo-font-size-s:   calc(0.875rem  * var(--qs-scale, 1));
    --lumo-font-size-m:   calc(1rem      * var(--qs-scale, 1));
    --lumo-font-size-l:   calc(1.125rem  * var(--qs-scale, 1));
}

/* ═══════════════════════════════════════════════════════════════════════════
 * Переключатель темы в подвале сайдбара
 *
 * Класс .web-theme-toggle навешивался в WebLayout с самого начала, но правил
 * под него в этом файле не было ни одного. При этом обеим кнопкам проставлен
 * setWidthFull() — то есть внутри HorizontalLayout каждая просила 100% ширины
 * строки. Первая занимала строку целиком, вторая выезжала за её правый край и
 * обрезалась сайдбаром: пользователь видел одну кнопку «Hell» и не мог
 * переключиться на тёмную тему вообще. Замерено: строка 231px, обе кнопки по
 * 231px, «Dunkel» начиналась ровно там, где строка заканчивалась.
 *
 * flex: 1 1 0 делит строку поровну. setWidthFull() из WebLayout убран — пока он
 * там стоял, ширину приходилось перебивать через width: auto !important, потому
 * что инлайн-стиль выигрывает у класса.
 * ═══════════════════════════════════════════════════════════════════════════ */

.web-theme-toggle {
    /* Не влезли рядом — встают друг под друга. При 26 языках интерфейса подобрать
       ширину, в которую поместится любая пара подписей, нельзя: немецкие
       «Hell / Dunkel» влезают, русские «Светлая / Тёмная» уже нет, и Lumo обрезает
       подпись кнопки многоточием («Светл…»). Перенос строки надёжнее подгонки. */
    flex-wrap: wrap;
    gap: var(--lumo-space-xs);
}

.web-theme-toggle > vaadin-button {
    /* basis по содержимому, а не 0: делить строку ровно пополам нельзя — половины
       не хватает на длинную подпись. min-width: fit-content запрещает сжиматься
       ниже текста, иначе вместо переноса получаем многоточие. */
    flex: 1 1 auto;
    min-width: fit-content;
}
