Files
ForbiddenStarsApp/frontend/src/styles/global.css
T
NotBigGhostandClaude Opus 5 cc64bb4644 Рейтинг: ввод деталей партии и предупреждения в формах
Форма завершения и правка результатов (MatchDetailPage): у каждого игрока
в PlaceEditor поля «цели» и «миры» на конец партии (у выбывшего миры
заблокированы нулём), общий блок «Итог партии» — причина победы и раунд
окончания из max_rounds партии. Всё новое уходит в общий черновик.
Причина «последний выживший» ставится сама при одном невыбывшем, выбор
заблокирован; вернули второго — причина сбрасывается и её нужно выбрать.
То же в админской правке партии.

Предупреждения (domain/finishWarnings.ts, отправку не блокируют): тип победы
и цели/миры лидеров не согласуются, досрочный конец без N целей у победителя,
у соперника целей больше, чем у победителя. Настройки группы: переключатель
«9 раундов при 5–6 игроках» через PATCH /groups/{id}. schema.d.ts
перегенерирован. #23

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
2026-09-14 22:45:25 +03:00

485 lines
14 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.
@import "./tokens.css";
* {
box-sizing: border-box;
/* Убираем синий/серый прямоугольник подсветки при тапе на мобильных. */
-webkit-tap-highlight-color: transparent;
}
html, body, #root {
height: 100%;
margin: 0;
}
/* Высота каркаса — по видимой области (dvh): на мобильных 100% берётся от layout
viewport, который меняется при сворачивании адресной строки. Правило выше остаётся
фоллбэком для движков без dvh. */
#root { height: 100dvh; }
body {
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
Arial, sans-serif;
-webkit-font-smoothing: antialiased;
overscroll-behavior-y: none;
overflow: hidden; /* прокручивается только .app-main */
}
a { color: var(--accent-2); text-decoration: none; }
h1, h2, h3 { margin: 0 0 8px; line-height: 1.2; }
h1 { font-size: 22px; }
h2 { font-size: 18px; }
h3 { font-size: 15px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; }
button {
font: inherit;
cursor: pointer;
}
input, select, textarea {
font: inherit;
width: 100%;
background: var(--surface-2);
color: var(--text);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 10px 12px;
outline: none;
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); }
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 14px;
margin-bottom: 12px;
color: var(--text); /* карточки-кнопки (список партий) — белый текст по умолчанию */
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
background: var(--surface-2);
color: var(--text);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 10px 14px;
font-weight: 600;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn-ghost { background: transparent; }
.btn-danger { background: transparent; color: var(--danger); border-color: var(--danger); }
.btn-danger-solid { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn[disabled] { opacity: 0.5; pointer-events: none; }
.btn-block { width: 100%; }
.row { display: flex; align-items: center; gap: 10px; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.stack { display: flex; flex-direction: column; gap: 10px; }
.muted { color: var(--text-muted); }
.small { font-size: 13px; }
.center { text-align: center; }
.label { font-size: 13px; color: var(--text-muted); margin-bottom: 4px; display: block; }
.field { margin-bottom: 12px; }
.badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
border-radius: 999px;
background: var(--surface-2);
border: 1px solid var(--border);
font-size: 12px;
}
/* Бейдж незавершённой партии («идёт») */
.badge-live { color: var(--accent-2); }
.error-text { color: var(--danger); font-size: 13px; }
/* Формула в справке */
.formula {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 13px;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 10px 12px;
text-align: center;
color: var(--text);
}
/* Модальное окно подтверждения */
.modal-overlay {
position: fixed;
inset: 0;
z-index: 300;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
}
.modal {
width: 100%;
max-width: 360px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 18px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}
.modal-title { font-weight: 700; font-size: 16px; margin-bottom: 8px; }
.modal-msg { color: var(--text-muted); font-size: 14px; margin: 0 0 16px; }
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; }
/* «Барабан» рандома фракции — мелькающее название вместо select */
.faction-spin {
display: flex;
align-items: center;
justify-content: center;
padding: 10px 12px;
border: 1px solid var(--accent);
border-radius: var(--radius-sm);
background: var(--surface-2);
color: var(--text);
font-weight: 700;
white-space: nowrap;
overflow: hidden;
animation: faction-roll 0.45s ease-in-out infinite;
}
@keyframes faction-roll {
0% { opacity: 0.5; transform: translateY(-3px); }
50% { opacity: 1; transform: translateY(0); }
100% { opacity: 0.5; transform: translateY(3px); }
}
/* Тумблер-переключатель (вкл/выкл) */
.switch { position: relative; display: inline-block; width: 44px; height: 26px; flex: 0 0 auto; }
.switch input {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
cursor: pointer;
}
.switch-track {
display: inline-flex;
align-items: center;
width: 44px;
height: 26px;
padding: 2px;
border-radius: 999px;
background: var(--surface-2);
border: 1px solid var(--border);
transition: background 0.15s ease, border-color 0.15s ease;
}
.switch-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--text);
transition: transform 0.15s ease;
}
.switch input:checked + .switch-track { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .switch-track .switch-thumb { transform: translateX(18px); background: #fff; }
.switch input:disabled + .switch-track { opacity: 0.5; }
/* ─── Уведомления ─────────────────────────────────────────────────────────── */
/* Зоны верхней панели: слева back/заголовок, по центру название, справа колокольчик */
.top-bar-left { display: flex; align-items: center; min-width: 0; flex: 0 1 auto; }
.top-bar-center {
flex: 1;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 8px;
}
.top-bar-right { display: flex; align-items: center; flex: none; }
.top-bar-home {
background: none;
border: none;
padding: 0;
color: var(--text);
font-weight: 700;
font-size: 17px;
/* На узких экранах длинное название иначе распирает шапку по горизонтали. */
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.notif-bell-wrap { position: relative; }
.notif-bell {
position: relative;
display: grid;
place-items: center;
width: 38px;
height: 38px;
border: none;
background: none;
color: var(--text);
border-radius: 50%;
}
.notif-badge {
position: absolute;
top: 2px;
right: 2px;
min-width: 16px;
height: 16px;
padding: 0 4px;
border-radius: 999px;
background: var(--accent);
color: #fff;
font-size: 10px;
font-weight: 700;
line-height: 16px;
text-align: center;
}
.notif-panel {
position: absolute;
top: calc(100% + 8px);
right: 0;
width: min(320px, 86vw);
max-height: 70dvh;
overflow-y: auto;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
z-index: 50;
}
.notif-panel-head {
padding: 10px 14px;
font-weight: 700;
border-bottom: 1px solid var(--border);
}
.notif-empty { padding: 18px 14px; text-align: center; }
.notif-list { display: flex; flex-direction: column; }
.notif-item {
display: block;
width: 100%;
text-align: left;
padding: 10px 14px;
background: none;
border: none;
border-bottom: 1px solid var(--border);
color: var(--text);
}
.notif-item:last-child { border-bottom: none; }
.notif-item.unread { background: var(--surface-2); }
.notif-item-title { font-weight: 600; }
.notif-item-body { margin-top: 2px; }
.notif-item-time { margin-top: 4px; }
/* Подсказки ников под инпутом (приглашение игрока и т.п.) */
.suggest-wrap { position: relative; flex: 1; min-width: 0; }
.suggest-list {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
background: var(--surface-2); /* как у input/select — выпадашка продолжает поле */
border: 1px solid var(--border);
border-radius: var(--radius-sm);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
z-index: 50;
overflow: hidden;
}
.suggest-row {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 8px 10px;
background: none;
border: none;
border-bottom: 1px solid var(--border);
color: var(--text);
font: inherit;
font-weight: 600;
text-align: left;
cursor: pointer;
touch-action: manipulation; /* тап без задержки двойного тапа */
}
.suggest-row:last-child { border-bottom: none; }
.suggest-row:hover, .suggest-row:active { background: var(--border); }
.suggest-row .nick { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Кнопка-триггер PickerSelect: выглядит как select, раскрывает .suggest-list. */
.picker-trigger {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
min-height: 42px; /* высота обычного input/select — строки формы не «прыгают» */
background: var(--surface-2);
color: var(--text);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 8px 12px;
font: inherit;
text-align: left;
cursor: pointer;
outline: none;
touch-action: manipulation;
}
.picker-trigger:focus { border-color: var(--accent); }
.picker-trigger .nick { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker-placeholder { color: var(--muted); }
.picker-chevron { margin-left: auto; flex: none; color: var(--muted); }
/* ─── Редактор мест перетаскиванием (PlaceEditor, завершение партии) ─── */
.rank-list { position: relative; display: flex; flex-direction: column; gap: 8px; }
.rank-block {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 8px 10px;
user-select: none;
-webkit-user-select: none;
}
.rank-row { display: flex; align-items: center; gap: 10px; }
.rank-grip {
touch-action: none; /* хват: drag вместо скролла страницы */
cursor: grab;
color: var(--text-muted);
font-size: 18px;
padding: 6px 4px;
flex: none;
}
.rank-info { flex: 1; min-width: 0; }
.rank-nick { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rank-faction { font-size: 12px; color: var(--text-muted); }
.rank-comment {
display: block;
width: 100%;
margin-top: 8px;
font-size: 13px;
padding: 7px 10px;
border-radius: 8px;
}
/* Строка под игроком: комментарий + цели и миры на конец партии */
.rank-details { display: flex; gap: 6px; align-items: flex-end; }
.rank-details .rank-comment { flex: 1; min-width: 0; }
.rank-count { flex: 0 0 58px; margin-top: 8px; }
.rank-count span { display: block; font-size: 10px; color: var(--text-muted); text-align: center; }
.rank-count input {
width: 100%;
font-size: 13px;
padding: 7px 4px;
border-radius: 8px;
text-align: center;
}
/* Предупреждения о несогласованном вводе (не блокируют отправку) */
.warn-list {
margin: 10px 0 0;
padding: 8px 10px 8px 26px;
border: 1px solid var(--accent-2);
border-radius: var(--radius-sm);
background: rgba(240, 160, 75, 0.08);
color: var(--accent-2);
font-size: 13px;
}
.warn-list li + li { margin-top: 4px; }
.rank-unlink {
flex: none;
width: 30px; height: 30px;
border-radius: 8px;
border: 1px solid var(--border);
background: none;
color: var(--text-muted);
cursor: pointer;
display: flex; align-items: center; justify-content: center;
}
/* Блок ничьей: общая рамка, общий хват и общее место */
.rank-tie {
display: flex;
align-items: center;
gap: 6px;
border: 1px solid var(--accent-2);
border-radius: var(--radius-sm);
padding: 6px;
background: rgba(240, 160, 75, 0.05);
}
.rank-tie-col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.rank-tie-badge {
flex: none;
display: flex; flex-direction: column; align-items: center; gap: 2px;
padding: 0 2px;
}
.rank-tie-badge .tag {
font-size: 10px;
color: var(--accent-2);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.dragging {
position: relative;
z-index: 30;
opacity: 0.95;
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6);
}
.merge-target { outline: 2px solid var(--accent-2); outline-offset: 1px; }
.drop-indicator {
position: absolute;
left: 0; right: 0;
height: 3px; border-radius: 2px;
background: var(--accent-2);
display: none;
z-index: 20;
pointer-events: none;
}
.elim-zone {
border: 1px dashed var(--border);
border-radius: var(--radius-sm);
padding: 10px;
margin-top: 12px;
}
.elim-zone.highlight { border-color: var(--accent); background: rgba(224, 83, 61, 0.08); }
.elim-zone .zone-title { font-size: 12px; color: var(--text-muted); }
.elim-zone .rank-block { opacity: 0.6; margin-top: 8px; }
/* Всплывашки сверху экрана в момент прихода */
.toast-top-wrap {
position: fixed;
top: 10px;
left: 50%;
transform: translateX(-50%);
width: min(var(--app-max-w), 94vw);
display: flex;
flex-direction: column;
gap: 8px;
z-index: 200;
pointer-events: none;
}
.toast-top {
pointer-events: auto;
display: block;
width: 100%;
text-align: left;
background: var(--surface-2);
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
color: var(--text);
padding: 10px 14px;
border-radius: var(--radius-sm);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}
.toast-top-error { border-left-color: var(--danger); }
.toast-top-title { font-weight: 700; }
.toast-top-body { margin-top: 2px; font-size: 13px; color: var(--text-muted); }