#blazor-error-ui {
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

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

:root {
    font-size: var(--rz-root-font-size);
}

body {
    font-family: var(--rz-text-font-family);
    color: var(--rz-text-color);
    font-size: var(--rz-body-font-size);
    line-height: var(--rz-body-line-height);
    background-color: var(--rz-body-background-color);
}

.rz-body {
    --rz-body-padding: 0;
}

a {
    color: var(--rz-link-color);
}

a:hover,
a:focus {
    color: var(--rz-link-hover-color);
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

.blazor-error-boundary::after {
    content: "An error has occurred."
}

.loading-progress {
    position: relative;
    display: block;
    width: 8rem;
    height: 8rem;
    margin: 20vh auto 1rem auto;
}

.loading-progress circle {
    fill: none;
    stroke: #e0e0e0;
    stroke-width: 0.6rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}

.loading-progress circle:last-child {
    stroke: #1b6ec2;
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: bold;
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}

.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Loading");
}

/* RadzenDataFilter */
.rz-datafilter-operator-bar,
.rz-datafilter-operator-bar + hr {
    display: none !important;
}

.rz-datafilter-bar .rz-splitbutton-menubutton {
    display: none !important;
}

.rz-datafilter-bar .rz-splitbutton > button.rz-button-icon-only .rz-button-text {
    display: inline-block !important;
    padding: 0 .5rem;
    white-space: nowrap;
}

.rz-datafilter-bar .rz-splitbutton > button.rz-button-icon-only .rzi {
    display: none !important;
}

.rz-datafilter-bar .rz-splitbutton > button.rz-button-icon-only,
.rz-datafilter-bar .rz-splitbutton > button.rz-button-icon-only .rz-button-box {
    width: auto !important;
    min-width: unset !important;
    background-color: #ede7f6;
}

.rz-datafilter-bar .rz-splitbutton > button.rz-button-icon-only .rz-button-text::after {
    content: "+";
    font-size: inherit;
    color: #6200ea;
}

/* Редактор значения — на всю ширину, как выпадашки свойства и оператора */
.rz-datafilter .rz-datafilter-editor {
    flex: 1 1 100%;
}

/* Подпись типа + поле значения колонкой: тема даёт полям в editor flex-basis 10rem (рассчитан на строку),
   в колонке он становится высотой — поле раздувалось до 160px */
.rz-datafilter .rz-datafilter-item .filter-editor-stack > * {
    flex: 0 0 auto;
    min-width: 0;
}

/* Ширину колонки задаёт контейнер, а не содержимое: иначе длинная строка выбранных значений (nowrap)
   распирает ряд фильтра, и оператор уезжает вправо за панель — вместо обрезки многоточием */
.rz-datafilter .rz-datafilter-item .filter-editor-stack {
    contain: inline-size;
}

/* Мультивыбор: все выбранные значения строкой, лишнее обрезается многоточием (вместо «N items selected») */
.rz-datafilter-editor .rz-dropdown .rz-dropdown-label {
    text-overflow: ellipsis;
}

/* Открытая выпадашка DataFilter: панель переносится в body, поэтому скоуп — через :has по открытому combobox.
   Radzen ставит items-wrapper inline max-height:200px (≈4 пункта по 48px) — даём до 12 пунктов по 36px.
   Высота фиксированная и меньше экрана: иначе Radzen ужимает панель своим inline max-height, !important
   ему мешает, и панель уезжает за верх экрана или дрожит между положениями сверху/снизу.
   Без .rz-open: Radzen замеряет панель для выбора «вверх/вниз» ДО этого класса (и до переноса в body —
   панель ещё внутри .rz-datafilter), замер должен видеть итоговую высоту */
.rz-datafilter .rz-dropdown-panel .rz-dropdown-items-wrapper,
body:has(.rz-datafilter [aria-expanded="true"]) > .rz-dropdown-panel .rz-dropdown-items-wrapper {
    max-height: min(432px, 50vh) !important;
}

.rz-datafilter .rz-dropdown-panel .rz-dropdown-items li,
body:has(.rz-datafilter [aria-expanded="true"]) > .rz-dropdown-panel .rz-dropdown-items li {
    min-height: 36px;
    padding-block: 6px;
    line-height: 24px;
}

/* У оператора пунктов мало — поиск не нужен */
.rz-datafilter-operator .rz-dropdown-panel .rz-dropdown-filter-container,
body:has(.rz-datafilter-operator[aria-expanded="true"]) > .rz-dropdown-panel .rz-dropdown-filter-container {
    display: none;
}

/* Pager */
.rz-pager .rz-dropdown {
    min-width: 85px; !important;
}

/* Fleet: поля фильтров — 24 px, как чипы второй строки и элементы шапки; шрифт 12px/500, как у чипов. Подсказка
   (Rack…, Host…) — тёмная, а не серая: читается с расстояния; заполненное поле и так выделено fleet-filter--active. */
.fleet-filter-dd,
.fleet-filter-tb {
    font-size: 12px;
    font-weight: 500;
}

.fleet-filter-dd .rz-placeholder,
.fleet-filter-tb::placeholder {
    color: var(--rz-text-color);
    font-weight: 500;
    opacity: 1;
}

/* Allow dropdown panel to expand wider than trigger to fit item text */
.rz-multiselect-panel {
    min-width: max-content !important;
}

/* Выбранные значения идут через запятую (MaxSelectedLabels без предела) и обрезаются многоточием, полный список
   Radzen сам кладёт в title метки. Блок, а не flex: на голом тексте во flex-контейнере text-overflow не
   срабатывает, поэтому центрирование по вертикали — через line-height на всю высоту поля, а вертикальный
   padding корня (7px у Radzen) снят, иначе метке остаётся 8px. */
.rz-dropdown.fleet-filter-dd {
    padding-top: 0 !important;
    padding-bottom: 0 !important;
}
.rz-dropdown.fleet-filter-dd .rz-dropdown-label {
    display: block !important;
    height: 100% !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    line-height: 22px !important;
    overflow: hidden !important;
    white-space: nowrap !important;
    text-overflow: ellipsis !important;
}
.rz-dropdown.fleet-filter-dd .rz-dropdown-trigger {
    display: flex !important;
    align-items: center !important;
    height: 100% !important;
}

/* Fleet: поле фильтра, в котором что-то выбрано/введено, и счётчик «видно / всего» при активном
   фильтре. Сдержанно (рамка + лёгкий тон), чтобы пять активных фильтров не превращали тулбар
   в гирлянду. box-shadow, а не border: не меняет размер 24px-полей и не спорит с border,
   которым material-тема Radzen рисует нижнее подчёркивание поля. */
.fleet-filter--active,
.fleet-filter--active:hover,
.fleet-filter--active:focus,
.fleet-filter--active:focus-within {
    box-shadow: inset 0 0 0 1px var(--rz-primary) !important;
    background-color: var(--rz-primary-lighter) !important;
}
.fleet-count-chip.fleet-filter--active {
    color: var(--rz-primary);
}

/* Badge on pages */
/* Лише власний рядок пункту: у групи це <div>, у сторінки — <a>; вкладені пункти групи правило не зачіпає */
.menu-badge > .rz-navigation-item-wrapper > .rz-navigation-item-link {
    position: relative;
    padding-right: 28px;
}

.menu-badge > .rz-navigation-item-wrapper > .rz-navigation-item-link::after {
    content: var(--badge);
    position: relative;
    min-width: 26px;
    height: 26px;
    line-height: 26px;
    padding: 0 6px;
    border-radius: 999px;
    background: #ff6464;
    color: #fff;
    font-size: 13px;
    font-weight: 600;
    text-align: center;
    white-space: nowrap;
}

/* RadzenButton ButtonStyle */
.rz-button.danger-style {
    background-color: #FFEBEE !important;
    color: #EF5350 !important;
}

.rz-button.primary-style {
    background-color: #E6E8F4 !important;
    color: #3F50B4 !important;
}

.rz-button.light-style {
    background-color: white !important;
    color: black !important;
}

/* RadzenButton DialogAction */
.rz-button.dialog-cancel {
    text-transform: none !important;
    flex: 1 1 0;
    min-width: 0;
    background-color: white !important;
    color: black !important;
    box-shadow: inset 0 0 0 1px #e0e0e0 !important;
}

.rz-button.dialog-confirm {
    text-transform: none !important;
    flex: 1 1 0;
    min-width: 0;
    background-color: rgb(97,0,232) !important;
}

.rz-button.dialog-delete {
    text-transform: none !important;
    flex: 1 1 0;
    min-width: 0;
    background-color: rgb(227, 57, 53) !important;
}

/* Transparent buttons */
.rz-button.transparent {
    background-color: transparent !important;
    color: black !important;
    box-shadow: inset 0 0 0 1px #bcbcbc !important;
}

.rz-button.transparent-violet {
    background-color: transparent !important;
    color: #6100e8 !important;
    box-shadow: inset 0 0 0 1px #b29cd9 !important;
}

.rz-button.trans-border-icon-color { /* transparent with colored border and icon, text is black */
    background-color: transparent !important;
    color: black !important;
    box-shadow: inset 0 0 0 1px var(--color) !important;
    
    & .rz-button-icon-left {
        color: var(--color) !important;
    }
}

.rz-button.trans-text-icon-color { /* transparent with colored text and icon, border is gray */
    background-color: transparent !important;
    color: var(--color) !important;
    box-shadow: inset 0 0 0 1px #dedede !important;
    padding: 5px 0; !important;

    & .rz-button-icon-left {
        color: var(--color) !important;
    }
}

/* Presets button */
.rz-button.selected-preset {
    background-color: #F3E5F5 !important;
    color: #a900fd !important;
    box-shadow: inset 0 0 0 1px #cc92d6 !important;
}

.rz-button.unselected-preset {
    background-color: transparent !important;
    color: #6100e8 !important;
    box-shadow: inset 0 0 0 1px #bcbcbc !important;
}

/* Mass action panel */
.mass-action-panel {
    position: fixed;
    bottom: 85px;
    left: 50%;
    transform: translateX(-50%);
    width: max-content;
    max-width: calc(100% - 250px);
    background: white;
    z-index: 1000;
    padding: 0.5rem 0.5rem;
    border-radius: 10px;
    border: 1px solid #c8c8c8;
}

.mass-action-panel .rz-button:hover {
    background-color: #ececec !important;
}

.mass-action-panel .rz-button {
    text-transform: none !important;
    color: black !important;
    width: max-content;
}

.mass-action-panel .divider {
    width: 1px;
    height: 16px;
    background: rgba(0,0,0,0.5);
    margin: 0 5px;
    display: inline-block;
    vertical-align: middle;
}

/* GamepadControlPad (src/PlayHub.Client/BlazorComponents/GamepadControlPad.razor) — real-time
   joystick control, shared by ScriptStacksPage's mass-action-panel and Fleet's Stream tab (inside
   .fr-modal, which doesn't have .mass-action-panel's own divider rule above, hence the separate
   scope here rather than reusing it). Moved out of ScriptStacksPage.razor's inline <style> when
   the button grid was extracted into this component. */
/* Раскладка DualShock 4 (.gp-ds4) — три колонки: слева L2 над L1 и крестовина, справа R2 над R1 и фигурные кнопки,
   в середине L3 · Share · Touchpad · Options · R3, под ними стики, между стиками PS и подсказка про Shift. Высота средней колонки
   подогнана под боковые (--gp-top считается из них), чтобы пад не рос по высоте. Размеры — переменными, --lg
   переопределяет их для вкладки Stream. */
.gamepad-control-pad .gp-ds4 {
    --gp-cell: 20px;
    --gp-gap: 2px;
    --gp-stick: 60px;
    --gp-knob: 24px;
    --gp-col-gap: 6px;
    /* Промежуток между стиками: под Share · Touchpad · Options, PS и подсказку — ширина пада по содержимому. */
    --gp-center: 100px;
    --gp-cross: calc(var(--gp-cell) * 3 + var(--gp-gap) * 2);
    /* Плоские кнопки: курки и L3/R3. */
    --gp-flat: calc(var(--gp-cell) * 0.75);
    --gp-side-h: calc(var(--gp-cross) + var(--gp-flat) * 2 + var(--gp-gap) * 2);
    --gp-top: calc(var(--gp-side-h) - var(--gp-stick) - var(--gp-gap));
    display: flex;
    align-items: flex-start;
    gap: var(--gp-col-gap);
    user-select: none;
}

/* Общее .small-button растягивает кнопку на ячейку (width/height 100%) — в раскладке DS4 размеры свои. */
.gamepad-control-pad .gp-ds4 .small-button {
    width: var(--gp-cell);
    height: var(--gp-cell);
}

.gamepad-control-pad .gp-ds4 .gp-side {
    display: flex;
    flex-direction: column;
    gap: var(--gp-gap);
}

.gamepad-control-pad .gp-ds4 .gp-shoulder {
    width: var(--gp-cross);
    height: var(--gp-flat);
}

.gamepad-control-pad .gp-ds4 .gp-cross {
    display: grid;
    grid-template-columns: repeat(3, var(--gp-cell));
    grid-template-rows: repeat(3, var(--gp-cell));
    gap: var(--gp-gap);
}

.gamepad-control-pad .gp-ds4 .gp-cross .gp-u { grid-row: 1; grid-column: 2; }
.gamepad-control-pad .gp-ds4 .gp-cross .gp-l { grid-row: 2; grid-column: 1; }
.gamepad-control-pad .gp-ds4 .gp-cross .gp-r { grid-row: 2; grid-column: 3; }
.gamepad-control-pad .gp-ds4 .gp-cross .gp-d { grid-row: 3; grid-column: 2; }

/* Средний блок: колонки «поле · стик · центр · стик · поле». Сверху L3 над левым стиком, Share · Touchpad · Options
   по центру, R3 над правым; ниже стики, между ними PS (сразу под Touchpad) и подсказка про Shift. */
.gamepad-control-pad .gp-ds4 .gp-mid {
    display: grid;
    grid-template-columns: var(--gp-gap) var(--gp-stick) var(--gp-center) var(--gp-stick) var(--gp-gap);
    grid-template-rows: var(--gp-top) var(--gp-stick);
    row-gap: var(--gp-gap);
}

.gamepad-control-pad .gp-ds4 .gp-mid-top {
    grid-area: 1 / 2 / 2 / 5;
    justify-self: center;
    align-self: end;
    display: flex;
    gap: calc(var(--gp-gap) * 2);
}

/* Share · Touchpad · Options — по размеру надписи. */
.gamepad-control-pad .gp-ds4 .gp-mid-top .small-button {
    width: auto;
    height: var(--gp-flat);
    padding: 0 6px;
}

.gamepad-control-pad .gp-ds4 .gp-stick--l { grid-area: 2 / 2; }
.gamepad-control-pad .gp-ds4 .gp-stick--r { grid-area: 2 / 4; }

.gamepad-control-pad .gp-ds4 .gp-mid-center {
    grid-area: 2 / 3;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.gamepad-control-pad .gp-ds4 .gp-ps { border-radius: 50%; flex: none; margin-top: 6px; }

/* touch-action:none — иначе на тач-экране перетаскивание ручки прокручивает модалку вместо стика. */
.gamepad-control-pad .gp-ds4 .gp-stick {
    position: relative;
    flex: none;
    width: var(--gp-stick);
    height: var(--gp-stick);
    border-radius: 50%;
    background: radial-gradient(circle at 50% 45%, #5a5a5a, #2b2b2b 70%);
    box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.55);
    touch-action: none;
    cursor: grab;
}

.gamepad-control-pad .gp-ds4 .gp-stick-knob {
    position: absolute;
    left: 50%;
    top: 50%;
    width: var(--gp-knob);
    height: var(--gp-knob);
    border-radius: 50%;
    background: radial-gradient(circle at 40% 35%, #9a9a9a, #4a4a4a);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
    transform: translate(-50%, -50%);
    transition: transform 0.08s ease-out;
    pointer-events: none;
}

/* Пока ручку тянут — без анимации: она идёт за курсором, а не догоняет его. */
.gamepad-control-pad .gp-ds4 .gp-stick--active { cursor: grabbing; }
.gamepad-control-pad .gp-ds4 .gp-stick--active .gp-stick-knob { transition: none; }

/* L3/R3 — над своими стиками. */
.gamepad-control-pad .gp-ds4 .gp-click {
    width: calc(var(--gp-cell) * 1.4);
    height: var(--gp-flat);
    justify-self: center;
    align-self: end;
}

.gamepad-control-pad .gp-ds4 .gp-click--l { grid-area: 1 / 2; }
.gamepad-control-pad .gp-ds4 .gp-click--r { grid-area: 1 / 4; }

.gamepad-control-pad .gp-ds4 .gp-cross .small-button { background-color: #404040; }
.gamepad-control-pad .gp-ds4 .gp-mid .small-button,
.gamepad-control-pad .gp-ds4 .gp-shoulder { background-color: #808080; }
.gamepad-control-pad .gp-ds4 .small-button:hover { filter: brightness(1.25); }

/* Комбинация (Shift): отмеченные кнопки — в рамке, подсказка подсвечена, пока Shift зажат. */
.gamepad-control-pad .gp-ds4 .small-button.gp-latched {
    outline: 3px solid #F5A623;
    outline-offset: 1px;
}

/* Подсказка про комбинацию — между стиками, под PS. */
.gamepad-control-pad .gp-hint {
    padding: 0 6px;
    font-size: 10.5px;
    line-height: 1.3;
    text-align: center;
    color: var(--rz-text-secondary-color, #5F5E5A);
}

.gamepad-control-pad .gp-hint kbd {
    padding: 0 4px;
    border: 1px solid #C8C7BF;
    border-radius: 3px;
    background: #F1EFE8;
    font-family: inherit;
}

.gamepad-control-pad .gp-hint--active {
    color: #B86E00;
    font-weight: 600;
}

.gamepad-control-pad .small-button {
    width: 100%;
    height: 100%;
    font-size: 10px;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}

.gamepad-control-pad .small-button:hover {
    background-color: #0056b3;
}

/* Large=true (Fleet's Stream tab — see GamepadControlPad.razor's comment on the parameter). The
   base 20px cells were sized for ScriptStacksPage's cramped floating bar; Stream tab has a whole
   dedicated modal, so the same tiny grid there just reads as an afterthought next to the video. */
/* Ширина --lg пада по содержимому — 534px (84 × 2 + 9 × 2 + 3 + 96 + 150 + 96 + 3): по ней выровнены ящики
   Script/Text ниже (.gamepad-script-panel). Меняешь размеры — пересчитай там. */
.gamepad-control-pad--lg .gp-ds4 {
    --gp-center: 150px;
    /* Высота пада = боковой блок (2 курка + крестовина): с этими размерами полоса не выталкивает видео в прокрутку. */
    --gp-cell: 26px;
    --gp-flat: 17px;
    --gp-gap: 3px;
    --gp-stick: 96px;
    --gp-knob: 40px;
    --gp-col-gap: 9px;
}

.gamepad-control-pad--lg .small-button {
    font-size: 13px;
    border-radius: 6px;
}

.gamepad-control-pad--lg .small-button .material-symbols-outlined {
    font-size: 20px;
}

.gamepad-control-pad .divider {
    width: 1px;
    height: 16px;
    background: rgba(0,0,0,0.5);
    margin: 0 5px;
    display: inline-block;
    vertical-align: middle;
}

/* CollapsibleScriptPanel=true's drawer (see GamepadControlPad.razor) — flex-basis:100% forces it
   onto its own row below the button grid instead of trying to squeeze into the flex-wrap row. */
.gamepad-control-pad .gamepad-script-panel {
    flex-basis: 100%;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px dashed rgba(0,0,0,0.15);
}

/* Without this the textarea's own inline width:100% ate the row and squeezed "Gamepad Script
   Guide" (the last flex item) until its text wrapped onto two lines. flex-basis here overrides
   that inline width for sizing purposes, so the textarea takes a fixed(-ish) share instead of all
   of it, and the buttons get flex-shrink:0 so they always keep their natural, single-line width.
   534px = the --lg pad's own rendered width (see .gamepad-control-pad--lg .gp-ds4
   above) — .gamepad-script-panel only ever renders when
   CollapsibleScriptPanel=true, which only Fleet's Stream tab passes (Large=true, i.e. --lg), so
   this can target that exact width directly instead of a generic round number. The point is
   visual: the textarea's right edge now lines up with the button grid's own right edge (where
   the row above it has its "|" divider before "Hide script"), instead of stopping short and
   leaving a gap before Send/Guide. */
.gamepad-control-pad .gamepad-script-panel textarea {
    flex: 0 1 534px;
    min-width: 160px;
}

.gamepad-control-pad .gamepad-script-panel .rz-button {
    flex-shrink: 0;
    white-space: nowrap;
}

/* Ящик «Text» (GamepadControlPad, CollapsibleScriptPanel) — та же полоса, что у ящика скрипта. Поле
   заканчивается там же, где пад и textarea скрипта: 534px пада минус иконка (22px) и зазор (8px),
   так что счётчик и Send стоят в той же колонке, что Send/Guide ящика скрипта. */
.gamepad-control-pad .gamepad-text-panel .gamepad-text-icon {
    flex-shrink: 0;
    width: 22px;
    font-size: 22px;
    color: var(--rz-text-secondary-color, #5F5E5A);
}

.gamepad-control-pad .gamepad-text-panel input {
    flex: 0 1 504px;
    min-width: 160px;
    font-family: monospace;
    font-size: 13px;
}

.gamepad-control-pad .gamepad-text-panel .gamepad-text-count {
    flex-shrink: 0;
    min-width: 40px;
    font-size: 11px;
    color: var(--rz-text-secondary-color, #5F5E5A);
    font-variant-numeric: tabular-nums;
}

/* Presets */
/* fixed внутри RadzenBody (у него transform): top отсчитывается от низа шапки; 140px — под строкой кнопок
   (Pause/Resume/Terminate), чтобы открытая панель их не перекрывала */
.custom-sidebar {
    position: fixed;
    inset: 140px 0 0 auto;
    width: 300px;
    padding: 15px;
    background-color: white;
    box-shadow: -5px -5px 5px rgba(0,0,0,0.1);
    z-index: 1000;
}

/* Scrollbar */
.scrollbar-hidden {
    overflow-y: auto;
    -ms-overflow-style: none;
    scrollbar-width: none;
}
.scrollbar-hidden::-webkit-scrollbar {
    width: 0; height: 0;
}

/* Dashboard */
.dashboard-frame {
    background-color: white;
    border: 1px solid slategray;
    border-radius: 14px;
    padding: 10px;
}

.rz-progressbar.dashboard-progress-bar {
    border-radius: 100px;
    height: 10px;
    
    & .rz-progressbar-value {
        border-radius: 100px;
        background: linear-gradient(to right, #4d45e3, #22d0eb);
    }
}

/* Chip */
.chip {
    height: 24px;
    background-color: rgb(241, 238, 246);
    border-radius: 100px;
    border: 1px solid #b29cd9;
    padding: 0 5px;
}


/* Other */
.btn-grow {
    flex: 1 1 0;
    min-width: 0;
}

.rz-button.keep-case {
    text-transform: none !important;
}

.rz-form-field-content {
    border-color: rgba(0,0,0,0) !important; /* Invisible border on RadzenFormField */
}

@font-face {
    font-family: "Digital";
    src: url('../fonts/digital-7.ttf');
}
/* Fleet page (moved from FleetPage.razor.css — scoped CSS bundle is not linked in App.razor) */
.fleet-toolbar--loading {
    opacity: 0.55;
    pointer-events: none;
    transition: opacity 0.15s;
}

.fleet-group--hidden {
    visibility: hidden;
    pointer-events: none;
}

/* Тулбар — одна строка (фильтры, за ними чипы) с переносом по ширине; набор и размеры элементов от данных не
   меняются (FleetPage), поэтому перенос стабилен. Справа — место под шестерёнку настроек. */
.fleet-toolbar {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding-right: 22px;
}

.fleet-toolbar-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.5rem;
}

/* Чип «Freeze Racks» (стеллажи без совпадений при фильтре): фиксированная ширина, в одну строку —
   смена состояния не двигает тулбар. */
.fleet-freeze-racks {
    width: 104px;
    white-space: nowrap;
    flex-shrink: 0;
}

.fleet-freeze-short {
    display: none;
}

/* Место под спиннер загрузки — занято всегда. */
.fleet-toolbar-spinner {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex-shrink: 0;
}

/* Шапка MainLayout: toggle | слот HeaderSlot (часы или блок страницы) | профиль. 1fr auto 1fr — часы ровно
   по центру; блок страницы (.ph-header-page) прижимается к toggle и забирает всю середину. */
.ph-header {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    /* Страница с блоком в шапке ужимает его и прячет имя профиля по ширине всей шапки (FleetPage). */
    container: ph-header / inline-size;
}

.ph-header:has(.ph-header-page) {
    grid-template-columns: auto minmax(0, 1fr) auto;
}

.ph-header-center {
    min-width: 0;
}

/* Не влез даже компактный блок страницы — обрезаем, а не наезжаем на индикатор и профиль. */
.ph-header:has(.ph-header-page) .ph-header-center {
    overflow: hidden;
}

/* Обрыв хабов (HubConnectionHealth) — слева от профиля, на всех страницах. */
.ph-hub-reconnecting {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    height: 26px;
    padding: 0 10px;
    border-radius: 13px;
    background: var(--rz-warning);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

/* На странице со своим блоком в шапке (Fleet) на узком окне — только иконка, текст в title: иначе блок страницы
   залез бы под индикатор. */
@media (max-width: 1599px) {
    .ph-header:has(.ph-header-page) .ph-hub-reconnecting {
        padding: 0 4px;
    }

    .ph-header:has(.ph-header-page) .ph-hub-reconnecting-text {
        display: none;
    }
}

/* Элементы управления Fleet в шапке (FleetPage → HeaderSlot) — на фоне primary. */
.fleet-header-bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-left: 0.25rem;
    white-space: nowrap;
}

/* Все элементы шапки — 24 px, как кнопки ExtraSmall. Ширина — под текст с фильтром «видно / всего» (~228 px
   при 13px), чтобы включение фильтра не сдвигало соседей. */
.fleet-header-bar .fleet-count-chip {
    min-width: 240px;
    height: 24px;
    font-size: 13px;
}

/* Компактная шапка (@container fleet-header) — в FleetPage.razor: порог зависит от состава блока. */

.fleet-header-bar .fleet-count-chip:not(.fleet-filter--active) {
    color: #fff;
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.35);
}

/* При фильтре общий .fleet-filter--active дал бы полупрозрачный primary с primary-текстом — на фоне шапки
   того же цвета счётчик пропадал. Здесь — белый, как подсвеченный элемент. */
.fleet-header-bar .fleet-count-chip.fleet-filter--active {
    color: var(--rz-primary);
    background-color: #fff !important;
    border-color: #fff;
    box-shadow: none !important;
}

/* Выбранная кнопка (Primary) сливалась бы с фоном шапки того же цвета. */
.fleet-header-bar .rz-button.rz-primary {
    background-color: var(--rz-primary-darker);
    box-shadow: inset 0 0 0 1px #fff;
}

.fleet-header-label {
    font-size: 12px;
    color: rgba(255, 255, 255, 0.85);
}

.fleet-header-bar .rz-progressbar-circular-value {
    stroke: #fff;
}

/* Число в чипе: цифры одной ширины, место под максимум фермы (min-width из FleetPage.ChipCountDigits). */
.fleet-chip-count {
    display: inline-block;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Fleet Settings: все вкладки в одной ячейке грида — высота по самой большой, окно не прыгает при
   переключении. Radzen держит неактивные панели в DOM с инлайновым display:none — их оставляем в потоке
   невидимыми (visibility:hidden убирает их и из фокуса). */
.fleet-settings-tabs .rz-tabview-panels {
    display: grid;
}

.fleet-settings-tabs .rz-tabview-panel {
    grid-area: 1 / 1;
}

.fleet-settings-tabs .rz-tabview-panel[style*="display:none"],
.fleet-settings-tabs .rz-tabview-panel[style*="display: none"] {
    display: block !important;
    visibility: hidden;
}

/* Чип с нулём: не исчезает, а гаснет — соседи не сдвигаются. Кликабелен. */
.fleet-chip--muted {
    opacity: 0.45;
}

.fleet-count-chip {
    display: inline-flex;
    align-items: center;
    height: 22px;
    padding: 0 8px;
    border: 1px solid var(--rz-border-color);
    border-radius: var(--rz-border-radius, 4px);
    font-size: 11px;
    font-weight: 500;
    color: var(--rz-text-secondary-color);
    background: var(--rz-base-background-color);
    white-space: nowrap;
    flex-shrink: 0;
    user-select: none;
    font-variant-numeric: tabular-nums;
}

.fleet-alert-chip {
    display: inline-flex;
    align-items: center;
    height: 24px;
    padding: 4px 8px;
    border: none;
    border-radius: var(--rz-border-radius, 4px);
    font-size: 12px;
    font-weight: 500;
    color: #fff;
    background: #D32F2F;
    white-space: nowrap;
    flex-shrink: 0;
    user-select: none;
    cursor: pointer;
    transition: box-shadow 0.15s;
}

.fleet-alert-chip:hover {
    box-shadow: 0 2px 6px rgba(211, 47, 47, 0.5), 0 1px 3px rgba(211, 47, 47, 0.4);
}

.fleet-alert-chip.active {
    outline: 2px solid #D32F2F;
    outline-offset: 1px;
}

/* Алертов нет: чип не исчезает, а гаснет до вида соседних Light-кнопок «WF …»; включённый фильтр
   Alerted при этом — синий, как выбранная кнопка (Primary), чтобы его было видно и можно было отжать. */
.fleet-alert-chip--idle {
    color: var(--rz-on-base-200, #333);
    background: var(--rz-base-200, #e9ecef);
}

.fleet-alert-chip--idle:hover {
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}

.fleet-alert-chip--idle.active {
    color: var(--rz-on-primary, #fff);
    background: var(--rz-primary);
    outline: none;
}

/* Match Pending/Done chips — same shape as .fleet-alert-chip, different hues. Both colors are
   picked dark enough to keep white chip text at >=4.5:1 contrast (unlike the canvas's lighter
   #97C459 "Done" glyph fill, which only ever sits behind a colored square, not white text). */
.fleet-chip {
    display: inline-flex;
    align-items: center;
    height: 24px;
    padding: 4px 8px;
    border: none;
    border-radius: var(--rz-border-radius, 4px);
    font-size: 12px;
    font-weight: 500;
    color: #fff;
    white-space: nowrap;
    flex-shrink: 0;
    user-select: none;
    cursor: pointer;
    transition: box-shadow 0.15s;
}

/* Чип пресета: цвет — из пресета (общие пресеты в БД, произвольный #RRGGBB), инлайн-переменными
   style="--chip-bg:#RRGGBB; --chip-fg:#000000|#FFFFFF". Текст — чёрный/белый по контрасту WCAG
   (UIExtensions.GetWcagContrastColor), обводка активного и тень наведения — из того же цвета. */
.fleet-chip--preset { background: var(--chip-bg, #546E7A); color: var(--chip-fg, #FFFFFF); }
.fleet-chip--preset:hover {
    box-shadow: 0 2px 6px color-mix(in srgb, var(--chip-bg, #546E7A) 50%, transparent),
                0 1px 3px color-mix(in srgb, var(--chip-bg, #546E7A) 40%, transparent);
}
.fleet-chip--preset.active { outline: 2px solid var(--chip-bg, #546E7A); outline-offset: 1px; }
/* Открыт редактор пресета (превью): чипы не переключают активный пресет, пока его не закончат. */
.fleet-chip--preset.disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; }
.fleet-stage-preset-label.disabled { cursor: not-allowed; }
.fleet-stage-preset-label.disabled:hover { background: none; }
/* Список пресетов не загрузился — чип-кнопка повтора; ключ активного пресета при этом не теряется. */
.fleet-chip--error {
    background: var(--rz-base-background-color, #fff);
    color: var(--rz-danger, #B3261E);
    box-shadow: inset 0 0 0 1.5px var(--rz-danger, #B3261E);
}

/* Конструктор условий по стадиям (FleetStageRulesPanel) — поповер под кнопкой «Stages…».
   Подложка на весь экран ловит клик мимо поповера и закрывает его. */
.fleet-stage-anchor {
    position: relative;
    display: inline-flex;
    flex-shrink: 0;
}
.fleet-stage-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1000;
}
.fleet-stage-panel {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    z-index: 1001;
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 560px;
    padding: 8px;
    background: var(--rz-base-background-color);
    border: 1px solid var(--rz-border-color);
    border-radius: var(--rz-border-radius, 4px);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
    font-size: 11px;
}
.fleet-stage-row {
    display: flex;
    align-items: center;
    gap: 4px;
}
.fleet-stage-row .rz-dropdown,
.fleet-stage-row .rz-numeric {
    font-size: 11px;
    height: 26px;
}
.fleet-stage-unit {
    color: var(--rz-text-secondary-color);
    white-space: nowrap;
}
.fleet-stage-sentence {
    margin: -2px 0 2px 2px;
    color: var(--rz-text-color);
    font-size: 11px;
    font-style: italic;
}
.fleet-stage-hint {
    color: var(--rz-text-secondary-color);
    font-size: 11px;
}
.fleet-stage-footer {
    display: flex;
    align-items: center;
    gap: 4px;
}
.fleet-stage-save {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
}
.fleet-stage-section {
    display: flex;
    justify-content: space-between;
    margin-top: 4px;
    padding-top: 6px;
    border-top: 1px solid var(--rz-border-color);
    font-weight: 600;
    color: var(--rz-text-secondary-color);
}
.fleet-stage-presets {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.fleet-stage-preset {
    display: flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
}
.fleet-stage-preset-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
    padding: 2px 4px;
    border-radius: var(--rz-border-radius, 4px);
}
.fleet-stage-preset-label:hover {
    background: var(--rz-base-200, rgba(0, 0, 0, 0.06));
}
.fleet-stage-preset--active .fleet-stage-preset-label {
    font-weight: 600;
    color: var(--rz-primary);
    background: var(--rz-primary-lighter);
}
.fleet-stage-active-preset {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 4px 6px;
    border-radius: var(--rz-border-radius, 4px);
    background: var(--rz-primary-lighter);
}
/* Кнопка «Stages: <пресет> +N» — длинное имя своего пресета не должно раздувать тулбар. */
.fleet-stage-btn {
    max-width: 240px;
}
.fleet-stage-btn .rz-button-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Редактор пресета в поповере Stages (свой буфер, отдельно от рабочих условий). */
.fleet-stage-editor {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 8px;
    border-radius: var(--rz-border-radius, 4px);
    background: var(--rz-primary-lighter);
}
.fleet-stage-editor-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
}
.fleet-stage-editor-fields {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px 10px;
}
.fleet-stage-field {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.fleet-stage-field--wide { grid-column: 1 / -1; }
.fleet-stage-field > label {
    font-size: 11px;
    color: var(--rz-text-secondary-color);
}
.fleet-stage-colors {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 5px;
}
.fleet-stage-swatch {
    width: 20px;
    height: 20px;
    padding: 0;
    border-radius: 50%;
    border: 2px solid var(--rz-base-background-color, #fff);
    box-shadow: 0 0 0 1px var(--rz-border-color);
    cursor: pointer;
}
.fleet-stage-swatch--selected { box-shadow: 0 0 0 2px var(--rz-primary); }
.fleet-stage-hex {
    font-family: ui-monospace, Consolas, monospace;
    font-size: 11px;
    color: var(--rz-text-secondary-color);
}
.fleet-stage-editor-actions {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    margin-left: auto;
}
.fleet-stage-error { color: var(--rz-danger); font-size: 11px; }
/* Плашки: импорт локальных пресетов (warn) и конфликт/удаление редактируемого пресета (error). */
.fleet-stage-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding: 6px 8px;
    border-radius: var(--rz-border-radius, 4px);
    background: #FFF4E0;
    color: #8A5300;
}
.fleet-stage-banner > span { flex: 1; min-width: 200px; }
.fleet-stage-banner--error { background: #FDECEA; color: #A12A20; }
/* Свотч цвета у названия пресета в списке. */
.fleet-stage-dot {
    width: 10px;
    height: 10px;
    border-radius: 3px;
    flex-shrink: 0;
}
.fleet-stage-lock,
.fleet-stage-meta {
    color: var(--rz-text-secondary-color);
    font-size: 10.5px;
    white-space: nowrap;
}
.fleet-stage-preset--editing {
    background: var(--rz-primary-lighter);
    border-radius: var(--rz-border-radius, 4px);
}
.fleet-stage-preset--unsupported .fleet-stage-preset-label {
    color: var(--rz-text-secondary-color);
    text-decoration: line-through;
    cursor: not-allowed;
}
.fleet-stage-unsupported {
    color: var(--rz-danger);
    font-size: 10.5px;
    white-space: nowrap;
}
.fleet-stage-confirm {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--rz-danger);
    white-space: nowrap;
}
/* «Previewing: <name>» над полотном, пока открыт редактор пресета. */
.fleet-preview-banner {
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 10px;
    border-radius: var(--rz-border-radius, 4px);
    background: var(--rz-primary);
    color: var(--rz-on-primary, #fff);
    font-size: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

/* «⚠ N not on grid» — только информирует, не кликается. Контурный, а не заливной: отличается
   от чипов-фильтров рядом (заливной янтарный Match pending — почти тот же оттенок). */
.fleet-chip--warn {
    background: var(--rz-base-background-color, #fff);
    color: #8A5300;
    box-shadow: inset 0 0 0 1.5px #8A5300;
    cursor: help;
}
.fleet-chip--warn:hover { box-shadow: inset 0 0 0 1.5px #8A5300; }

/* fixed, а не absolute: .fleet-viewport обрезает (overflow:hidden) и кончается выше низа страницы.
   Блок-контейнер fixed — RadzenBody (у него transform), его низ = низ окна, так что bottom:16px —
   тот же отступ, что у кнопки Refresh в шапке от верха; left/right — паддинг RadzenBody 16px + 12px. */
.fleet-action-bar {
    position: fixed;
    bottom: 16px;
    /* inset + auto margins + fit-content centers the bar; left:50%+translate would
       cap shrink-to-fit width at half the container and force premature wrapping */
    left: 28px;
    right: 28px;
    margin-inline: auto;
    width: fit-content;
    max-width: 100%;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 4px;
    padding: 4px 8px;
    background: var(--rz-base-background-color, #fff);
    border: 1px solid var(--rz-border-color, #e0e0e0);
    border-radius: 8px;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
    animation: fleet-action-bar-in 0.15s ease-out;
}

/* Распорка в конце скролла канваса (fleet-canvas.js init): полоса ~37px заходит на канвас ~29px
   (её низ на 8px ниже низа канваса) + запас. Постоянная, чтобы выделение ничего не сдвигало;
   при переносе полосы в 2 строки не хватит. */
.fleet-canvas-bottom-spacer {
    height: 40px;
}

@keyframes fleet-action-bar-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* clip-path triangle, not the border-trick (border-top + border-right:transparent): a border
   only PAINTS a triangle, the element's actual hit-box stays the full square, so the "invisible"
   transparent half was still swallowing clicks meant for the first device glyph right next to it.
   clip-path clips hit-testing too — clicks outside the polygon fall through to the canvas below. */
.fleet-select-all-corner {
    position: absolute;
    top: 0;
    left: 0;
    width: 26px;
    height: 26px;
    background: var(--rz-primary, #185FA5);
    clip-path: polygon(0 0, 100% 0, 0 100%);
    cursor: pointer;
    z-index: 6;
    opacity: 0.55;
    transition: opacity 0.12s ease-out;
}

.fleet-select-all-corner:hover {
    opacity: 0.9;
}

/* Same look as .fleet-action-bar, but for the account selection inside the glyph modal's Grid
   tab. fleet.js physically relocates this element's DOM node to be a direct child of .fr-modal
   (fleetRenderer.portalAccountBar — see its comment for why: RadzenBody's transform traps
   position:fixed and z-index tricks from ever out-stacking a document.body-appended modal, so
   moving the node is the only reliable fix). Once inside .fr-modal, sticky is all that's needed —
   normal DOM nesting already keeps it above the modal's own content, and below Radzen's
   dialog/context-menu stack (those live outside the modal entirely, at a higher global z-index). */
.fleet-account-action-bar {
    position: sticky;
    bottom: 0;
    margin-top: 12px;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 6px;
    padding: 8px 10px;
    background: #fff;
    border: 1px solid #C8C7BF;
    border-radius: 8px;
    box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.15);
}

.fleet-action-divider {
    width: 1px;
    align-self: stretch;
    background: var(--rz-border-color, #e0e0e0);
    margin: 0 2px;
    flex-shrink: 0;
}

/* Same portalling trick as .fleet-account-action-bar (see its comment), for the glyph modal's
   Stream tab: "Take control" toggle + GamepadControlPad, physically relocated into .fr-modal by
   fleetRenderer.portalStreamControls (and into the Chains Stream dialog by chains.js). The bar lives
   inside DeviceStreamControls' always-rendered root, which carries `hidden` while inactive. */
/* Column, not row: state controls (paused/resume/take control) and input (the gamepad) are two
   distinct concerns, and stacking them explicitly keeps the divider between them in a fixed place
   instead of wherever flex-wrap happened to break the old single-row layout. */
/* width:972px (960px native frame + deviceStream.js .dstr's 6px bezel on each side — keep in sync with
   that padding) + margin:0 auto — both rows inside are left-aligned content (the status row, the
   button grid), not centered, so letting this bar stretch to the modal's full 1540px width (needed
   for Grid/Glyph, see fleet.js's .fr-modal comment) left it reading as visually detached from the
   video frame sitting right above it: a wide empty stretch of card to the right of a narrower
   control cluster. A literal px value, not width:fit-content, on purpose: fit-content re-measures
   on every content change, and GamepadControlPad's collapsible script drawer (RadzenTextArea
   width:100%) has no real intrinsic width of its own to measure against once its normal 100%
   parent is itself being fit-content'd — the browser fell back to some default textarea size,
   which made this bar (and everything centered inside it, including the button grid) visibly jump
   sideways every time the drawer opened/closed. Pinning the width instead gives that 100% a real
   number to resolve against, so the drawer opens in place. */
.fleet-stream-control-bar {
    position: sticky;
    bottom: 0;
    margin: 12px auto 0;
    z-index: 5;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 8px 12px;
    width: 972px;
    max-width: 100%;
    box-sizing: border-box;
    background: #fff;
    border: 1px solid #C8C7BF;
    border-radius: 8px;
    box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.15);
}

.fleet-stream-status-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.fleet-stream-gamepad-row {
    display: flex;
    padding-top: 6px;
    border-top: 1px solid #F1EFE8;
}

/* Таблиця параметрів: колонки по вмісту, ширину, що лишилась, забирає Description (переноситься) */
.params-grid .rz-grid-table { table-layout: auto !important; width: 100%; }
.params-grid th, .params-grid td { white-space: nowrap; }
.params-grid th.params-desc, .params-grid td.params-desc { white-space: normal; min-width: 240px; }
/* Radzen ставить nowrap на сам .rz-cell-data, тож перенос треба вмикати на ньому; anywhere — для довгих слів без пробілів */
.params-grid td.params-desc .rz-cell-data { white-space: normal; overflow-wrap: anywhere; }

/* Pipelines: рядки дуже широкі — рядок під курсором підсвічується; вибраний рядок Radzen (rz-state-highlight) не чіпаємо */
.pipelines-grid .rz-data-row:not(.rz-state-highlight):hover > td { background-color: #E3EEFB !important; }
.pipelines-grid-name { cursor: pointer; }
.pipelines-grid-name:hover { text-decoration: underline; }
/* Відбиток вмісту версії (pipeline-version-sync): моноширинний, приглушений, щоб порівнювати середовища на око */
.pipelines-fingerprint { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.8em; color: var(--rz-text-secondary-color, #6c757d); }

/* PipelineTreePicker: на кнопці текст ліворуч, стрілка праворуч; довгі назви — з «…», повні — у title */
.pipeline-tree-picker-button > .rz-button-box { flex: 1 1 auto; min-width: 0; justify-content: space-between; }
.pipeline-tree-picker-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
/* Попап не вужчий за кнопку (min-width ставить Radzen через syncWidth), інакше — за вмістом; !important перебиває width,
   яку Radzen пише інлайном */
.pipeline-tree-picker-popup { width: max-content !important; max-width: min(460px, calc(100vw - 16px)); }
/* Відступи мітки Radzen переносимо на власний рядок: тоді клік будь-де по мітці — клік по рядку з нашим обробником */
.rz-treenode-label.pipeline-tree-picker-label { flex: 1 1 auto; min-width: 0; padding: 0; }
.pipeline-tree-picker-row {
    display: flex; align-items: center; gap: 0.25rem; width: 100%; min-width: 0; box-sizing: border-box;
    padding-block: var(--rz-tree-node-padding-block); padding-inline: var(--rz-tree-node-padding-inline);
}
.pipeline-tree-picker-item { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
