/* Основные стили для 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); }