Files
DevOpsLab/app/static/css/main.css
Сергей Антропов 1fbf9185a2 feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок
- Для push операций отображается registry вместо платформ
- Сохранение пользователя при создании push лога
- Исправлена ошибка с logger в push_docker_image endpoint
- Улучшено отображение истории сборок с визуальными индикаторами
2026-02-15 22:59:02 +03:00

3772 lines
92 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.

/* Основные стили для Resource Planner */
/* Автор: Сергей Антропов - https://devops.org.ru */
:root {
--primary-color: #0d6efd;
--secondary-color: #6c757d;
--success-color: #198754;
--danger-color: #dc3545;
--warning-color: #ffc107;
--info-color: #0dcaf0;
--light-color: #f8f9fa;
--dark-color: #212529;
--border-radius: 0.5rem;
--box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--box-shadow-lg: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
--sidebar-width: 260px;
--sidebar-bg: linear-gradient(180deg, #1e293b 0%, #0f172a 100%);
--sidebar-text: #e2e8f0;
--sidebar-hover: rgba(255, 255, 255, 0.1);
--sidebar-active: rgba(102, 126, 234, 0.2);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
color: #1f2937;
background: #f8f9fa;
min-height: 100vh;
display: flex;
overflow-x: hidden;
}
/* Утилита для flex: позволяет flex-потомкам сжиматься и корректно распределять высоту */
.min-h-0 {
min-height: 0;
}
/* Кнопка «Добавить» в форме расписания — та же высота, что у полей ввода (form-control) */
.sync-add-btn {
height: calc(2.5rem + 0.75rem + 2px) !important;
}
/* Карточка «Использование ресурсов» в карточке сервера — на 5rem ниже по высоте */
.server-usage-card {
max-height: calc(100% - 1.6rem);
}
/* Переключатель «Включено» в расписании синхронизации — крупнее, ровно по середине ячейки по высоте и ширине */
.sync-schedule-toggle-cell {
text-align: center !important;
vertical-align: middle !important;
display: table-cell !important;
}
.sync-schedule-toggle-cell .sync-schedule-toggle-form {
display: flex !important;
justify-content: center !important;
align-items: center !important;
width: 100% !important;
min-height: 100% !important;
margin: 0 !important;
}
.sync-schedule-toggle-wrap.form-switch .form-check-input {
width: 2.75rem;
height: 1.4rem;
cursor: pointer;
}
.sync-schedule-toggle-wrap.form-switch {
padding-left: 0.5rem;
min-height: 1.4rem;
margin-top: 0rem !important;
}
.sync-schedule-toggle-wrap.form-switch .form-check-input:checked {
background-position: right 0.35rem center;
}
/* Переключатель «Логи» в расписании — ровно по середине ячейки по высоте и ширине */
.sync-schedule-savelog-cell {
text-align: center !important;
vertical-align: middle !important;
display: table-cell !important;
}
.sync-schedule-savelog-cell .sync-schedule-savelog-form {
display: flex !important;
justify-content: center !important;
align-items: center !important;
width: 100% !important;
min-height: 100% !important;
margin: 0 !important;
}
.sync-schedule-savelog-cell .sync-schedule-savelog-wrap.form-switch .form-check-input {
width: 2.75rem;
height: 1.4rem;
cursor: pointer;
}
.sync-schedule-savelog-cell .sync-schedule-savelog-wrap.form-switch {
padding-left: 0.5rem;
min-height: 1.4rem;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.sync-schedule-savelog-cell .sync-schedule-savelog-wrap.form-switch .form-check-input:checked {
background-position: right 0.35rem center;
}
/* Форма редактирования интервала в таблице расписания — уменьшенная по высоте (~вдвое) */
.sync-interval-form-compact .form-control,
.sync-interval-form-compact .form-select,
.sync-interval-form-compact .btn {
min-height: 1.15rem;
height: 1.5rem;
padding: 0.05rem 0.25rem;
font-size: 1rem;
line-height: 1.1;
}
.sync-interval-form-compact .form-select {
padding-right: 1.25rem;
}
/* Sidebar */
.sidebar {
width: var(--sidebar-width);
background: var(--sidebar-bg);
color: var(--sidebar-text);
position: fixed;
left: 0;
top: 0;
height: 100vh;
display: flex;
flex-direction: column;
z-index: 1000;
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
overflow-y: auto;
overflow-x: hidden;
transition: width 0.25s ease;
}
/* Хэндл для изменения ширины сайдбара (только на десктопе, когда развёрнут) */
.sidebar-resize-handle {
display: none;
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 6px;
cursor: col-resize;
z-index: 10;
background: transparent;
}
.sidebar-resize-handle:hover,
.sidebar-resize-handle.dragging {
background: rgba(102, 126, 234, 0.3);
}
@media (min-width: 992px) {
.sidebar:not(.collapsed) .sidebar-resize-handle {
display: block;
}
}
@media (max-width: 991.98px) {
.sidebar-resize-handle {
display: none !important;
}
}
/* Кнопка сворачивания сайдбара (только на десктопе) */
.sidebar-collapse-btn {
display: none;
position: absolute;
right: 0.5rem;
top: 50%;
transform: translateY(-50%);
width: 1.75rem;
height: 1.75rem;
padding: 0;
border: none;
border-radius: 0.25rem;
background: rgba(255, 255, 255, 0.1);
color: var(--sidebar-text);
cursor: pointer;
align-items: center;
justify-content: center;
transition: background 0.2s, color 0.2s;
}
.sidebar-collapse-btn:hover {
background: rgba(255, 255, 255, 0.2);
color: #fff;
}
.sidebar-collapse-icon {
font-size: 0.75rem;
transition: transform 0.25s ease;
}
.sidebar.collapsed .sidebar-collapse-icon {
transform: rotate(180deg);
}
@media (min-width: 992px) {
.sidebar-collapse-btn {
display: flex;
}
}
@media (max-width: 991.98px) {
.sidebar-collapse-btn {
display: none !important;
}
}
/* Свёрнутое состояние: только иконки с title при наведении */
.sidebar.collapsed {
width: 64px !important;
}
.sidebar.collapsed .brand-text,
.sidebar.collapsed .menu-text,
.sidebar.collapsed .dropdown-arrow {
opacity: 0;
width: 0;
overflow: hidden;
padding: 0;
margin: 0;
pointer-events: none;
}
/* Скрываем выпадающее меню по умолчанию в свернутом виде */
.sidebar.collapsed .sidebar-dropdown-menu {
opacity: 0;
max-height: 0;
overflow: hidden;
padding: 0;
margin: 0;
pointer-events: none;
}
/* Показываем выпадающее меню при раскрытии — иконки с title */
.sidebar.collapsed .sidebar-dropdown-item.open .sidebar-dropdown-menu {
opacity: 1;
max-height: 500px;
overflow: visible;
padding: 0.25rem 0;
margin: 0.25rem 0;
pointer-events: auto;
}
.sidebar.collapsed .sidebar-dropdown-menu .sidebar-menu-link {
justify-content: center;
padding: 0.6rem 0.75rem;
padding-left: 0.75rem;
}
.sidebar.collapsed .sidebar-brand {
justify-content: center;
padding: 0.5rem;
}
.sidebar.collapsed .sidebar-menu-link {
justify-content: center;
padding: 0.75rem;
}
.sidebar.collapsed .sidebar-dropdown-toggle {
justify-content: center;
}
.sidebar.collapsed .sidebar-menu-divider {
margin: 0.5rem 0.75rem;
}
/* Кнопка выхода: только иконка по центру */
.sidebar.collapsed .btn-logout-sidebar .menu-text {
opacity: 0;
width: 0;
overflow: hidden;
padding: 0;
margin: 0;
}
.sidebar.collapsed .btn-logout-sidebar {
justify-content: center;
width: 2.5rem;
min-width: 2.5rem;
padding: 0.875rem 0;
margin: 0 auto;
gap: 0;
}
.sidebar.collapsed .btn-logout-sidebar .menu-icon {
margin: 0;
}
.sidebar.collapsed .sidebar-footer {
display: flex;
justify-content: center;
padding: 1rem 0.5rem;
}
.sidebar.collapsed .sidebar-logout {
margin: 0;
}
.sidebar.collapsed .sidebar-header {
padding: 1rem 0.5rem;
}
.sidebar.collapsed .sidebar-collapse-btn {
right: 50%;
transform: translate(50%, -50%);
display: flex !important; /* Всегда видна кнопка, даже когда sidebar свернут */
z-index: 10; /* Поверх других элементов */
}
.sidebar-header {
padding: 1.75rem 1.25rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
background: linear-gradient(180deg, rgba(102, 126, 234, 0.15) 0%, transparent 100%);
position: relative;
overflow: hidden;
}
/* Колонка с бейджем версии и текстом ЦИСМ.ЦОД: бейдж ровно над надписью, прижат вправо по букве Д */
/* Колонка высотой как иконка (2rem); по центру по вертикали только надпись — её центр совпадает с центром логотипа */
.sidebar-brand-text-column {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
width: fit-content;
height: 2rem;
}
/* Контейнер бейджей в шапке: логин слева, версия справа, тот же размер */
.sidebar-header-badges {
position: absolute;
right: 0;
bottom: 100%;
margin-bottom: -0.2rem;
display: inline-flex;
flex-direction: row;
align-items: center;
gap: 0.25rem;
white-space: nowrap;
}
.sidebar.collapsed .sidebar-header-badges {
display: none;
}
/* Бейдж логина админа: красный, тот же размер что и версия */
.sidebar-user-badge {
display: inline-flex;
align-items: center;
padding: 0.1rem 0.25rem;
font-size: 0.375rem;
font-weight: 500;
color: #fff;
background: #c0392b;
border: 1px solid #a93226;
border-radius: 0.125rem;
white-space: nowrap;
max-width: 6rem;
overflow: hidden;
text-overflow: ellipsis;
}
/* Бейдж версии: тот же размер, прижат к правому краю */
.sidebar-version-badge {
display: inline-flex;
align-items: center;
padding: 0.1rem 0.25rem;
font-size: 0.375rem;
font-weight: 500;
color: rgba(255, 255, 255, 0.75);
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 0.125rem;
white-space: nowrap;
}
.sidebar-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, #667eea, #764ba2, transparent);
animation: shimmer 3s ease-in-out infinite;
}
@keyframes shimmer {
0%, 100% { opacity: 0.5; }
50% { opacity: 1; }
}
.sidebar-brand {
display: flex;
align-items: center;
gap: 1rem;
font-size: 1.1rem;
font-weight: 700;
color: #fff;
text-decoration: none;
position: relative;
transition: transform 0.3s ease;
padding: 0.5rem;
border-radius: 0.5rem;
margin: -0.5rem;
}
.sidebar-brand:hover {
transform: translateX(2px);
background: rgba(255, 255, 255, 0.05);
text-decoration: none;
}
.brand-icon {
font-size: 2rem;
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.4));
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
display: inline-block;
animation: pulse 2s ease-in-out infinite;
position: relative;
width: 1.2em;
text-align: center;
}
.brand-icon::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 120%;
height: 120%;
background: radial-gradient(circle, rgba(102, 126, 234, 0.3) 0%, transparent 70%);
border-radius: 50%;
z-index: -1;
animation: glow 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
@keyframes glow {
0%, 100% { opacity: 0.5; transform: translate(-50%, -50%) scale(1); }
50% { opacity: 0.8; transform: translate(-50%, -50%) scale(1.1); }
}
.brand-text {
background: linear-gradient(135deg, #ffffff 0%, #e0e7ff 50%, #c7d2fe 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
letter-spacing: 0.02em;
line-height: 1.3;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.sidebar-nav {
flex: 1;
padding: 1rem 0;
overflow-y: auto;
display: flex;
flex-direction: column;
min-height: 0;
}
.sidebar-menu {
list-style: none;
padding: 0;
flex: 1 1 auto;
margin: 0;
}
.sidebar-menu-item {
margin: 0.25rem 0;
}
.sidebar-menu-divider {
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
margin: 0.75rem 1.25rem;
border: none;
list-style: none;
}
.sidebar-menu-link {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.875rem 1.25rem;
color: var(--sidebar-text);
text-decoration: none;
transition: var(--transition);
border-left: 3px solid rgba(102, 126, 234, 0.3);
font-weight: 500;
font-size: 0.9375rem;
background: rgba(0, 0, 0, 0.15);
border-radius: 0.375rem;
margin: 0 0.5rem;
}
.sidebar-menu-link:hover {
background: rgba(0, 0, 0, 0.25);
border-left-color: #667eea;
color: #fff;
transform: translateX(2px);
}
.sidebar-menu-link.active {
background: var(--sidebar-active);
border-left-color: #667eea;
color: #fff;
}
.menu-icon {
font-size: 1.25rem;
width: 24px;
text-align: center;
color: rgba(255, 255, 255, 0.5);
opacity: 0.7;
display: inline-block;
transition: color 0.2s ease, opacity 0.2s ease, transform 0.2s ease;
position: relative;
}
.menu-icon.fas,
.menu-icon.far {
width: 1.2em;
}
.sidebar-menu-link:hover .menu-icon {
color: rgba(255, 255, 255, 0.8);
opacity: 0.9;
transform: scale(1.05);
}
.sidebar-menu-link.active .menu-icon {
color: rgba(255, 255, 255, 0.95);
opacity: 1;
}
.menu-text {
flex: 1;
}
.dropdown-arrow {
font-size: 0.75rem;
opacity: 0.7;
transition: transform 0.3s ease;
margin-left: auto;
}
.sidebar-dropdown-item {
position: relative;
margin: 0.25rem 0;
}
.sidebar-dropdown-item .sidebar-dropdown-toggle {
/* Стили наследуются от .sidebar-menu-link */
}
.sidebar-dropdown-item .sidebar-dropdown-toggle:hover {
/* Стили наследуются от .sidebar-menu-link:hover */
}
.sidebar-dropdown-item.open .dropdown-arrow {
transform: rotate(180deg);
}
.sidebar-dropdown-menu {
list-style: none;
padding: 0;
/* margin: 0.25rem 0.5rem 0; */
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease, padding 0.3s ease, background 0.3s ease, border 0.3s ease, box-shadow 0.3s ease;
background: transparent;
border-radius: 0.375rem;
border: 1px solid transparent;
box-shadow: none;
}
.sidebar-dropdown-item.open .sidebar-dropdown-menu {
max-height: 500px;
padding: 0.5rem 0;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
}
.sidebar-dropdown-menu .sidebar-menu-link {
padding-left: 1.75rem;
padding-right: 1rem;
font-size: 0.875rem;
padding-top: 0.625rem;
padding-bottom: 0.625rem;
margin: 0.125rem 0.5rem;
border-radius: 0.25rem;
background: transparent;
}
.sidebar-dropdown-menu .sidebar-menu-link:hover {
background: rgba(102, 126, 234, 0.15);
transform: translateX(2px);
}
.sidebar-dropdown-menu .sidebar-menu-link.active {
background: rgba(102, 126, 234, 0.25);
border-left-color: #667eea;
}
.sidebar-dropdown-menu .sidebar-menu-link .menu-icon {
font-size: 1rem;
width: 20px;
opacity: 0.8;
}
.sidebar-dropdown-menu .sidebar-menu-link:hover .menu-icon,
.sidebar-dropdown-menu .sidebar-menu-link.active .menu-icon {
opacity: 1;
color: rgba(255, 255, 255, 0.95);
}
.sidebar-dropdown-toggle {
cursor: pointer;
}
.sidebar-dropdown-item:has(.sidebar-menu-link.active) .sidebar-dropdown-toggle {
background: var(--sidebar-active);
border-left-color: #667eea;
color: #fff;
}
.sidebar-dropdown-item:has(.sidebar-menu-link.active) .sidebar-dropdown-toggle .menu-icon {
color: rgba(255, 255, 255, 0.95);
opacity: 1;
}
.sidebar-dropdown-link {
display: block;
padding: 0.625rem 1.25rem 0.625rem 1.75rem;
color: var(--sidebar-text);
text-decoration: none;
font-size: 0.875rem;
transition: var(--transition);
border-left: 3px solid transparent;
}
.sidebar-dropdown-link:hover {
background: var(--sidebar-hover);
border-left-color: #667eea;
color: #fff;
padding-left: 1.875rem;
}
.sidebar-dropdown-link:active,
.sidebar-dropdown-link.active {
background: var(--sidebar-active);
border-left-color: #667eea;
color: #fff;
}
.sidebar-footer {
padding: 1.25rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.2);
}
/* Бейджи в самом низу sidebar-nav: пользователь и версия, в одну строку, приглушённые под цвет menu-text */
.sidebar-nav-badges {
display: flex;
flex-wrap: nowrap;
align-items: center;
justify-content: center;
gap: 0.5rem;
margin-top: auto;
padding: 0.75rem 0.5rem 0;
min-height: 1.5rem;
flex-shrink: 0;
}
.sidebar-badge {
display: inline-flex;
align-items: center;
padding: 0.2rem 0.5rem;
font-size: 0.75rem;
font-weight: 500;
color: rgba(255, 255, 255, 0.75);
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 0.25rem;
white-space: nowrap;
max-width: 50%;
overflow: hidden;
text-overflow: ellipsis;
}
.sidebar.collapsed .sidebar-nav-badges {
display: none;
}
.sidebar-export-import {
display: flex;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.sidebar-export-import .btn {
flex: 1;
padding: 0.625rem 0.75rem;
border-radius: 0.5rem;
font-weight: 500;
font-size: 0.875rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
text-decoration: none;
transition: var(--transition);
border: 1px solid;
}
.btn-export-sidebar {
background: rgba(13, 110, 253, 0.2);
border-color: rgba(13, 110, 253, 0.3);
color: #93c5fd;
}
.btn-export-sidebar:hover {
background: rgba(13, 110, 253, 0.3);
border-color: rgba(13, 110, 253, 0.5);
color: #fff;
transform: translateY(-2px);
}
.btn-import-sidebar {
background: rgba(25, 135, 84, 0.2);
border-color: rgba(25, 135, 84, 0.3);
color: #86efac;
}
.btn-import-sidebar:hover {
background: rgba(25, 135, 84, 0.3);
border-color: rgba(25, 135, 84, 0.5);
color: #fff;
transform: translateY(-2px);
}
.sidebar-user {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1rem;
padding: 0.75rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 0.5rem;
}
.user-icon {
font-size: 1.25rem;
}
.user-name {
font-weight: 600;
color: #fff;
font-size: 0.9375rem;
}
.sidebar-logout {
width: 100%;
padding: 0 0.5rem;
}
/* Кнопка выхода в стиле пунктов меню */
.btn-logout-sidebar {
/* Наследует стили от .sidebar-menu-link */
background: rgba(220, 53, 69, 0.15) !important;
border-left-color: rgba(220, 53, 69, 0.4) !important;
color: #fca5a5 !important;
}
.btn-logout-sidebar:hover {
background: rgba(220, 53, 69, 0.25) !important;
border-left-color: #dc3545 !important;
color: #fff !important;
transform: translateX(2px);
}
.btn-logout-sidebar .menu-icon {
color: rgba(252, 165, 165, 0.7);
}
.btn-logout-sidebar:hover .menu-icon {
color: #fff;
opacity: 1;
}
/* Main wrapper */
.main-wrapper {
flex: 1;
display: flex;
flex-direction: column;
min-height: 100vh;
transition: var(--transition);
}
body.with-sidebar .main-wrapper {
margin-left: var(--sidebar-width);
transition: margin-left 0.25s ease;
}
/* Content header */
.content-header {
background: #fff;
border-bottom: 1px solid #e5e7eb;
padding: 1rem 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
position: sticky;
top: 0;
z-index: 100;
}
.content-header-inner {
max-width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
.header-actions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
flex-shrink: 0;
}
.header-actions .btn {
white-space: nowrap;
}
/* Адаптивность для header-actions на мобильных устройствах */
@media (max-width: 576px) {
.header-actions {
width: 100%;
justify-content: flex-start;
}
.header-actions .btn {
flex: 1 1 auto;
min-width: 0;
}
}
.page-title {
font-size: 1.5rem;
font-weight: 700;
color: #1f2937;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
margin: 0;
}
/* Main content */
.main-content {
flex: 1;
min-width: 0; /* чтобы широкий контент (логи, pre) не растягивал страницу */
padding: 1rem 1.5rem;
width: 100%;
max-width: 100%;
overflow-x: auto;
background: transparent;
}
/* Стили для блоков с кодом Dockerfile - предотвращение выхода за границы */
.dockerfile-code-wrapper {
width: 100%;
max-width: 100%;
overflow-x: auto;
overflow-y: hidden;
}
.dockerfile-code-wrapper pre {
width: 100%;
max-width: 100%;
overflow-x: auto;
overflow-y: hidden;
word-wrap: break-word;
white-space: pre-wrap;
word-break: break-all;
margin: 0;
}
.dockerfile-code-wrapper code {
display: block;
width: 100%;
max-width: 100%;
overflow-x: auto;
word-wrap: break-word;
white-space: pre-wrap;
word-break: break-all;
font-family: 'Courier New', Courier, monospace;
font-size: 0.875rem;
line-height: 1.5;
}
/* Стили для модального окна сообщений */
#messageModal .modal-dialog-centered {
display: flex;
align-items: center;
min-height: calc(100% - 1rem);
}
#messageModal .modal-content {
border-radius: 0.5rem;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
#messageModal .modal-header {
border-radius: 0.5rem 0.5rem 0 0;
border-bottom: none;
padding: 1rem 1.5rem;
}
#messageModal .modal-body {
padding: 1.5rem;
font-size: 1rem;
line-height: 1.6;
}
#messageModal .modal-footer {
border-top: 1px solid #dee2e6;
padding: 0.75rem 1.5rem;
}
#messageModal .modal-header.bg-success,
#messageModal .modal-header.bg-danger,
#messageModal .modal-header.bg-warning,
#messageModal .modal-header.bg-info {
color: white;
}
#messageModal .modal-header.bg-warning {
color: #212529;
}
/* Стили для модального окна подтверждения */
#confirmModal .modal-dialog-centered {
display: flex;
align-items: center;
min-height: calc(100% - 1rem);
}
#confirmModal .modal-content {
border-radius: 0.5rem;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
#confirmModal .modal-header {
border-radius: 0.5rem 0.5rem 0 0;
border-bottom: none;
padding: 1rem 1.5rem;
background-color: #ffc107 !important;
color: #212529;
}
#confirmModal .modal-body {
padding: 1.5rem;
font-size: 1rem;
line-height: 1.6;
}
#confirmModal .modal-footer {
border-top: 1px solid #dee2e6;
padding: 0.75rem 1.5rem;
gap: 0.5rem;
}
#confirmModal .modal-footer .btn {
min-width: 120px;
}
/* Убираем контейнеры для максимального использования пространства */
.container-xl,
.container-lg,
.container-md,
.container-sm,
.container {
max-width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
/* Страница лога синхронизации: окно лога на всю высоту экрана, отступ снизу 2rem, без отступа у card-header */
.sync-run-log-page .sync-run-log-card {
display: flex;
flex-direction: column;
/* Высота: весь экран минус шапка страницы, card-header и 2rem отступ снизу */
height: calc(100vh - 7rem);
margin-bottom: 0 !important;
}
.sync-run-log-page .sync-run-log-card-header {
margin-bottom: 0 !important;
padding-bottom: 0.5rem !important;
flex-shrink: 0;
}
.sync-run-log-page .sync-run-log-card .card-body {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.sync-run-log-page .sync-log-scroll-wrapper {
overflow-x: auto !important;
overflow-y: auto !important;
-webkit-overflow-scrolling: touch;
flex: 1;
min-height: 0;
}
.sync-run-log-page .sync-run-log-pre {
white-space: pre !important;
width: max-content !important;
min-width: 100% !important;
display: block !important;
}
/* Карточки */
.card {
border: none;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
transition: var(--transition);
margin-bottom: 1rem;
background: #fff;
overflow: hidden;
}
.card-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding: 0.875rem 1rem;
font-weight: 600;
font-size: 0.9375rem;
border-radius: var(--border-radius) var(--border-radius) 0 0;
color: #fff;
}
/* Белый цвет для всех заголовков в card-header */
.card-header h1,
.card-header h2,
.card-header h3,
.card-header h4,
.card-header h5,
.card-header h6,
.card-header .h1,
.card-header .h2,
.card-header .h3,
.card-header .h4,
.card-header .h5,
.card-header .h6 {
color: #fff !important;
margin: 0;
}
.card-body {
padding: 1rem;
}
/* Единый стиль форм Planning UI — выравнивание, группы, фоны */
.planning-modal-form .modal-body {
padding: 1rem 1.25rem;
}
.planning-modal-form .form-label {
margin-bottom: 0.35rem;
}
.planning-modal-form .form-text {
font-size: 0.8rem;
margin-top: 0.25rem;
}
/* Группа с фоном — для ресурсов, параметров */
.planning-form-group {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 0.5rem;
padding: 1rem 1.25rem;
margin-bottom: 1rem;
}
.planning-form-group:last-child {
margin-bottom: 0;
}
/* Подсказка в модальном окне — карточка с пояснением */
.planning-modal-hint {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 0.5rem;
margin-bottom: 1rem;
}
.planning-modal-hint .card-body {
padding: 0.75rem 1rem;
}
.planning-modal-hint .card-text {
font-size: 0.875rem;
margin-bottom: 0;
}
.planning-form-group-title {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #64748b;
margin-bottom: 0.75rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
/* Имя/название — на всю ширину */
.planning-form-field-full {
width: 100%;
}
.planning-modal-form .row.g-3 {
--bs-gutter-x: 1rem;
--bs-gutter-y: 0.75rem;
}
/* Формы добавления/редактирования сервера в Planning UI */
.planning-server-form-section {
padding-bottom: 0.5rem;
}
.planning-server-form-section.planning-form-group {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 0.5rem;
padding: 1rem 1.25rem;
}
.planning-server-form-section h6 {
letter-spacing: 0.05em;
padding-bottom: 0.25rem;
border-bottom: 1px solid #e9ecef;
}
/* Карточки кластеров Ceph в Planning UI */
.planning-ceph-cluster-card {
transition: box-shadow 0.2s ease, border-color 0.2s ease;
}
.planning-ceph-cluster-card:hover {
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15) !important;
border-color: rgba(102, 126, 234, 0.4) !important;
}
.planning-ceph-pools-list .d-flex.align-items-center {
transition: background 0.15s ease;
}
.planning-ceph-pools-list .d-flex.align-items-center:hover {
background: rgba(102, 126, 234, 0.08) !important;
}
/* Планирование: компактные заголовки карточек и более широкие строки таблиц */
.planning-workspace .card-header.d-flex.justify-content-between.align-items-center {
margin-bottom: 0;
}
.planning-workspace .card-header {
margin-bottom: 0;
}
.planning-workspace .table td,
.planning-workspace .table th {
padding: 0.65rem 0.75rem;
vertical-align: middle;
}
.planning-workspace .table tbody tr {
min-height: 2.5rem;
}
/* Диски и GPU в инвентаре серверов — выделение, количество отдельно, add-кнопки внизу */
.planning-inv-disks-col,
.planning-inv-gpus-col {
min-width: 0;
}
.planning-inv-item {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
padding: 0.25rem 0.5rem;
border-radius: 0.35rem;
font-size: 0.8rem;
gap: 0.15rem;
}
.planning-inv-disk {
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
border: 1px solid #86efac;
color: #166534;
}
.planning-inv-pool {
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
border: 1px solid #93c5fd;
color: #1e40af;
}
.planning-inv-gpu {
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
border: 1px solid #fcd34d;
color: #92400e;
}
.planning-inv-item-count {
font-weight: 700;
font-size: 0.85em;
color: inherit;
opacity: 0.95;
padding: 0 0.2rem;
}
/* Сворачивание дисков/GPU при количестве > 1 — native details/summary */
.planning-inv-collapse-details {
list-style: none;
}
.planning-inv-collapse-details summary {
list-style: none;
cursor: pointer;
}
.planning-inv-collapse-details summary::-webkit-details-marker {
display: none;
}
.planning-inv-collapse-btn {
color: var(--bs-secondary);
text-decoration: none;
}
.planning-inv-collapse-btn:hover {
color: var(--bs-primary);
}
.planning-inv-chevron {
display: inline-block;
transition: transform 0.2s ease;
}
.planning-inv-collapse-details[open] .planning-inv-chevron {
transform: rotate(180deg);
}
/* Каждая запись в развёрнутом списке — на новой строке */
.planning-inv-collapse-content .planning-inv-item {
align-self: flex-start;
width: 100%;
max-width: 100%;
}
/* Модалки дисков — на 30% шире (500px * 1.3 ≈ 650px) */
.modal-dialog-disk {
max-width: 650px;
}
.planning-inv-add-btns {
padding-top: 0.25rem;
border-top: 1px dashed #e2e8f0;
}
.planning-inv-add-btns .btn-link {
opacity: 0.8;
}
.planning-inv-add-btns .btn-link:hover {
opacity: 1;
}
/* Иконки действий в планировании — с бордерами */
.planning-workspace .planning-action-btn,
.planning-workspace .col-actions .btn-link,
.planning-workspace .col-actions .btn,
.planning-workspace .planning-inv-add-btns .btn-link,
.planning-workspace .planning-inv-item .btn-link {
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.35rem;
padding: 0.2rem 0.35rem !important;
}
.planning-workspace .col-actions .btn-link:hover,
.planning-workspace .planning-inv-add-btns .btn-link:hover,
.planning-workspace .planning-inv-item .btn-link:hover {
border-color: rgba(0, 0, 0, 0.25);
background: rgba(0, 0, 0, 0.04);
}
/* Заголовок карточки Ceph кластера — имя слева, кнопки прижаты вправо */
.planning-ceph-cluster-card .card-header {
flex-wrap: nowrap;
overflow: hidden;
gap: 0.5rem;
}
.planning-ceph-cluster-card .card-header strong {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.planning-ceph-cluster-card .card-header .col-actions {
flex-shrink: 0;
}
/* Кнопки в заголовке карточки Ceph кластера — видимы и с обрамлением */
.planning-ceph-cluster-card .card-header .col-actions .btn-link,
.planning-ceph-cluster-card .card-header .col-actions button {
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 0.35rem;
padding: 0.35rem 0.5rem !important;
min-width: 1.75rem;
min-height: 1.75rem;
display: inline-flex;
align-items: center;
justify-content: center;
background: #fff;
text-decoration: none !important;
}
.planning-ceph-cluster-card .card-header .col-actions .btn-link:hover,
.planning-ceph-cluster-card .card-header .col-actions button:hover {
background: #f8f9fa;
border-color: rgba(0, 0, 0, 0.3);
}
.planning-ceph-cluster-card .card-header .col-actions .fa-stack {
display: inline-flex;
align-items: center;
justify-content: center;
}
/* Мини-карточки статистики Ceph кластера (replication, reserved, fast, bulk) */
.planning-ceph-stats-row {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.planning-ceph-stat-card {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border: 1px solid #e2e8f0;
border-radius: 0.4rem;
padding: 0.4rem 0.6rem;
font-size: 0.8rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.planning-ceph-stat-card.planning-ceph-stat-fast {
border-left: 3px solid #eab308;
background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
color: #92400e;
}
.planning-ceph-stat-card.planning-ceph-stat-bulk {
border-left: 3px solid #64748b;
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
color: #475569;
}
/* Мини-карточки в Ceph кластере: слева — инфо, справа — пулы */
.planning-ceph-info-card .planning-ceph-stat-card,
.planning-ceph-pools-card .planning-ceph-stat-card {
padding: 0.35rem 0.5rem;
}
/* Мини-карточки Ceph пулов */
.planning-ceph-pool-card {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border: 1px solid #e2e8f0;
border-radius: 0.5rem;
padding: 0.6rem 0.75rem;
margin-bottom: 0.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
transition: box-shadow 0.2s;
}
.planning-ceph-pool-card:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.planning-ceph-pool-card.fast {
border-left: 3px solid #eab308;
background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
}
.planning-ceph-pool-card.bulk {
border-left: 3px solid #64748b;
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
}
/* Колонка Действия: 12% ширины для кнопок */
.col-actions {
width: 12%;
white-space: nowrap;
}
.col-actions .btn-link {
padding: 0 0.2rem;
}
/* Таблицы */
.table {
background-color: #fff;
border-radius: var(--border-radius);
overflow: hidden;
margin-bottom: 0;
font-size: 0.875rem;
}
.table thead {
background-color: #f8f9fa;
}
.table th {
font-weight: 600;
border-bottom: 2px solid #dee2e6;
padding: 0 0.5rem 0.5rem;
font-size: 0.8125rem;
white-space: nowrap;
}
.table td {
padding: 0 0.5rem 0.5rem;
vertical-align: middle;
font-size: 0.875rem;
}
/* Столбец Контент в списке хранилищ: перенос слов */
.storage-content-cell {
white-space: normal;
word-break: normal;
overflow-wrap: break-word;
max-width: 12rem;
}
.table tbody tr {
transition: var(--transition);
}
.table tbody tr:hover {
background-color: #f8f9fa;
}
/* Формы на страницах */
.form-page {
padding: 0;
}
.form-page .card {
margin-bottom: 1rem;
}
/* Убираем большие отступы */
.row {
margin-left: -0.5rem;
margin-right: -0.5rem;
}
.row > * {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.mb-3 {
margin-bottom: 1rem !important;
}
.mt-3 {
margin-top: 1rem !important;
}
.py-3,
.pt-3,
.pb-3 {
padding-top: 1rem !important;
padding-bottom: 1rem !important;
}
/* Кнопки в заголовках */
.d-flex.justify-content-between.align-items-center {
margin-bottom: 1rem;
}
.d-flex.justify-content-between.align-items-center h2 {
margin: 0;
font-size: 1.5rem;
color: #1f2937;
font-weight: 700;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
/* Заголовки в контенте */
.main-content h1,
.main-content h2,
.main-content h3,
.main-content h4,
.main-content h5,
.main-content h6 {
color: #1f2937;
font-weight: 600;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.main-content h2 {
font-size: 1.5rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid #e9ecef;
}
.main-content h3 {
font-size: 1.25rem;
margin-bottom: 0.75rem;
}
.main-content h4 {
font-size: 1.125rem;
margin-bottom: 0.5rem;
}
/* Оверлей при открытом сайдбаре на мобильных */
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 999;
opacity: 0;
transition: opacity 0.3s ease;
}
.sidebar-overlay.visible {
display: block;
opacity: 1;
}
@media (min-width: 992px) {
.sidebar-overlay {
display: none !important;
}
}
/* Кнопка гамбургера для открытия сайдбара (только на узких экранах) */
.sidebar-toggle-btn {
display: none;
align-items: center;
justify-content: center;
width: 2.5rem;
height: 2.5rem;
padding: 0;
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
background: #fff;
color: #374151;
cursor: pointer;
transition: background 0.2s, color 0.2s;
}
.sidebar-toggle-btn:hover {
background: #f3f4f6;
color: #1f2937;
}
@media (max-width: 991.98px) {
.sidebar-toggle-btn {
display: inline-flex;
}
}
@media (min-width: 992px) {
.sidebar-toggle-btn {
display: none !important;
}
}
/* Адаптивность: сайдбар сворачивается влево, открывается по кнопке */
@media (max-width: 991.98px) {
.sidebar {
transform: translateX(-100%);
transition: transform 0.3s ease;
z-index: 1001;
box-shadow: none;
}
.sidebar.open {
transform: translateX(0);
box-shadow: 4px 0 20px rgba(0, 0, 0, 0.2);
}
body.with-sidebar .main-wrapper {
margin-left: 0;
}
.main-content {
padding: 0.75rem 1rem;
}
.content-header {
padding: 0.75rem 1rem;
}
}
@media (min-width: 992px) {
.main-content {
padding: 1rem 2rem;
}
.content-header {
padding: 1rem 2rem;
}
}
/* Группы кнопок */
.btn-group {
display: inline-flex;
gap: 0.25rem;
align-items: center;
}
.btn-group .btn {
margin: 0;
padding: 0.375rem 0.5rem;
font-size: 0.875rem;
white-space: nowrap;
}
/* Утилиты */
.shadow-sm {
box-shadow: var(--box-shadow);
}
.shadow {
box-shadow: var(--box-shadow-lg);
}
.rounded {
border-radius: var(--border-radius);
}
/* Анимации */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.3s ease;
}
/* Скроллбар для sidebar */
.sidebar::-webkit-scrollbar {
width: 6px;
}
.sidebar::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
}
.sidebar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
.sidebar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}
/* Заголовки таблиц с сортировкой: перенос по пробелам (FP32 TFLOPs → FP32 / TFLOPs) */
th.sortable,
th.text-center.sortable {
white-space: normal;
word-break: normal;
overflow-wrap: break-word;
min-width: 0;
}
th.sortable a,
th.text-center.sortable a {
white-space: normal;
word-break: normal;
overflow-wrap: break-word;
flex-wrap: wrap;
justify-content: center;
text-align: center;
max-width: 100%;
}
/* Sticky header для таблиц */
.table-responsive .sticky-top {
position: sticky;
top: 0;
z-index: 10;
}
/* Навигация для сводок */
.summary-nav .card {
margin-bottom: 1rem;
}
.summary-nav .btn {
white-space: nowrap;
}
/* Таблицы в полную ширину */
.table-responsive {
width: 100%;
}
.table {
width: 100%;
margin-bottom: 0;
}
/* Центрирование всех таблиц по умолчанию */
.table th,
.table td {
text-align: center;
vertical-align: middle;
}
/* Исключения для текстовых полей (если нужно) */
.table th.text-start,
.table td.text-start {
text-align: left;
}
.table th.text-end,
.table td.text-end {
text-align: right;
}
/* Колонка Cluster в листинге VM: разрешаем перенос строк */
.vm-cluster-cell {
white-space: normal;
word-wrap: break-word;
overflow-wrap: break-word;
max-width: 12rem;
}
/* Report Cards Styles */
.report-card {
border-radius: 1rem;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
background: #ffffff;
position: relative;
overflow: hidden;
}
.report-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
transform: scaleX(0);
transform-origin: left;
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.report-card:hover {
transform: translateY(-8px);
box-shadow: 0 12px 40px rgba(102, 126, 234, 0.25) !important;
}
.report-card:hover::before {
transform: scaleX(1);
}
.report-card-header {
height: 120px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.report-card-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.1;
background: radial-gradient(circle at 30% 50%, rgba(255, 255, 255, 0.3) 0%, transparent 70%);
}
.report-card-header::after {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
animation: rotate 20s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.report-icon-wrapper {
position: relative;
z-index: 1;
width: 80px;
height: 80px;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(10px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
background: rgba(255, 255, 255, 0.2);
}
.report-card:hover .report-icon-wrapper {
transform: scale(1.1) rotate(5deg);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.2);
}
.report-icon-large {
font-size: 2.5rem;
color: #ffffff;
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.2));
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.report-card:hover .report-icon-large {
transform: scale(1.1);
}
/* Color variants for report cards */
.report-card-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.report-card-info {
background: linear-gradient(135deg, #0dcaf0 0%, #0aa2c0 100%);
}
.report-card-success {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
}
.report-card-warning {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
}
.report-card-danger {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
}
/* Report card buttons */
.btn-report-action {
border: none;
border-radius: 0.75rem;
padding: 0.75rem 1.5rem;
font-weight: 600;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
}
.btn-report-action::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.btn-report-action:hover::before {
width: 300px;
height: 300px;
}
.btn-report-action span,
.btn-report-action i {
position: relative;
z-index: 1;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.btn-report-action:hover span {
transform: translateX(-4px);
}
.btn-report-action:hover i {
transform: translateX(4px);
}
.report-btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
}
.report-btn-primary:hover {
background: linear-gradient(135deg, #5568d3 0%, #6a3d8f 100%);
color: #ffffff;
box-shadow: 0 8px 24px rgba(102, 126, 234, 0.4);
}
.report-btn-info {
background: linear-gradient(135deg, #0dcaf0 0%, #0aa2c0 100%);
color: #ffffff;
}
.report-btn-info:hover {
background: linear-gradient(135deg, #0bb6d8 0%, #0891b2 100%);
color: #ffffff;
box-shadow: 0 8px 24px rgba(13, 202, 240, 0.4);
}
.report-btn-success {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
color: #ffffff;
}
.report-btn-success:hover {
background: linear-gradient(135deg, #059669 0%, #047857 100%);
color: #ffffff;
box-shadow: 0 8px 24px rgba(16, 185, 129, 0.4);
}
.report-btn-warning {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
color: #ffffff;
}
.report-btn-warning:hover {
background: linear-gradient(135deg, #d97706 0%, #b45309 100%);
color: #ffffff;
box-shadow: 0 8px 24px rgba(245, 158, 11, 0.4);
}
.report-btn-danger {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
color: #ffffff;
}
.report-btn-danger:hover {
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
color: #ffffff;
box-shadow: 0 8px 24px rgba(239, 68, 68, 0.4);
}
/* Stat Cards Styles */
.stat-card {
background: #ffffff;
border: 2px solid #e5e7eb;
border-radius: 0.75rem;
padding: 1rem;
display: flex;
align-items: center;
gap: 1rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
height: 100%;
min-height: 100px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.stat-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
border-color: #667eea;
}
.stat-card-icon {
width: 48px;
height: 48px;
border-radius: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
flex-shrink: 0;
}
.stat-card-content {
flex: 1;
min-width: 0;
}
.stat-card-label {
font-size: 0.75rem;
color: #6b7280;
text-transform: uppercase;
font-weight: 600;
letter-spacing: 0.5px;
margin-bottom: 0.25rem;
}
.stat-card-value {
font-size: 1.25rem;
font-weight: 700;
color: #1f2937;
line-height: 1.2;
}
.stat-card-subvalue {
font-size: 0.875rem;
color: #6b7280;
margin-top: 0.25rem;
font-weight: 500;
}
/* Stat Card Color Variants */
.stat-card-cpu .stat-card-icon {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
}
.stat-card-ram .stat-card-icon {
background: linear-gradient(135deg, #0dcaf0 0%, #0aa2c0 100%);
color: #ffffff;
}
.stat-card-hdd .stat-card-icon {
background: linear-gradient(135deg, #6c757d 0%, #5a6268 100%);
color: #ffffff;
}
.stat-card-ssd .stat-card-icon {
background: linear-gradient(135deg, #ffc107 0%, #ffb300 100%);
color: #212529;
}
.stat-card-nvme .stat-card-icon {
background: linear-gradient(135deg, #198754 0%, #157347 100%);
color: #ffffff;
}
.stat-card-gpu .stat-card-icon {
background: linear-gradient(135deg, #dc3545 0%, #bb2d3b 100%);
color: #ffffff;
}
/* Endpoint Selector Styles */
.endpoint-selector-wrapper {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.75rem 1rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.endpoint-selector-wrapper:hover {
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
}
.endpoint-selector-label {
display: flex;
align-items: center;
gap: 0.5rem;
color: #ffffff;
font-weight: 600;
font-size: 0.9rem;
margin: 0;
white-space: nowrap;
}
.endpoint-selector-label i {
font-size: 1.1rem;
opacity: 0.9;
}
.endpoint-selector {
flex: 1;
min-width: 200px;
padding: 0.5rem 1rem;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 0.5rem;
background: rgba(255, 255, 255, 0.95);
color: #1f2937;
font-weight: 500;
font-size: 0.95rem;
transition: all 0.3s ease;
cursor: pointer;
}
.endpoint-selector:hover {
border-color: rgba(255, 255, 255, 0.5);
background: #ffffff;
}
.endpoint-selector:focus {
outline: none;
border-color: rgba(255, 255, 255, 0.8);
background: #ffffff;
box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.2);
}
/* Pagination Styles */
/* Автор: Сергей Антропов - https://devops.org.ru */
.pagination {
display: flex;
list-style: none;
padding: 0;
margin: 0;
gap: 0.5rem;
}
.pagination .page-item {
margin: 0;
}
.pagination .page-link {
display: flex;
align-items: center;
justify-content: center;
min-width: 2.5rem;
height: 2.5rem;
padding: 0.5rem 0.75rem;
border: 2px solid #e5e7eb;
border-radius: 0.5rem;
background: #ffffff;
color: #4b5563;
text-decoration: none;
font-weight: 600;
font-size: 0.875rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.pagination .page-link:hover {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
border-color: #667eea;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
text-decoration: none;
}
.pagination .page-item.active .page-link {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
font-weight: 700;
}
.pagination .page-item.active .page-link:hover {
background: linear-gradient(135deg, #5568d3 0%, #6a3d8f 100%);
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
}
.pagination .page-item.disabled .page-link {
background: #f3f4f6;
color: #9ca3af;
border-color: #e5e7eb;
cursor: not-allowed;
opacity: 0.6;
box-shadow: none;
}
.pagination .page-item.disabled .page-link:hover {
background: #f3f4f6;
color: #9ca3af;
border-color: #e5e7eb;
transform: none;
box-shadow: none;
}
.pagination.pagination-sm .page-link {
min-width: 2rem;
height: 2rem;
padding: 0.375rem 0.625rem;
font-size: 0.8125rem;
}
/* Pagination Per Page Select Styles */
/* Автор: Сергей Антропов - https://devops.org.ru */
.pagination-per-page-wrapper {
position: relative;
display: inline-block;
}
.pagination-per-page-select {
display: flex;
align-items: center;
justify-content: center;
min-width: 4rem;
height: 2rem;
padding: 0.375rem 0.75rem 0.375rem 0.5rem;
border: 2px solid #e5e7eb;
border-radius: 0.5rem;
background: #ffffff;
color: #4b5563;
font-weight: 600;
font-size: 0.8125rem;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%234b5563' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.5rem center;
background-size: 0.75rem;
padding-right: 2rem;
}
.pagination-per-page-select:hover {
background-color: #f9fafb;
border-color: #667eea;
color: #667eea;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(102, 126, 234, 0.2);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23667eea' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
}
.pagination-per-page-select:focus {
outline: none;
border-color: #667eea;
background-color: #ffffff;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1), 0 4px 12px rgba(102, 126, 234, 0.2);
color: #667eea;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23667eea' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
}
.pagination-per-page-select:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.pagination-per-page-select option {
padding: 0.5rem;
background: #ffffff;
color: #4b5563;
font-weight: 600;
}
.pagination-per-page-select option:hover {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
}
.pagination-per-page-select option:checked {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
}
/* Endpoint Status Toggle Buttons */
/* Автор: Сергей Антропов - https://devops.org.ru */
.endpoint-status-toggle {
margin-bottom: 1rem;
}
.endpoint-status-toggle .btn-group-toggle {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.endpoint-status-btn {
flex: 1;
min-width: 140px;
padding: 0.75rem 1.25rem;
border: 2px solid #e5e7eb;
border-radius: 0.5rem;
background: #ffffff;
color: #4b5563;
font-weight: 600;
font-size: 0.875rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.endpoint-status-btn:hover {
border-color: #667eea;
color: #667eea;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
background: #f9fafb;
}
.endpoint-status-btn.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
font-weight: 700;
}
.endpoint-status-btn.active:hover {
background: linear-gradient(135deg, #5568d3 0%, #6a3d8f 100%);
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
}
.endpoint-status-btn i {
font-size: 1rem;
}
/* Специальные цвета для разных статусов */
.endpoint-status-btn[data-status="planned"]:not(.active) {
border-color: #f59e0b;
color: #f59e0b;
}
.endpoint-status-btn[data-status="planned"]:not(.active):hover {
background: #fef3c7;
border-color: #f59e0b;
}
.endpoint-status-btn[data-status="planned"].active {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
border-color: #f59e0b;
}
.endpoint-status-btn[data-status="disabled"]:not(.active) {
border-color: #ef4444;
color: #ef4444;
}
.endpoint-status-btn[data-status="disabled"]:not(.active):hover {
background: #fee2e2;
border-color: #ef4444;
}
.endpoint-status-btn[data-status="disabled"].active {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
border-color: #ef4444;
}
.endpoint-status-btn[data-status="enabled"]:not(.active) {
border-color: #10b981;
color: #10b981;
}
.endpoint-status-btn[data-status="enabled"]:not(.active):hover {
background: #d1fae5;
border-color: #10b981;
}
.endpoint-status-btn[data-status="enabled"].active {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
border-color: #10b981;
}
/* Responsive pagination */
@media (max-width: 768px) {
.pagination {
flex-wrap: wrap;
justify-content: center;
}
.pagination .page-link {
min-width: 2rem;
height: 2rem;
padding: 0.375rem 0.5rem;
font-size: 0.8125rem;
}
.pagination-per-page-select {
min-width: 3.5rem;
height: 1.875rem;
font-size: 0.75rem;
padding: 0.25rem 1.75rem 0.25rem 0.375rem;
}
.endpoint-status-btn {
min-width: 100%;
flex: none;
}
}
/* HTMX Spinner Styles */
.htmx-indicator {
display: none;
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
display: inline-block;
opacity: 1;
}
.htmx-request button {
opacity: 0.7;
cursor: wait;
}
/* Скрываем иконку во время загрузки, когда показывается спиннер */
.htmx-request .htmx-indicator-opposite {
display: none;
}
.spinner-border-sm {
width: 1rem;
height: 1rem;
border-width: 0.15em;
}
/* Модальный overlay для синхронизации */
.sync-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.75);
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
backdrop-filter: blur(2px);
-webkit-backdrop-filter: blur(2px);
}
.sync-modal {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 1rem;
padding: 2.5rem 3rem;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
min-width: 300px;
max-width: 90%;
text-align: center;
animation: fadeInScale 0.3s ease-out;
}
@keyframes fadeInScale {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
.sync-modal .spinner-border {
border-width: 0.25em;
}
.sync-modal .spinner-border.text-primary {
border-color: rgba(255, 255, 255, 0.3);
border-right-color: #ffffff;
}
/* Индикатор прогресса при общей синхронизации */
.sync-progress-indicator {
max-width: 280px;
margin-left: auto;
margin-right: auto;
}
.sync-progress-bar-indeterminate {
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
overflow: hidden;
}
.sync-progress-bar-indeterminate::after {
content: '';
display: block;
height: 100%;
width: 40%;
background: rgba(255, 255, 255, 0.9);
border-radius: 2px;
animation: syncProgressIndeterminate 1.5s ease-in-out infinite;
}
@keyframes syncProgressIndeterminate {
0% { transform: translateX(-100%); }
50% { transform: translateX(250%); }
100% { transform: translateX(-100%); }
}
/* Карточки результатов синхронизации по кластерам */
.sync-results-list {
text-align: left;
}
.sync-results-cards {
display: flex;
flex-direction: column;
gap: 0.5rem;
max-height: 280px;
overflow-y: auto;
padding-right: 0.25rem;
}
.sync-results-cards::-webkit-scrollbar {
width: 6px;
}
.sync-results-cards::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
.sync-results-cards::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 3px;
}
.sync-result-card {
background: rgba(255, 255, 255, 0.12);
border-radius: 0.5rem;
padding: 0.75rem 1rem;
border-left: 4px solid transparent;
}
.sync-result-card-ok {
border-left-color: rgba(255, 255, 255, 0.6);
background: rgba(255, 255, 255, 0.1);
}
.sync-result-card-err {
border-left-color: rgba(255, 200, 200, 0.9);
background: rgba(255, 100, 100, 0.15);
}
.sync-result-card-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.35rem;
}
.sync-result-card-icon {
font-size: 0.85rem;
opacity: 0.9;
}
.sync-result-card-ok .sync-result-card-icon {
color: rgba(255, 255, 255, 0.9);
}
.sync-result-card-err .sync-result-card-icon {
color: rgba(255, 220, 220, 0.95);
}
.sync-result-card-name {
font-weight: 600;
color: rgba(255, 255, 255, 0.95);
font-size: 0.9rem;
}
.sync-result-card-body {
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.85);
line-height: 1.4;
padding-left: 1.4rem;
}
.sync-result-icon-ok {
color: rgba(255, 255, 255, 0.9) !important;
}
.sync-result-icon-err {
color: rgba(255, 220, 220, 0.95) !important;
}
.sync-modal h5 {
font-weight: 600;
font-size: 1.25rem;
}
/* Стили для секций дашборда */
.dashboard-section {
margin-bottom: 3rem;
}
.dashboard-section:last-child {
margin-bottom: 0;
}
.dashboard-section h4 {
font-weight: 600;
font-size: 1.125rem;
color: #495057;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid #e9ecef;
display: flex;
align-items: center;
gap: 0.5rem;
}
/* CodeMirror стили для ошибок */
.cm-error-line {
background-color: #ffe6e6 !important;
}
.cm-error-marker {
color: #dc3545;
font-weight: bold;
cursor: help;
}
/* CodeMirror общие стили */
.CodeMirror {
border: 1px solid #dee2e6;
border-radius: 0.375rem;
font-family: 'Courier New', monospace;
font-size: 14px;
height: auto;
min-height: 300px;
}
.CodeMirror-focused {
border-color: #86b7fe;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.dashboard-section h4 i {
color: #6c757d;
font-size: 1rem;
}
/* Страница настроек подкруток дашборда (в стиле проекта) */
.dashboard-settings-page .dashboard-tweak-table th {
font-weight: 600;
color: #495057;
}
.dashboard-settings-page .dashboard-tweak-table .tweak-input {
max-width: 6rem;
margin-left: auto;
margin-right: auto;
text-align: center;
}
.dashboard-settings-page .dashboard-tweak-datacenter-cell {
font-weight: 500;
}
.dashboard-settings-page .dashboard-tweak-datacenter-icon {
width: 2rem;
height: 2rem;
border-radius: var(--border-radius);
background: linear-gradient(135deg, var(--primary-color) 0%, #0a58ca 100%);
color: #fff;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.sync-modal p {
font-size: 0.9rem;
opacity: 0.9;
}
/* Карточки ресурсов сервера - одинаковый размер */
.resource-card {
display: flex;
flex-direction: column;
}
.resource-card > .card {
height: 100%;
display: flex;
flex-direction: column;
min-height: 180px;
}
.resource-card > .card > .card-body {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
/* Одинаковая высота для карточек основной информации и использования ресурсов */
.row.align-items-stretch > [class*="col-"] > .card.h-100,
.row > [class*="col-"] > .card.h-100 {
display: flex;
flex-direction: column;
}
.row.align-items-stretch > [class*="col-"] > .card.h-100 > .card-body,
.row > [class*="col-"] > .card.h-100 > .card-body {
flex: 1;
display: flex;
flex-direction: column;
}
/* Карточка CEPH пула */
.ceph-pool-hero { position: relative; }
.ceph-pool-hero-bg {
position: absolute;
inset: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
opacity: 0.95;
}
.ceph-pool-hero .card-body { position: relative; z-index: 1; }
.ceph-pool-icon {
width: 56px;
height: 56px;
background: rgba(255, 255, 255, 0.2);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
color: #fff;
}
.ceph-pool-stats-mini {
display: flex;
gap: 1.5rem;
justify-content: flex-end;
flex-wrap: wrap;
}
.ceph-pool-stats-mini .stat-item { text-align: center; }
.ceph-pool-stats-mini .stat-value {
display: block;
font-size: 1.25rem;
font-weight: 600;
color: #fff;
}
.ceph-pool-stats-mini .stat-label {
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.8);
}
.ceph-pool-progress {
background: rgba(255, 255, 255, 0.2);
border-radius: 4px;
}
.ceph-stat-card {
padding: 0.75rem;
border-radius: 8px;
text-align: center;
background: #f8f9fa;
border-left: 4px solid #dee2e6;
}
.ceph-stat-card-raw { border-left-color: #667eea; }
.ceph-stat-card-usable { border-left-color: #38ef7d; }
.ceph-stat-card-used { border-left-color: #ff9a9e; }
.ceph-stat-card-free { border-left-color: #a1c4fd; }
.ceph-stat-card-full { border-left-color: #4facfe; }
.ceph-stat-card-overhead { border-left-color: #fcb69f; }
.ceph-stat-value {
display: block;
font-weight: 600;
font-size: 1rem;
}
.ceph-stat-label {
font-size: 0.7rem;
color: #6c757d;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Карточки дополнительной информации в CEPH пуле */
.ceph-info-card {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
background: #f8f9fa;
border-radius: 0.5rem;
border: 1px solid #e9ecef;
transition: all 0.2s ease;
}
.ceph-info-card:hover {
background: #fff;
border-color: #667eea;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.15);
}
a:hover .ceph-info-card { color: inherit; }
.ceph-info-card-icon {
width: 40px;
height: 40px;
min-width: 40px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
border-radius: 0.375rem;
font-size: 1rem;
}
.ceph-info-card-icon-rbd {
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
}
.ceph-info-card-icon-vm {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.ceph-info-card-body { flex: 1; min-width: 0; }
.ceph-info-card-title {
font-weight: 600;
color: #212529;
margin-bottom: 0.25rem;
}
.ceph-info-card-stats {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1rem;
font-size: 0.8rem;
color: #6c757d;
}
.ceph-info-stat { white-space: nowrap; }
.ceph-info-card-meta {
font-size: 0.75rem;
}
.ceph-info-card-arrow {
color: #adb5bd;
font-size: 0.75rem;
flex-shrink: 0;
}
.ceph-info-card:hover .ceph-info-card-arrow {
color: #667eea;
}
.ceph-info-card-storage .ceph-info-card-arrow { margin-left: auto; }
.ceph-info-card-empty {
padding: 1rem;
justify-content: center;
background: #f8f9fa;
border-style: dashed;
}
.ceph-info-card-clickable {
cursor: pointer;
}
/* --- Страница экспорта данных --- */
/* Общие стили для страниц экспорта, импорта и очистки */
.page-export .export-hero,
.page-import .import-hero,
.page-clear .clear-hero {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
border-radius: var(--border-radius);
padding: 1.5rem 1.75rem;
margin-bottom: 1.5rem;
box-shadow: var(--box-shadow-lg);
}
.page-export .export-hero .export-hero-title,
.page-import .import-hero .import-hero-title,
.page-clear .clear-hero .clear-hero-title {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.35rem;
}
.page-export .export-hero .export-hero-desc,
.page-import .import-hero .import-hero-desc,
.page-clear .clear-hero .clear-hero-desc {
font-size: 0.9rem;
opacity: 0.95;
}
.page-export .export-section-card,
.page-import .import-section-card,
.page-clear .clear-section-card {
background: #fff;
border-radius: var(--border-radius);
border: 1px solid rgba(0, 0, 0, 0.08);
box-shadow: var(--box-shadow);
overflow: hidden;
height: 100%;
}
.page-export .export-section-card .card-header-export,
.page-import .import-section-card .card-header-import,
.page-clear .clear-section-card .card-header-export {
font-weight: 600;
font-size: 0.95rem;
padding: 1rem 1.25rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
display: flex;
align-items: center;
gap: 0.5rem;
}
.page-export .export-section-card .card-header-export.on-default,
.page-import .import-section-card .card-header-import.on-default,
.page-clear .clear-section-card .card-header-export.on-default {
background: linear-gradient(180deg, rgba(34, 197, 94, 0.08) 0%, transparent 100%);
color: #15803d;
}
.page-export .export-section-card .card-header-export.off-default,
.page-import .import-section-card .card-header-import.off-default,
.page-clear .clear-section-card .card-header-export.off-default {
background: linear-gradient(180deg, rgba(99, 102, 241, 0.08) 0%, transparent 100%);
color: #4338ca;
}
.page-export .export-section-card .card-body,
.page-import .import-section-card .card-body,
.page-clear .clear-section-card .card-body {
padding: 1.25rem 1.25rem;
}
.page-export .export-section-card .form-check,
.page-import .import-section-card .form-check,
.page-clear .clear-section-card .form-check {
padding-left: 2.5rem;
min-height: 1.75rem;
align-items: center;
}
.page-export .export-section-card .form-check-input,
.page-import .import-section-card .form-check-input,
.page-clear .clear-section-card .form-check-input {
width: 2.25rem;
height: 1.15rem;
margin-top: 0;
cursor: pointer;
}
.page-export .export-section-card .form-check-label,
.page-import .import-section-card .form-check-label,
.page-clear .clear-section-card .form-check-label {
cursor: pointer;
font-size: 0.9rem;
}
.page-export .export-section-card .export-sub-option,
.page-import .import-section-card .import-sub-option,
.page-clear .clear-section-card .export-sub-option {
margin-left: 2.75rem;
padding-left: 0.75rem;
border-left: 2px solid rgba(0, 0, 0, 0.12);
}
.page-export .export-actions,
.page-import .import-actions,
.page-clear .clear-actions {
margin-top: 1.75rem;
padding-top: 1.25rem;
border-top: 1px solid rgba(0, 0, 0, 0.08);
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
align-items: center;
}
.page-export .export-actions .btn-download,
.page-import .import-actions .btn-upload {
padding: 0.6rem 1.25rem;
font-weight: 600;
box-shadow: var(--box-shadow);
}
.page-export .export-actions .btn-download:hover,
.page-import .import-actions .btn-upload:hover {
box-shadow: 0 0.35rem 0.6rem rgba(13, 110, 253, 0.35);
}
.page-clear .clear-actions .btn-clear {
padding: 0.6rem 1.25rem;
font-weight: 600;
box-shadow: var(--box-shadow);
}
.page-clear .clear-actions .btn-clear:hover {
box-shadow: 0 0.35rem 0.6rem rgba(220, 53, 69, 0.35);
}
.page-export .alert-danger,
.page-import .alert-danger,
.page-clear .alert-danger {
border-radius: var(--border-radius);
}
.page-export .export-section-card .form-check-switch-row,
.page-import .import-section-card .form-check-switch-row,
.page-clear .clear-section-card .form-check-switch-row {
margin-bottom: 0.85rem;
}
.page-export .export-section-card .form-check-switch-row:last-child,
.page-import .import-section-card .form-check-switch-row:last-child,
.page-clear .clear-section-card .form-check-switch-row:last-child {
margin-bottom: 0;
}
.page-import .import-section-card .form-label {
font-weight: 500;
color: #374151;
}
.page-import .import-section-card .form-text {
font-size: 0.85rem;
margin-top: 0.25rem;
}
.page-import .import-warning-card {
background: linear-gradient(180deg, rgba(245, 158, 11, 0.08) 0%, transparent 100%);
border: 1px solid rgba(245, 158, 11, 0.3);
border-radius: var(--border-radius);
padding: 1rem 1.25rem;
margin-bottom: 1.25rem;
}
.page-import .import-warning-card .alert-warning {
margin-bottom: 0;
border: none;
background: transparent;
padding: 0;
}
.page-import .import-stats-summary ul li {
padding-left: 0;
}
.page-import .import-stats-summary .alert-info {
border-radius: var(--border-radius);
}
.page-clear .clear-warning-card {
background: linear-gradient(180deg, rgba(220, 53, 69, 0.08) 0%, transparent 100%);
border: 1px solid rgba(220, 53, 69, 0.3);
border-radius: var(--border-radius);
padding: 1rem 1.25rem;
margin-bottom: 1.25rem;
}
.page-clear .clear-stats-summary ul li {
padding-left: 0;
}
/* Строка фильтра и поиска справочника оборудования — одна высота и вид */
.equipment-filter-row {
display: flex;
align-items: center;
gap: 0.75rem;
}
.equipment-filter-row .equipment-filter-select {
width: 180px;
min-height: 31px;
flex-shrink: 0;
}
.equipment-filter-row .equipment-filter-search {
width: 280px;
}
.equipment-filter-row .equipment-filter-search .form-control,
.equipment-filter-row .equipment-filter-search .input-group-text {
min-height: 31px;
}
.equipment-filter-row .form-select-sm {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
/* Цвет типа оборудования (swatch в списке типов) */
.equipment-type-color-swatch {
display: inline-block;
width: 1.25rem;
height: 1.25rem;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 0.25rem;
vertical-align: middle;
margin-right: 0.35rem;
}
/* ——— ЦОДы в помещении: современный список (Автор: Сергей Антропов - https://devops.org.ru) ——— */
/* Одинаковая высота заголовков карточек «Стойки» и «ЦОДы в помещении» */
.dc-racks-card-header,
.dc-endpoints-in-room-card .dc-endpoints-card-header {
min-height: 3.5rem;
display: flex;
align-items: center;
}
.dc-endpoints-in-room-card .card-header {
border-bottom: 1px solid #e9ecef;
}
.dc-endpoints-add-form {
margin-bottom: 1rem;
}
.dc-endpoints-add-form:last-child {
margin-bottom: 0;
}
/* Высота выпадающего списка и кнопки «Добавить ЦОД» как у селектора помещений (.endpoint-selector) */
.dc-endpoints-add-form .dc-endpoints-add-select,
.dc-endpoints-add-form .btn {
min-height: 2.5rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.dc-endpoints-add-form .dc-endpoints-add-select {
padding-left: 1rem;
padding-right: 1rem;
font-size: 0.95rem;
}
.dc-endpoints-add-select {
min-width: 10rem;
max-width: 14rem;
}
.dc-endpoints-in-room-body {
padding: 0.75rem 1rem;
}
.dc-endpoint-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.6rem 0.75rem;
margin-bottom: 0.5rem;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 0.5rem;
transition: background 0.2s, border-color 0.2s, box-shadow 0.2s;
}
.dc-endpoint-item:last-child {
margin-bottom: 0;
}
.dc-endpoint-item:hover {
background: #f1f5f9;
border-color: #cbd5e1;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
.dc-endpoint-item-icon {
width: 2.25rem;
height: 2.25rem;
border-radius: 0.5rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.875rem;
flex-shrink: 0;
}
.dc-endpoint-item-name {
flex: 1;
min-width: 0;
font-weight: 500;
color: #334155;
font-size: 0.9375rem;
}
.dc-endpoint-item-remove {
flex-shrink: 0;
}
.dc-endpoint-item-remove .btn-link {
opacity: 0.6;
transition: opacity 0.2s;
}
.dc-endpoint-item-remove .btn-link:hover {
opacity: 1;
}
.dc-endpoints-empty {
text-align: center;
padding: 1.5rem 1rem;
background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
border-radius: 0.5rem;
border: 1px dashed #cbd5e1;
}
.dc-endpoints-empty-icon {
width: 3rem;
height: 3rem;
margin: 0 auto 0.75rem;
border-radius: 50%;
background: #e2e8f0;
color: #64748b;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
}
.dc-endpoints-empty-text {
font-size: 0.9375rem;
color: #475569;
font-weight: 500;
}
.dc-endpoints-empty-hint {
font-size: 0.8125rem;
color: #94a3b8;
margin-top: 0.25rem;
}
/* Таблица размещения в стойке */
.rack-layout-card-header {
padding: 0;
margin-bottom: 0rem !important;
}
.rack-layout-table td.rack-cell-content {
vertical-align: middle;
min-height: 2rem;
}
/* Название строго по центру ячейки независимо от кнопок */
.rack-cell-inner {
position: relative;
min-height: 2rem;
display: flex;
justify-content: flex-end;
align-items: center;
}
.rack-placement-name-center {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
}
.rack-placement-actions-pos {
position: relative;
z-index: 2;
}
.rack-placement-badge.rack-placement-server,
.rack-placement-badge.rack-placement-equipment,
.rack-placement-badge.rack-placement-label {
display: inline-block;
padding: 0.35rem 0.6rem;
background-color: #e9ecef;
color: #495057;
border-radius: 0.25rem;
font-weight: 500;
}
.rack-placement-server,
.rack-placement-equipment,
.rack-placement-label {
padding: 0.25rem 0;
}
.rack-placement-server a:hover {
text-decoration: underline;
}
.rack-placement-vms {
border-left: 2px solid #e2e8f0;
padding-left: 0.5rem;
}
.rack-placement-vms .badge {
font-size: 0.7rem;
}
/* Кнопки на размещении: не сливаются с цветным фоном ячейки */
.rack-placement-actions {
background: rgba(255, 255, 255, 0.95);
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 0.35rem;
padding: 0.2rem 0.35rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.rack-placement-actions .btn {
border-width: 1px;
}
.rack-placement-actions .btn:hover {
background: rgba(0, 0, 0, 0.05);
}
/* Ручка перетаскивания размещения */
.rack-drag-handle {
cursor: grab;
}
.rack-drag-handle:active {
cursor: grabbing;
}
/* Подсветка ячейки при перетаскивании (валидная зона сброса) */
.rack-drop-cell.rack-drop-valid {
outline: 2px dashed var(--bs-primary, #0d6efd);
outline-offset: -2px;
background-color: rgba(13, 110, 253, 0.12) !important;
}
/* Карта стоек */
/* Форма настройки сетки: компактная высота (~на 30% ниже стандартной) */
.rack-map-size-form .form-control {
padding-top: 0.15rem;
padding-bottom: 0.15rem;
line-height: 1.25;
font-size: 0.8rem;
min-height: 1.5rem;
}
.rack-map-size-form .btn {
padding-top: 0.15rem;
padding-bottom: 0.15rem;
line-height: 1.25;
font-size: 0.8rem;
min-height: 1.5rem;
}
.rack-map-size-form label {
font-size: 0.8rem;
}
.rack-map-grid-wrapper {
overflow-x: auto;
}
.rack-map-table {
table-layout: fixed;
}
.rack-map-cell {
width: 2.5rem;
min-width: 2.5rem;
max-width: 8rem;
height: 2.5rem;
cursor: pointer;
font-size: 0.75rem;
}
.rack-map-cell:hover {
background-color: rgba(0, 0, 0, 0.05) !important;
}
.rack-map-cell-filled {
font-weight: 500;
}
.rack-map-cell-rack {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
display: inline-block;
}
.rack-map-cell-zone {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
display: inline-block;
}
.rack-map-zone-cold_corridor {
background-color: rgba(13, 202, 240, 0.2);
color: #0aa2c0;
}
.rack-map-zone-hot_corridor {
background-color: rgba(220, 53, 69, 0.2);
color: #b02a37;
}
.rack-map-zone-other {
background-color: rgba(108, 117, 125, 0.2);
color: #495057;
}
.rack-map-cell-selected {
outline: 2px solid var(--bs-primary);
outline-offset: -2px;
background-color: rgba(13, 110, 253, 0.2) !important;
}
/* ========== Обзор ЦОД: карта 60%, метрики 40%, по 2 карточки в ряд ========== */
.rack-overview-intro {
max-width: 36rem;
margin-left: auto;
margin-right: auto;
}
.rack-overview-intro .rack-overview-intro-icon {
line-height: 1;
}
.rack-overview-layout {
align-items: stretch;
}
@media (min-width: 992px) {
.rack-overview-col-map {
flex: 0 0 70%;
max-width: 70%;
min-width: 0;
}
.rack-overview-col-metrics {
flex: 0 0 30%;
max-width: 30%;
min-width: 0;
}
}
.rack-overview-col-map {
min-width: 0;
}
/* Сетка миникарточек: 2 столбца, 4 строки одинаковой высоты, карточки растягиваются по ширине и высоте ячейки */
.rack-overview-metrics-sidebar {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: repeat(4, minmax(4.5rem, 1fr));
gap: 0.35rem;
}
/* Обёртки карточек: на всю ширину ячейки сетки (Bootstrap .col-6 даёт 50%, переопределяем только здесь) */
.rack-overview-metrics-sidebar > * {
width: 100% !important;
min-width: 0;
min-height: 0;
display: flex;
}
.rack-overview-metrics-sidebar .rack-overview-metric-mini {
flex: 1;
width: 100%;
min-height: 0;
padding: 0.5rem 0.65rem;
flex-direction: row;
align-items: center;
gap: 0.6rem;
}
.rack-overview-metric-mini .stat-card-icon {
width: 34px;
height: 34px;
min-width: 34px;
min-height: 34px;
font-size: 1rem;
}
.rack-overview-metric-mini .stat-card-content {
flex: 1;
min-width: 0;
}
.rack-overview-metric-mini .stat-card-label {
font-size: 0.7rem;
margin-bottom: 0.15rem;
}
.rack-overview-metric-mini .stat-card-value {
font-size: 1rem;
font-weight: 700;
}
.rack-overview-metric-mini .stat-card-subvalue {
font-size: 0.7rem;
margin-top: 0.05rem;
}
.rack-overview-grid-wrapper {
overflow: auto;
min-height: 200px;
flex: 1 1 0;
min-width: 0;
width: 100%;
}
.rack-overview-table {
table-layout: fixed;
font-size: 0.8rem;
height: 100%;
width: 100%;
margin-bottom: 0 !important;
}
.rack-overview-table tbody {
height: 100%;
}
.rack-overview-table tr {
height: calc(100% / var(--map-rows, 10));
}
.rack-overview-table td {
padding: 0.25rem !important;
vertical-align: middle;
text-align: center;
width: calc(100% / var(--map-cols, 10));
transition: background-color 0.15s ease, box-shadow 0.15s ease;
}
.rack-overview-cell {
cursor: default;
min-height: 2rem;
}
/* Лёгкое выделение при наведении (без агрессивной синей обводки) */
.rack-overview-cell:hover {
background-color: rgba(0, 0, 0, 0.04) !important;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}
/* Светлый фон ячейки: холодный и горячий коридор */
.rack-overview-cell.rack-overview-cell-zone-cold_corridor {
background-color: #e0f4fc;
}
.rack-overview-cell.rack-overview-cell-zone-hot_corridor {
background-color: #ffebee;
}
.rack-overview-cell.rack-overview-cell-zone-cold_corridor:hover {
background-color: #ccebf7 !important;
}
.rack-overview-cell.rack-overview-cell-zone-hot_corridor:hover {
background-color: #ffcdd2 !important;
}
/* Вся ячейка стойки кликабельна — открывает модалку */
.rack-overview-cell-clickable {
cursor: pointer;
}
.rack-overview-cell-clickable:hover {
background-color: rgba(13, 110, 253, 0.12) !important;
box-shadow: inset 0 0 0 1px rgba(13, 110, 253, 0.25);
}
.rack-overview-cell-filled .rack-overview-cell-rack {
padding: 0.15rem 0.35rem;
border-radius: 0.35rem;
transition: background 0.2s;
}
.rack-overview-cell-clickable .rack-overview-cell-rack:hover {
background: rgba(13, 110, 253, 0.15);
}
.rack-overview-cell-rack i {
margin-right: 0.2rem;
}
.rack-overview-cell-zone {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 0.7rem;
padding: 0.1rem 0.25rem;
border-radius: 0.25rem;
}
.rack-overview-zone-cold_corridor {
color: #0d7ea4;
}
.rack-overview-zone-hot_corridor {
color: #b02a37;
}
/* Модалка содержимого стойки — крупнее, с переворотом спереди/сзади */
.rack-overview-modal .modal-dialog {
max-width: 640px;
}
.rack-overview-modal-content {
border: 2px solid #e5e7eb;
border-radius: 0.75rem;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.rack-overview-modal-body {
max-height: 75vh;
overflow-y: auto;
}
.rack-overview-modal-placeholder {
padding: 2rem !important;
}
/* 3D-переворот стойки: спереди ↔ сзади */
.rack-overview-flip-wrapper {
perspective: 1200px;
min-height: 200px;
}
.rack-overview-flip-inner {
position: relative;
width: 100%;
min-height: 180px;
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
transform-style: preserve-3d;
}
.rack-overview-flip-wrapper.rack-overview-flip-flipped .rack-overview-flip-inner {
transform: rotateY(180deg);
}
.rack-overview-face {
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
padding: 1rem 2.25rem;
padding-bottom: 1.5rem;
}
.rack-overview-face-front {
position: relative;
transform: rotateY(0deg);
}
.rack-overview-face-back {
position: absolute;
top: 0;
left: 0;
right: 0;
transform: rotateY(180deg);
}
/* Кнопки разворота в шапке модалки справа */
.rack-overview-modal-header-actions {
display: flex;
align-items: center;
gap: 0.25rem;
margin-left: auto;
margin-right: 0.25rem;
}
.rack-overview-face-header {
margin-bottom: 0.5rem;
}
.rack-overview-face-header .rack-overview-rack-title {
margin-bottom: 0.75rem;
}
.rack-overview-flip-btn {
flex-shrink: 0;
}
.rack-overview-face-label {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Крупный бадж «Спереди» / «Сзади» по центру */
.rack-overview-side-badge {
display: inline-block;
padding: 0.45rem 1.1rem;
font-size: 1rem;
font-weight: 700;
letter-spacing: 0.04em;
border-radius: 2rem;
text-transform: uppercase;
}
.rack-overview-side-badge.rack-overview-flip-trigger {
cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.rack-overview-side-badge.rack-overview-flip-trigger:hover {
transform: scale(1.05);
box-shadow: 0 4px 14px rgba(224, 168, 0, 0.5);
}
.rack-overview-side-badge-front,
.rack-overview-side-badge-back {
background: linear-gradient(135deg, #ffc107 0%, #e0a800 100%);
color: #1f2937;
box-shadow: 0 2px 10px rgba(224, 168, 0, 0.4);
}
/* Компактные цветные полоски U в модалке */
.rack-overview-rack-contents {
padding: 0;
}
.rack-overview-rack-title-main {
font-size: 1.2rem;
font-weight: 700;
color: #1f2937;
letter-spacing: -0.02em;
}
.rack-overview-rack-subtitle {
font-size: 0.9rem;
font-weight: 500;
color: #6b7280;
}
.rack-overview-rack-title h6 {
font-weight: 600;
color: #1f2937;
}
/* Схематичная стойка: рамка (контур), левая/правая «рейки» с юнитами, середина — слоты */
.rack-overview-rack-frame {
margin-top: 0.75rem;
border: 3px solid #4b5563;
border-radius: 8px;
background: #374151;
box-shadow: inset 0 0 0 1px #1f2937, 0 4px 12px rgba(0, 0, 0, 0.2);
overflow: hidden;
}
.rack-overview-rack-frame .rack-overview-u-grid {
background: #1f2937;
margin-top: 0;
border-radius: 0;
}
/* Сетка: слева юниты | полоски размещений | справа юниты */
.rack-overview-u-grid {
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-rows: repeat(var(--rack-units, 42), 22px);
gap: 1px 0;
align-items: stretch;
}
/* Левая и правая колонки — «рейки» стойки с номерами U */
.rack-overview-u-label {
display: flex;
align-items: center;
justify-content: center;
font-size: 0.65rem;
font-weight: 700;
color: #9ca3af;
min-width: 2rem;
background: #374151;
border-right: 1px solid #1f2937;
}
.rack-overview-u-label-left {
padding-left: 0.35rem;
padding-right: 0.25rem;
border-right: 2px solid #4b5563;
}
.rack-overview-u-label-right {
border-right: none;
border-left: 2px solid #4b5563;
padding-left: 0.25rem;
padding-right: 0.35rem;
}
.rack-overview-u-strip-wrapper {
grid-column: 2;
min-height: 0;
height: 100%;
background: #1f2937;
}
.rack-overview-u-strip {
display: flex;
align-items: center;
gap: 0.75rem;
height: 100%;
padding: 0 0.6rem;
border-radius: 0.4rem;
font-size: 0.85rem;
font-weight: 500;
text-decoration: none;
color: #fff;
transition: filter 0.2s, box-shadow 0.2s;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.rack-overview-u-strip:hover {
filter: brightness(1.1);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.rack-overview-u-strip-no-link {
cursor: default;
pointer-events: none;
}
.rack-overview-u-strip-u {
flex-shrink: 0;
font-size: 0.75rem;
opacity: 0.95;
}
.rack-overview-u-strip-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Спиннер обновления метрик в реальном времени (карточки ВМ): HTMX добавляет .htmx-request к индикатору */
.realtime-metrics-spinner {
display: none;
z-index: 5;
}
.realtime-metrics-spinner.htmx-request {
display: inline-block;
}
/* Спиннер обновления realtime-метрик на дашборде (раз в 3 мин) */
.dashboard-realtime-spinner {
display: none;
z-index: 5;
}
.dashboard-realtime-spinner.htmx-request {
display: inline-block;
}
/* Логи для DevOpsLab */
.log-container {
background: #1f2937;
color: #10b981;
padding: 1rem;
border-radius: 0.5rem;
font-family: 'Courier New', monospace;
font-size: 0.875rem;
max-height: 500px;
overflow-y: auto;
}
.log-line {
margin-bottom: 0.25rem;
line-height: 1.5;
}
.log-error {
color: #ef4444;
}
.log-warning {
color: #f59e0b;
}
.log-info {
color: #3b82f6;
}
/* Profile page styles */
/* Автор: Сергей Антропов - https://devops.org.ru */
.avatar-circle {
width: 100px;
height: 100px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 2.5rem;
font-weight: bold;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* Tab navigation styles */
.nav-tabs .nav-link {
color: #667eea;
border: none;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
}
.nav-tabs .nav-link:hover {
border-bottom-color: #667eea;
color: #764ba2;
background: rgba(102, 126, 234, 0.05);
}
.nav-tabs .nav-link.active {
color: #667eea;
border-bottom-color: #667eea;
background: transparent;
font-weight: 600;
}
.card-header-tabs {
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
margin-bottom: 0;
}
.card-header-tabs .nav-link {
color: rgba(255, 255, 255, 0.8);
border-bottom-color: transparent;
}
.card-header-tabs .nav-link:hover {
color: #fff;
border-bottom-color: rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.1);
}
.card-header-tabs .nav-link.active {
color: #fff;
border-bottom-color: #fff;
background: rgba(255, 255, 255, 0.15);
}