gendesign/frontend/src/app/site-finder/analysis/[cad]/v2/v2.module.css
Light1YT c3e091c811
All checks were successful
CI / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Successful in 52s
CI / openapi-codegen-check (pull_request) Successful in 1m48s
feat(site-finder/v2): resource legend overlay + light theme polish
— V2MapLegend overlay над cockpit-картой (электр/вода/канал/тепло/газ)
  с nearest_m + статус-точка, источник utilities.summary (subtype-buckets
  зеркалят backend _nearest() aliases; electric расширен power_line, gas
  включает legacy generic pipeline).
— Per-theme заголовок карты: dark «СПУТНИК · СВЕЖИЙ СНИМОК», light
  «СПУТНИК · СХЕМА ПОДКЛЮЧЕНИЙ» (toggle через CSS [data-theme], без JS).
— Точечный light-theme contrast в v2.module.css: kv/eng значения,
  drawer eyebrow, .more / .ver / активная вкладка с cyan→accent-blue,
  status-dot halo off на белом. Dark не тронут.

Scope: только ptica-v2/* и v2.module.css. PticaMapInner (шарится со
старым ptica) не тронут.
2026-06-22 11:26:13 +05:00

2057 lines
41 KiB
CSS

/*
* Site Finder v2 — «ПТИЦА» cockpit, ported 1:1 from the approved static
* prototype (ptica-v2/index.html). EVERY token and rule is scoped under
* `.v2Root[data-theme="dark"]` / `[data-theme="light"]` so the cockpit theme
* NEVER leaks into the rest of the (light) application — the wrapper carries the
* class AND the data-theme attribute.
*
* Fonts come from the globally-wired next/font CSS vars (--font-inter /
* --font-plex-mono — see app/layout.tsx); no new font deps.
*/
/* ===================== SCOPED TOKENS ===================== */
.v2Root {
--font-ui:
var(--font-inter), "Inter", "Manrope", -apple-system, "Segoe UI", system-ui,
sans-serif;
--font-mono:
var(--font-plex-mono), "IBM Plex Mono", "Roboto Mono", ui-monospace,
monospace;
--radius-xs: 4px;
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--line: 1px;
--line-strong: 1.5px;
--accent-blue: #2f6f9f;
--accent-cyan: #7fd0ee;
--accent-cyan-strong: #8ed3ff;
--accent-green: #38c172;
--accent-green-soft: #2faa63;
--accent-yellow: #e0a93b;
--accent-red: #d2655b;
--accent-purple: #9b80d6;
--accent-orange: #e46c35;
--res-electric: #e6b23c;
--res-water: #4d9bd7;
--res-sewer: #9b80d6;
--res-heat: #e46c35;
--res-gas: #d2655b;
--res-poi: #3fa77d;
--gauge-track: 9px;
}
.v2Root[data-theme="dark"] {
--bg: #060f16;
--bg-2: #08131c;
--grid-line: rgba(120, 165, 190, 0.06);
--surface: rgba(11, 26, 36, 0.72);
--surface-2: rgba(9, 22, 31, 0.62);
--surface-strong: rgba(12, 30, 42, 0.94);
--surface-muted: rgba(26, 52, 67, 0.55);
--surface-inset: rgba(4, 12, 18, 0.6);
--text: #dcebf2;
--text-strong: #f2fafe;
--text-muted: #8ba6b3;
--text-soft: #5f7886;
--border: rgba(150, 192, 214, 0.18);
--border-strong: rgba(150, 192, 214, 0.4);
--border-faint: rgba(150, 192, 214, 0.1);
--map-bg: #0a1820;
--glow: rgba(110, 200, 240, 0.28);
--glow-strong: rgba(110, 200, 240, 0.55);
--shadow: 0 18px 70px rgba(0, 0, 0, 0.5);
--verdict: var(--accent-green);
--gauge-good: var(--accent-green);
--chip-bg: rgba(20, 42, 56, 0.6);
--topbar-bg: rgba(7, 16, 23, 0.9);
}
.v2Root[data-theme="light"] {
--bg: #eaeef2;
--bg-2: #e4e9ee;
--grid-line: rgba(70, 110, 135, 0.07);
--surface: #ffffff;
--surface-2: rgba(255, 255, 255, 0.85);
--surface-strong: #ffffff;
--surface-muted: rgba(222, 231, 238, 0.7);
--surface-inset: rgba(232, 238, 243, 0.8);
--text: #102132;
--text-strong: #06141f;
--text-muted: #51616f;
--text-soft: #768493;
--border: rgba(40, 70, 95, 0.2);
--border-strong: rgba(40, 70, 95, 0.36);
--border-faint: rgba(40, 70, 95, 0.1);
--map-bg: #d9e4ea;
--glow: rgba(70, 140, 180, 0.2);
--glow-strong: rgba(70, 140, 180, 0.4);
--shadow:
0 1px 2px rgba(16, 24, 40, 0.06), 0 10px 28px rgba(40, 68, 92, 0.09);
--verdict: #1f9d57;
--gauge-good: #1f9d57;
--chip-bg: rgba(225, 234, 241, 0.85);
--topbar-bg: rgba(255, 255, 255, 0.92);
}
/* ===================== ROOT / BASE ===================== */
.v2Root {
position: relative;
width: 100%;
min-height: 100vh;
font-family: var(--font-ui);
background: var(--bg);
color: var(--text);
-webkit-font-smoothing: antialiased;
font-size: 12px;
line-height: 1.3;
font-variant-numeric: tabular-nums;
}
.v2Root :where(button) {
font: inherit;
cursor: pointer;
color: inherit;
border: none;
background: none;
}
.v2Root svg {
display: block;
}
.mono {
font-family: var(--font-mono);
}
.muted {
color: var(--text-muted);
}
.soft {
color: var(--text-soft);
}
.dash {
color: var(--text-soft);
font-family: var(--font-mono);
}
.app {
width: 100%;
height: 100vh;
display: grid;
grid-template-rows: 56px 1fr 26px;
background:
linear-gradient(var(--grid-line) 1px, transparent 1px) 0 0 / 34px 34px,
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px) 0 0 / 34px 34px,
radial-gradient(1200px 700px at 30% -10%, var(--bg-2), var(--bg));
position: relative;
overflow: hidden;
}
.lbl {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 0.13em;
text-transform: uppercase;
color: var(--text-muted);
font-weight: 500;
}
.sectionTitle {
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--text-strong);
font-weight: 600;
}
.sectionSub {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 0.13em;
text-transform: uppercase;
color: var(--text-soft);
font-weight: 500;
}
/* ===================== TOPBAR ===================== */
.topbar {
grid-row: 1;
display: grid;
grid-template-columns: 300px 1fr 300px;
align-items: center;
border-bottom: 1px solid var(--border);
background: var(--topbar-bg);
padding: 0 18px;
backdrop-filter: blur(6px);
z-index: 10;
}
.brand {
display: flex;
align-items: center;
gap: 11px;
text-decoration: none;
color: inherit;
}
.brandLogo {
width: 30px;
height: 30px;
color: var(--accent-cyan);
flex: 0 0 auto;
}
.brandLogo svg {
width: 100%;
height: 100%;
filter: drop-shadow(0 0 6px var(--glow));
}
.brandName {
font-family: var(--font-mono);
font-weight: 600;
font-size: 18px;
letter-spacing: 0.34em;
color: var(--text-strong);
}
.brandSub {
font-size: 7.5px;
letter-spacing: 0.05em;
color: var(--text-soft);
text-transform: uppercase;
margin-top: 2px;
line-height: 1.25;
max-width: 200px;
}
.tabs {
display: flex;
justify-content: center;
align-items: center;
gap: 30px;
height: 100%;
}
.tab {
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--text-soft);
font-weight: 500;
position: relative;
height: 100%;
display: flex;
align-items: center;
}
.tabActive {
color: var(--text-strong);
}
.tabActive::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 2px;
background: var(--accent-cyan);
box-shadow: 0 0 8px var(--glow-strong);
}
.topbarRight {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 14px;
}
.clock {
text-align: right;
}
.clock .time {
font-family: var(--font-mono);
font-size: 15px;
font-weight: 500;
color: var(--text-strong);
letter-spacing: 0.04em;
}
.clock .date {
font-family: var(--font-mono);
font-size: 8.5px;
color: var(--text-soft);
letter-spacing: 0.06em;
margin-top: 1px;
}
.ver {
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.08em;
color: var(--accent-cyan);
padding: 3px 7px;
border: 1px solid var(--border);
border-radius: var(--radius-xs);
background: var(--surface-2);
}
.tbIcons {
display: flex;
gap: 6px;
}
.tbIc {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--border);
border-radius: var(--radius-xs);
color: var(--text-muted);
background: var(--surface-2);
}
.tbIc svg {
width: 13px;
height: 13px;
}
/* ===================== BODY GRID ===================== */
.body {
grid-row: 2;
display: grid;
grid-template-columns: 76px 1fr;
min-height: 0;
}
/* rail */
.rail {
display: flex;
flex-direction: column;
align-items: center;
border-right: 1px solid var(--border);
background: var(--surface-2);
padding: 10px 0;
gap: 2px;
}
.railItem {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 9px 0;
color: var(--text-soft);
position: relative;
}
.railItem svg {
width: 19px;
height: 19px;
}
.railItem .rlbl {
font-family: var(--font-mono);
font-size: 7px;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.railItemActive {
color: var(--accent-cyan);
}
.railItemActive::before {
content: "";
position: absolute;
left: 0;
top: 6px;
bottom: 6px;
width: 2px;
background: var(--accent-cyan);
box-shadow: 0 0 8px var(--glow-strong);
}
.railSpacer {
flex: 1;
}
/* content area */
.content {
display: grid;
min-width: 0;
min-height: 0;
gap: 8px;
padding: 8px;
grid-template-rows:
minmax(0, 2.18fr) minmax(0, 1.18fr) minmax(0, 1.3fr)
minmax(0, 1fr);
overflow-y: auto;
}
/* card primitive */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
display: flex;
flex-direction: column;
min-width: 0;
min-height: 0;
position: relative;
overflow: hidden;
}
.cardH {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 11px 7px;
border-bottom: 1px solid var(--border-faint);
flex: 0 0 auto;
}
.cardH .ttl {
display: flex;
align-items: baseline;
gap: 8px;
min-width: 0;
}
.cardB {
padding: 9px 11px;
flex: 1;
min-height: 0;
}
.more {
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--accent-cyan);
white-space: nowrap;
display: flex;
align-items: center;
gap: 3px;
}
.moreRow {
text-align: right;
margin-top: 6px;
}
.moreRowAuto {
text-align: right;
margin-top: auto;
}
/* bracket corners (light only). bc1 = bottom-left, bc2 = bottom-right; the two
top corners are the ::before / ::after of the card itself. */
.brk::before,
.brk::after,
.bc1,
.bc2 {
content: "";
position: absolute;
width: 15px;
height: 15px;
pointer-events: none;
z-index: 3;
}
.v2Root[data-theme="light"] .brk::before {
top: -1px;
left: -1px;
border-top: 2px solid var(--border-strong);
border-left: 2px solid var(--border-strong);
}
.v2Root[data-theme="light"] .brk::after {
top: -1px;
right: -1px;
border-top: 2px solid var(--border-strong);
border-right: 2px solid var(--border-strong);
}
.v2Root[data-theme="light"] .brk .bc1 {
bottom: -1px;
left: -1px;
border-bottom: 2px solid var(--border-strong);
border-left: 2px solid var(--border-strong);
}
.v2Root[data-theme="light"] .brk .bc2 {
bottom: -1px;
right: -1px;
border-bottom: 2px solid var(--border-strong);
border-right: 2px solid var(--border-strong);
}
/* rows */
.row1 {
display: grid;
grid-template-columns: 1.74fr 1.06fr 0.86fr;
gap: 8px;
min-height: 0;
}
.row2 {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 8px;
min-height: 0;
}
.row3 {
display: grid;
grid-template-columns: 1fr 1fr 1.25fr 1fr;
gap: 8px;
min-height: 0;
}
.row4 {
display: grid;
grid-template-columns: 1.55fr 1.05fr 1.1fr;
gap: 8px;
min-height: 0;
}
/* ===================== MAP ===================== */
.mapCard {
padding: 0;
}
/* The Leaflet map fills the card; PticaMapInner brings its own internal
overlay/controls (reused plumbing). */
.mapMount {
position: absolute;
inset: 0;
border-radius: var(--radius-sm);
overflow: hidden;
}
.mapLoading {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: var(--map-bg);
color: var(--text-soft);
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.mapHead {
position: absolute;
top: 9px;
left: 11px;
z-index: 5;
display: flex;
align-items: baseline;
gap: 8px;
pointer-events: none;
}
.mapHead .ttl {
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--text-strong);
font-weight: 600;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}
/* ===================== PASSPORT ===================== */
.passport .cardB {
display: flex;
flex-direction: column;
gap: 0;
padding: 0;
}
.ppId {
display: flex;
align-items: center;
gap: 8px;
padding: 9px 11px;
border-bottom: 1px solid var(--border-faint);
}
.ppId .v {
font-family: var(--font-mono);
font-size: 14px;
color: var(--text-strong);
letter-spacing: 0.02em;
}
.ppId .cp {
width: 14px;
height: 14px;
color: var(--text-soft);
margin-left: auto;
}
.ppGrid {
display: grid;
grid-template-columns: 1fr 1fr;
}
.ppCell {
padding: 9px 11px;
border-bottom: 1px solid var(--border-faint);
}
.ppCell:nth-child(odd) {
border-right: 1px solid var(--border-faint);
}
.ppCell .k {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--text-soft);
margin-bottom: 4px;
}
.ppCell .vv {
font-size: 12px;
color: var(--text);
font-weight: 500;
line-height: 1.3;
}
.ppCell .vv.big {
font-family: var(--font-mono);
font-size: 13px;
}
.ppCellWide {
grid-column: span 2;
border-right: none;
}
.statusOn {
color: var(--accent-green);
font-family: var(--font-mono);
letter-spacing: 0.06em;
text-transform: uppercase;
font-size: 12px;
}
/* ===================== RIGHT COLUMN row1 ===================== */
.colStack {
display: grid;
grid-template-rows: 1.35fr 1fr;
gap: 8px;
min-height: 0;
}
.gaugeCard .cardB {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
}
.gaugeWrap {
position: relative;
width: 118px;
height: 118px;
}
.gaugeWrap svg {
width: 100%;
height: 100%;
transform: rotate(-90deg);
}
.gaugeCenter {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.gaugeCenter .pct {
font-family: var(--font-mono);
font-size: 30px;
font-weight: 600;
line-height: 1;
}
.gaugeCenter .pct.mutedG {
color: var(--text-soft);
}
.gaugeCenter .cap {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 0.1em;
text-transform: uppercase;
margin-top: 3px;
text-align: center;
}
.gaugeFoot {
font-family: var(--font-mono);
font-size: 8px;
color: var(--text-soft);
letter-spacing: 0.06em;
}
.invest .cardB {
display: flex;
flex-direction: column;
gap: 8px;
}
.invest .scoreRow {
display: flex;
align-items: center;
justify-content: space-between;
}
.invest .score {
font-family: var(--font-mono);
font-size: 26px;
font-weight: 600;
color: var(--text-strong);
}
.invest .score .u {
font-size: 13px;
color: var(--text-soft);
}
.invest .score.dashed {
color: var(--text-soft);
}
.invest .spark {
height: 26px;
color: var(--accent-cyan);
opacity: 0.8;
}
.invest .spark svg {
height: 100%;
width: auto;
}
.invest .prRow {
display: flex;
gap: 10px;
}
.invest .pr {
flex: 1;
}
.invest .pr .k {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-soft);
margin-bottom: 3px;
}
.invest .pr .vv {
font-size: 13px;
font-weight: 600;
color: var(--accent-green);
}
.invest .pr .vv.amb {
color: var(--accent-yellow);
}
.invest .pr .vv.none {
color: var(--text-soft);
font-family: var(--font-mono);
}
/* ===================== ROW2 dev scan ===================== */
.kv {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 8px;
padding: 3.5px 0;
}
.kv .k {
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text-soft);
white-space: nowrap;
}
.kv .v {
font-size: 11px;
color: var(--text);
font-weight: 500;
text-align: right;
font-variant-numeric: tabular-nums;
}
.kv .v.mono {
font-family: var(--font-mono);
}
.kv .v.dash {
color: var(--text-soft);
font-family: var(--font-mono);
}
.engRow {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
padding: 3px 0;
font-size: 9.5px;
}
.engRow .nm {
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--text);
display: flex;
align-items: center;
gap: 6px;
}
.engRow .nm .sd {
width: 7px;
height: 7px;
border-radius: 50%;
flex: 0 0 auto;
box-shadow: 0 0 5px currentColor;
}
.engRow .st {
display: flex;
align-items: center;
gap: 6px;
}
.engRow .st .stt {
font-size: 9.5px;
font-weight: 500;
}
.engRow .st .dm {
font-family: var(--font-mono);
font-size: 9px;
color: var(--text-soft);
min-width: 46px;
text-align: right;
}
.ok {
color: var(--accent-green);
}
.warnc {
color: var(--accent-orange);
}
.badge {
font-family: var(--font-mono);
font-size: 7.5px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--accent-yellow);
border: 1px solid var(--accent-yellow);
border-radius: 3px;
padding: 1px 5px;
opacity: 0.85;
}
.badge.soon {
color: var(--accent-cyan);
border-color: var(--accent-cyan);
}
.note {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.04em;
color: var(--text-soft);
margin-top: 3px;
}
.emptyState {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 0.04em;
color: var(--text-soft);
text-align: center;
padding: 10px 0;
}
/* small risk gauge in row2 */
.minigauge {
position: relative;
width: 74px;
height: 74px;
margin: 0 auto;
}
.minigauge svg {
width: 100%;
height: 100%;
transform: rotate(-90deg);
}
.minigauge .c {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.minigauge .c .p {
font-family: var(--font-mono);
font-size: 18px;
font-weight: 600;
color: var(--accent-cyan);
}
.riskCard .cardB {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
}
.riskCard .rk {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-soft);
}
/* ===================== ROW3 ===================== */
.oksPlan {
flex: 1;
display: flex;
gap: 9px;
min-height: 0;
}
.oksMini {
flex: 0 0 96px;
background: var(--surface-inset);
border: 1px solid var(--border-faint);
border-radius: var(--radius-xs);
position: relative;
overflow: hidden;
}
.oksMini svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.oksKv {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.dpGrid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 7px 10px;
flex: 1;
align-content: center;
}
.dpCell .k {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.07em;
text-transform: uppercase;
color: var(--text-soft);
margin-bottom: 3px;
}
.dpCell .v {
font-family: var(--font-mono);
font-size: 15px;
font-weight: 600;
color: var(--text-strong);
white-space: nowrap;
}
.dpCell .v .u {
font-size: 10px;
color: var(--text-soft);
}
.dpCell .v.dash {
color: var(--text-soft);
}
.dpCellWide {
grid-column: span 3;
border-top: 1px solid var(--border-faint);
padding-top: 6px;
margin-top: 2px;
}
/* product card */
.chips {
display: flex;
gap: 5px;
margin-bottom: 9px;
flex-wrap: wrap;
}
.chip {
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.04em;
padding: 3px 8px;
border: 1px solid var(--border);
border-radius: var(--radius-xs);
color: var(--text-soft);
background: var(--surface-2);
}
.chipActive {
color: var(--accent-cyan);
border-color: var(--accent-cyan);
background: var(--chip-bg);
box-shadow: 0 0 8px var(--glow);
}
.chipDim {
opacity: 0.45;
}
.aptrow {
display: flex;
align-items: center;
gap: 8px;
padding: 2.5px 0;
}
.aptrow .nm {
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--text-soft);
width: 34px;
flex: 0 0 auto;
}
.aptrow .track {
flex: 1;
height: 6px;
background: var(--surface-inset);
border-radius: 3px;
overflow: hidden;
}
.aptrow .track i {
display: block;
height: 100%;
background: var(--accent-cyan);
opacity: 0.85;
border-radius: 3px;
}
.aptrow .pc {
font-family: var(--font-mono);
font-size: 9px;
color: var(--text-soft);
width: 30px;
text-align: right;
}
.qgTitle {
font-family: var(--font-mono);
font-size: 7.5px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--text-soft);
margin-bottom: 5px;
}
.dimBars .track i {
background: var(--text-soft);
opacity: 0.3;
}
/* insolation */
.insoCard .cardB {
display: flex;
align-items: center;
gap: 12px;
}
.insoIso {
flex: 0 0 110px;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-soft);
}
.insoIso svg {
width: 108px;
height: 96px;
}
.insoTxt {
flex: 1;
}
.insoTxt .d {
font-size: 10px;
color: var(--text-soft);
margin-top: 6px;
line-height: 1.4;
max-width: 150px;
}
/* ===================== ROW4 ===================== */
.finCard .cardB {
display: flex;
align-items: stretch;
gap: 14px;
}
.finMain {
display: flex;
gap: 18px;
flex: 1;
align-items: center;
flex-wrap: wrap;
}
.finBig .k {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-soft);
margin-bottom: 4px;
}
.finBig .v {
font-family: var(--font-mono);
font-size: 25px;
font-weight: 600;
color: var(--text-strong);
line-height: 1;
}
.finBig .v.dashed {
color: var(--text-soft);
}
.finBig .u {
font-family: var(--font-mono);
font-size: 9px;
color: var(--text-soft);
margin-top: 3px;
letter-spacing: 0.06em;
}
.finSep {
width: 1px;
background: var(--border-faint);
align-self: stretch;
}
.finRatios {
display: flex;
gap: 16px;
align-items: center;
}
.finRatios .r .k {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-soft);
margin-bottom: 4px;
}
.finRatios .r .v {
font-family: var(--font-mono);
font-size: 18px;
font-weight: 600;
color: var(--text-strong);
}
.finRatios .r .v.dashed {
color: var(--text-soft);
}
.buyGauge {
flex: 0 0 auto;
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
border-left: 1px solid var(--border-faint);
padding-left: 14px;
}
/* legal */
.legalGrid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0 14px;
flex: 1;
align-content: start;
}
.legalRow {
display: flex;
align-items: baseline;
justify-content: space-between;
padding: 4px 0;
border-bottom: 1px solid var(--border-faint);
}
.legalRow .k {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--text-soft);
}
.legalRow .v {
font-size: 11px;
color: var(--text);
font-weight: 500;
}
.legalRow .v.no {
color: var(--accent-green);
}
.legalRow .v.dash {
color: var(--text-soft);
font-family: var(--font-mono);
}
/* verdict */
.verdictCard .cardB {
display: flex;
align-items: center;
gap: 16px;
}
.verdictBig {
flex: 0 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
}
.verdictBig .v {
font-family: var(--font-mono);
font-size: 34px;
font-weight: 700;
letter-spacing: 0.04em;
color: var(--verdict);
line-height: 1;
text-shadow: 0 0 14px var(--glow);
}
.verdictBig .v.warn {
color: var(--accent-yellow);
}
.verdictBig .v.bad {
color: var(--accent-red);
}
.verdictBig .note {
margin-top: 8px;
}
.verdictCheck {
flex: 1;
display: flex;
flex-direction: column;
gap: 0;
min-width: 0;
}
.vchk {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 0;
border-bottom: 1px solid var(--border-faint);
font-size: 10.5px;
}
.vchk svg {
width: 14px;
height: 14px;
flex: 0 0 auto;
}
.vchk.okRow {
color: var(--text);
}
.vchk.okRow svg {
color: var(--accent-green);
}
.vchk.warnRow svg {
color: var(--accent-orange);
}
.vchk.warnRow {
color: var(--text);
}
.vchk.badRow svg {
color: var(--accent-red);
}
.vchk.badRow {
color: var(--text);
}
/* ===================== FOOTER ===================== */
.footer {
grid-row: 3;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid var(--border);
background: var(--topbar-bg);
padding: 0 18px;
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.06em;
color: var(--text-soft);
text-transform: uppercase;
}
.footer .left {
display: flex;
gap: 10px;
align-items: center;
}
.footer .left b {
color: var(--text-muted);
font-weight: 600;
}
.footDot {
color: var(--text-soft);
opacity: 0.6;
}
.footer .right {
color: var(--text-soft);
}
/* ===================== TOAST ===================== */
.toast {
position: fixed;
left: 50%;
bottom: 64px;
z-index: 90;
transform: translateX(-50%) translateY(14px);
background: var(--surface-strong);
border: 1px solid var(--border-strong);
border-radius: var(--radius-sm);
padding: 11px 18px;
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.04em;
color: var(--text-strong);
box-shadow: var(--shadow);
opacity: 0;
visibility: hidden;
transition:
opacity 0.2s,
transform 0.2s,
visibility 0.2s;
}
.toastShow {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
}
/* theme toggle */
.themeToggle {
position: fixed;
right: 14px;
bottom: 38px;
z-index: 50;
width: 38px;
height: 38px;
border-radius: 50%;
background: var(--surface-strong);
border: 1px solid var(--border-strong);
color: var(--accent-cyan);
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--shadow);
font-size: 16px;
}
/* ===================== STATE SCREENS ===================== */
.stateScreen {
grid-row: 2;
display: flex;
align-items: center;
justify-content: center;
padding: 40px;
}
.stateBox {
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.08em;
color: var(--text-muted);
text-transform: uppercase;
}
.stateError {
font-family: var(--font-mono);
font-size: 12px;
color: var(--accent-red);
margin-bottom: 12px;
}
.stateLink {
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.06em;
color: var(--accent-cyan);
text-transform: uppercase;
text-decoration: none;
}
/* ===================== OVERLAY (shared scrim) ===================== */
.scrim {
position: fixed;
inset: 0;
z-index: 60;
background: rgba(2, 8, 12, 0.55);
opacity: 0;
visibility: hidden;
transition:
opacity 0.22s ease,
visibility 0.22s ease;
backdrop-filter: blur(1.5px);
}
.v2Root[data-theme="light"] .scrim {
background: rgba(30, 50, 70, 0.32);
}
.scrimOpen {
opacity: 1;
visibility: visible;
}
/* ===================== DRAWER ===================== */
.drawer {
position: fixed;
top: 0;
right: 0;
bottom: 0;
z-index: 70;
width: 480px;
max-width: 92vw;
background: var(--surface-strong);
border-left: 1px solid var(--border-strong);
box-shadow: -24px 0 70px rgba(0, 0, 0, 0.42);
transform: translateX(100%);
transition: transform 0.26s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
flex-direction: column;
background-image:
linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size:
34px 34px,
34px 34px;
}
.v2Root[data-theme="light"] .drawer {
box-shadow: -18px 0 50px rgba(40, 68, 92, 0.18);
}
.drawerOpen {
transform: translateX(0);
}
.drawerH {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 10px;
padding: 16px 18px 13px;
border-bottom: 1px solid var(--border);
flex: 0 0 auto;
background: var(--topbar-bg);
}
.drawerH .dhLeft {
min-width: 0;
}
.drawerH .dhEyebrow {
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--accent-cyan);
margin-bottom: 5px;
}
.drawerH .dhTitle {
font-family: var(--font-mono);
font-size: 15px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--text-strong);
font-weight: 600;
line-height: 1.15;
}
.drawerH .dhSum {
font-size: 11px;
color: var(--text-muted);
margin-top: 7px;
line-height: 1.45;
max-width: 380px;
}
.drawerClose {
flex: 0 0 auto;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--border);
border-radius: var(--radius-xs);
background: var(--surface-2);
color: var(--text-muted);
font-size: 15px;
line-height: 1;
}
.drawerClose:hover {
color: var(--text-strong);
border-color: var(--border-strong);
}
.drawerB {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 16px 18px 22px;
}
.drawerB::-webkit-scrollbar {
width: 7px;
}
.drawerB::-webkit-scrollbar-thumb {
background: var(--border-strong);
border-radius: 4px;
}
/* drawer content primitives */
.dsec {
margin-bottom: 18px;
}
.dsec:last-child {
margin-bottom: 0;
}
.dsecT {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--text-soft);
margin-bottom: 9px;
display: flex;
align-items: center;
gap: 8px;
}
.dsecT::after {
content: "";
flex: 1;
height: 1px;
background: var(--border-faint);
}
.drow {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 14px;
padding: 7px 0;
border-bottom: 1px solid var(--border-faint);
}
.drow:last-child {
border-bottom: none;
}
.drow .k {
font-family: var(--font-mono);
font-size: 9.5px;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--text-soft);
flex: 0 0 auto;
max-width: 48%;
}
.drow .v {
font-size: 12px;
color: var(--text);
font-weight: 500;
text-align: right;
font-variant-numeric: tabular-nums;
line-height: 1.35;
}
.drow .v.mono {
font-family: var(--font-mono);
}
.drow .v.dash {
color: var(--text-soft);
font-family: var(--font-mono);
}
.drow .v.ok {
color: var(--verdict);
}
.drow .v.warnc {
color: var(--accent-orange);
}
.dnote {
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.04em;
color: var(--text-soft);
margin-top: 10px;
line-height: 1.5;
}
.dsource {
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text-soft);
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid var(--border-faint);
display: flex;
align-items: center;
gap: 7px;
}
.dsource svg {
width: 12px;
height: 12px;
flex: 0 0 auto;
opacity: 0.8;
}
.dbadge {
display: inline-block;
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 2px 7px;
border-radius: 3px;
border: 1px solid var(--accent-yellow);
color: var(--accent-yellow);
}
.dbadge.cyan {
border-color: var(--accent-cyan);
color: var(--accent-cyan);
}
.dbadge.green {
border-color: var(--verdict);
color: var(--verdict);
}
.dbadgeRight {
margin-left: auto;
}
/* drawer data table */
.dtable {
width: 100%;
border-collapse: collapse;
font-variant-numeric: tabular-nums;
}
.dtable th,
.dtable td {
text-align: right;
padding: 6px 6px;
border-bottom: 1px solid var(--border-faint);
white-space: nowrap;
}
.dtable th {
font-family: var(--font-mono);
font-size: 7.5px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text-soft);
font-weight: 500;
}
.dtable td {
font-size: 10px;
color: var(--text);
}
.dtable th:first-child,
.dtable td:first-child {
text-align: left;
}
.dtable td.mono {
font-family: var(--font-mono);
}
.dtable tr.me td {
color: var(--accent-cyan);
}
.dtable td.dash {
color: var(--text-soft);
font-family: var(--font-mono);
}
/* factor bars */
.dfac {
margin: 9px 0;
}
.dfacH {
display: flex;
justify-content: space-between;
font-family: var(--font-mono);
font-size: 9px;
color: var(--text-soft);
text-transform: uppercase;
letter-spacing: 0.04em;
margin-bottom: 4px;
}
.dfacH b {
color: var(--text);
font-weight: 500;
}
.dfacTrack {
height: 7px;
background: var(--surface-inset);
border-radius: 4px;
overflow: hidden;
}
.dfacTrack i {
display: block;
height: 100%;
border-radius: 4px;
}
/* drawer export buttons */
.dexport {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 9px;
}
.dbtn {
display: flex;
align-items: center;
gap: 9px;
padding: 11px 12px;
border: 1px solid var(--border);
border-radius: var(--radius-xs);
background: var(--surface-2);
color: var(--text);
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.04em;
text-transform: uppercase;
text-align: left;
}
.dbtn:hover {
border-color: var(--accent-cyan);
color: var(--accent-cyan);
box-shadow: 0 0 10px var(--glow);
}
.dbtn svg {
width: 15px;
height: 15px;
flex: 0 0 auto;
opacity: 0.85;
}
.dbtn .ds {
font-size: 7.5px;
color: var(--text-soft);
text-transform: none;
letter-spacing: 0;
}
.dbtn b {
font-weight: 600;
display: block;
}
/* ===================== CHAT ===================== */
.chatFab {
position: fixed;
right: 14px;
bottom: 84px;
z-index: 55;
width: 46px;
height: 46px;
border-radius: 50%;
background: var(--accent-blue);
border: 1px solid var(--accent-cyan);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
box-shadow:
0 8px 28px rgba(0, 0, 0, 0.4),
0 0 14px var(--glow-strong);
}
.chatFab svg {
width: 21px;
height: 21px;
}
.chatPanel {
position: fixed;
right: 0;
top: 0;
bottom: 0;
z-index: 75;
width: 380px;
max-width: 90vw;
background: var(--surface-strong);
border-left: 1px solid var(--border-strong);
box-shadow: -24px 0 70px rgba(0, 0, 0, 0.42);
transform: translateX(110%);
transition: transform 0.26s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
flex-direction: column;
}
.v2Root[data-theme="light"] .chatPanel {
box-shadow: -18px 0 50px rgba(40, 68, 92, 0.18);
}
.chatPanelOpen {
transform: translateX(0);
}
.chatH {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 14px 16px 12px;
border-bottom: 1px solid var(--border);
background: var(--topbar-bg);
flex: 0 0 auto;
}
.chatH .chL {
display: flex;
align-items: center;
gap: 9px;
min-width: 0;
}
.chatH .chIc {
width: 26px;
height: 26px;
border-radius: 50%;
background: var(--accent-blue);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
flex: 0 0 auto;
}
.chatH .chIc svg {
width: 14px;
height: 14px;
}
.chatH .chT {
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-strong);
font-weight: 600;
}
.chatH .chS {
font-family: var(--font-mono);
font-size: 8px;
letter-spacing: 0.06em;
color: var(--text-soft);
margin-top: 2px;
}
.chatMsgs {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
}
.chatMsgs::-webkit-scrollbar {
width: 7px;
}
.chatMsgs::-webkit-scrollbar-thumb {
background: var(--border-strong);
border-radius: 4px;
}
.msg {
max-width: 84%;
font-size: 12px;
line-height: 1.5;
padding: 10px 12px;
border-radius: 12px;
white-space: pre-wrap;
}
.msgBot {
align-self: flex-start;
background: var(--surface-muted);
border: 1px solid var(--border-faint);
color: var(--text);
border-top-left-radius: 3px;
}
.msgUser {
align-self: flex-end;
background: var(--accent-blue);
color: #fff;
border-top-right-radius: 3px;
}
.chatChips {
display: flex;
flex-wrap: wrap;
gap: 7px;
padding: 0 16px 12px;
flex: 0 0 auto;
}
.chatChip {
font-family: var(--font-mono);
font-size: 9.5px;
letter-spacing: 0.02em;
padding: 6px 11px;
border: 1px solid var(--border);
border-radius: 14px;
color: var(--text-muted);
background: var(--surface-2);
}
.chatChip:hover {
border-color: var(--accent-cyan);
color: var(--accent-cyan);
}
.chatInput {
display: flex;
gap: 8px;
padding: 12px 14px;
border-top: 1px solid var(--border);
background: var(--topbar-bg);
flex: 0 0 auto;
}
.chatInput input {
flex: 1;
background: var(--surface-inset);
border: 1px solid var(--border);
border-radius: var(--radius-xs);
color: var(--text);
font-family: var(--font-ui);
font-size: 12px;
padding: 9px 11px;
outline: none;
}
.chatInput input:focus {
border-color: var(--accent-cyan);
}
.chatInput input::placeholder {
color: var(--text-soft);
}
.chatSend {
flex: 0 0 auto;
width: 38px;
border: 1px solid var(--accent-cyan);
border-radius: var(--radius-xs);
background: var(--accent-blue);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.chatSend svg {
width: 16px;
height: 16px;
}
.chatTyping {
align-self: flex-start;
display: flex;
gap: 4px;
padding: 11px 13px;
background: var(--surface-muted);
border: 1px solid var(--border-faint);
border-radius: 12px;
border-top-left-radius: 3px;
}
.chatTyping i {
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--text-soft);
animation: tdot 1s infinite;
}
.chatTyping i:nth-child(2) {
animation-delay: 0.2s;
}
.chatTyping i:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes tdot {
0%,
60%,
100% {
opacity: 0.3;
transform: translateY(0);
}
30% {
opacity: 1;
transform: translateY(-3px);
}
}
/* =====================================================================
* MAP — per-theme title + resource legend overlay
* (matches prototype `ptica-v2/index.html` `.map-head` + `.legend .lg-res`)
* ===================================================================== */
/* Per-theme map title — both spans rendered, CSS shows one. Mirrors the
prototype `.tnum-d` / `.tnum-l` toggle. */
.mapTitleDark,
.mapTitleLight {
display: none;
}
.v2Root[data-theme="dark"] .mapTitleDark {
display: inline;
}
.v2Root[data-theme="light"] .mapTitleLight {
display: inline;
}
/* Resource legend (top-left, under the map title) — compact 5-row panel. */
.mapLegend {
position: absolute;
left: 11px;
top: 34px;
z-index: 6;
min-width: 168px;
max-width: 200px;
background: var(--surface-strong);
border: 1px solid var(--border);
border-radius: var(--radius-xs);
padding: 8px 9px;
backdrop-filter: blur(3px);
pointer-events: none;
}
.mapLegendTitle {
font-family: var(--font-mono);
font-size: 7.5px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--text-soft);
margin: 0 0 5px;
}
.mapLegendRow {
display: flex;
align-items: center;
gap: 6px;
height: 15px;
font-size: 9px;
}
.mapLegendDot {
width: 8px;
height: 8px;
border-radius: 50%;
flex: 0 0 auto;
box-shadow: 0 0 5px currentColor;
}
.mapLegendLabel {
font-family: var(--font-mono);
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--text);
font-size: 8.5px;
}
.mapLegendDist {
margin-left: auto;
font-family: var(--font-mono);
font-size: 8.5px;
color: var(--text-muted);
font-variant-numeric: tabular-nums;
}
.mapLegendDistEmpty {
color: var(--text-soft);
opacity: 0.7;
}
/* =====================================================================
* LIGHT THEME — targeted contrast polish
* Tokens stay; just bump the value-side text and the cyan accents that
* appear washed-out on white. Dark theme is untouched.
* ===================================================================== */
/* kv-row: value text on white needs darker ink. */
.v2Root[data-theme="light"] .kv .v {
color: var(--text-strong);
}
/* Инженерия status-row: distance was --text-soft (#768493 ≈ AA-borderline
at 12px on white); push to --text-muted (#51616f) for solid AA. */
.v2Root[data-theme="light"] .engRow .st .dm {
color: var(--text-muted);
}
/* Status-dot glow on white reads as a halo — soften it. */
.v2Root[data-theme="light"] .engRow .nm .sd {
box-shadow: none;
}
/* Map-legend value column — same readability bump on white. */
.v2Root[data-theme="light"] .mapLegendDist {
color: var(--text-strong);
}
.v2Root[data-theme="light"] .mapLegendLabel {
color: var(--text-strong);
}
.v2Root[data-theme="light"] .mapLegendDot {
box-shadow: none;
}
/* Map title (light): drop the dark text-shadow that makes thin uppercase
look smudgy on a satellite-light map base. */
.v2Root[data-theme="light"] .mapHead .ttl {
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.6);
color: var(--text-strong);
}
/* Cards `Подробнее →` — cyan on white is faint. Swap to the deeper blue. */
.v2Root[data-theme="light"] .more {
color: var(--accent-blue);
}
/* Drawer eyebrow (cyan in dark) — use blue on white for AA. */
.v2Root[data-theme="light"] .drawerH .dhEyebrow {
color: var(--accent-blue);
}
/* Topbar version-chip: cyan text on a white pill is illegible. */
.v2Root[data-theme="light"] .ver {
color: var(--accent-blue);
border-color: var(--border-strong);
background: var(--surface);
}
/* Topbar clock — separator-like vertical line between time + date is faint;
strengthen the date line and the chip border for visible structure. */
.v2Root[data-theme="light"] .clock .date {
color: var(--text-muted);
}
/* Active tab underline — cyan + glow on white reads as a pale smudge.
Swap to brand blue + drop the glow halo. */
.v2Root[data-theme="light"] .tabActive::after {
background: var(--accent-blue);
box-shadow: none;
}
/* Rail active marker — same treatment. */
.v2Root[data-theme="light"] .railItemActive {
color: var(--accent-blue);
}
.v2Root[data-theme="light"] .railItemActive::before {
background: var(--accent-blue);
box-shadow: none;
}
/* Generic note / drawer note / source — bump from --text-soft to --text-muted
on white so the caveat copy stays readable but still secondary. */
.v2Root[data-theme="light"] .note,
.v2Root[data-theme="light"] .dnote,
.v2Root[data-theme="light"] .dsource {
color: var(--text-muted);
}
/* Section separator line inside drawer (`::after` on .dsecT) — barely visible
on white at 10% alpha; strengthen the border-faint reach within drawer. */
.v2Root[data-theme="light"] .dsecT::after,
.v2Root[data-theme="light"] .drow {
border-color: rgba(40, 70, 95, 0.16);
}