Files
KindClustersDashboard/app/static/style.css
T
inecs 9363d4e076 UI: навигация, документация, favicon; журнал развёртывания; валидация формы
- Меню API: ссылка «Теги образов», обёртка прокрутки пилюль, z-index и padding против обрезки hover
- Документация: ширина колонки как у дашборда (72rem)
- Favicon SVG + GET /favicon.ico, link в base.html
- provision_log.json, GET .../provision-log, кнопка в таблице кластеров
- Валидация create: имя, workers, тег kindest; модалка alert
- Прочие правки из сессии (clusters, job_store, стили, шаблоны)
2026-04-04 08:15:15 +03:00

1678 lines
35 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Минимальные стили веб-интерфейса Kind Clusters Dashboard.
Автор: Сергей Антропов — https://devops.org.ru */
/* Тема: тёмная по умолчанию; светлая — html[data-theme="light"]. Переключатель в шапке + localStorage. */
html {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, sans-serif;
}
html:not([data-theme="light"]) {
color-scheme: dark;
--bg: #0f1419;
--fg: #e7ecf1;
--muted: #8b98a5;
--card: #1a2332;
--accent: #3b82f6;
--border: #2f3d52;
}
/* Светлая тема: основной текст и акценты в синей гамме (как тёмная), без «чистого» чёрного */
html[data-theme="light"] {
color-scheme: light;
--bg: #eef2fb;
--fg: #172c46;
--muted: #5a6f8f;
--card: #ffffff;
--border: #b8c9e8;
--accent: #3b82f6;
}
body {
margin: 0;
padding: 0;
background: var(--bg);
color: var(--fg);
line-height: 1.45;
}
body.modal-open {
overflow: hidden;
}
/* Пропуск к основному содержимому (a11y) */
.skip-link {
position: absolute;
left: -9999px;
top: 0.5rem;
z-index: 200;
padding: 0.5rem 1rem;
background: var(--accent);
color: #fff;
border-radius: 6px;
font-weight: 600;
}
.skip-link:focus {
left: 0.5rem;
outline: 2px solid var(--fg);
outline-offset: 2px;
}
/* Верхняя навигация */
.top-nav {
border-bottom: 1px solid var(--border);
background: var(--card);
}
.top-nav-inner {
max-width: 72rem;
margin: 0 auto;
padding: 0.65rem 1.25rem;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.nav-brand {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
}
.nav-brand-link {
text-decoration: none;
color: inherit;
}
.nav-brand-link:hover .nav-title {
color: var(--accent);
}
.nav-brand-link:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
border-radius: 6px;
}
.nav-logo-img {
width: 1.75rem;
height: 1.75rem;
flex-shrink: 0;
display: block;
}
.nav-title {
font-weight: 700;
font-size: 1.05rem;
}
.nav-tag {
font-size: 0.8rem;
}
/* Шапка: один flex-ряд, все пилюли + API + тема с align-items: center */
.nav-links {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: 0.45rem;
}
.nav-links.top-nav-actions {
justify-content: flex-end;
flex: 1 1 auto;
min-width: 0;
/* Не overflow-x: здесь — иначе обрезается .nav-dropdown__menu (absolute под кнопкой API). */
overflow: visible;
}
/* Горизонтальная прокрутка только подписей «Панель» / «Документация», без выпадающего меню. */
.nav-links-scroll {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: 0.45rem;
min-width: 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: thin;
/*
* При overflow-x: auto вертикаль становится auto — обрезает translateY(-1px) и тень у .nav-pill:hover.
* Внутренний padding даёт место сверху/снизу; API вне .nav-links-scroll, там обрезки не было.
* z-index как у .nav-dropdown — общий stacking context при overflow.
*/
padding-top: 0.45rem;
padding-bottom: 0.45rem;
position: relative;
z-index: 160;
}
/* Выпадающее меню «API»: Swagger, ReDoc, Health в отдельном окне */
.nav-dropdown {
position: relative;
z-index: 160;
display: inline-flex;
align-items: center;
flex-shrink: 0;
align-self: center;
}
.nav-dropdown__trigger {
font: inherit;
cursor: pointer;
margin: 0;
}
.nav-dropdown__trigger::after {
content: "▾";
margin-left: 0.35rem;
font-size: 0.72em;
opacity: 0.85;
display: inline-block;
transition: transform 0.18s ease;
vertical-align: middle;
}
.nav-dropdown--open .nav-dropdown__trigger::after {
transform: rotate(-180deg);
}
.nav-dropdown__menu {
position: absolute;
top: calc(100% + 0.35rem);
right: 0;
z-index: 160;
min-width: 11rem;
margin: 0;
padding: 0.35rem 0;
list-style: none;
background: var(--card);
border: 1px solid var(--border);
border-radius: 10px;
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
}
html[data-theme="light"] .nav-dropdown__menu {
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.12);
}
.nav-dropdown__menu[hidden] {
display: none !important;
}
.nav-dropdown__menu li {
margin: 0;
}
.nav-dropdown__link {
display: flex;
align-items: center;
width: 100%;
box-sizing: border-box;
padding: 0.48rem 1rem;
font-size: 0.8125rem;
font-weight: 600;
text-decoration: none;
color: var(--fg);
background: transparent;
border: none;
cursor: pointer;
font-family: inherit;
}
.nav-dropdown__link:hover,
.nav-dropdown__link:focus-visible {
background: rgba(59, 130, 246, 0.14);
color: var(--accent);
outline: none;
}
.nav-dropdown__link.nav-pill--ext::after {
content: "↗";
margin-left: auto;
padding-left: 0.5rem;
font-size: 0.72em;
opacity: 0.72;
font-weight: 700;
}
/* Переключатель светлая / тёмная тема (в конце шапки, высота как у пилюль) */
.theme-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.42rem 0.75rem;
min-width: 2.5rem;
border-radius: 999px;
font-size: 0.8125rem;
font-weight: 600;
border: 1px solid var(--border);
background: rgba(0, 0, 0, 0.18);
color: var(--fg);
cursor: pointer;
transition:
background 0.18s ease,
border-color 0.18s ease,
color 0.18s ease,
box-shadow 0.18s ease;
}
html[data-theme="light"] .theme-toggle {
background: rgba(59, 130, 246, 0.1);
border-color: rgba(59, 130, 246, 0.28);
color: var(--accent);
}
.theme-toggle:hover {
border-color: var(--accent);
background: rgba(59, 130, 246, 0.14);
color: var(--accent);
box-shadow: 0 2px 12px rgba(59, 130, 246, 0.2);
}
.theme-toggle:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
}
.theme-toggle__icon {
display: block;
width: 1.1rem;
height: 1.1rem;
flex-shrink: 0;
}
html[data-theme="light"] .theme-toggle__icon--sun {
display: none;
}
html[data-theme="light"] .theme-toggle__icon--moon {
display: block;
}
html:not([data-theme="light"]) .theme-toggle__icon--sun {
display: block;
}
html:not([data-theme="light"]) .theme-toggle__icon--moon {
display: none;
}
/* Визуально кнопка темы сидела ниже пилюль (<a> vs <button>); поднимаем на 0.75rem */
.theme-toggle--nav-end {
flex-shrink: 0;
align-self: center;
padding-left: 0.65rem;
padding-right: 0.65rem;
min-height: calc(0.8125rem * 1.35 + 0.84rem);
box-sizing: border-box;
transform: translateY(-0.3rem);
}
/* Пункты меню: компактные «пилюли» */
.nav-link.nav-pill {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.42rem 0.95rem;
border-radius: 999px;
font-size: 0.8125rem;
font-weight: 600;
letter-spacing: 0.02em;
text-decoration: none;
border: 1px solid var(--border);
background: rgba(0, 0, 0, 0.18);
color: var(--fg);
transition:
background 0.18s ease,
border-color 0.18s ease,
color 0.18s ease,
box-shadow 0.18s ease,
transform 0.12s ease;
}
.nav-link.nav-pill:hover {
text-decoration: none;
border-color: var(--accent);
background: rgba(59, 130, 246, 0.14);
color: var(--accent);
box-shadow: 0 2px 12px rgba(59, 130, 246, 0.2);
transform: translateY(-1px);
}
.nav-link.nav-pill:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
}
/* Активный раздел: «Панель» на дашборде или «Документация» на /documentation */
.nav-link.nav-pill.nav-pill--active:not(.nav-pill--ext) {
border-color: rgba(59, 130, 246, 0.85);
box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.35), 0 2px 14px rgba(59, 130, 246, 0.18);
background: rgba(59, 130, 246, 0.16);
color: #93c5fd;
}
html[data-theme="light"] .nav-link.nav-pill {
background: rgba(59, 130, 246, 0.1);
border-color: rgba(59, 130, 246, 0.28);
color: var(--fg);
}
html[data-theme="light"] .nav-link.nav-pill.nav-pill--active:not(.nav-pill--ext) {
color: #1d4ed8;
background: rgba(59, 130, 246, 0.18);
border-color: rgba(37, 99, 235, 0.45);
}
/* Внешние окна: иконка «новое окно» */
.nav-link.nav-pill--ext::after {
content: "↗";
margin-left: 0.35rem;
font-size: 0.72em;
opacity: 0.72;
font-weight: 700;
}
/* Обычная ссылка без пилюли (если понадобится) */
.nav-link:not(.nav-pill) {
color: var(--accent);
text-decoration: none;
font-size: 0.9rem;
}
.nav-link:not(.nav-pill):hover {
text-decoration: underline;
}
.app-main {
max-width: 72rem;
margin: 0 auto;
padding: 1.25rem;
outline: none;
}
.app-footer {
max-width: 72rem;
margin: 0 auto;
padding: 1rem 1.25rem 2rem;
border-top: 1px solid var(--border);
font-size: 0.85rem;
}
.footer-inner {
text-align: center;
}
.footer-line {
margin: 0 0 0.5rem;
}
.footer-copyright {
margin: 0;
font-size: 0.9rem;
color: var(--fg);
}
.footer-copyright a {
color: var(--accent);
font-weight: 600;
}
/* Верхний ряд: CTA «Создать кластер» и статистика по ~50% ширины */
.top-dashboard-row {
display: grid;
gap: 1rem;
margin-bottom: 1rem;
align-items: stretch;
}
@media (min-width: 960px) {
.top-dashboard-row {
grid-template-columns: 1fr 1fr;
}
}
/* Левая карточка — переход к форме создания */
.cta-hero-card {
margin-bottom: 0;
display: flex;
align-items: center;
justify-content: center;
min-height: min(16rem, 42vh);
}
.cta-hero-inner {
text-align: center;
padding: 1.35rem 1.25rem;
width: 100%;
max-width: 26rem;
margin: 0 auto;
}
.cta-hero-card .page-title {
margin: 0 0 0.5rem;
font-size: clamp(1.35rem, 2.5vw, 1.65rem);
}
.cta-hero-lead {
margin: 0 0 2.15rem;
font-size: 0.92rem;
line-height: 1.5;
max-width: 22rem;
margin-left: auto;
margin-right: auto;
}
.cta-hero-inner .btn-cta-large {
margin-top: 0.15rem;
}
.stats-side-card {
display: flex;
flex-direction: column;
gap: 0.85rem;
}
.stats-side-card h2 {
margin: 0;
flex-shrink: 0;
font-size: 1.05rem;
}
/* Пояснение: узлы kind и метрики идут через Docker или Podman (CONTAINER_CLI) */
.stats-engine-hint {
margin: 0;
flex-shrink: 0;
font-size: 0.8rem;
line-height: 1.35;
}
/* Одна карточка «Ошибка» на всю ширину блока статистики (health или /stats) */
.stats-failure-wrap {
width: 100%;
flex-shrink: 0;
}
.stats-failure-card {
width: 100%;
box-sizing: border-box;
min-height: 4.75rem;
padding: 0.85rem 0.65rem;
}
/* Среда: Docker API · Kind · Kubectl · Каталогов — один ряд, 4 колонки */
.stats-env-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.65rem;
flex-shrink: 0;
}
@media (max-width: 520px) {
.stats-env-cards {
grid-template-columns: 1fr;
}
}
.stats-env-placeholder {
grid-column: 1 / -1;
margin: 0;
font-size: 0.85rem;
text-align: center;
padding: 0.5rem 0;
}
.stats-env-card {
min-height: 4.5rem;
padding: 0.65rem 0.4rem;
}
.stats-env-card--ok {
border-color: rgba(34, 197, 94, 0.45);
background: rgba(34, 197, 94, 0.08);
}
.stats-env-card--bad {
border-color: rgba(239, 68, 68, 0.45);
background: rgba(239, 68, 68, 0.08);
}
.stats-env-card--bad .stats-tile__value {
color: #f87171;
}
html[data-theme="light"] .stats-env-card--bad .stats-tile__value {
color: #b91c1c;
}
.stats-env-card__detail {
display: block;
font-size: 0.7rem;
line-height: 1.3;
margin-top: 0.35rem;
text-align: center;
word-break: break-word;
}
.stats-env-card--metric {
border-color: var(--border);
background: rgba(0, 0, 0, 0.08);
}
html[data-theme="light"] .stats-env-card--metric {
background: rgba(59, 130, 246, 0.07);
}
/* Метрики: по 4 карточки в ряд (4 метрики — один ряд) */
.stats-metrics-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.65rem;
flex: 1 1 auto;
align-content: start;
}
@media (max-width: 520px) {
.stats-metrics-grid {
grid-template-columns: repeat(2, 1fr);
}
}
.stats-tile {
border-radius: 10px;
padding: 0.72rem 0.45rem;
text-align: center;
display: flex;
flex-direction: column;
gap: 0.3rem;
justify-content: center;
min-height: 4.1rem;
border: 1px solid var(--border);
background: rgba(0, 0, 0, 0.1);
}
html[data-theme="light"] .stats-tile {
background: rgba(59, 130, 246, 0.07);
}
.stats-tile__value {
font-size: 1.28rem;
font-weight: 700;
font-variant-numeric: tabular-nums;
color: var(--accent);
line-height: 1.15;
}
.stats-tile__label {
font-size: 0.68rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.045em;
color: var(--muted);
line-height: 1.25;
}
/* Метрики узла (общие стили: донаты на главной и раскрытие под строкой кластера) */
.stats-node-res {
margin-bottom: 0.45rem;
padding: 0.35rem 0.45rem;
border-radius: 6px;
background: rgba(0, 0, 0, 0.12);
border: 1px solid var(--border);
}
.stats-node-res code {
display: block;
font-size: 0.76rem;
margin-bottom: 0.25rem;
word-break: break-all;
}
.stats-node-res__metrics {
margin: 0;
padding-left: 1rem;
list-style: disc;
}
.stats-node-res__metrics li {
margin: 0.1rem 0;
}
.stats-node-res__metrics strong {
font-weight: 600;
color: var(--muted);
}
html[data-theme="light"] .stats-node-res {
background: rgba(59, 130, 246, 0.07);
}
.btn-cta-large {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: min(22rem, 100%);
margin-top: 0;
padding: 0.95rem 2.25rem;
font-size: 1.15rem;
font-weight: 650;
border-radius: 12px;
text-decoration: none;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
border: 1px solid transparent;
background: var(--accent);
color: #fff;
cursor: pointer;
}
a.btn-cta-large:hover {
filter: brightness(1.08);
}
/* Сводка-денаты по узлам */
.aggregate-donuts-card {
margin-bottom: 1rem;
}
.donuts-card-title {
margin: 0 0 0.75rem;
}
.donut-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
gap: 1rem 1.25rem;
align-items: start;
}
.donut-item {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 0.35rem;
}
.donut-svg {
width: 6.5rem;
height: 6.5rem;
flex-shrink: 0;
}
.donut-track {
stroke: var(--border);
}
.donut-fill {
stroke: var(--accent);
stroke-linecap: round;
}
.donut-meta {
display: flex;
flex-direction: column;
gap: 0.12rem;
max-width: 11rem;
}
.donut-meta__title {
font-size: 0.72rem;
font-weight: 650;
text-transform: uppercase;
letter-spacing: 0.03em;
color: var(--muted);
}
.donut-meta__val {
font-size: 0.88rem;
font-weight: 600;
word-break: break-word;
}
.donut-meta__sub {
font-size: 0.75rem;
}
.aggregate-donuts-placeholder {
margin: 0;
font-size: 0.9rem;
line-height: 1.45;
max-width: 40rem;
}
/* Вторая строка таблицы: раскрытие ресурсов */
.cluster-resources-expand-row td {
border-top: none;
padding-top: 0;
}
.cluster-resources-expand-cell {
padding: 0 0.75rem 0.65rem;
background: rgba(0, 0, 0, 0.06);
}
html[data-theme="light"] .cluster-resources-expand-cell {
background: rgba(59, 130, 246, 0.06);
}
.cluster-nodes-resources {
margin: 0;
}
.cluster-nodes-resources summary {
cursor: pointer;
font-weight: 600;
font-size: 0.88rem;
padding: 0.35rem 0;
color: var(--accent);
}
.cluster-nodes-resources__empty {
margin: 0.25rem 0 0;
font-size: 0.85rem;
}
.cluster-row-node-res {
margin-top: 0.45rem;
}
.cluster-create-hero {
margin-bottom: 1.35rem;
}
/* Карточка формы: чуть плотнее внутри, заголовок отступает от полей */
.card.create-cluster-card {
padding: 0.85rem 1rem 1rem;
margin-bottom: 1rem;
}
.create-cluster-card__title {
margin: 0 0 1rem;
padding: 0;
font-size: 1.02rem;
}
/* Сетка 2×2: имя | тег, затем подсказка на всю ширину, workers | версия; выравнивание по нижнему краю ячеек */
.create-form-grid {
display: grid;
gap: 0.4rem 1.15rem;
margin-top: 0;
}
.create-form-field {
display: flex;
flex-direction: column;
gap: 0.2rem;
min-width: 0;
}
.create-form-field > label {
margin: 0;
font-size: 0.8125rem;
line-height: 1.3;
font-weight: 600;
}
.create-form-grid .create-form-field input,
.create-form-grid .create-form-field select {
width: 100%;
max-width: none;
margin: 0;
padding: 0.35rem 0.5rem;
min-height: 2.3rem;
box-sizing: border-box;
line-height: 1.25;
}
@media (min-width: 640px) {
.create-form-grid {
grid-template-columns: 1fr 1fr;
align-items: end;
}
}
.create-form-span {
grid-column: 1 / -1;
}
/* Ссылка на актуальные теги kindest/node (Docker Hub), новая вкладка */
.create-form-doc-link {
color: var(--accent);
font-weight: 650;
text-decoration: none;
}
.create-form-doc-link:hover {
text-decoration: underline;
text-underline-offset: 2px;
}
.create-form-doc-link:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
border-radius: 3px;
}
.create-form-doc-link__ext {
margin-left: 0.12rem;
font-size: 0.82em;
font-weight: 700;
color: var(--accent);
opacity: 0.85;
}
.create-form-label-rest {
font-weight: 600;
}
.create-ver-hint {
margin: 0;
font-size: 0.8rem;
line-height: 1.35;
}
.create-ver-hint--full {
grid-column: 1 / -1;
margin: 0.1rem 0 0.25rem;
}
.create-form-grid .create-actions {
margin-top: 0.35rem;
padding-top: 0.15rem;
width: 100%;
justify-content: center;
}
.create-form-grid .create-actions button {
margin-top: 0;
}
/* Одна карточка: заголовок + описание + строка состояния среды */
.hero-panel {
margin-bottom: 1rem;
}
.hero-panel-main {
margin-bottom: 0.85rem;
}
.hero-panel .page-title {
margin: 0 0 0.35rem;
}
.hero-panel .page-lead {
margin: 0;
max-width: 48rem;
}
.hero-panel-status {
margin: 0;
border-radius: 8px;
padding: 0.65rem 0.85rem;
font-size: 0.9rem;
border: 1px solid var(--border);
}
.hero-panel-status.ok {
border-color: #15803d;
background: rgba(34, 197, 94, 0.12);
}
.hero-panel-status.degraded {
border-color: #b45309;
background: rgba(245, 158, 11, 0.12);
}
.hero-panel-status.err {
border-color: #b91c1c;
background: rgba(239, 68, 68, 0.12);
}
/* Прогресс создания кластера */
.create-progress-wrap {
margin-top: 1rem;
padding-top: 0.75rem;
border-top: 1px solid var(--border);
}
.create-progress-hint {
margin: 0 0 0.65rem;
font-size: 0.85rem;
}
.progress-track {
height: 0.65rem;
border-radius: 999px;
background: rgba(0, 0, 0, 0.25);
border: 1px solid var(--border);
overflow: hidden;
}
.progress-bar {
height: 100%;
border-radius: 999px;
background: linear-gradient(90deg, var(--accent), #60a5fa);
transition: width 0.35s ease;
}
.progress-label {
margin: 0.45rem 0 0.65rem;
font-size: 0.88rem;
min-height: 1.35rem;
}
#create-progress-wrap .btn-secondary {
margin-top: 0;
}
/* Журнал kind create в панели прогресса (потоковые строки из API) */
.job-log-panel-wrap {
margin: 0 0 0.65rem;
}
.job-log-label {
display: block;
font-size: 0.8rem;
margin-bottom: 0.25rem;
}
.job-log-panel {
margin: 0;
max-height: min(55vh, 420px);
overflow: auto;
padding: 0.5rem 0.65rem;
font-size: 0.78rem;
line-height: 1.35;
background: rgba(0, 0, 0, 0.2);
border: 1px solid var(--border);
border-radius: 6px;
white-space: pre-wrap;
word-break: break-word;
}
/* Заголовок блока заданий + кнопка очистки */
.jobs-card-toolbar {
margin-bottom: 0.65rem;
}
.jobs-card-title {
margin: 0;
font-size: 1.15rem;
line-height: 1.3;
}
.page-intro {
margin-bottom: 1rem;
}
.page-title {
margin: 0 0 0.35rem;
font-size: 1.35rem;
}
.page-lead {
margin: 0;
max-width: 42rem;
}
.toolbar {
margin-bottom: 1rem;
}
/* Тост уведомлений */
.toast {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 150;
max-width: min(22rem, calc(100vw - 2rem));
padding: 0.65rem 1rem;
border-radius: 8px;
border: 1px solid var(--border);
background: var(--card);
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.25);
font-size: 0.9rem;
}
.toast-ok {
border-color: #15803d;
}
.toast-error {
border-color: #b91c1c;
}
/* Бейджи статусов */
.badge {
display: inline-block;
padding: 0.15rem 0.45rem;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 600;
border: 1px solid var(--border);
background: rgba(0, 0, 0, 0.15);
}
html[data-theme="light"] .badge {
background: rgba(59, 130, 246, 0.12);
}
.badge-ok {
border-color: #15803d;
color: #4ade80;
}
.badge-err {
border-color: #b91c1c;
color: #f87171;
}
.badge-run {
border-color: #b45309;
color: #fbbf24;
}
.badge-cancelled {
border-color: #6b7280;
color: #d1d5db;
}
.badge-stopped {
border-color: #b45309;
color: #fcd34d;
}
.cluster-name {
font-weight: 600;
}
/* Состояние загрузки таблиц */
.job-details {
margin-top: 0.75rem;
border: 1px solid var(--border);
border-radius: 8px;
padding: 0.5rem 0.75rem;
background: rgba(0, 0, 0, 0.12);
}
html[data-theme="light"] .job-details {
background: rgba(59, 130, 246, 0.08);
}
.job-details summary {
cursor: pointer;
font-size: 0.9rem;
font-weight: 600;
}
.job-json-pre {
max-height: 12rem;
margin-top: 0.5rem;
}
/* Спиннер в модалке */
.modal-head {
margin-bottom: 0.5rem;
align-items: flex-start;
}
.modal-head--cluster {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.65rem;
margin-bottom: 0.65rem;
text-align: center;
}
.modal-title-text--center {
width: 100%;
text-align: center;
padding-right: 0;
flex: none;
}
.modal-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 0.65rem 1rem;
}
.modal-dl-wrap {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem 0.75rem;
margin: 0 0 0.65rem;
}
.modal-toolbar .modal-dl-wrap {
margin: 0;
}
.modal-dl-hint {
font-size: 0.8rem;
}
.modal-title-text {
margin: 0;
font-size: 1.05rem;
flex: 1;
padding-right: 0.5rem;
}
.modal-sub {
margin: 0 0 0.75rem;
}
.modal-section-title {
margin: 0.5rem 0 0;
font-size: 0.85rem;
}
.modal-spinner {
display: flex;
align-items: center;
justify-content: center;
gap: 0.65rem;
padding: 1rem 0;
}
.spinner {
width: 1.5rem;
height: 1.5rem;
border: 3px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 0.7s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
button:disabled,
input:disabled,
select:disabled {
opacity: 0.55;
cursor: not-allowed;
}
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
summary:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.header h1 {
margin: 0 0 0.25rem;
font-size: 1.5rem;
}
.muted {
color: var(--muted);
}
.grid {
display: grid;
gap: 1rem;
margin: 1.25rem 0;
}
@media (min-width: 900px) {
.grid {
grid-template-columns: 1fr 1fr;
align-items: start;
}
}
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 10px;
padding: 1rem 1.1rem;
}
.card h2 {
margin-top: 0;
font-size: 1.1rem;
}
label {
display: block;
margin: 0.65rem 0 0.25rem;
font-size: 0.9rem;
}
input,
select,
button {
font: inherit;
}
input,
select {
width: 100%;
max-width: 28rem;
padding: 0.45rem 0.55rem;
border-radius: 6px;
border: 1px solid var(--border);
background: transparent;
color: inherit;
}
button {
margin-top: 0.75rem;
padding: 0.45rem 0.85rem;
border-radius: 6px;
border: 1px solid var(--border);
background: var(--accent);
color: #fff;
cursor: pointer;
}
button:hover {
filter: brightness(1.08);
}
.row {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
align-items: center;
}
.row.spread {
justify-content: space-between;
align-items: center;
}
.mono {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 0.85rem;
white-space: pre-wrap;
word-break: break-word;
background: rgba(0, 0, 0, 0.2);
padding: 0.6rem;
border-radius: 6px;
max-height: 14rem;
overflow: auto;
}
html[data-theme="light"] .mono {
background: rgba(59, 130, 246, 0.1);
}
.table-wrap {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
th .th-hint {
font-weight: 500;
opacity: 0.7;
font-size: 0.82em;
}
th,
td {
border-bottom: 1px solid var(--border);
padding: 0.45rem 0.35rem;
text-align: left;
vertical-align: top;
}
.msg {
margin-top: 0.5rem;
}
.hidden {
display: none;
}
.footer {
margin-top: 2rem;
font-size: 0.9rem;
}
.footer a {
color: var(--accent);
}
/* Плашка состояния среды (Docker/Podman, kind, kubectl) */
.status-banner {
border-radius: 8px;
padding: 0.65rem 0.85rem;
margin-bottom: 1rem;
font-size: 0.9rem;
border: 1px solid var(--border);
}
.status-banner.ok {
border-color: #15803d;
background: rgba(34, 197, 94, 0.12);
}
.status-banner.degraded {
border-color: #b45309;
background: rgba(245, 158, 11, 0.12);
}
.status-banner.err {
border-color: #b91c1c;
background: rgba(239, 68, 68, 0.12);
}
.stats-dl {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.25rem 1rem;
margin: 0;
font-size: 0.9rem;
}
.stats-dl dt {
margin: 0;
color: var(--muted);
}
.stats-dl dd {
margin: 0;
}
button.btn-secondary,
a.btn-secondary {
background: transparent;
color: var(--accent);
border-color: var(--accent);
text-decoration: none;
display: inline-block;
margin-top: 0;
margin-right: 0.35rem;
padding: 0.35rem 0.65rem;
font-size: 0.85rem;
}
button.btn-small {
margin-top: 0;
padding: 0.3rem 0.55rem;
font-size: 0.8rem;
}
/* Колонка «Действия»: ширина по содержимому (иконки в ряд) */
#tbl-clusters th.col-actions,
#tbl-clusters td.actions {
width: 1%;
padding: 0.3rem 0.25rem;
text-align: center;
vertical-align: middle;
}
#tbl-clusters td.actions {
white-space: nowrap;
}
.actions-toolbar {
display: inline-flex;
flex-wrap: nowrap;
align-items: center;
gap: 0.12rem;
}
/* Обёртка иконки; текст подсказки — в #action-tooltip (JS, fixed), иначе режется overflow таблицы */
.icon-tooltip-host {
position: relative;
display: inline-flex;
vertical-align: middle;
}
/* Плавающая подсказка над/под иконкой (полный текст, переносы строк) */
.action-tooltip-floating {
position: fixed;
z-index: 400;
left: 0;
top: 0;
max-width: min(22rem, calc(100vw - 20px));
max-height: min(70vh, 22rem);
overflow-y: auto;
padding: 0.55rem 0.7rem;
box-sizing: border-box;
font-size: 0.8rem;
font-weight: 500;
line-height: 1.45;
text-align: left;
white-space: normal;
overflow-wrap: break-word;
word-break: break-word;
hyphens: auto;
color: var(--fg);
background: var(--card);
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
pointer-events: none;
}
.action-tooltip-floating.hidden {
display: none;
}
.icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.95rem;
height: 1.95rem;
padding: 0;
margin: 0;
border: 1px solid var(--border);
border-radius: 6px;
background: rgba(0, 0, 0, 0.15);
color: var(--fg);
cursor: pointer;
transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.icon-btn:hover:not(:disabled) {
background: rgba(59, 130, 246, 0.2);
border-color: var(--accent);
color: var(--accent);
}
.icon-btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.icon-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.icon-btn svg {
width: 1.05rem;
height: 1.05rem;
flex-shrink: 0;
}
.icon-btn--secondary {
background: transparent;
}
.icon-btn--danger {
color: #f87171;
border-color: rgba(185, 28, 28, 0.5);
background: rgba(185, 28, 28, 0.08);
}
.icon-btn--danger:hover:not(:disabled) {
color: #fca5a5;
border-color: #ef4444;
background: rgba(239, 68, 68, 0.14);
}
html[data-theme="light"] .icon-btn {
background: rgba(59, 130, 246, 0.08);
}
html[data-theme="light"] .icon-btn--danger {
color: #b91c1c;
}
/* Модальное окно «Состояние кластера» */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.55);
display: flex;
align-items: center;
justify-content: center;
padding: 1.25rem 1rem;
z-index: 100;
overflow-y: auto;
}
.modal-overlay.hidden {
display: none;
}
/* Поверх модалки «Состояние» (z-index 100), если когда-либо понадобится стек */
.confirm-modal-overlay {
z-index: 150;
}
/* Журнал развёртывания: между окном «Состояние» (100) и подтверждением (150) */
.provision-log-modal-overlay {
z-index: 120;
}
.modal-box--provision-log .provision-log-body {
max-height: min(70vh, 32rem);
overflow: auto;
margin: 0.35rem 0 0;
padding: 0.65rem 0.75rem;
font-size: 0.78rem;
line-height: 1.45;
white-space: pre-wrap;
word-break: break-word;
border-radius: 8px;
border: 1px solid var(--border);
background: rgba(0, 0, 0, 0.2);
}
html[data-theme="light"] .modal-box--provision-log .provision-log-body {
background: rgba(0, 0, 0, 0.04);
}
.modal-box--confirm {
max-width: 24rem;
text-align: center;
}
.confirm-modal-title {
margin: 0 0 0.5rem;
font-size: 1.05rem;
text-align: center;
}
.confirm-modal-message {
margin: 0 0 0.25rem;
white-space: pre-wrap;
text-align: center;
}
.confirm-modal-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 0.65rem 0.85rem;
margin-top: 1.15rem;
}
.confirm-modal-actions button {
margin-top: 0;
}
.modal-box {
background: var(--card);
border: 1px solid var(--border);
border-radius: 10px;
max-width: 52rem;
width: 100%;
max-height: calc(100vh - 2.5rem);
max-height: calc(100dvh - 2.5rem);
overflow-y: auto;
padding: 1rem 1.15rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
margin: auto;
}
.modal-box h3 {
margin: 0 0 0.75rem;
font-size: 1.05rem;
}
.modal-box pre {
max-height: 40vh;
margin: 0.5rem 0 1rem;
}
.modal-close {
flex-shrink: 0;
margin-top: 0;
}
button.btn-danger,
a.btn-danger {
background: transparent;
color: #f87171;
border-color: #b91c1c;
}
html[data-theme="light"] button.btn-danger,
html[data-theme="light"] a.btn-danger {
color: #b91c1c;
}
button.btn-danger:hover {
filter: brightness(1.12);
}
/* Хлебные крошки на странице документации — ширина как у .app-main (с дашбордом). */
.doc-breadcrumbs {
max-width: none;
width: 100%;
margin: 0 0 0.65rem;
padding: 0;
box-sizing: border-box;
}
.doc-breadcrumbs__list {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.35rem 0.15rem;
margin: 0;
padding: 0;
list-style: none;
font-size: 0.8125rem;
}
.doc-breadcrumbs__item {
display: inline-flex;
align-items: center;
gap: 0.35rem;
}
.doc-breadcrumbs__item:not(:last-child)::after {
content: "";
opacity: 0.45;
font-weight: 500;
margin-left: 0.2rem;
}
.doc-breadcrumbs__link {
color: var(--accent);
text-decoration: none;
}
.doc-breadcrumbs__link:hover {
text-decoration: underline;
text-underline-offset: 2px;
}
.doc-breadcrumbs__item--current {
color: var(--muted);
font-weight: 600;
max-width: 100%;
word-break: break-word;
}
/* Страница «Документация»: Markdown → карточки; ширина блока = ширина main (как card на дашборде). */
.readme-doc-shell {
max-width: none;
width: 100%;
margin: 0;
box-sizing: border-box;
}
.readme-doc-loading {
margin: 0 0 0.75rem;
font-size: 0.9rem;
}
.readme-doc-page {
width: 100%;
}
.readme-doc-page-inner {
width: 100%;
}
.readme-doc-root {
display: flex;
flex-direction: column;
gap: 1rem;
}
.readme-section-card {
margin: 0;
}
/* Заголовок секции (h2) и содержимое — в одной карточке: без лишнего зазора сверху у первого заголовка */
.readme-section-card.markdown-body > h1:first-child,
.readme-section-card.markdown-body > h2:first-child {
margin-top: 0;
}
.readme-section-card.markdown-body {
font-size: 0.95rem;
line-height: 1.55;
}
.readme-section-card.markdown-body h1,
.readme-section-card.markdown-body h2,
.readme-section-card.markdown-body h3,
.readme-section-card.markdown-body h4 {
margin: 1.25rem 0 0.5rem;
line-height: 1.25;
font-weight: 650;
}
.readme-section-card.markdown-body h1 {
font-size: 1.45rem;
border-bottom: 1px solid var(--border);
padding-bottom: 0.35rem;
}
.readme-section-card.markdown-body h2 {
font-size: 1.2rem;
border-bottom: 1px solid var(--border);
padding-bottom: 0.25rem;
}
.readme-section-card.markdown-body h3 {
font-size: 1.05rem;
}
.readme-section-card.markdown-body p {
margin: 0.5rem 0;
}
.readme-section-card.markdown-body ul,
.readme-section-card.markdown-body ol {
margin: 0.5rem 0;
padding-left: 1.35rem;
}
.readme-section-card.markdown-body li {
margin: 0.2rem 0;
}
.readme-section-card.markdown-body a {
color: var(--accent);
text-decoration: underline;
text-underline-offset: 2px;
}
.readme-section-card.markdown-body a:hover {
filter: brightness(1.15);
}
.readme-section-card.markdown-body code {
font-size: 0.88em;
padding: 0.12em 0.35em;
border-radius: 4px;
background: rgba(0, 0, 0, 0.22);
border: 1px solid var(--border);
}
.readme-section-card.markdown-body pre {
margin: 0.65rem 0;
padding: 0.65rem 0.85rem;
overflow-x: auto;
border-radius: 8px;
border: 1px solid var(--border);
background: rgba(0, 0, 0, 0.25);
font-size: 0.82rem;
line-height: 1.4;
}
.readme-section-card.markdown-body pre code {
padding: 0;
border: none;
background: transparent;
font-size: inherit;
}
.readme-section-card.markdown-body blockquote {
margin: 0.65rem 0;
padding: 0.35rem 0.75rem;
border-left: 4px solid var(--accent);
background: rgba(59, 130, 246, 0.08);
color: var(--muted);
}
.readme-section-card.markdown-body table {
width: 100%;
border-collapse: collapse;
margin: 0.75rem 0;
font-size: 0.88rem;
}
.readme-section-card.markdown-body th,
.readme-section-card.markdown-body td {
border: 1px solid var(--border);
padding: 0.4rem 0.55rem;
text-align: left;
}
.readme-section-card.markdown-body th {
background: rgba(0, 0, 0, 0.15);
font-weight: 600;
}
.readme-section-card.markdown-body hr {
border: none;
border-top: 1px solid var(--border);
margin: 1.25rem 0;
}
html[data-theme="light"] .readme-section-card.markdown-body code {
background: rgba(59, 130, 246, 0.1);
}
html[data-theme="light"] .readme-section-card.markdown-body pre {
background: rgba(59, 130, 246, 0.06);
}
html[data-theme="light"] .readme-section-card.markdown-body th {
background: rgba(59, 130, 246, 0.14);
}