/* Здесь только композиция страниц. Компоненты, цвета и общую типографику задаёт Bootstrap. */
/* Минимум равен viewport (видимой области окна). Единица dvh (динамическая высота
   окна) учитывает мобильную панель браузера; vh (высота окна) — запасной вариант. */
.page-shell {
    min-height: 100vh;
    min-height: 100dvh;
}

/* Сохраняем свободное пространство справа: основной блок ограничен по ширине
   и смещён влево. По вертикали его размещает общий каркас страницы. */
.onboarding {
    --page-gutter: clamp(1.5rem, 7.78vw, 8rem);
    width: min(62.5rem, calc(100% - 2 * var(--page-gutter)));
    margin: 0 0 0 var(--page-gutter);
}

/* Стартовый экран использует всю ширину между существующими боковыми отступами.
   Пропорция 70/30 считается после вычета свободного промежутка между областями. */
.onboarding--start {
    width: calc(100% - 2 * var(--page-gutter));
    position: relative;
    isolation: isolate;
}
/* Собственный слой сохраняет текст и кнопку над уходящими под них линиями. */
.onboarding-content { min-width: 0; position: relative; z-index: 1; }

@media (min-width: 1200px) {
    .onboarding--start {
        display: grid;
        grid-template-columns: minmax(0, 7fr) minmax(0, 3fr);
        align-items: center;
        column-gap: clamp(1.5rem, 4vw, 4rem);
    }
}

/* SVG (векторная графика) немного расширяется в обе стороны, не меняя сетку 70/30.
   Справа расширение меньше внешнего отступа страницы. Mask (маска прозрачности)
   плавно гасит оба края; градиент используется только для прозрачности, не как фон. */
.ecosystem {
    --ecosystem-overlap: clamp(14rem, 24vw, 30rem);
    --ecosystem-right-bleed: clamp(1.25rem, 2.5vw, 2.5rem);
    --ecosystem-fade: linear-gradient(to right, transparent, rgb(0 0 0 / .02) 14%, rgb(0 0 0 / .08) 30%, rgb(0 0 0 / .35) 43%, #000 59%, #000 82%, rgb(0 0 0 / .35) 94%, transparent);
    color: var(--bs-primary);
    pointer-events: none;
    min-width: 0;
    min-height: 0;
    align-self: stretch;
    position: relative;
    z-index: 0;
}
.ecosystem__drawing {
    /* Абсолютный слой не участвует в расчёте высоты сетки. Её задаёт только текст;
       SVG (векторная графика) вписывается в эту высоту и не удлиняет документ. */
    position: absolute;
    inset-block: 0;
    inset-inline-end: calc(0px - var(--ecosystem-right-bleed));
    display: block;
    width: calc(100% + var(--ecosystem-overlap) + var(--ecosystem-right-bleed));
    max-width: none;
    height: 100%;
    overflow: hidden;
    -webkit-mask-image: var(--ecosystem-fade);
    mask-image: var(--ecosystem-fade);
}
.ecosystem__links, .ecosystem__pulse, .ecosystem__stream {
    fill: none;
    stroke: currentColor;
    stroke-width: 1;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.ecosystem__links path { vector-effect: non-scaling-stroke; }
.ecosystem__links { opacity: .24; }
.ecosystem__connections { opacity: .3; }
.ecosystem__nodes { fill: currentColor; opacity: .55; }
.ecosystem__hub { fill: currentColor; opacity: .65; }
/* Эти подписи относятся только к иллюстрации: глобальная типографика не меняется.
   Текст остаётся текстом SVG, а содержание поступает из общего языкового словаря. */
.ecosystem__labels, .ecosystem__hub-label, .ecosystem__operation {
    fill: currentColor;
    font-family: var(--bs-font-sans-serif);
    font-size: var(--bs-body-font-size);
}
.ecosystem__labels { fill-opacity: .75; }
.ecosystem__business-label { font-size: 1.125rem; font-weight: 600; fill-opacity: 1; }
.ecosystem__hub-label { font-size: 1.25rem; font-weight: 600; }
/* Одно место для последовательных событий каждой сети. Без движения видна
   только первая подпись: остальные не наслаиваются и не перегружают рисунок. */
.ecosystem__operation { opacity: 0; }
.ecosystem__operation:first-child { opacity: .5; }
.ecosystem__pulse {
    opacity: 0;
    stroke-width: 2;
    stroke-dasharray: .055 .945;
    vector-effect: non-scaling-stroke;
}
/* Дополнительные короткие штрихи идут по существующим связям. В статичном режиме
   они скрыты; длина повторения делит единичный путь на четыре равных участка. */
.ecosystem__stream {
    opacity: 0;
    stroke-width: 1.4;
    stroke-dasharray: .025 .225;
    vector-effect: non-scaling-stroke;
}

/* Движение включается только при наличии сценария, широкого окна и разрешения
   prefers-reduced-motion (предпочтения снижения движения). В остальных случаях
   исходные координаты дают законченный статичный рисунок, импульсы невидимы. */
@media (min-width: 1200px) and (prefers-reduced-motion: no-preference) {
    .ecosystem--motion .ecosystem__organism {
        transform-origin: 0 0;
        animation: ecosystem-drift var(--drift-time) ease-in-out var(--phase) infinite;
    }
    .ecosystem--motion .ecosystem__network {
        transform-origin: 0 0;
        animation: ecosystem-breathe var(--breath-time) ease-in-out var(--phase) infinite;
    }
    .ecosystem--motion .ecosystem__pulse {
        animation: ecosystem-pulse var(--pulse-time) linear var(--pulse-phase) infinite;
    }
    /* Пять этапов каждого примера используют один период с разными фазами.
       Подпись события и импульс на его связи появляются согласованно. */
    .ecosystem--motion .ecosystem__operation {
        animation: ecosystem-operation var(--operation-time) ease-in-out var(--operation-phase) infinite;
    }
    .ecosystem--motion .ecosystem__transaction {
        animation-name: ecosystem-transaction;
    }
    .ecosystem--motion .ecosystem__stream {
        opacity: .3;
        animation: ecosystem-stream var(--stream-time) linear var(--stream-phase) infinite;
    }
}

/* Положение и скорость совпадают на концах цикла. Вращение и дыхание сохраняют
   центр каждой сети: внешняя связь с upHome остаётся в той же точке. */
@keyframes ecosystem-drift {
    0%, 100% { transform: rotate(-.8deg); }
    50% { transform: rotate(.8deg); }
}
@keyframes ecosystem-breathe {
    0%, 100% { transform: scale(.98, 1.01); }
    50% { transform: scale(1.025, .985); }
}

/* Один мягкий проход занимает малую часть длинного цикла. Штрих движется
   по той же кривой, что и связь, и исчезает до возврата в исходную точку. */
@keyframes ecosystem-pulse {
    0%, 68% { opacity: 0; stroke-dashoffset: 1; }
    72% { opacity: .65; }
    82% { opacity: .65; }
    87%, 100% { opacity: 0; stroke-dashoffset: 0; }
}

/* Одно событие занимает пятую часть периода, остальные сменяют его по очереди.
   Возврат импульса в начало происходит только в невидимом состоянии. */
@keyframes ecosystem-operation {
    0%, 2%, 19%, 100% { opacity: 0; }
    6%, 14% { opacity: .6; }
}
@keyframes ecosystem-transaction {
    0%, 2% { opacity: 0; stroke-dashoffset: 1; }
    6%, 14% { opacity: .45; }
    19%, 100% { opacity: 0; stroke-dashoffset: 0; }
}
/* Сдвиг равен целому числу повторений штриха, поэтому граница цикла незаметна. */
@keyframes ecosystem-stream {
    from { stroke-dashoffset: 1; }
    to { stroke-dashoffset: 0; }
}

/* Уплотнение надписи относится только к бренду, не к шрифту остальных компонентов. */
.brand { letter-spacing: -0.05em; }
/* На широком экране согласованные части заголовка образуют отдельные строки. */
.hero-title > span { display: block; }
.hero-description { max-width: 46rem; margin-top: 1.5rem; }
/* После замечания о неизменившемся размере уменьшаем только сноску upID:
   0.75rem дают 12 px при базовых 16 px. Стандартный small (мелкий текст)
   Bootstrap и типографика остальных элементов остаются прежними. */
.upid-footnote { font-size: .75rem; }
.steps, .start-action { margin-top: 2rem; }
/* Разделители используют цвет и толщину из темы; внешняя рамка задана в шаблоне. */
.step + .step { border-inline-start: var(--bs-border-width) solid var(--bs-border-color); }
/* Экран входа использует одну стандартную карточку Bootstrap, без композиции
   и декора стартовой страницы. Здесь только ограничение ширины и место для кода. */
.upid-screen { max-width: 48rem; }
.upid-qr-placeholder { max-width: 11rem; }
.upid-phone-fields { max-width: 22rem; }

/* На компьютере обе панели участвуют в размере одной ячейки CSS Grid (двумерной
   сетки), поэтому высота не зависит от выбранной вкладки или длины перевода.
   Visibility (видимость) исключает скрытую панель из фокуса и чтения скринридером.
   На телефоне сохраняется стандартная естественная высота активной панели Bootstrap. */
@media (min-width: 768px) {
    .upid-method-content {
        display: grid;
        align-items: start;
        min-height: 14rem;
    }
    .upid-method-content > .tab-pane {
        display: block;
        grid-area: 1 / 1;
        visibility: hidden;
    }
    .upid-method-content > .tab-pane.active { visibility: visible; }
}
/* Сохранённый класс для поясняющей подписи; текущий шаблон эту подпись не выводит. */
.action-note { flex: 1 1 5rem; }
.step { overflow-wrap: anywhere; }

/* Ниже стандартного порога lg (широкого экрана) три этапа идут друг под другом.
   Боковые разделители заменяются верхними, чтобы длинный первый этап читался целиком. */
@media (max-width: 991.98px) {
    .hero-title > span { display: inline; }
    .step + .step {
        border-inline-start: 0;
        border-top: var(--bs-border-width) solid var(--bs-border-color);
    }
}

/* На низких окнах сокращаем интервалы только внутри основного блока. */
@media (max-height: 740px) {
    .hero-description, .steps, .start-action { margin-top: 1rem; }
}

/* Размер меняется у заголовка конкретной страницы, а не у компонентов Bootstrap. */
@media (max-height: 640px) {
    .hero-title { font-size: 1.75rem; }
}

/* В низком окне без декоративной области сохраняется прежнее расположение
   знакомства и действий рядом. CSS Grid (двумерная сетка) действует только
   внутри текстовой области и не нарушает пропорцию 70/30 на широком экране. */
@media (min-width: 576px) and (max-width: 1199.98px) and (max-height: 500px) {
    .onboarding--start { --page-gutter: 1.5rem; }
    .onboarding-content {
        display: grid;
        grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
        align-items: start;
        gap: 0.5rem 1.5rem;
    }
    .onboarding-content > .hero-title { grid-column: 1; grid-row: 1; }
    .onboarding-content > .hero-title > span { display: inline; }
    .onboarding-content > .hero-description { grid-column: 1; grid-row: 2; margin: 0; }
    .onboarding-content > .steps { grid-column: 2; grid-row: 1; margin: 0; }
    .onboarding-content > .start-action { grid-column: 2; grid-row: 2; margin: 0; }
}
