1492 lines
37 KiB
HTML
1492 lines
37 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>MoexVibe - брокерский счет</title>
|
||
<style>
|
||
:root {
|
||
--canvas: #f4f6f4;
|
||
--surface: #ffffff;
|
||
--surface-subtle: #f8faf8;
|
||
--surface-tint: #eef6f1;
|
||
--text: #17211c;
|
||
--muted: #647067;
|
||
--soft: #8a948e;
|
||
--line: #dfe6e1;
|
||
--line-strong: #cbd8d0;
|
||
--primary: #176747;
|
||
--primary-dark: #0f4e35;
|
||
--primary-soft: #e5f2ea;
|
||
--blue: #2563eb;
|
||
--blue-soft: #e8f0ff;
|
||
--amber: #b7791f;
|
||
--amber-soft: #fff5dc;
|
||
--negative: #b42318;
|
||
--positive: #176747;
|
||
--shadow: 0 12px 34px rgba(24, 38, 31, 0.08);
|
||
--radius: 8px;
|
||
color-scheme: light;
|
||
}
|
||
|
||
* {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
margin: 0;
|
||
background: var(--canvas);
|
||
color: var(--text);
|
||
font-family:
|
||
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
button,
|
||
input {
|
||
font: inherit;
|
||
}
|
||
|
||
button:focus-visible,
|
||
a:focus-visible,
|
||
input:focus-visible {
|
||
outline: 2px solid #5f9ed8;
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
a {
|
||
color: var(--primary);
|
||
text-decoration: none;
|
||
}
|
||
|
||
.app-shell {
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.topbar {
|
||
height: 56px;
|
||
border-bottom: 1px solid var(--line);
|
||
background: rgba(255, 255, 255, 0.88);
|
||
backdrop-filter: blur(12px);
|
||
display: grid;
|
||
grid-template-columns: minmax(180px, 1fr) auto minmax(180px, 1fr);
|
||
align-items: center;
|
||
gap: 24px;
|
||
padding: 0 18px;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 20;
|
||
}
|
||
|
||
.brand-row,
|
||
.user-row,
|
||
.main-nav {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.brand-row {
|
||
gap: 16px;
|
||
}
|
||
|
||
.brand {
|
||
font-weight: 800;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.search {
|
||
width: min(210px, 36vw);
|
||
height: 38px;
|
||
border: 1px solid var(--line-strong);
|
||
border-radius: var(--radius);
|
||
background: #fff;
|
||
color: var(--muted);
|
||
padding: 0 12px;
|
||
}
|
||
|
||
.main-nav {
|
||
gap: 8px;
|
||
justify-content: center;
|
||
}
|
||
|
||
.main-nav a {
|
||
color: #2f3a34;
|
||
font-size: 13px;
|
||
font-weight: 650;
|
||
padding: 8px 10px;
|
||
border-radius: var(--radius);
|
||
}
|
||
|
||
.main-nav a.active {
|
||
color: var(--primary);
|
||
background: var(--primary-soft);
|
||
}
|
||
|
||
.user-row {
|
||
justify-content: flex-end;
|
||
gap: 12px;
|
||
color: var(--muted);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.ghost-button {
|
||
border: 1px solid var(--line);
|
||
background: #fff;
|
||
color: var(--muted);
|
||
border-radius: var(--radius);
|
||
padding: 7px 12px;
|
||
}
|
||
|
||
.page {
|
||
width: min(1040px, calc(100vw - 32px));
|
||
margin: 28px auto 56px;
|
||
display: grid;
|
||
gap: 18px;
|
||
}
|
||
|
||
.page-title-row {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
min-height: 72px;
|
||
}
|
||
|
||
.page-title-main {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 18px;
|
||
min-width: 0;
|
||
min-height: 72px;
|
||
flex: 1 1 auto;
|
||
}
|
||
|
||
.page-title-skeleton {
|
||
width: 100%;
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
.page-title-skeleton-main {
|
||
display: grid;
|
||
gap: 10px;
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
}
|
||
|
||
.page-title-skeleton-main .skel.full {
|
||
width: min(100%, 320px);
|
||
height: 44px;
|
||
}
|
||
|
||
.page-title-skeleton-main .skel.medium {
|
||
width: min(100%, 96px);
|
||
height: 12px;
|
||
}
|
||
|
||
.page-title-skeleton-side {
|
||
display: grid;
|
||
gap: 8px;
|
||
flex: 0 0 108px;
|
||
min-width: 108px;
|
||
align-content: center;
|
||
}
|
||
|
||
.page-title-skeleton-side .skel.short:first-child {
|
||
width: 74px;
|
||
height: 12px;
|
||
}
|
||
|
||
.page-title-skeleton-side .skel.short:last-child {
|
||
width: 104px;
|
||
height: 32px;
|
||
}
|
||
|
||
h1 {
|
||
margin: 0;
|
||
font-size: clamp(30px, 4vw, 48px);
|
||
line-height: 1.02;
|
||
letter-spacing: 0;
|
||
font-weight: 760;
|
||
}
|
||
|
||
.page-yield {
|
||
display: grid;
|
||
gap: 2px;
|
||
min-width: 108px;
|
||
}
|
||
|
||
.page-yield .metric-label {
|
||
font-size: 11px;
|
||
}
|
||
|
||
.page-yield-value {
|
||
font-size: 24px;
|
||
font-weight: 760;
|
||
line-height: 1;
|
||
}
|
||
|
||
.screen-toggle {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
border: 1px solid var(--line-strong);
|
||
background: #fff;
|
||
border-radius: var(--radius);
|
||
padding: 3px;
|
||
gap: 2px;
|
||
}
|
||
|
||
.screen-toggle button {
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--muted);
|
||
border-radius: 6px;
|
||
padding: 7px 11px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
font-weight: 650;
|
||
}
|
||
|
||
.screen-toggle button.active {
|
||
color: #fff;
|
||
background: var(--primary);
|
||
}
|
||
|
||
.account-tabs {
|
||
display: flex;
|
||
gap: 4px;
|
||
border-bottom: 1px solid var(--line);
|
||
overflow-x: auto;
|
||
padding-bottom: 5px;
|
||
}
|
||
|
||
.account-tabs a {
|
||
flex: 0 0 auto;
|
||
color: var(--muted);
|
||
font-size: 13px;
|
||
font-weight: 650;
|
||
padding: 9px 12px;
|
||
border-radius: var(--radius);
|
||
}
|
||
|
||
.account-tabs a.active {
|
||
background: var(--primary-soft);
|
||
color: var(--primary);
|
||
}
|
||
|
||
.hero {
|
||
border: 1px solid #91c7a8;
|
||
background:
|
||
linear-gradient(135deg, rgba(229, 242, 234, 0.9), rgba(255, 255, 255, 0.85)),
|
||
var(--surface);
|
||
border-radius: 18px;
|
||
padding: 20px;
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
gap: 14px;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.hero-kicker,
|
||
.metric-label,
|
||
.section-kicker,
|
||
th {
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.hero-name {
|
||
margin-top: 3px;
|
||
font-size: 28px;
|
||
line-height: 1.12;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.metric {
|
||
background: rgba(255, 255, 255, 0.58);
|
||
border: 1px solid rgba(145, 199, 168, 0.46);
|
||
border-radius: var(--radius);
|
||
padding: 12px;
|
||
min-height: 78px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.metric-value {
|
||
margin-top: 5px;
|
||
font-size: 18px;
|
||
font-weight: 760;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.metric-value.positive {
|
||
color: var(--positive);
|
||
}
|
||
|
||
.metric-value.negative {
|
||
color: var(--negative);
|
||
}
|
||
|
||
.metric-note {
|
||
color: var(--soft);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.metric-note.positive {
|
||
color: #3f745a;
|
||
}
|
||
|
||
.metric-note.negative {
|
||
color: #a04e4e;
|
||
}
|
||
|
||
.portfolio-trend {
|
||
border-radius: 14px;
|
||
display: grid;
|
||
gap: 10px;
|
||
min-width: 0;
|
||
}
|
||
|
||
.portfolio-trend-head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
}
|
||
|
||
.portfolio-trend-meta {
|
||
display: grid;
|
||
gap: 2px;
|
||
}
|
||
|
||
.portfolio-trend-title {
|
||
margin: 0;
|
||
font-size: 24px;
|
||
line-height: 1.08;
|
||
font-weight: 780;
|
||
}
|
||
|
||
.portfolio-trend svg {
|
||
width: 100%;
|
||
height: 120px;
|
||
display: block;
|
||
margin: 0 -20px;
|
||
width: calc(100% + 40px);
|
||
}
|
||
|
||
.trend-area {
|
||
fill: rgba(23, 103, 71, 0.08);
|
||
}
|
||
|
||
.trend-area-fill {
|
||
fill: rgba(23, 103, 71, 0.12);
|
||
}
|
||
|
||
.trend-line {
|
||
fill: none;
|
||
stroke: var(--primary);
|
||
stroke-width: 3;
|
||
stroke-linecap: round;
|
||
stroke-linejoin: round;
|
||
}
|
||
|
||
.trend-guide {
|
||
stroke: rgba(23, 103, 71, 0.14);
|
||
stroke-width: 1;
|
||
stroke-dasharray: 4 6;
|
||
}
|
||
|
||
.trend-point {
|
||
fill: #fff;
|
||
stroke: var(--primary);
|
||
stroke-width: 3;
|
||
}
|
||
|
||
.trend-axis {
|
||
position: relative;
|
||
margin: 0 -20px;
|
||
width: calc(100% + 40px);
|
||
height: 24px;
|
||
color: var(--soft);
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.trend-axis span {
|
||
position: absolute;
|
||
bottom: 0;
|
||
transform: translateX(-50%);
|
||
}
|
||
|
||
.trend-axis span:first-child {
|
||
left: 12px;
|
||
transform: translateX(0);
|
||
}
|
||
|
||
.trend-axis span:nth-child(2) {
|
||
left: 20%;
|
||
}
|
||
|
||
.trend-axis span:nth-child(3) {
|
||
left: 40%;
|
||
}
|
||
|
||
.trend-axis span:nth-child(4) {
|
||
left: 60%;
|
||
}
|
||
|
||
.trend-axis span:nth-child(5) {
|
||
left: 80%;
|
||
}
|
||
|
||
.trend-axis span:last-child {
|
||
left: calc(100% - 12px);
|
||
transform: translateX(-100%);
|
||
}
|
||
|
||
.analytics-panel {
|
||
min-height: 254px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.card.portfolio-history-card {
|
||
border-color: #91c7a8;
|
||
background:
|
||
linear-gradient(135deg, rgba(229, 242, 234, 0.96), rgba(255, 255, 255, 0.86)),
|
||
var(--surface);
|
||
}
|
||
|
||
.portfolio-history-card .data-only,
|
||
.portfolio-history-card .loading-only {
|
||
min-height: 208px;
|
||
}
|
||
|
||
.card {
|
||
background: var(--surface);
|
||
border: 1px solid var(--line);
|
||
border-radius: 14px;
|
||
box-shadow: var(--shadow);
|
||
padding: 18px;
|
||
min-width: 0;
|
||
}
|
||
|
||
.card-head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 14px;
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.card-title {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 10px;
|
||
}
|
||
|
||
h2 {
|
||
margin: 0;
|
||
font-size: 22px;
|
||
line-height: 1.18;
|
||
letter-spacing: 0;
|
||
font-weight: 760;
|
||
}
|
||
|
||
.count-badge {
|
||
color: var(--muted);
|
||
background: var(--surface-subtle);
|
||
border: 1px solid var(--line);
|
||
border-radius: 999px;
|
||
padding: 3px 8px;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.card-link {
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.table-toolbar {
|
||
display: grid;
|
||
grid-template-columns: minmax(240px, 1fr) auto auto;
|
||
gap: 10px;
|
||
align-items: center;
|
||
border: 1px solid var(--line);
|
||
background: var(--surface-subtle);
|
||
border-radius: 10px;
|
||
padding: 9px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.control-group {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
min-width: 0;
|
||
}
|
||
|
||
.control-label {
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
font-weight: 750;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.chips {
|
||
display: flex;
|
||
gap: 6px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.chip {
|
||
height: 30px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
border: 1px solid var(--line-strong);
|
||
border-radius: 999px;
|
||
background: #fff;
|
||
color: var(--muted);
|
||
padding: 0 10px;
|
||
font-size: 12px;
|
||
font-weight: 760;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.chip.active {
|
||
border-color: transparent;
|
||
color: #fff;
|
||
background: var(--primary);
|
||
}
|
||
|
||
.chip.info.active {
|
||
background: var(--blue);
|
||
}
|
||
|
||
.chip.warning.active {
|
||
color: #473000;
|
||
background: #f4c447;
|
||
}
|
||
|
||
.period-wrap {
|
||
position: relative;
|
||
}
|
||
|
||
.period-button,
|
||
.apply-button,
|
||
.pager-button {
|
||
height: 32px;
|
||
border-radius: var(--radius);
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
font-weight: 760;
|
||
}
|
||
|
||
.period-button {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
border: 1px solid var(--line-strong);
|
||
background: #fff;
|
||
color: var(--text);
|
||
padding: 0 10px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.calendar-icon {
|
||
width: 14px;
|
||
height: 14px;
|
||
border: 1.8px solid currentColor;
|
||
border-radius: 3px;
|
||
position: relative;
|
||
}
|
||
|
||
.calendar-icon::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: -1.8px;
|
||
right: -1.8px;
|
||
top: 3px;
|
||
border-top: 1.8px solid currentColor;
|
||
}
|
||
|
||
.chevron-icon {
|
||
width: 7px;
|
||
height: 7px;
|
||
border-right: 1.8px solid currentColor;
|
||
border-bottom: 1.8px solid currentColor;
|
||
color: var(--soft);
|
||
transform: rotate(45deg) translateY(-1px);
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.period-popover {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 40px;
|
||
width: 330px;
|
||
display: none;
|
||
background: #fff;
|
||
border: 1px solid var(--line);
|
||
border-radius: 12px;
|
||
box-shadow: 0 22px 55px rgba(24, 38, 31, 0.16);
|
||
padding: 12px;
|
||
z-index: 10;
|
||
}
|
||
|
||
.period-popover.open {
|
||
display: block;
|
||
}
|
||
|
||
.preset-row {
|
||
display: flex;
|
||
gap: 6px;
|
||
flex-wrap: wrap;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.preset {
|
||
border: 1px solid var(--line);
|
||
background: var(--surface-subtle);
|
||
color: var(--muted);
|
||
border-radius: 999px;
|
||
padding: 6px 10px;
|
||
font-size: 12px;
|
||
font-weight: 750;
|
||
}
|
||
|
||
.preset.active {
|
||
color: var(--primary);
|
||
background: var(--primary-soft);
|
||
border-color: #b6d7c3;
|
||
}
|
||
|
||
.calendar-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(7, 1fr);
|
||
gap: 4px;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
text-align: center;
|
||
}
|
||
|
||
.calendar-grid span {
|
||
padding: 5px 0;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.calendar-grid .day {
|
||
color: var(--text);
|
||
background: var(--surface-subtle);
|
||
}
|
||
|
||
.calendar-grid .in-range {
|
||
background: var(--primary-soft);
|
||
color: var(--primary);
|
||
}
|
||
|
||
.calendar-grid .selected {
|
||
background: var(--primary);
|
||
color: #fff;
|
||
}
|
||
|
||
.popover-actions {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: 12px;
|
||
}
|
||
|
||
.link-button {
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.apply-button {
|
||
border: 0;
|
||
background: var(--primary);
|
||
color: #fff;
|
||
padding: 0 12px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.apply-button:hover,
|
||
.screen-toggle button.active:hover {
|
||
background: var(--primary-dark);
|
||
}
|
||
|
||
.table-wrap {
|
||
overflow-x: auto;
|
||
}
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 13px;
|
||
}
|
||
|
||
th {
|
||
text-align: left;
|
||
padding: 8px 10px;
|
||
background: var(--surface-subtle);
|
||
border-bottom: 1px solid var(--line);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
td {
|
||
padding: 8px 10px;
|
||
border-bottom: 1px solid var(--line);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
tbody tr:hover td {
|
||
background: #fbfcfb;
|
||
}
|
||
|
||
.ticker {
|
||
font-weight: 780;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.ticker-wrap {
|
||
display: grid;
|
||
gap: 3px;
|
||
}
|
||
|
||
.instrument-name {
|
||
color: var(--muted);
|
||
font-size: 11px;
|
||
line-height: 1.25;
|
||
}
|
||
|
||
.amount,
|
||
.status,
|
||
.num {
|
||
text-align: right;
|
||
}
|
||
|
||
.amount {
|
||
font-weight: 780;
|
||
}
|
||
|
||
.positive {
|
||
color: var(--positive);
|
||
}
|
||
|
||
.negative {
|
||
color: var(--negative);
|
||
}
|
||
|
||
.planned {
|
||
color: var(--soft);
|
||
}
|
||
|
||
.type-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: 24px;
|
||
padding: 0 10px;
|
||
border-radius: 999px;
|
||
border: 1px solid var(--line-strong);
|
||
background: #fff;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
font-weight: 760;
|
||
}
|
||
|
||
.type-badge.coupon {
|
||
background: var(--blue-soft);
|
||
border-color: #cddcff;
|
||
color: var(--blue);
|
||
}
|
||
|
||
.type-badge.dividend {
|
||
background: var(--primary-soft);
|
||
border-color: #bcdcc9;
|
||
color: var(--primary);
|
||
}
|
||
|
||
.type-badge.maturity {
|
||
background: var(--amber-soft);
|
||
border-color: #f0d898;
|
||
color: #8d6400;
|
||
}
|
||
|
||
.type-badge.tax {
|
||
background: #fef0ef;
|
||
border-color: #f6c6c2;
|
||
color: var(--negative);
|
||
}
|
||
|
||
.status-pill {
|
||
display: inline-flex;
|
||
justify-content: center;
|
||
min-width: 82px;
|
||
border-radius: 999px;
|
||
background: var(--primary-soft);
|
||
color: var(--primary);
|
||
padding: 4px 8px;
|
||
font-size: 12px;
|
||
font-weight: 760;
|
||
}
|
||
|
||
.status-pill.negative {
|
||
background: #fef0ef;
|
||
color: var(--negative);
|
||
}
|
||
|
||
.status-pill.planned {
|
||
background: #eef2ef;
|
||
color: var(--muted);
|
||
}
|
||
|
||
.muted {
|
||
color: var(--muted);
|
||
}
|
||
|
||
.table-footer {
|
||
min-height: 34px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-top: 10px;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.table-footer.compact {
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.pager {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.pager-button {
|
||
min-width: 34px;
|
||
border: 1px solid var(--line-strong);
|
||
background: #fff;
|
||
color: var(--primary);
|
||
}
|
||
|
||
.pager-button:disabled {
|
||
color: #b8c0bb;
|
||
border-color: var(--line);
|
||
cursor: default;
|
||
}
|
||
|
||
.analytics-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 12px;
|
||
}
|
||
|
||
.analytics-summary {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.analytics-item {
|
||
border: 1px solid var(--line);
|
||
background: var(--surface-subtle);
|
||
border-radius: var(--radius);
|
||
padding: 12px;
|
||
min-height: 72px;
|
||
}
|
||
|
||
.analytics-item.positive {
|
||
background: linear-gradient(180deg, #f3faf5, #edf7f0);
|
||
border-color: #cae6d3;
|
||
}
|
||
|
||
.analytics-item.negative {
|
||
background: linear-gradient(180deg, #fff7f5, #fff1ef);
|
||
border-color: #f3d2cb;
|
||
}
|
||
|
||
.analytics-value {
|
||
margin-top: 4px;
|
||
font-size: 15px;
|
||
font-weight: 760;
|
||
}
|
||
|
||
.analytics-skeleton-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 12px;
|
||
}
|
||
|
||
.analytics-skeleton-card {
|
||
border: 1px solid var(--line);
|
||
background: var(--surface-subtle);
|
||
border-radius: var(--radius);
|
||
padding: 12px;
|
||
min-height: 72px;
|
||
display: grid;
|
||
gap: 10px;
|
||
}
|
||
|
||
.analytics-summary .analytics-skeleton-card {
|
||
min-height: 78px;
|
||
}
|
||
|
||
.allocation-skeleton {
|
||
display: grid;
|
||
gap: 14px;
|
||
}
|
||
|
||
.allocation-skeleton-row {
|
||
display: grid;
|
||
gap: 8px;
|
||
}
|
||
|
||
.chart-loading {
|
||
min-height: 208px;
|
||
display: grid;
|
||
gap: 14px;
|
||
}
|
||
|
||
.chart-loading-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
gap: 16px;
|
||
}
|
||
|
||
.chart-loading-meta {
|
||
display: grid;
|
||
gap: 8px;
|
||
}
|
||
|
||
.chart-loading-visual {
|
||
position: relative;
|
||
height: 144px;
|
||
margin: 0 -20px;
|
||
width: calc(100% + 40px);
|
||
border-radius: 12px;
|
||
background: rgba(23, 103, 71, 0.05);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.chart-loading-visual::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 26px;
|
||
border-top: 1px dashed rgba(23, 103, 71, 0.16);
|
||
}
|
||
|
||
.chart-loading-visual::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 18px 0 26px;
|
||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
|
||
animation: shimmer 1.8s ease-in-out infinite;
|
||
}
|
||
|
||
.chart-loading svg {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
}
|
||
|
||
.chart-loading .trend-area-fill {
|
||
fill: rgba(23, 103, 71, 0.08);
|
||
}
|
||
|
||
.chart-loading .trend-line {
|
||
stroke: rgba(23, 103, 71, 0.55);
|
||
stroke-width: 2.5;
|
||
stroke-dasharray: 10 10;
|
||
animation: dashflow 10s linear infinite;
|
||
}
|
||
|
||
.allocation-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: baseline;
|
||
gap: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.allocation-total {
|
||
font-weight: 780;
|
||
}
|
||
|
||
.allocation-list {
|
||
display: grid;
|
||
gap: 12px;
|
||
}
|
||
|
||
.alloc-row {
|
||
display: grid;
|
||
gap: 6px;
|
||
}
|
||
|
||
.alloc-meta {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.bar-track {
|
||
height: 9px;
|
||
background: #edf1ee;
|
||
border-radius: 999px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bar {
|
||
height: 100%;
|
||
border-radius: 999px;
|
||
}
|
||
|
||
.bar.shares {
|
||
width: 28.6%;
|
||
background: #4f6bed;
|
||
}
|
||
|
||
.bar.bonds {
|
||
width: 70.6%;
|
||
background: #e7a328;
|
||
}
|
||
|
||
.bar.cash {
|
||
width: 0.8%;
|
||
background: #8f6ad8;
|
||
}
|
||
|
||
.loading-only {
|
||
display: none;
|
||
}
|
||
|
||
body.loading .data-only {
|
||
display: none;
|
||
}
|
||
|
||
body.loading .loading-only {
|
||
display: block;
|
||
}
|
||
|
||
body.loading .page-title-main.loading-only {
|
||
display: none;
|
||
}
|
||
|
||
.skeleton-table {
|
||
display: grid;
|
||
gap: 0;
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.skeleton-row {
|
||
display: grid;
|
||
grid-template-columns: 120px 1.4fr 1fr 110px;
|
||
gap: 16px;
|
||
align-items: center;
|
||
min-height: 38px;
|
||
padding: 8px 10px;
|
||
border-bottom: 1px solid var(--line);
|
||
}
|
||
|
||
.skeleton-row:last-child {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.income-skeleton .skeleton-row {
|
||
grid-template-columns: 120px 1.4fr 1fr 120px;
|
||
}
|
||
|
||
.skel {
|
||
height: 13px;
|
||
border-radius: 999px;
|
||
background: linear-gradient(90deg, #edf1ee 0%, #f8faf8 48%, #edf1ee 100%);
|
||
background-size: 220% 100%;
|
||
animation: shimmer 1.35s ease-in-out infinite;
|
||
}
|
||
|
||
.skel.short {
|
||
width: 64px;
|
||
}
|
||
|
||
.skel.medium {
|
||
width: 92px;
|
||
}
|
||
|
||
.skel.long {
|
||
width: 74%;
|
||
}
|
||
|
||
.skel.full {
|
||
width: 100%;
|
||
}
|
||
|
||
@keyframes shimmer {
|
||
0% {
|
||
background-position: 120% 0;
|
||
}
|
||
100% {
|
||
background-position: -120% 0;
|
||
}
|
||
}
|
||
|
||
@keyframes dashflow {
|
||
from {
|
||
stroke-dashoffset: 120;
|
||
}
|
||
to {
|
||
stroke-dashoffset: 0;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 860px) {
|
||
.topbar {
|
||
grid-template-columns: 1fr;
|
||
height: auto;
|
||
align-items: stretch;
|
||
padding: 12px 16px;
|
||
gap: 10px;
|
||
}
|
||
|
||
.main-nav,
|
||
.user-row {
|
||
justify-content: flex-start;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.page-title-row {
|
||
align-items: flex-start;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.page-title-main {
|
||
align-items: flex-start;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.hero,
|
||
.analytics-grid,
|
||
.analytics-summary,
|
||
.analytics-skeleton-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.table-toolbar {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.control-group {
|
||
align-items: flex-start;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.period-popover {
|
||
left: 0;
|
||
right: auto;
|
||
width: min(330px, calc(100vw - 64px));
|
||
}
|
||
|
||
.skeleton-row {
|
||
grid-template-columns: 80px 1fr 90px;
|
||
}
|
||
|
||
.skeleton-row span:nth-child(4),
|
||
.skeleton-row span:nth-child(5) {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.skel {
|
||
animation: none;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app-shell">
|
||
<header class="topbar">
|
||
<div class="brand-row">
|
||
<div class="brand">MoexVibe</div>
|
||
<input class="search" value="Поиск" aria-label="Поиск" readonly />
|
||
</div>
|
||
<nav class="main-nav" aria-label="Основная навигация">
|
||
<a href="#">Портфели</a>
|
||
<a href="#" class="active">Брокер</a>
|
||
<a href="#">Скринер</a>
|
||
</nav>
|
||
<div class="user-row">
|
||
<span>Sergey</span>
|
||
<button class="ghost-button" type="button">Выйти</button>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="page">
|
||
<div class="page-title-row">
|
||
<div class="data-only page-title-main">
|
||
<h1>Брокерский счёт</h1>
|
||
<div class="page-yield" aria-label="Доходность счёта">
|
||
<div class="metric-label">Доходность</div>
|
||
<div class="page-yield-value negative">-6,84%</div>
|
||
<div class="metric-note positive">За день: +1 688,04 ₽</div>
|
||
</div>
|
||
</div>
|
||
<div class="loading-only page-title-main page-title-skeleton" aria-hidden="true">
|
||
<div class="page-title-skeleton-main">
|
||
<span class="skel full"></span>
|
||
<span class="skel medium"></span>
|
||
</div>
|
||
<div class="page-title-skeleton-side">
|
||
<span class="skel short"></span>
|
||
<span class="skel short"></span>
|
||
</div>
|
||
</div>
|
||
<div class="screen-toggle" aria-label="Состояние данных">
|
||
<button type="button" class="active" data-mode="data">Данные</button>
|
||
<button type="button" data-mode="loading">Загрузка</button>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="account-tabs" aria-label="Разделы брокерского счёта">
|
||
<a class="active" href="#">Обзор</a>
|
||
<a href="#">Акции</a>
|
||
<a href="#">Облигации</a>
|
||
<a href="#">Операции</a>
|
||
<a href="#">События</a>
|
||
<a href="#">Аналитика</a>
|
||
</nav>
|
||
|
||
<section class="card portfolio-history-card" aria-labelledby="income-title">
|
||
<div class="card-head">
|
||
<div class="card-title">
|
||
<h2 id="income-title">Стоимость портфеля за 6 месяцев</h2>
|
||
</div>
|
||
</div>
|
||
<div class="data-only">
|
||
<div class="portfolio-trend" aria-label="График стоимости портфеля за последние шесть месяцев">
|
||
<div class="portfolio-trend-head">
|
||
<div class="portfolio-trend-meta">
|
||
<div class="metric-label">Текущая стоимость</div>
|
||
<div class="metric-value">1 884 891,72 ₽</div>
|
||
</div>
|
||
</div>
|
||
<svg viewBox="0 0 1000 120" role="img" aria-label="Рост стоимости портфеля за шесть месяцев">
|
||
<title>Стоимость портфеля с января по июнь 2026</title>
|
||
<desc>График показывает изменение стоимости портфеля за последние шесть месяцев, от января до июня 2026.</desc>
|
||
<path class="trend-guide" d="M0 96H1000"></path>
|
||
<rect class="trend-area" x="0" y="24" width="1000" height="96"></rect>
|
||
<path class="trend-area-fill" d="M0 98 C70 98, 130 95, 200 93 S340 82, 400 79 S540 73, 600 72 S740 51, 800 44 S930 29, 1000 24 L1000 120 L0 120 Z"></path>
|
||
<path class="trend-line" d="M0 98 C70 98, 130 95, 200 93 S340 82, 400 79 S540 73, 600 72 S740 51, 800 44 S930 29, 1000 24"></path>
|
||
</svg>
|
||
<div class="trend-axis" aria-hidden="true">
|
||
<span>Янв</span>
|
||
<span>Фев</span>
|
||
<span>Мар</span>
|
||
<span>Апр</span>
|
||
<span>Май</span>
|
||
<span>Июн</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="loading-only">
|
||
<div class="chart-loading" aria-hidden="true">
|
||
<div class="chart-loading-head">
|
||
<div class="chart-loading-meta">
|
||
<span class="skel long"></span>
|
||
<span class="skel medium"></span>
|
||
</div>
|
||
</div>
|
||
<div class="chart-loading-visual">
|
||
<svg viewBox="0 0 1000 144" preserveAspectRatio="none">
|
||
<path class="trend-area-fill" d="M0 120 C90 120, 160 116, 230 112 S390 100, 460 94 S620 90, 690 82 S850 56, 920 46 S970 38, 1000 34 L1000 144 L0 144 Z"></path>
|
||
<path class="trend-line" d="M0 120 C90 120, 160 116, 230 112 S390 100, 460 94 S620 90, 690 82 S850 56, 920 46 S970 38, 1000 34"></path>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="card" aria-labelledby="analytics-title">
|
||
<div class="card-head">
|
||
<div class="card-title">
|
||
<h2 id="analytics-title">Аналитика доходности</h2>
|
||
</div>
|
||
</div>
|
||
<div class="data-only analytics-panel">
|
||
<div class="analytics-summary">
|
||
<div class="metric">
|
||
<div>
|
||
<div class="metric-label">Стоимость портфеля</div>
|
||
<div class="metric-value">1 884 891,72 ₽</div>
|
||
</div>
|
||
</div>
|
||
<div class="metric">
|
||
<div>
|
||
<div class="metric-label">Всего доходов</div>
|
||
<div class="metric-value positive">186 087,53 ₽</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="analytics-grid">
|
||
<div class="analytics-item positive"><div class="section-kicker">Пополнения</div><div class="analytics-value positive">113 773,03 ₽</div></div>
|
||
<div class="analytics-item negative"><div class="section-kicker">Выводы</div><div class="analytics-value negative">-684 753,92 ₽</div></div>
|
||
<div class="analytics-item positive"><div class="section-kicker">Дивиденды</div><div class="analytics-value positive">27 137,88 ₽</div></div>
|
||
<div class="analytics-item positive"><div class="section-kicker">Купоны</div><div class="analytics-value positive">158 949,65 ₽</div></div>
|
||
<div class="analytics-item negative"><div class="section-kicker">Комиссия</div><div class="analytics-value negative">-3 418,72 ₽</div></div>
|
||
<div class="analytics-item negative"><div class="section-kicker">Уплаченные налоги</div><div class="analytics-value negative">-24 906,14 ₽</div></div>
|
||
</div>
|
||
</div>
|
||
<div class="loading-only analytics-panel">
|
||
<div class="analytics-summary">
|
||
<div class="analytics-skeleton-card"><span class="skel medium"></span><span class="skel short"></span></div>
|
||
<div class="analytics-skeleton-card"><span class="skel medium"></span><span class="skel short"></span></div>
|
||
</div>
|
||
<div class="analytics-skeleton-grid">
|
||
<div class="analytics-skeleton-card"><span class="skel medium"></span><span class="skel short"></span></div>
|
||
<div class="analytics-skeleton-card"><span class="skel medium"></span><span class="skel short"></span></div>
|
||
<div class="analytics-skeleton-card"><span class="skel medium"></span><span class="skel short"></span></div>
|
||
<div class="analytics-skeleton-card"><span class="skel medium"></span><span class="skel short"></span></div>
|
||
<div class="analytics-skeleton-card"><span class="skel medium"></span><span class="skel short"></span></div>
|
||
<div class="analytics-skeleton-card"><span class="skel medium"></span><span class="skel short"></span></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="card" aria-labelledby="allocation-title">
|
||
<div class="card-head">
|
||
<div class="card-title">
|
||
<h2 id="allocation-title">Структура</h2>
|
||
</div>
|
||
</div>
|
||
<div class="data-only">
|
||
<div class="allocation-head">
|
||
<span class="allocation-total">1 884 891,72 ₽</span>
|
||
</div>
|
||
<div class="allocation-list">
|
||
<div class="alloc-row">
|
||
<div class="alloc-meta"><strong>Акции</strong><span class="muted">539 344,64 ₽ · 28,6%</span></div>
|
||
<div class="bar-track"><div class="bar shares"></div></div>
|
||
</div>
|
||
<div class="alloc-row">
|
||
<div class="alloc-meta"><strong>Облигации</strong><span class="muted">1 330 791,92 ₽ · 70,6%</span></div>
|
||
<div class="bar-track"><div class="bar bonds"></div></div>
|
||
</div>
|
||
<div class="alloc-row">
|
||
<div class="alloc-meta"><strong>Деньги</strong><span class="muted">14 755,16 ₽ · 0,8%</span></div>
|
||
<div class="bar-track"><div class="bar cash"></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="loading-only">
|
||
<div class="allocation-skeleton">
|
||
<span class="skel short"></span>
|
||
<div class="allocation-skeleton-row"><span class="skel medium"></span><span class="skel full"></span></div>
|
||
<div class="allocation-skeleton-row"><span class="skel medium"></span><span class="skel full"></span></div>
|
||
<div class="allocation-skeleton-row"><span class="skel medium"></span><span class="skel full"></span></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="card" aria-labelledby="events-title">
|
||
<div class="card-head">
|
||
<div class="card-title">
|
||
<h2 id="events-title">Последние события</h2>
|
||
</div>
|
||
<a class="card-link" href="#">Все события</a>
|
||
</div>
|
||
<div class="data-only">
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Дата</th>
|
||
<th>Инструмент</th>
|
||
<th>Тип</th>
|
||
<th class="num">Сумма</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>15.05.2026</td><td><div class="ticker-wrap"><div class="ticker">HeadHunter Group</div><div class="instrument-name">HEAD</div></div></td><td><span class="type-badge dividend">Дивиденд</span></td><td class="amount positive">+1 631,00 ₽</td></tr>
|
||
<tr><td>15.05.2026</td><td><div class="ticker-wrap"><div class="ticker">ОФЗ 26241</div><div class="instrument-name">RU000A10A4C1</div></div></td><td><span class="type-badge maturity">Погашение</span></td><td class="amount positive">+150,00 ₽</td></tr>
|
||
<tr><td>14.05.2026</td><td><div class="ticker-wrap"><div class="ticker">Самолет БО-П14</div><div class="instrument-name">RU000A10B461</div></div></td><td><span class="type-badge tax">Налог</span></td><td class="amount negative">-87,00 ₽</td></tr>
|
||
<tr><td>14.05.2026</td><td><div class="ticker-wrap"><div class="ticker">Система 001Р-31</div><div class="instrument-name">RU000A10A3Z4</div></div></td><td><span class="type-badge coupon">Купон</span></td><td class="amount positive">+1 212,45 ₽</td></tr>
|
||
<tr><td>13.05.2026</td><td><div class="ticker-wrap"><div class="ticker">ЕвроТранс 001Р-06</div><div class="instrument-name">RU000A10B883</div></div></td><td><span class="type-badge coupon">Купон</span></td><td class="amount positive">+13,97 ₽</td></tr>
|
||
<tr><td>13.05.2026</td><td><div class="ticker-wrap"><div class="ticker">Газпром капитал 2Р-09</div><div class="instrument-name">RU000A10B024</div></div></td><td><span class="type-badge coupon">Купон</span></td><td class="amount positive">+326,92 ₽</td></tr>
|
||
<tr><td>13.05.2026</td><td><div class="ticker-wrap"><div class="ticker">РЖД 001Р-37R</div><div class="instrument-name">RU000A10AEF9</div></div></td><td><span class="type-badge coupon">Купон</span></td><td class="amount positive">+43,56 ₽</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="table-footer compact">
|
||
<div class="pager">
|
||
<button class="pager-button" type="button" disabled>←</button>
|
||
<span>1</span>
|
||
<button class="pager-button" type="button">→</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="loading-only">
|
||
<div class="skeleton-table">
|
||
<div class="skeleton-row"><span class="skel short"></span><span class="skel long"></span><span class="skel medium"></span><span class="skel short"></span></div>
|
||
<div class="skeleton-row"><span class="skel short"></span><span class="skel full"></span><span class="skel medium"></span><span class="skel short"></span></div>
|
||
<div class="skeleton-row"><span class="skel short"></span><span class="skel long"></span><span class="skel medium"></span><span class="skel short"></span></div>
|
||
<div class="skeleton-row"><span class="skel short"></span><span class="skel full"></span><span class="skel medium"></span><span class="skel short"></span></div>
|
||
<div class="skeleton-row"><span class="skel short"></span><span class="skel long"></span><span class="skel medium"></span><span class="skel short"></span></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
</div>
|
||
|
||
<script>
|
||
const modeButtons = document.querySelectorAll('[data-mode]');
|
||
modeButtons.forEach((button) => {
|
||
button.addEventListener('click', () => {
|
||
modeButtons.forEach((item) => item.classList.remove('active'));
|
||
button.classList.add('active');
|
||
document.body.classList.toggle('loading', button.dataset.mode === 'loading');
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('[data-popover]').forEach((button) => {
|
||
button.addEventListener('click', (event) => {
|
||
event.stopPropagation();
|
||
const id = button.getAttribute('data-popover');
|
||
const popover = document.getElementById(id);
|
||
document.querySelectorAll('.period-popover').forEach((item) => {
|
||
if (item !== popover) item.classList.remove('open');
|
||
});
|
||
popover.classList.toggle('open');
|
||
});
|
||
});
|
||
|
||
document.addEventListener('click', () => {
|
||
document.querySelectorAll('.period-popover').forEach((item) => item.classList.remove('open'));
|
||
});
|
||
|
||
document.querySelectorAll('.period-popover').forEach((popover) => {
|
||
popover.addEventListener('click', (event) => event.stopPropagation());
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|