studio/src/community/KubikonStudio.module.css
МИН 31adbf151b Initial public release: Студия Рублокса v1.0
Open-source веб-студия для создания игр Рублокса, двойная лицензия
AGPL-3.0 + Коммерческая.

Главное:
- Vite 5 + React 18 + Babylon 7.54.3 + Monaco Editor + Colyseus 0.16
- Самодостаточный движок ~28к строк (66 файлов): BlockManager,
  TerrainVoxelBuilder, ModelManager, DecoManager, PlayerController,
  ScriptSandboxWorker, MultiplayerSync, 30+ GD-гейммодов
- Главный редактор KubikonEditor (~37к строк) + панели, ScriptEditor (Monaco)
- Витрина игр (KubikonFeed, KubikonStudio, KubikonDocs, KubikonLearn)
- Geometry Dash sub-app (GdMenu, GdShop, GdRules, GdCoverArt)
- 10 admin-preview каталогов для дизайнеров (скины, музыка, порталы и т.д.)
- Конфигурируемый бэкенд через VITE_API_BASE — работает со staging
  (dev-api.rublox.pro) без настройки
- Standalone-режим (VITE_STANDALONE=true) — открыть пустой редактор без бэка
- Полная документация (на русском): README, ARCHITECTURE, CONTRIBUTING,
  SECURITY, CHANGELOG
- ESLint + Prettier + EditorConfig
- Legal: LICENSE (AGPL-3.0), LICENSE-COMMERCIAL.md, CLA.md, COPYRIGHT.md
- Issue templates: bug_report, feature_request, security_disclosure

Перед публикацией:
- Все импорты из minecraftia заменены на локальные
- Все хардкоды URL (minecraftia-school.ru) и внутренних IP убраны → env
- Admin-эндпоинты Kubikon3DService вырезаны (остаются в приватном репо)
- AdminKubikonModeration не публикуется (модерация — в team.rublox.pro)
- 93 МБ ассетов public/kubikon-assets вынесены в .gitignore
  (раздаются через release artifact)
2026-05-27 23:41:10 +03:00

1023 lines
24 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.

/* ========== KUBIKON STUDIO — стартовый экран Рублокс Studio ==========
Синяя wow-палитра, glassmorphism, плавные анимации.
Палитра построена вокруг логотипа A02 — синий #3357ff.
*/
.studio *,
.studio *::before,
.studio *::after {
box-sizing: border-box;
}
.studio {
/* Тёмная тема в стиле Roblox Studio: почти-чёрный фон,
панели чуть светлее, белый текст, синий акцент Рублокса. */
--bg-darkest: #161616;
--bg-dark: #1e1e1e;
--bg-mid: #232323;
--bg-light: #2a2a2a;
--bg-subtle: #1c1c1c;
--accent: #4f74ff;
--accent-deep: #1e2da5;
--accent-soft: #2a3354;
--accent2: #8b5cf6;
--pink: #ec4899;
--gold: #f59e0b;
--cyan: #06b6d4;
--text: #f2f3f5;
--text-dim: #b6bac4;
--text-muted: #7f848f;
--border: #333333;
--border-soft: #2c2c2c;
--border-strong: #444444;
--danger: #ef4444;
--success: #10b981;
--warning: #f59e0b;
--radius: 10px;
--radius-lg: 14px;
--radius-xl: 20px;
--radius-2xl: 28px;
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04);
--shadow: 0 1px 3px rgba(15, 23, 42, 0.06), 0 2px 6px rgba(15, 23, 42, 0.04);
--shadow-md: 0 4px 16px rgba(15, 23, 42, 0.08), 0 2px 6px rgba(15, 23, 42, 0.04);
--shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.12), 0 6px 12px rgba(15, 23, 42, 0.06);
--shadow-accent: 0 8px 24px rgba(51, 87, 255, 0.32);
--gradient-brand: linear-gradient(135deg, #3357ff 0%, #1e2da5 100%);
--gradient-hero: linear-gradient(135deg, #3357ff 0%, #6d28d9 50%, #ec4899 100%);
--gradient-cool: linear-gradient(135deg, #06b6d4 0%, #3357ff 100%);
--gradient-purple: linear-gradient(135deg, #8b5cf6 0%, #3357ff 100%);
--font: "Roboto Condensed", system-ui, -apple-system, sans-serif;
/* Структура: высота = ровно видимая область .wrap_page (минус 61px хедер).
Внутри — flex-row. Sidebar статичный (height: 100%), main — отдельный
скролл-контейнер с overflow-y: auto. Когда main докручен до низа,
дальше начинает скроллить внешний .wrap_page и показывает футер. */
position: relative;
display: flex;
align-items: stretch;
height: calc(100vh - 61px);
width: 100%;
max-width: 100%;
background: var(--bg-darkest);
color: var(--text);
box-sizing: border-box;
font-family: var(--font);
/* Лёгкий синий «свет» в углах — на тёмном фоне чуть ярче */
background-image:
radial-gradient(ellipse 600px 400px at 0% 0%, rgba(79, 116, 255, 0.10) 0%, transparent 70%),
radial-gradient(ellipse 600px 400px at 100% 100%, rgba(139, 92, 246, 0.08) 0%, transparent 70%);
}
/* ===== Sidebar =====
Не двигается вообще: статичная колонка во всю высоту .studio.
.studio высотой = видимая область .wrap_page, поэтому sidebar
занимает всю видимую часть и не уходит за неё.
Скроллится только .main (см. ниже). */
.sidebar {
width: 240px;
flex-shrink: 0;
height: 100%;
background: #1c1c1c;
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
z-index: 10;
box-shadow: 1px 0 12px rgba(0, 0, 0, 0.3);
}
.sidebarHeader {
padding: 20px 18px;
display: flex;
align-items: center;
gap: 12px;
border-bottom: 1px solid var(--border-soft);
}
/* Плашка-логотип: сам RublocsLogo рисует скруглённый квадрат
(через свой проп bg) — фирменный логотип Рублокса целиком.
Дополнительный фон/градиент тут не нужен. */
.sidebarLogo {
width: 44px;
height: 44px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.sidebarTitle {
display: flex;
flex-direction: column;
line-height: 1;
}
.brandName {
font-size: 18px;
font-weight: 900;
color: var(--text);
letter-spacing: -0.5px;
}
.brandSub {
font-size: 11px;
color: var(--accent);
font-weight: 800;
text-transform: uppercase;
letter-spacing: 1.4px;
margin-top: 4px;
}
.sidebarNav {
flex: 1;
padding: 14px 12px;
display: flex;
flex-direction: column;
gap: 4px;
overflow-y: auto;
}
.navItem {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 14px;
background: transparent;
border: none;
border-radius: var(--radius);
color: var(--text-dim);
font-size: 14px;
font-weight: 700;
cursor: pointer;
transition: all 160ms ease;
text-align: left;
width: 100%;
font-family: inherit;
letter-spacing: 0.1px;
}
.navItem:hover {
background: var(--bg-light);
color: var(--text);
}
/* Активный пункт — в Roblox Studio это просто подсветка панелью
и синяя полоска слева, без яркого градиента. */
.navActive,
.navActive:hover {
background: var(--bg-light);
color: #fff;
box-shadow: inset 3px 0 0 var(--accent);
}
.navActive .navIcon { color: var(--accent); }
.navIcon {
width: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
}
.navItem:hover .navIcon { color: var(--text-dim); }
/* Кнопка «Новая игра» — акцентная, синяя, наверху меню */
.navNewGame {
display: flex;
align-items: center;
gap: 12px;
padding: 11px 14px;
margin-bottom: 8px;
background: var(--accent);
border: none;
border-radius: var(--radius);
color: #fff;
font-size: 14px;
font-weight: 800;
cursor: pointer;
transition: all 160ms ease;
text-align: left;
width: 100%;
font-family: inherit;
}
.navNewGame:hover {
background: #5d80ff;
transform: translateY(-1px);
}
.navNewGameIco {
width: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.sidebarFooter {
padding: 14px 14px 18px;
border-top: 1px solid var(--border-soft);
}
.docsBtn {
width: 100%;
padding: 10px 14px;
background: var(--bg-light);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--text);
font-size: 13px;
font-weight: 700;
cursor: pointer;
font-family: inherit;
transition: all 150ms ease;
}
.docsBtn:hover {
background: var(--accent-soft);
border-color: var(--accent);
color: var(--accent);
}
/* ===== Main ===== */
.main {
flex: 1;
min-width: 0;
height: 100%;
/* Внутренний скролл — sidebar остаётся неподвижным. */
overflow-y: auto;
overflow-x: hidden;
padding: 24px 32px 60px;
width: 100%;
/* Когда main докручен до низа, браузер автоматически
начинает прокручивать родителя (.wrap_page) дальше — к футеру.
overscroll-behavior: auto разрешает это (это значение по умолчанию,
пишем явно для ясности). */
overscroll-behavior: auto;
}
.main > * {
max-width: 1240px;
margin-left: auto;
margin-right: auto;
}
.topBar {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 16px;
margin-bottom: 28px;
flex-wrap: wrap;
}
.pageTitle {
margin: 0;
font-size: 32px;
font-weight: 900;
color: var(--text);
letter-spacing: -1px;
}
.pageSub {
margin: 4px 0 0;
font-size: 14px;
color: var(--text-dim);
font-weight: 500;
}
.topBarActions {
display: flex;
gap: 10px;
align-items: center;
}
.searchBox {
padding: 10px 18px;
background: var(--bg-light);
border: 1px solid var(--border);
border-radius: 999px;
color: var(--text-dim);
font-size: 13px;
font-weight: 600;
cursor: pointer;
font-family: inherit;
transition: all 200ms ease;
}
.searchBox:hover {
border-color: var(--accent);
color: var(--accent);
box-shadow: var(--shadow-sm);
}
.profileBtn {
padding: 10px 18px;
background: var(--gradient-brand);
border: 1px solid transparent;
border-radius: 999px;
color: #fff;
font-size: 13px;
font-weight: 800;
cursor: pointer;
font-family: inherit;
box-shadow: var(--shadow-accent);
transition: all 200ms ease;
}
.profileBtn:hover {
transform: translateY(-2px);
box-shadow: 0 12px 28px rgba(51, 87, 255, 0.45);
}
/* ===== Hero — крупный баннер с кадром игры (как в Roblox Studio) ===== */
.hero {
position: relative;
border-radius: var(--radius-xl);
margin-bottom: 36px;
overflow: hidden;
min-height: 230px;
display: flex;
align-items: center;
border: 1px solid var(--border);
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.4);
}
/* кадр игры на весь баннер */
.heroImg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
/* затемнение слева — чтобы белый текст читался поверх кадра */
.heroOverlay {
position: absolute;
inset: 0;
background: linear-gradient(100deg,
rgba(8, 11, 22, 0.90) 0%,
rgba(8, 11, 22, 0.66) 40%,
rgba(8, 11, 22, 0.10) 70%,
transparent 100%);
pointer-events: none;
}
.heroContent {
position: relative;
z-index: 2;
flex: 1;
min-width: 280px;
max-width: 560px;
padding: 38px 44px;
}
.heroBadge {
display: inline-flex;
align-items: center;
background: rgba(255, 255, 255, 0.16);
border: 1px solid rgba(255, 255, 255, 0.30);
color: #fff;
padding: 5px 14px;
border-radius: 999px;
font-size: 11px;
font-weight: 800;
letter-spacing: 1.4px;
text-transform: uppercase;
margin-bottom: 14px;
}
.heroTitle {
margin: 0 0 12px;
font-size: 38px;
line-height: 1.08;
font-weight: 900;
color: #fff;
letter-spacing: -1px;
text-shadow: 0 2px 18px rgba(0, 0, 0, 0.55);
}
.heroDesc {
margin: 0 0 22px;
font-size: 15.5px;
color: rgba(255, 255, 255, 0.92);
line-height: 1.55;
text-shadow: 0 1px 10px rgba(0, 0, 0, 0.5);
}
.heroBtn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 13px 26px;
background: var(--accent);
border: none;
border-radius: var(--radius);
color: #fff;
font-size: 15px;
font-weight: 900;
cursor: pointer;
font-family: inherit;
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);
transition: all 200ms ease;
letter-spacing: 0.2px;
}
.heroBtn:hover {
background: #5d80ff;
transform: translateY(-2px);
}
@keyframes studioGradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
/* ===== «Изучай Студию» — карточки-гайды (Discover Studio) ===== */
.guideGrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
/* Карточки — светлые, в стиле вики (белый фон, тёмный текст). */
.guideCard {
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: var(--radius-lg);
overflow: hidden;
cursor: pointer;
transition: border-color 180ms ease, transform 180ms ease, box-shadow 180ms ease;
text-align: left;
font-family: inherit;
padding: 0;
display: flex;
flex-direction: column;
}
.guideCard:hover {
transform: translateY(-3px);
border-color: #3357ff;
box-shadow: 0 14px 32px rgba(15, 23, 42, 0.16);
}
.guideImage {
position: relative;
aspect-ratio: 16 / 10;
overflow: hidden;
}
/* превью-скриншот статьи */
.guideImg {
position: relative;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: transform 300ms ease;
}
.guideCard:hover .guideImg { transform: scale(1.05); }
/* запасной фон под картинкой (если скрин не загрузился) */
.guideFallback {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.92);
}
.guideBody { padding: 14px 16px 16px; }
.guideTitle {
font-size: 15px;
font-weight: 800;
color: #0f172a;
margin-bottom: 5px;
}
.guideDesc {
font-size: 12.5px;
color: #64748b;
line-height: 1.5;
}
@keyframes studioFadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes studioFadeInScale {
from { opacity: 0; transform: scale(0.94); }
to { opacity: 1; transform: scale(1); }
}
@keyframes studioPulseGlow {
0%, 100% { box-shadow: 0 8px 24px rgba(51, 87, 255, 0.32); }
50% { box-shadow: 0 8px 24px rgba(51, 87, 255, 0.32),
0 0 0 12px rgba(51, 87, 255, 0.08); }
}
@keyframes studioSpin { to { transform: rotate(360deg); } }
/* ===== Sections ===== */
.section {
margin-bottom: 36px;
animation: studioFadeIn 400ms ease both;
}
.sectionHead {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 16px;
gap: 12px;
}
.sectionTitle {
margin: 0;
font-size: 22px;
font-weight: 800;
color: var(--text);
letter-spacing: -0.4px;
}
.sectionMore {
background: transparent;
border: none;
color: var(--accent);
font-size: 13px;
font-weight: 800;
cursor: pointer;
font-family: inherit;
padding: 6px 12px;
border-radius: 999px;
transition: all 150ms ease;
}
.sectionMore:hover {
background: var(--accent-soft);
}
/* подпись справа от заголовка секции (как в Roblox Studio) */
.sectionSub {
font-size: 13px;
color: var(--text-dim);
font-weight: 500;
text-align: right;
}
/* ===== Templates ===== */
.tplGrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
.tplCard {
position: relative;
padding: 0;
background: var(--bg-mid);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
cursor: pointer;
transition: all 280ms cubic-bezier(0.34, 1.56, 0.64, 1);
font-family: inherit;
box-shadow: var(--shadow);
}
.tplCard:hover:not(:disabled) {
transform: translateY(-6px);
border-color: var(--accent);
box-shadow: var(--shadow-lg), 0 0 0 1px var(--accent);
}
.tplCard:disabled {
cursor: not-allowed;
opacity: 0.85;
}
.tplImageWrap {
position: relative;
width: 100%;
aspect-ratio: 16 / 11;
overflow: hidden;
background: var(--bg-light);
}
.tplImage {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 2;
transition: transform 600ms cubic-bezier(0.2, 0.8, 0.4, 1);
}
.tplCard:hover .tplImage {
transform: scale(1.08);
}
.tplFallback {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.tplFallbackEmoji {
font-size: 64px;
opacity: 0.8;
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.30));
}
.tplOverlay {
position: absolute;
inset: 0;
background: linear-gradient(180deg,
rgba(15, 23, 42, 0) 30%,
rgba(15, 23, 42, 0.55) 70%,
rgba(15, 23, 42, 0.85) 100%);
z-index: 3;
pointer-events: none;
}
.tplContent {
position: absolute;
left: 14px;
right: 14px;
bottom: 12px;
z-index: 4;
color: #fff;
}
.tplBadge {
width: 36px;
height: 36px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.20);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.30);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 18px;
margin-bottom: 6px;
}
.tplTitle {
font-size: 17px;
font-weight: 800;
letter-spacing: -0.2px;
margin-bottom: 2px;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
.tplDesc {
font-size: 12px;
opacity: 0.92;
font-weight: 600;
line-height: 1.35;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}
.tplCta {
position: absolute;
inset: 0;
z-index: 5;
display: flex;
align-items: center;
justify-content: center;
background: rgba(51, 87, 255, 0.86);
backdrop-filter: blur(4px);
color: #fff;
font-size: 16px;
font-weight: 800;
opacity: 0;
transition: opacity 220ms ease;
}
.tplCard:hover .tplCta {
opacity: 1;
}
.tplLoading {
position: absolute;
inset: 0;
z-index: 6;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
background: rgba(15, 23, 42, 0.78);
color: #fff;
font-weight: 800;
}
.tplLoadingSpinner {
width: 32px;
height: 32px;
border: 3px solid rgba(255, 255, 255, 0.2);
border-top-color: #fff;
border-radius: 50%;
animation: studioSpin 0.9s linear infinite;
}
/* ===== Project / template cards (универсальные) ===== */
.templatesGrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
/* Карточки игр — светлые, в стиле вики (белый фон, тёмный текст). */
.templateCard {
position: relative;
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: var(--radius-lg);
overflow: hidden;
cursor: pointer;
transition: all 280ms cubic-bezier(0.34, 1.56, 0.64, 1);
text-align: left;
display: flex;
flex-direction: column;
box-shadow: var(--shadow);
font-family: inherit;
color: var(--text);
}
.templateCard:hover {
transform: translateY(-6px);
border-color: #3357ff;
box-shadow: 0 16px 36px rgba(15, 23, 42, 0.16);
}
.templatePreview {
position: relative;
width: 100%;
aspect-ratio: 4 / 3;
overflow: hidden;
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-deep) 100%) !important;
display: flex;
align-items: center;
justify-content: center;
}
.templatePreview img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 600ms cubic-bezier(0.2, 0.8, 0.4, 1);
}
.templateCard:hover .templatePreview img {
transform: scale(1.08);
}
.templateEmoji {
font-size: 56px;
color: #fff;
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.30));
}
.templateBody {
padding: 12px 14px 14px;
}
.templateTitle {
font-size: 15px;
font-weight: 800;
color: #0f172a;
margin-bottom: 4px;
letter-spacing: -0.2px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.templateDesc {
font-size: 12px;
color: #64748b;
font-weight: 600;
}
/* ===== Empty states ===== */
.emptyState {
padding: 56px 32px;
text-align: center;
background: var(--bg-mid);
border: 1px dashed var(--border-strong);
border-radius: var(--radius-xl);
animation: studioFadeInScale 360ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.emptyEmoji {
font-size: 64px;
margin-bottom: 12px;
animation: studioFloat 4s ease-in-out infinite;
display: inline-block;
}
.emptyTitle {
font-size: 20px;
font-weight: 800;
color: var(--text);
margin-bottom: 6px;
letter-spacing: -0.3px;
}
.emptyDesc {
font-size: 14px;
color: var(--text-dim);
margin-bottom: 20px;
}
.emptyBtn {
padding: 12px 24px;
background: var(--gradient-brand);
color: #fff;
border: none;
border-radius: var(--radius);
font-size: 14px;
font-weight: 800;
cursor: pointer;
font-family: inherit;
box-shadow: var(--shadow-accent);
transition: all 220ms cubic-bezier(0.34, 1.56, 0.64, 1);
letter-spacing: 0.2px;
}
.emptyBtn:hover {
transform: translateY(-2px) scale(1.03);
box-shadow: 0 12px 28px rgba(51, 87, 255, 0.45);
}
.emptyStateSmall {
padding: 28px;
text-align: center;
color: var(--text-muted);
font-size: 14px;
font-weight: 600;
}
/* ===== Footer block ===== */
.footerBlock {
margin-top: 48px;
padding-top: 24px;
border-top: 1px solid var(--border-soft);
}
.footerInfo {
font-size: 12px;
color: var(--text-muted);
text-align: center;
font-weight: 600;
letter-spacing: 0.4px;
}
/* ===== Modal — подтверждение удаления ===== */
.modalBackdrop {
position: fixed;
inset: 0;
z-index: 2200;
background: rgba(15, 23, 42, 0.55);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
animation: studioFadeIn 200ms ease;
}
.modalCard {
background: var(--bg-mid);
border: 1px solid var(--border);
border-radius: var(--radius-2xl);
padding: 32px 28px;
width: 100%;
max-width: 440px;
text-align: center;
color: var(--text);
box-shadow: var(--shadow-lg), 0 0 0 1px rgba(51, 87, 255, 0.06);
animation: studioFadeInScale 280ms cubic-bezier(0.34, 1.56, 0.64, 1);
font-family: inherit;
}
.modalIcon {
width: 76px;
height: 76px;
margin: 0 auto 16px;
border-radius: 50%;
background: linear-gradient(135deg, #ff6f7a 0%, #c0303f 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 36px;
box-shadow: 0 12px 28px rgba(239, 68, 68, 0.36),
0 0 0 8px rgba(239, 68, 68, 0.12);
animation: studioFloat 3.6s ease-in-out infinite;
}
.modalTitle {
font-size: 22px;
font-weight: 800;
color: var(--text);
margin-bottom: 8px;
letter-spacing: -0.4px;
}
.modalText {
font-size: 14px;
color: var(--text-dim);
margin-bottom: 24px;
line-height: 1.55;
}
.modalActions {
display: flex;
gap: 10px;
justify-content: center;
}
.modalCancelBtn {
padding: 10px 22px;
background: var(--bg-light);
border: 1px solid var(--border);
color: var(--text);
border-radius: var(--radius);
font-size: 14px;
font-weight: 700;
cursor: pointer;
font-family: inherit;
transition: all 150ms ease;
}
.modalCancelBtn:hover:not(:disabled) {
background: var(--bg-darkest);
border-color: var(--border-strong);
}
.modalCancelBtn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.modalDeleteBtn {
padding: 10px 22px;
background: linear-gradient(135deg, #ff6f7a 0%, #c0303f 100%);
border: 1px solid transparent;
color: #fff;
border-radius: var(--radius);
font-size: 14px;
font-weight: 800;
cursor: pointer;
font-family: inherit;
box-shadow: 0 6px 16px rgba(239, 68, 68, 0.32);
transition: all 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.modalDeleteBtn:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 12px 28px rgba(239, 68, 68, 0.45);
}
.modalDeleteBtn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* ===== Адаптив ===== */
@media (max-width: 900px) {
.studio {
flex-direction: column;
}
.sidebar {
position: relative;
top: auto;
height: auto;
width: 100%;
flex-direction: row;
align-items: center;
padding: 8px 14px;
gap: 8px;
overflow-x: auto;
}
.sidebarHeader {
padding: 6px 10px;
border: none;
}
.sidebarNav {
flex-direction: row;
flex: 1;
padding: 0;
overflow-y: visible;
}
.sidebarFooter { display: none; }
.main { padding: 16px 18px 40px; }
.pageTitle { font-size: 24px; }
.heroTitle { font-size: 28px; }
.hero { padding: 28px 24px; flex-direction: column; }
.heroVisual { width: 160px; height: 160px; }
}