Bicycle/App/media/css/dashboard.css
Egor Isaev ac1334c784 dev
2026-08-14 14:05:41 +03:00

679 lines
18 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.

/* Токены — палитра Nocturne (App/design_handoff_personal_finance/nocturne.css), перенесённая на
существующие имена переменных проекта, чтобы не переписывать разметку/JS (см. README хендоффа —
пересобрать в целевом окружении, не копировать HTML как есть). Акцентные цвета счетов/статей
(--teal/--green/--purple/...) — уже тёмная палитра под этот же принцип (прямые цвета, не токены
системы), оставлены как есть. */
:root {
--bg: #161826;
--surface: #232532;
--surface-2: #292b31;
--surface-3: #3f424d;
--ink: #e9e9ed;
--ink-soft: #cfd3e5;
--muted: #9397ab;
--shadow: rgba(0, 0, 0, 0.5);
--divider: color-mix(in srgb, var(--ink) 16%, transparent);
--accent: #9184d9;
--accent-tint: #d2cefd;
--teal: #2dd4bf;
--green: #34d399;
--purple: #a78bfa;
--blue: #4c8dff;
--orange: #fb923c;
--pink: #f472b6;
--red: #f87171;
--amber: #fbbf24;
--gray: #6b7280;
--sans: "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--font-heading: "SF Pro Display", var(--sans);
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 14px;
--shadow-card: 0 0 0 1px var(--surface-3), 0 6px 18px var(--shadow);
--shadow-pop: 0 0 0 1px var(--surface-3), 0 16px 40px var(--shadow);
}
body {
background: var(--bg);
color: var(--ink);
font-family: var(--sans);
margin: 0;
}
main {
max-width: 72rem;
margin: 0 auto;
padding: 1.5rem 1.5rem 6.5rem;
}
.num { font-variant-numeric: tabular-nums; }
.ico { width: 1.05em; height: 1.05em; flex-shrink: 0; }
/* ---------- шапка: дата-плашка + теги (день недели/месяц/% бюджета) ---------- */
.dash-kicker {
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
}
.dash-tags {
display: flex;
gap: 0.6rem;
flex-wrap: wrap;
margin-top: 0.6rem;
}
.tag {
display: inline-flex;
align-items: center;
font-size: 0.72rem;
letter-spacing: 0.01em;
padding: 0.2rem 0.65rem;
border-radius: var(--radius-sm);
}
.tag-outline {
border: 1px solid var(--divider);
color: var(--ink-soft);
}
/* ---------- шапка: приветствие + баланс ---------- */
.top-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1.5rem;
flex-wrap: wrap;
margin-bottom: 2rem;
}
.hello {
font-size: 0.95rem;
color: var(--ink-soft);
}
.balance-label {
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--accent-tint);
margin-top: 0.6rem;
}
.balance {
font-family: var(--font-heading);
font-size: 3rem;
font-weight: 800;
letter-spacing: -0.02em;
line-height: 1;
}
.balance span { font-size: 1.4rem; font-weight: 600; color: var(--muted); }
.balance-total {
font-size: 1.15rem;
font-weight: 700;
margin-top: 0.4rem;
color: var(--ink-soft);
}
.balance-total .label {
font-size: 0.78rem;
font-weight: 500;
color: var(--muted);
margin-right: 0.4rem;
}
/* ---------- заголовок секции ---------- */
.section-label {
display: flex;
align-items: center;
justify-content: space-between;
font-family: var(--font-heading);
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
margin: 2rem 0 0.9rem;
}
.section-label:first-of-type { margin-top: 0; }
/* ---------- карточки счетов ---------- */
.grid-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
gap: 0.9rem;
}
.room-card {
position: relative;
background: var(--surface);
border-radius: var(--radius-lg);
padding: 1rem 1.1rem 1.15rem;
overflow: hidden;
box-shadow: var(--shadow-card);
}
.room-card::after {
content: "";
position: absolute;
left: 14%;
right: 14%;
bottom: 0;
height: 3px;
border-radius: 3px;
background: linear-gradient(90deg, transparent, var(--card-accent, var(--blue)), transparent);
opacity: 0.9;
}
.room-card-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
.room-icon {
width: 2.1rem;
height: 2.1rem;
border-radius: var(--radius-md);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 1rem;
background: color-mix(in srgb, var(--card-accent, var(--blue)) 20%, transparent);
color: var(--card-accent, var(--blue));
margin-bottom: 0.7rem;
}
.room-title { font-family: var(--font-heading); font-size: 0.92rem; font-weight: 600; }
.room-sub {
font-size: 0.76rem;
color: var(--muted);
margin-top: 0.15rem;
}
.room-value {
font-size: 1.3rem;
font-weight: 700;
margin-top: 0.6rem;
color: var(--card-accent, var(--ink));
}
/* три точки на карточке счёта */
.room-menu { position: relative; }
.room-menu-btn {
border: none;
background: transparent;
color: var(--muted);
padding: 0.15rem 0.3rem;
border-radius: 6px;
cursor: pointer;
}
.room-menu-btn:hover { color: var(--ink); background: var(--surface-3); }
.room-menu-list {
display: none;
position: absolute;
right: 0;
top: 1.8rem;
z-index: 5;
min-width: 160px;
background: var(--surface-2);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-pop);
padding: 0.35rem;
}
.room-menu.open .room-menu-list { display: block; }
.room-menu-list a {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.6rem;
border-radius: var(--radius-md);
font-size: 0.82rem;
color: var(--ink-soft);
}
.room-menu-list a:hover { background: var(--surface-3); color: var(--ink); }
.room-menu-list a.danger { color: var(--red); }
.room-menu-move {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.6rem;
border-radius: var(--radius-md);
font-size: 0.82rem;
color: var(--ink-soft);
cursor: pointer;
}
.room-menu-move:hover { background: var(--surface-3); color: var(--ink); }
.room-menu-move select {
flex: 1;
background: transparent;
border: none;
color: inherit;
font-size: inherit;
padding: 0;
}
/* ---------- бюджет по категориям — компактный список (дашборд, макет "Бюджет — обзор") ---------- */
.budget-list {
background: var(--surface);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
overflow: hidden;
margin-bottom: 1.5rem;
}
.budget-row {
padding: 0.65rem 1.1rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.budget-row + .budget-row { border-top: 1px solid var(--divider); }
.budget-group-head {
display: flex;
align-items: baseline;
justify-content: space-between;
padding: 0.55rem 1.1rem;
background: var(--surface-2);
}
.budget-group-head + .budget-row { border-top: none; }
.budget-row + .budget-group-head { border-top: 1px solid var(--divider); }
.budget-group-name {
font-family: var(--font-heading);
font-size: 0.74rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--card-accent, var(--ink-soft));
}
.budget-group-sum { font-size: 0.72rem; color: var(--muted); }
.budget-name { font-size: 0.84rem; font-weight: 600; }
.budget-plan-amount { font-size: 0.84rem; font-weight: 600; color: var(--ink-soft); white-space: nowrap; }
/* строка статьи план/факт — название сверху, бар+подпись снизу, % справа (см. дизайн-хендофф) */
.budget-row-main { flex: 1; min-width: 0; }
.budget-bar {
height: 7px;
border-radius: 4px;
background: var(--surface-3);
overflow: hidden;
margin-top: 0.35rem;
}
.budget-bar-fill { height: 100%; border-radius: 4px; }
.budget-row-caption { font-size: 0.72rem; color: var(--muted); margin-top: 0.3rem; }
.budget-row-side { flex-shrink: 0; font-size: 0.84rem; font-weight: 600; }
/* ---------- копилки/цели: кольцо прогресса ---------- */
.goal-card { display: flex; flex-direction: row; align-items: center; gap: 0.9rem; }
.goal-ring { flex-shrink: 0; }
.goal-ring text { font-family: var(--sans); }
/* пустое состояние + кнопка добавить */
.empty-state {
border: 1px dashed var(--surface-3);
border-radius: var(--radius-lg);
padding: 2.5rem 1rem;
text-align: center;
color: var(--muted);
}
.btn-add {
width: 2.1rem;
height: 2.1rem;
border-radius: 50%;
border: none;
background: var(--surface-3);
color: var(--ink);
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.btn-add:hover { background: var(--accent); }
/* ---------- архив счетов (модалка) ---------- */
.archive-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 0.7rem 0;
border-bottom: 1px solid var(--surface-3);
}
.archive-row:last-child { border-bottom: none; }
/* ---------- список статей (категории) ---------- */
.category-list {
display: flex;
flex-direction: column;
gap: 0.4rem;
margin-bottom: 1.5rem;
}
.category-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
background: var(--surface);
border-radius: var(--radius-lg);
padding: 0.7rem 0.9rem;
}
.category-row-main {
display: flex;
align-items: center;
gap: 0.6rem;
min-width: 0;
}
.category-group { font-family: var(--font-heading); font-weight: 600; }
.category-sub {
margin-left: 1.6rem;
background: var(--surface-2);
}
/* ---------- план на год: карточки (комп/планшет) + аккордeон (телефон) ----------
Своя раскладка на каждую ширину, ни одна не даёт горизонтальный скролл (см. обсуждение
дизайна в .claude/memory/бюджет_текущий_план.md):
- комп: карточка статьи — все 12 месяцев в один ряд;
- планшет: та же карточка, но сеткой 6×2 — через container query, ничего не жмётся;
- телефон: карточки-таблицы пропадают совсем, вместо них — раскрывающиеся аккордeоны
(тап по статье показывает все 12 месяцев в два столбика).
.budget-zoom — container для container query (не завязано на реальный viewport, а на
ширину самого блока — так раскладка одинаково работает и в узком, и в широком main). */
.budget-zoom { container-type: inline-size; }
.budget-plan-group { margin-bottom: 0.9rem; }
.budget-cards-wrap { display: block; }
.budget-accordion { display: none; }
.budget-cards { display: flex; flex-direction: column; gap: 0.5rem; }
.budget-card {
background: var(--surface);
border-radius: var(--radius-lg);
padding: 0.75rem 0.85rem;
box-shadow: var(--shadow-card);
}
.budget-card-name { font-family: var(--font-heading); font-size: 0.84rem; font-weight: 600; margin-bottom: 0.5rem; }
.budget-months-inline { display: flex; gap: 0.35rem; }
.budget-cell {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.15rem;
background: var(--surface-2);
border-radius: var(--radius-md);
padding: 0.35rem 0.15rem;
min-width: 0;
}
.budget-cell-label { font-size: 0.6rem; color: var(--muted); text-transform: uppercase; }
.plan-input {
width: 100%;
background: transparent;
border: none;
color: var(--ink);
text-align: center;
font-size: 0.72rem;
font-family: inherit;
padding: 0;
}
.plan-input:focus-visible { outline: 1px solid var(--accent); border-radius: 3px; }
.plan-input::-webkit-outer-spin-button, .plan-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.plan-input { -moz-appearance: textfield; }
/* планшет: та же карточка, но 12 месяцев — сеткой 6×2, не в один сжатый ряд */
@container (max-width: 740px) {
.budget-months-inline { display: grid; grid-template-columns: repeat(6, 1fr); gap: 0.4rem; }
}
/* телефон: карточки меняются на другой компонент — раскрывающиеся аккордeоны */
@container (max-width: 420px) {
.budget-cards-wrap { display: none; }
.budget-accordion { display: block; }
}
.budget-acc-card {
background: var(--surface);
border-radius: var(--radius-lg);
margin-bottom: 0.5rem;
overflow: hidden;
}
.budget-acc-card summary {
display: flex;
align-items: center;
gap: 0.6rem;
padding: 0.7rem 0.9rem;
font-family: var(--font-heading);
font-size: 0.84rem;
font-weight: 600;
cursor: pointer;
list-style: none;
}
.budget-acc-name { flex: 1; min-width: 0; }
.budget-acc-card summary::-webkit-details-marker { display: none; }
.budget-acc-card summary::after { content: "▾"; color: var(--muted); font-size: 0.7rem; margin-left: 0.3rem; }
.budget-acc-card details[open] summary::after { content: "▴"; }
.budget-acc-total { font-size: 0.76rem; color: var(--muted); font-weight: 500; white-space: nowrap; }
.budget-acc-caption {
padding: 0 0.9rem;
margin: 0.5rem 0 0.3rem;
font-size: 0.7rem;
color: var(--muted);
}
.budget-acc-body {
padding: 0 0.9rem 0.8rem;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.4rem 0.7rem;
}
.budget-acc-month {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.4rem;
background: var(--surface-2);
border-radius: var(--radius-md);
padding: 0.3rem 0.5rem;
}
.budget-acc-month span { font-size: 0.68rem; color: var(--muted); }
.budget-acc-month .plan-input { width: 4rem; flex-shrink: 0; text-align: right; }
/* ---------- нижний dock-навбар ---------- */
.dock {
position: fixed;
left: 50%;
bottom: 1.1rem;
transform: translateX(-50%);
display: flex;
align-items: stretch;
gap: 0.2rem;
background: rgba(23, 23, 29, 0.86);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 26px;
padding: 0.5rem;
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.04);
z-index: 30;
max-width: calc(100vw - 2rem);
overflow-x: auto;
}
.dock-item {
display: flex;
align-items: center;
justify-content: center;
padding: 0.7rem;
border-radius: 18px;
color: var(--muted);
text-decoration: none;
flex-shrink: 0;
transition: color 0.15s ease, background 0.15s ease;
}
.dock-item .ico { width: 1.35rem; height: 1.35rem; transition: transform 0.15s ease; }
a.dock-item:hover { color: var(--ink-soft); }
.dock-item.active {
color: var(--accent);
background: color-mix(in srgb, var(--accent) 16%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
}
.dock-item.active .ico { transform: translateY(-1px); }
@media (max-width: 640px) {
.top-row { flex-direction: column; }
.balance { font-size: 2.4rem; }
.dock-item { padding: 0.5rem 0.65rem; }
}
/* ---------- Bootstrap: формы/кнопки/модалки под токены Nocturne ----------
Разметка форм (модалки счетов/статей) — штатные Bootstrap-компоненты (.form-control, .btn-primary
и т.п.), меняем только их отрисовку под палитру/радиусы проекта, не трогая JS/структуру. */
.btn { border-radius: var(--radius-md); }
.btn-primary {
background: var(--accent);
border-color: var(--accent);
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
background: color-mix(in srgb, var(--accent) 85%, black) !important;
border-color: color-mix(in srgb, var(--accent) 85%, black) !important;
}
.btn-outline-light {
border-color: var(--surface-3);
color: var(--ink-soft);
}
.btn-outline-light:hover,
.btn-outline-light:focus,
.btn-outline-light:active {
background: var(--surface-3) !important;
border-color: var(--surface-3) !important;
color: var(--ink) !important;
}
.btn-secondary {
background: transparent;
border-color: var(--surface-3);
color: var(--ink-soft);
}
.btn-secondary:hover,
.btn-secondary:focus,
.btn-secondary:active {
background: var(--surface-3) !important;
border-color: var(--surface-3) !important;
color: var(--ink) !important;
}
.form-control,
.form-select {
background: var(--bg);
border-color: var(--surface-3);
color: var(--ink);
border-radius: var(--radius-md);
}
.form-control:focus,
.form-select:focus {
background: var(--bg);
border-color: var(--accent);
color: var(--ink);
box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--accent) 25%, transparent);
}
.form-check-input {
background-color: var(--bg);
border-color: var(--surface-3);
}
.form-check-input:checked {
background-color: var(--accent);
border-color: var(--accent);
}
.form-check-input:focus {
box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--accent) 25%, transparent);
}
.modal-content { border-radius: var(--radius-lg); }