gendesign/tradein-mvp/frontend/src/components/trade-in/trade-in.css
bot-backend 482d3caf08
All checks were successful
CI / changes (pull_request) Successful in 8s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
feat(tradein/ui): manual-review banner + premium-building badge on estimate (#2002)
2026-06-27 22:04:38 +03:00

2340 lines
66 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.

/* #657 white-label: Manrope — OPEN geometric grotesk для брендового режима.
Подключается всегда, но применяется только при активном бренде
(см. --font-sans override ниже). */
@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&display=swap');
:root {
/* surface */
--bg: oklch(99% 0.002 240);
--surface: oklch(100% 0 0);
--surface-2: oklch(97.5% 0.004 240);
--surface-3: oklch(96% 0.006 240);
/* ink */
--fg: oklch(18% 0.012 250);
--fg-2: oklch(32% 0.014 250);
--muted: #5f6570; /* #834 a11y follow-up: oklch(50%)≈#6b7280 (3.96 на тонир. KPI-фонах) → ≥4.8:1 на #fee2e2/#dbeafe тинтах */
--muted-2: #676d73; /* #834 a11y: было oklch(62%)=#82878c (3.6:1) → AA ≥4.65:1 на белом/off-white */
/* lines */
--border: oklch(92% 0.005 250);
--border-2: oklch(88% 0.006 250);
--border-strong: oklch(80% 0.008 250);
/* accents */
--accent: oklch(54% 0.18 258);
--accent-ink: oklch(36% 0.16 258);
--accent-soft: oklch(96% 0.03 258);
--accent-2: oklch(64% 0.17 50); /* orange — warn / secondary */
--accent-2-soft: oklch(96% 0.05 70);
--success: #197038; /* #834 a11y: было oklch(56%)≈#218a45 (4.4:1) → AA ≥5.5:1 на белом/success-soft */
--success-soft: oklch(96% 0.04 150);
--danger: oklch(56% 0.20 25);
--danger-soft: oklch(96% 0.04 25);
/* viz palette (price bars / sources) */
--viz-1: oklch(56% 0.16 258);
--viz-2: oklch(60% 0.16 200);
--viz-3: oklch(64% 0.15 150);
--viz-4: oklch(70% 0.15 80);
--viz-5: oklch(66% 0.17 30);
/* type */
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
/* #657 white-label font (Manrope) — применяется через .brand-active ниже */
--font-brand: 'Manrope', var(--font-sans);
/* layout */
--radius: 10px;
--radius-sm: 6px;
--radius-lg: 14px;
--shadow-sm: 0 1px 2px rgba(16,24,40,.04);
--shadow-md: 0 4px 18px -6px rgba(16,24,40,.10), 0 1px 2px rgba(16,24,40,.04);
--container: 1320px;
}
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
background: var(--bg);
color: var(--fg);
font: 15px/1.55 var(--font-sans);
font-feature-settings: 'cv11', 'ss03';
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.num, .mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
[data-tnum] { font-variant-numeric: tabular-nums; }
h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -0.012em; color: var(--fg); }
p { margin: 0; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
/* ============================================================
HEADER (white-label — wordmark slot)
============================================================ */
.topbar {
position: sticky;
top: 0;
z-index: 50;
background: color-mix(in oklch, var(--surface) 92%, transparent);
backdrop-filter: saturate(140%) blur(12px);
-webkit-backdrop-filter: saturate(140%) blur(12px);
border-bottom: 1px solid var(--border);
}
.topbar-inner {
max-width: var(--container);
margin: 0 auto;
padding: 12px 28px;
display: flex;
align-items: center;
gap: 28px;
}
.brand {
display: inline-flex;
align-items: center;
gap: 10px;
font-weight: 700;
letter-spacing: 0.06em;
font-size: 13px;
color: var(--fg);
}
.brand-mark {
width: 28px; height: 28px;
background: var(--fg);
color: var(--surface);
border-radius: 6px;
display: grid; place-items: center;
font-family: var(--font-mono);
font-size: 13px;
font-weight: 600;
}
.brand-mark-svg { width: 22px; height: 22px; display: block; }
.brand-name { letter-spacing: 0.22em; }
.brand-sep { width: 1px; height: 18px; background: var(--border); margin: 0 2px; }
.brand-product {
font-weight: 500;
letter-spacing: 0.04em;
font-size: 13px;
color: var(--muted);
text-transform: uppercase;
}
/* #657 white-label: логотип клиента вместо «TI»-метки (см. Topbar.tsx).
object-fit:contain + max-width — защита от расплющивания, если у бренда SVG
без нормального viewBox/с preserveAspectRatio="none" (как был хотлинк
logo-pink.svg). Полноразмерный wordmark (viewBox 1498×194) рендерится ~200×26. */
.brand-logo {
height: 26px;
width: auto;
max-width: 200px;
object-fit: contain;
object-position: left center;
display: block;
}
/* Активный бренд: переопределяем шрифт всего trade-in scope на Manrope.
--accent / --accent-2 переопределяются в рантайме из page.tsx
(document.documentElement.style.setProperty). Без ?brand= класс не
вешается → стандартный Inter + OKLCH-палитра нетронуты. */
.brand-active { --font-sans: var(--font-brand); }
.top-nav {
display: flex;
gap: 22px;
margin-left: auto;
align-items: center;
}
.top-nav a {
color: var(--fg-2);
font-size: 13px;
font-weight: 500;
}
.top-nav a.is-active { color: var(--fg); }
.top-cta {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 7px 12px;
border: 1px solid var(--border-2);
border-radius: 8px;
font-size: 13px;
font-weight: 500;
color: var(--fg);
background: var(--surface);
}
.top-user {
width: 30px; height: 30px;
border-radius: 50%;
background: var(--surface-3);
border: 1px solid var(--border);
display: grid; place-items: center;
font-size: 12px;
font-weight: 600;
color: var(--fg-2);
}
/* ============================================================
PAGE HEAD
============================================================ */
.page {
max-width: var(--container);
margin: 0 auto;
padding: 28px 28px 96px;
}
.crumbs {
display: flex; align-items: center; gap: 8px;
color: var(--muted-2);
font-size: 12px;
letter-spacing: 0.04em;
margin-bottom: 14px;
}
.crumbs span { color: var(--muted); }
.crumbs em { font-style: normal; color: var(--fg-2); }
.page-title {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 24px;
margin-bottom: 8px;
}
.page-title h1 {
font-size: 30px;
line-height: 1.1;
letter-spacing: -0.02em;
}
.page-meta {
display: inline-flex;
gap: 10px;
flex-wrap: wrap;
}
.pill {
display: inline-flex; align-items: center; gap: 6px;
padding: 4px 10px;
border-radius: 999px;
border: 1px solid var(--border-2);
background: var(--surface);
font-size: 12px;
color: var(--fg-2);
letter-spacing: 0.02em;
}
.pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--success); }
.pill .dot.danger { background: var(--danger); }
.pill .dot.warn { background: var(--accent-2); }
.pill .dot.muted { background: var(--muted-2); }
.pill .mono { font-size: 11px; }
.page-subtitle {
color: var(--muted);
font-size: 14px;
max-width: 720px;
}
/* ============================================================
MAIN GRID (sticky form / result column)
============================================================ */
.layout {
display: grid;
grid-template-columns: 360px 1fr;
gap: 40px;
margin-top: 28px;
align-items: start;
}
/* ---- form ---- */
.form-card {
position: sticky;
top: 84px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
max-height: calc(100vh - 104px);
/* overflow-y:auto (было hidden) — на коротких вьюпортах (13" MacBook @100%)
форма выше max-height; flex-shrink тела внутри max-height-контейнера не
срабатывает, и кнопка «Оценить» в .form-foot выдавливалась за границу и
обрезалась. Скролл на самой карточке делает кнопку всегда доступной. */
overflow-y: auto;
overflow-x: hidden;
display: flex;
flex-direction: column;
}
.form-head {
padding: 16px 18px;
border-bottom: 1px solid var(--border);
display: flex; align-items: center; gap: 12px;
}
.form-head h2 {
font-size: 14px;
letter-spacing: 0.02em;
}
.form-head .form-step {
margin-left: auto;
font-family: var(--font-mono);
font-size: 11px;
color: var(--muted);
letter-spacing: 0.08em;
}
.form-body {
padding: 14px 18px 6px;
/* overflow-y:visible — скроллом владеет .form-card целиком (см. выше); вложенный
auto-скролл тут не нужен (flex-shrink тела в max-height-контейнере не работал). */
overflow-y: visible;
flex: 1;
}
/* margin-bottom 12→9 — лёгкое уплотнение, чтобы форма помещалась на ноутбуках. */
.field { margin-bottom: 9px; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.field-row.triple { grid-template-columns: 1fr 1fr 1fr; }
label.field-label {
display: flex;
justify-content: space-between;
align-items: baseline;
font-size: 12px;
color: var(--fg-2);
font-weight: 500;
margin-bottom: 5px;
}
label.field-label .req { color: var(--accent-2); font-size: 11px; }
label.field-label .hint { color: var(--muted-2); font-size: 11px; font-weight: 400; }
.control {
width: 100%;
padding: 8px 10px;
border: 1px solid var(--border-2);
border-radius: var(--radius-sm);
background: var(--surface);
color: var(--fg);
font: 13px/1.4 var(--font-sans);
transition: border-color .12s, box-shadow .12s, background .12s;
}
.control:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent) 18%, transparent);
}
.control[readonly] { background: var(--surface-2); color: var(--fg-2); }
.control-with-icon { position: relative; }
.control-with-icon .control { padding-left: 32px; }
.control-with-icon .icon {
position: absolute;
left: 9px; top: 50%;
transform: translateY(-50%);
width: 16px; height: 16px;
color: var(--muted-2);
pointer-events: none;
}
.control-with-suffix { position: relative; }
.control-with-suffix .suffix {
position: absolute;
right: 12px; top: 50%;
transform: translateY(-50%);
font-family: var(--font-mono);
font-size: 11px;
color: var(--muted);
pointer-events: none;
}
select.control {
appearance: none;
background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
background-position: calc(100% - 14px) center, calc(100% - 10px) center;
background-size: 4px 4px, 4px 4px;
background-repeat: no-repeat;
padding-right: 26px;
}
.autocomplete {
position: relative;
}
.ac-list {
position: absolute;
top: calc(100% + 4px);
left: 0; right: 0;
background: var(--surface);
border: 1px solid var(--border-2);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-md);
z-index: 10;
max-height: 220px;
overflow-y: auto;
display: none;
}
.ac-list.is-open { display: block; }
.ac-item {
padding: 8px 10px;
font-size: 13px;
cursor: pointer;
display: flex; gap: 8px;
align-items: baseline;
border-bottom: 1px solid var(--border);
}
.ac-item:last-child { border-bottom: 0; }
.ac-item:hover, .ac-item.is-focus { background: var(--accent-soft); }
.ac-item .ac-main { color: var(--fg); }
.ac-item .ac-sub { color: var(--muted); font-size: 11px; margin-left: auto; font-family: var(--font-mono); }
.file-drop {
border: 1px dashed var(--border-strong);
border-radius: var(--radius-sm);
padding: 14px 12px;
text-align: center;
color: var(--muted);
font-size: 12px;
background: var(--surface-2);
}
.file-drop b { color: var(--fg-2); font-weight: 500; }
.form-foot {
padding: 10px 18px 12px;
border-top: 1px solid var(--border);
background: linear-gradient(to bottom, transparent, color-mix(in oklch, var(--surface-2) 60%, transparent) 40%);
/* flex-shrink:0 — футер с кнопкой «Оценить» не сжимается при скролле карточки. */
flex-shrink: 0;
}
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
padding: 10px 14px;
border-radius: 8px;
font: 500 13px/1 var(--font-sans);
cursor: pointer;
border: 1px solid transparent;
transition: background .12s, transform .04s, box-shadow .12s;
}
.btn:active { transform: translateY(1px); }
.btn-primary {
width: 100%;
background: var(--fg);
color: var(--surface);
padding: 12px 14px;
font-weight: 600;
}
.btn-primary:hover { background: oklch(28% 0.018 250); }
.btn-accent {
background: var(--accent);
color: var(--surface);
}
.btn-accent:hover { background: var(--accent-ink); }
.btn-ghost {
background: var(--surface);
color: var(--fg);
border-color: var(--border-2);
}
.btn-ghost:hover { background: var(--surface-2); }
.form-foot-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 8px;
font-size: 11px;
color: var(--muted);
}
.form-foot-meta .mono { font-size: 11px; }
.form-foot-meta .ok { color: var(--success); }
/* ============================================================
RESULT COLUMN — shared
============================================================ */
.result-col { min-width: 0; display: flex; flex-direction: column; gap: 28px; }
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
overflow: hidden;
}
.card-head {
padding: 18px 22px 14px;
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 16px;
border-bottom: 1px solid var(--border);
}
.section-kicker {
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.16em;
color: var(--muted-2);
text-transform: uppercase;
margin-bottom: 4px;
}
.card-head h2 {
font-size: 19px;
letter-spacing: -0.012em;
}
.card-head .card-meta {
text-align: right;
color: var(--muted);
font-size: 12px;
line-height: 1.5;
}
.card-head .card-meta b { color: var(--fg); font-weight: 600; }
.card-body { padding: 22px; }
.card-foot { padding: 12px 22px; background: var(--surface-2); border-top: 1px solid var(--border); font-size: 12px; color: var(--muted); }
/* ============================================================
PROGRESS BLOCK
============================================================ */
.progress-card { background: var(--surface); }
.progress-summary {
display: flex;
gap: 22px;
align-items: baseline;
flex-wrap: wrap;
margin-bottom: 16px;
}
.progress-summary .lead {
font-size: 13px;
color: var(--muted);
}
.progress-summary .num-done {
font-family: var(--font-mono);
font-size: 22px;
font-weight: 500;
color: var(--fg);
}
.progress-summary .num-done .total { color: var(--muted-2); font-weight: 400; }
.progress-overall {
flex: 1;
min-width: 200px;
height: 4px;
background: var(--surface-3);
border-radius: 2px;
overflow: hidden;
position: relative;
}
.progress-overall .bar {
height: 100%;
width: 64%;
background: linear-gradient(90deg, var(--accent) 0%, var(--viz-3) 100%);
transition: width .3s ease;
}
.progress-eta {
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.06em;
color: var(--muted-2);
}
.progress-list {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0 28px;
}
.progress-row {
display: grid;
grid-template-columns: 18px 110px 1fr auto;
align-items: center;
gap: 12px;
padding: 9px 0;
border-bottom: 1px solid var(--border);
font-size: 13px;
}
.progress-row:last-child, .progress-row:nth-last-child(2) { border-bottom: 0; }
.progress-row .status-icon {
width: 16px; height: 16px;
display: grid; place-items: center;
}
.status-icon.done { color: var(--success); }
.status-icon.loading { color: var(--accent); animation: spin 1.1s linear infinite; }
.status-icon.error { color: var(--danger); }
@keyframes spin { to { transform: rotate(360deg); } }
.progress-row .src-name {
color: var(--fg);
font-weight: 500;
letter-spacing: -0.005em;
}
.progress-row .src-bar {
height: 4px;
background: var(--surface-3);
border-radius: 2px;
overflow: hidden;
position: relative;
}
.progress-row .src-bar > i {
position: absolute;
inset: 0;
width: var(--p, 100%);
background: var(--accent);
transition: width .3s ease;
}
.progress-row.is-loading .src-bar > i { background: linear-gradient(90deg, var(--accent), transparent); }
.progress-row.is-error .src-bar > i { background: var(--danger-soft); }
.progress-row .src-value {
font-family: var(--font-mono);
font-size: 12px;
color: var(--muted);
letter-spacing: -0.01em;
white-space: nowrap;
}
.progress-row .src-value b { color: var(--fg); font-weight: 500; }
/* ============================================================
HERO (Section 1)
============================================================ */
.hero-card .card-body { padding: 0; }
.hero-top {
display: grid;
grid-template-columns: 320px 1fr;
gap: 0;
border-bottom: 1px solid var(--border);
}
.hero-photo {
aspect-ratio: 4/3;
position: relative;
background: linear-gradient(135deg, oklch(85% 0.02 250), oklch(78% 0.025 240));
overflow: hidden;
}
/* Реальное фото аналога должно перекрывать плейсхолдер-скелет (::before /
::after) — иначе абсолютные псевдоэлементы рисуются поверх <img>.
Если фото нет или оно не загрузилось — скелет остаётся виден. */
.hero-photo img {
position: relative;
z-index: 1;
}
.hero-photo .photo-meta {
position: absolute;
z-index: 2;
bottom: 10px; left: 12px;
display: inline-flex; gap: 6px;
padding: 4px 8px;
background: rgba(20, 22, 30, 0.7);
backdrop-filter: blur(8px);
border-radius: 4px;
color: rgba(255,255,255,.9);
font: 500 10px/1 var(--font-mono);
letter-spacing: 0.06em;
}
/* placeholder window/blocks pattern to suggest a building photo */
.hero-photo::before {
content: '';
position: absolute;
inset: 0;
background:
linear-gradient(180deg, transparent 55%, rgba(8,12,20,.55) 100%),
repeating-linear-gradient(
0deg,
oklch(82% 0.018 250) 0 14px,
oklch(76% 0.020 250) 14px 18px
),
repeating-linear-gradient(
90deg,
transparent 0 22px,
rgba(255,255,255,.10) 22px 24px,
transparent 24px 36px
);
background-blend-mode: normal, multiply, normal;
}
.hero-photo::after {
content: '';
position: absolute;
inset: 30% 18% 6% 18%;
background:
repeating-linear-gradient(90deg, oklch(80% 0.02 250) 0 24px, oklch(74% 0.02 250) 24px 28px),
repeating-linear-gradient(0deg, transparent 0 20px, rgba(255,255,255,.18) 20px 22px);
border-top: 1px solid rgba(255,255,255,.2);
border-left: 1px solid rgba(255,255,255,.2);
}
.hero-meta {
padding: 20px 22px;
display: flex;
flex-direction: column;
gap: 8px;
}
.hero-address {
font-size: 17px;
font-weight: 600;
letter-spacing: -0.012em;
margin-bottom: 4px;
}
.hero-cad {
font-family: var(--font-mono);
font-size: 11px;
color: var(--muted);
letter-spacing: 0.04em;
margin-bottom: 12px;
}
.meta-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px 20px;
margin-top: 4px;
}
.meta-grid .meta-row {
display: flex;
justify-content: space-between;
gap: 12px;
font-size: 13px;
padding-bottom: 8px;
border-bottom: 1px dashed var(--border-2);
}
.meta-grid .meta-row .k { color: var(--muted); }
.meta-grid .meta-row .v { color: var(--fg); font-weight: 500; }
.meta-grid .meta-row .v.mono { font-family: var(--font-mono); }
/* Два равнозначных ценовых ориентира: запрос vs ожидаемая продажа (#648 S5a).
Одинаковый размер value у обоих — ни один не доминирует. */
.hero-prices {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
padding: 20px 22px;
border-bottom: 1px solid var(--border);
}
.hero-prices.single { grid-template-columns: 1fr; }
.price-figure {
display: flex;
flex-direction: column;
gap: 4px;
padding: 16px 18px;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.price-figure[data-kind="sold"] {
background: var(--accent-soft);
border-color: color-mix(in oklch, var(--accent) 22%, var(--border));
}
/* ДКП-сделки (Росреестр) — нейтральный «факт-рынок» оттенок, отличный от
asking, чтобы читались как ДВА разных источника. */
.price-figure[data-kind="dkp"] {
background: var(--surface-3);
border-color: var(--border-strong, var(--border));
}
/* ── Две равноправные hero-цены: «Выставить» + «Получить» ──
Равный визуальный вес: одинаковый размер value у обоих. */
.hero-duo {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
padding: 20px 22px;
border-bottom: 1px solid var(--border);
}
.hero-duo--single { grid-template-columns: 1fr; }
.hero-duo__cell {
display: flex;
flex-direction: column;
gap: 5px;
padding: 18px 20px;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.hero-duo__cell[data-role=sold] {
background: var(--accent-soft);
border-color: color-mix(in oklch, var(--accent) 22%, var(--border));
}
.hero-duo__label {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--muted);
}
.hero-duo__cell[data-role=sold] .hero-duo__label { color: var(--accent-ink); }
.hero-duo__delta {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 600;
padding: 1px 6px;
border-radius: var(--radius-sm);
background: color-mix(in oklch, var(--accent) 16%, transparent);
color: var(--accent-ink);
}
.hero-duo__value {
font-size: 28px;
font-weight: 700;
line-height: 1.1;
letter-spacing: -0.02em;
color: var(--fg);
}
.hero-duo__range {
font-size: 12px;
color: var(--muted);
letter-spacing: 0.01em;
}
.hero-duo__perm2 {
font-size: 11px;
color: var(--muted-2);
letter-spacing: 0.04em;
}
.hero-duo__note {
margin-top: 4px;
font-size: 11px;
color: var(--muted);
font-style: italic;
}
/* ── Компактный ряд бенчмарков под hero-duo ── */
.hero-benchmarks {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 14px 22px;
border-bottom: 1px solid var(--border);
background: var(--surface-1, var(--bg));
}
.bench-chip {
display: flex;
flex-direction: column;
gap: 2px;
padding: 8px 12px;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
min-width: 120px;
}
.bench-chip__src {
font-size: 10px;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted-2);
}
.bench-chip__val {
font-size: 14px;
font-weight: 600;
letter-spacing: -0.01em;
color: var(--fg);
}
.bench-chip__sub {
font-size: 10px;
color: var(--muted);
}
/* ── empty-state headline (без данных) ── */
.headline-value-empty {
font-size: 22px;
font-weight: 600;
line-height: 1.27;
color: var(--fg);
}
.headline-note {
margin: 4px 0 0;
font-size: 12px;
line-height: 1.5;
color: var(--fg-2);
max-width: 60ch;
}
.headline-note b { font-weight: 700; }
.hero-prices-note {
margin: 0;
padding: 12px 22px 0;
font-size: 12px;
line-height: 1.5;
color: var(--muted);
}
.hero-prices-note b { color: var(--fg-2); font-weight: 600; }
.hero-bars {
padding: 18px 22px 20px;
border-bottom: 1px solid var(--border);
display: flex;
flex-direction: column;
gap: 22px;
}
.bar-block .bar-head {
display: flex; justify-content: space-between; align-items: baseline;
font-size: 12px;
color: var(--muted);
letter-spacing: 0.02em;
margin-bottom: 8px;
}
.bar-block .bar-head .bar-title { color: var(--fg-2); font-weight: 500; }
.bar-block .bar-head .bar-cv { font-family: var(--font-mono); font-size: 11px; color: var(--muted-2); }
.pricebar {
position: relative;
height: 56px;
border-bottom: 1px solid var(--border);
}
.pricebar .axis {
position: absolute;
left: 0; right: 0; top: 22px;
height: 6px;
background: var(--surface-3);
border-radius: 3px;
}
.pricebar .axis .range {
position: absolute;
top: 0; bottom: 0;
background: linear-gradient(90deg, var(--viz-1), var(--viz-2));
border-radius: 3px;
}
.pricebar .axis .median {
position: absolute;
top: -3px; bottom: -3px;
width: 2px;
background: var(--fg);
}
.pricebar .endpoint {
position: absolute;
top: 0;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.pricebar .endpoint .endpoint-val {
font: 600 13px/1 var(--font-mono);
color: var(--fg);
white-space: nowrap;
}
.pricebar .endpoint .endpoint-tick {
width: 1px;
height: 14px;
background: var(--border-strong);
margin-top: 4px;
}
.pricebar .median-label {
position: absolute;
bottom: 0;
transform: translateX(-50%);
font-family: var(--font-mono);
font-size: 11px;
color: var(--fg-2);
padding: 2px 6px;
background: var(--surface);
border: 1px solid var(--border-2);
border-radius: 4px;
white-space: nowrap;
}
.pricebar .exp {
position: absolute;
top: 36px;
font-family: var(--font-mono);
font-size: 10px;
color: var(--muted-2);
letter-spacing: 0.04em;
white-space: nowrap;
}
.pricebar .exp.left { left: 0; }
.pricebar .exp.right { right: 0; }
.hero-warnings {
padding: 18px 22px;
display: flex;
flex-direction: column;
gap: 10px;
}
.warn-row {
display: grid;
grid-template-columns: 22px 1fr;
gap: 10px;
align-items: flex-start;
font-size: 13px;
color: var(--fg-2);
line-height: 1.5;
}
.warn-row .ic {
width: 22px; height: 22px;
border-radius: 50%;
background: var(--accent-2-soft);
color: var(--accent-2);
display: grid; place-items: center;
flex-shrink: 0;
}
/* ============================================================
HERO TRUST + ACTIONS — свежесть, «как рассчитано», PDF, CTA
(web-преимущества над статичным PDF). Тема через brand --accent.
============================================================ */
.hero-trust {
padding: 14px 22px 0;
display: flex;
flex-direction: column;
gap: 12px;
}
.hero-trust-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.hero-trust-cta {
flex: 1 1 240px;
font-weight: 600;
}
.hero-trust-pdf {
flex: 0 1 auto;
}
.hero-trust-freshness {
display: inline-flex;
align-items: center;
gap: 7px;
margin: 0;
font-size: 12.5px;
color: var(--fg-2);
}
.hero-trust-freshness .dot {
width: 7px; height: 7px;
border-radius: 50%;
background: var(--success, #16a34a);
box-shadow: 0 0 0 3px color-mix(in oklch, var(--success, #16a34a) 22%, transparent);
flex-shrink: 0;
}
.hero-trust-freshness b { color: var(--fg); font-weight: 600; }
.hero-trust-how {
border: 1px solid var(--border);
border-radius: 8px;
background: var(--surface-2);
overflow: hidden;
}
.hero-trust-how > summary {
display: flex;
align-items: center;
gap: 10px;
padding: 11px 14px;
cursor: pointer;
list-style: none;
font-size: 13px;
color: var(--fg);
user-select: none;
}
.hero-trust-how > summary::-webkit-details-marker { display: none; }
.hero-trust-how .how-title { font-weight: 600; }
.hero-trust-how .how-band {
padding: 2px 9px;
border-radius: 999px;
font-size: 11.5px;
font-weight: 500;
background: color-mix(in oklch, var(--accent) 12%, transparent);
color: var(--accent-ink);
}
.hero-trust-how .how-chevron {
margin-left: auto;
color: var(--muted);
transition: transform .15s ease;
}
.hero-trust-how[open] .how-chevron { transform: rotate(180deg); }
.hero-trust-how .how-body {
padding: 4px 14px 14px;
border-top: 1px solid var(--border);
display: flex;
flex-direction: column;
gap: 12px;
}
.hero-trust-how .how-facts {
list-style: none;
margin: 12px 0 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 7px;
}
.hero-trust-how .how-facts li {
display: flex;
justify-content: space-between;
gap: 16px;
font-size: 13px;
}
.hero-trust-how .how-facts .k { color: var(--muted); }
.hero-trust-how .how-facts .v { color: var(--fg); font-weight: 500; text-align: right; }
.hero-trust-how .how-facts .v.mono { font-family: var(--font-mono); }
.hero-trust-how .how-explanation {
margin: 0;
font-size: 12.5px;
line-height: 1.5;
color: var(--fg-2);
padding: 10px 12px;
border-radius: 6px;
background: var(--surface);
border: 1px solid var(--border);
}
@media (max-width: 560px) {
.hero-trust-cta, .hero-trust-pdf { flex: 1 1 100%; }
}
/* ============================================================
FILTER + SOURCE chips
============================================================ */
.filters {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.chip {
display: inline-flex; align-items: center; gap: 6px;
padding: 4px 9px;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 999px;
font-size: 12px;
color: var(--fg-2);
}
.chip .k { color: var(--muted); font-size: 11px; }
.chip .v { font-family: var(--font-mono); font-size: 12px; color: var(--fg); }
.sources {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.source-chip {
display: inline-flex; align-items: center; gap: 6px;
padding: 4px 10px 4px 8px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 6px;
font-size: 12px;
color: var(--fg-2);
font-weight: 500;
letter-spacing: 0.01em;
}
.source-chip.is-error { color: var(--muted-2); }
.source-chip .src-dot {
width: 8px; height: 8px;
border-radius: 2px;
}
.source-chip .src-dot.cian { background: var(--viz-3); }
.source-chip .src-dot.avito { background: var(--viz-5); }
.source-chip .src-dot.dom { background: var(--viz-1); }
.source-chip .src-dot.yandex { background: var(--viz-4); }
.source-chip .src-dot.rosreestr { background: var(--fg); }
.source-chip .src-dot.etagi { background: var(--viz-2); }
.source-chip .src-dot.restate { background: var(--viz-2); }
.source-chip .src-dot.n1 { background: var(--muted-2); }
.source-chip .src-dot.domrf { background: var(--accent-2); }
/* ============================================================
LISTINGS / DEALS Section (2 & 3) — count strip + filters + bar + table
============================================================ */
.count-strip {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 22px;
padding: 16px 22px;
border-bottom: 1px solid var(--border);
background: var(--surface-2);
}
.count-cell .label {
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 4px;
}
.count-cell .value {
font-family: var(--font-mono);
font-size: 20px;
font-weight: 500;
color: var(--fg);
letter-spacing: -0.02em;
}
.count-cell .value .unit {
font-size: 12px;
color: var(--muted);
margin-left: 4px;
font-weight: 400;
}
.count-cell .sub {
font-size: 11px;
color: var(--muted);
margin-top: 2px;
}
.filters-row {
display: flex;
gap: 12px;
padding: 14px 22px;
flex-wrap: wrap;
align-items: center;
border-bottom: 1px solid var(--border);
}
.filters-row .lbl {
font-size: 11px;
color: var(--muted);
letter-spacing: 0.08em;
text-transform: uppercase;
margin-right: 6px;
}
.pricebar-block {
padding: 18px 22px;
border-bottom: 1px solid var(--border);
}
table.dt {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
table.dt thead th {
text-align: left;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
font-weight: 500;
padding: 12px 18px;
background: var(--surface-2);
border-bottom: 1px solid var(--border);
}
table.dt thead th.num,
table.dt tbody td.num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
table.dt tbody td {
padding: 13px 18px;
border-bottom: 1px solid var(--border);
vertical-align: middle;
color: var(--fg);
}
table.dt tbody tr:last-child td { border-bottom: 0; }
table.dt tbody td .addr {
display: flex; flex-direction: column; gap: 2px;
}
table.dt tbody td .addr .a-main { color: var(--fg); font-weight: 500; }
table.dt tbody td .addr .a-sub { color: var(--muted); font-size: 11px; font-family: var(--font-mono); }
table.dt tbody td .src-mini {
display: inline-flex; align-items: center; gap: 4px;
padding: 2px 6px;
border: 1px solid var(--border);
border-radius: 4px;
font-size: 11px;
color: var(--muted);
}
table.dt tbody td .src-mini .src-dot { width: 6px; height: 6px; border-radius: 1.5px; }
table.dt .row-link {
display: inline-flex;
width: 22px; height: 22px;
align-items: center; justify-content: center;
color: var(--muted-2);
border-radius: 4px;
}
table.dt .row-link:hover { background: var(--surface-2); color: var(--accent); }
.table-foot {
padding: 12px 22px;
display: flex; justify-content: space-between; align-items: center;
font-size: 12px;
color: var(--muted);
background: var(--surface-2);
border-top: 1px solid var(--border);
}
.table-foot a { font-family: var(--font-mono); font-size: 12px; }
/* warning banner */
.warn-banner {
display: grid;
grid-template-columns: 22px 1fr;
gap: 12px;
align-items: flex-start;
padding: 14px 22px;
background: var(--accent-2-soft);
border-top: 1px solid color-mix(in oklch, var(--accent-2) 25%, var(--border));
font-size: 13px;
color: var(--fg-2);
line-height: 1.5;
}
.warn-banner .ic {
width: 22px; height: 22px;
border-radius: 50%;
background: color-mix(in oklch, var(--accent-2) 25%, var(--surface));
color: var(--accent-2);
display: grid; place-items: center;
flex-shrink: 0;
}
.warn-banner b { color: var(--fg); font-weight: 600; }
/* ============================================================
OFFER Section (4)
============================================================ */
/* ── Prominent «Ваша выгода» block ── */
.offer-benefit {
margin: 18px 22px 4px;
padding: 18px 22px;
border-radius: var(--radius-lg);
background: var(--success-soft);
border: 1px solid color-mix(in oklch, var(--success) 35%, var(--border));
}
.offer-benefit .ob-label {
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--success);
font-weight: 600;
}
.offer-benefit .ob-value {
font-size: 28px;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--success);
margin: 4px 0 6px;
font-variant-numeric: tabular-nums;
}
.offer-benefit .ob-sub {
font-size: 12px;
color: var(--fg-2);
line-height: 1.5;
}
table.offer-tbl {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
table.offer-tbl thead th {
text-align: left;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
font-weight: 500;
padding: 14px 18px;
background: var(--surface-2);
border-bottom: 1px solid var(--border);
}
table.offer-tbl thead th.col-trade,
table.offer-tbl thead th.col-self {
text-align: right;
}
table.offer-tbl tbody td {
padding: 14px 18px;
border-bottom: 1px solid var(--border);
vertical-align: top;
color: var(--fg);
}
table.offer-tbl tbody td.col-trade,
table.offer-tbl tbody td.col-self {
text-align: right;
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
}
table.offer-tbl tbody td.col-trade {
color: var(--success);
font-weight: 500;
letter-spacing: -0.01em;
}
table.offer-tbl tbody td.col-self {
color: var(--fg);
}
table.offer-tbl tbody td .item-name {
font-weight: 500;
color: var(--fg);
margin-bottom: 2px;
}
table.offer-tbl tbody td .item-sub {
font-size: 11px;
color: var(--muted);
}
table.offer-tbl tfoot td {
padding: 16px 18px;
background: var(--fg);
color: var(--surface);
font-weight: 600;
letter-spacing: 0.02em;
font-size: 14px;
}
table.offer-tbl tfoot td.col-trade,
table.offer-tbl tfoot td.col-self {
text-align: right;
font-family: var(--font-mono);
}
table.offer-tbl tfoot td.col-trade { color: oklch(78% 0.14 150); }
.advantages {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 14px;
padding: 22px;
border-top: 1px solid var(--border);
background: var(--surface-2);
}
.advantage {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 16px;
display: flex;
flex-direction: column;
gap: 10px;
}
.advantage .adv-ic {
width: 32px; height: 32px;
border-radius: 8px;
background: var(--accent-soft);
color: var(--accent);
display: grid; place-items: center;
margin-bottom: 4px;
}
.advantage h3 {
font-size: 14px;
letter-spacing: -0.005em;
}
.advantage p {
font-size: 12px;
color: var(--muted);
line-height: 1.5;
}
.offer-cta {
padding: 22px;
display: flex;
gap: 12px;
justify-content: space-between;
align-items: center;
border-top: 1px solid var(--border);
background: var(--surface);
flex-wrap: wrap;
}
.offer-cta-text {
font-size: 13px;
color: var(--muted);
}
.offer-cta-text b { color: var(--fg); font-weight: 600; }
.offer-cta-buttons {
display: inline-flex; gap: 10px;
}
/* ============================================================
Footer
============================================================ */
.page-foot {
max-width: var(--container);
margin: 32px auto 0;
padding: 22px 28px 32px;
display: flex;
justify-content: space-between;
gap: 18px;
font-size: 12px;
color: var(--muted);
border-top: 1px solid var(--border);
flex-wrap: wrap;
}
.page-foot a { color: var(--muted); }
.page-foot a:hover { color: var(--fg); }
/* ============================================================
Responsive
============================================================ */
@media (max-width: 1100px) {
.layout { grid-template-columns: 1fr; }
.form-card { position: static; max-height: none; }
.hero-top { grid-template-columns: 1fr; }
.hero-duo { grid-template-columns: 1fr; }
.hero-prices { grid-template-columns: 1fr; }
.hero-photo { aspect-ratio: 16/9; }
.progress-list { grid-template-columns: 1fr; }
.advantages { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
.page { padding: 16px 16px 64px; }
.topbar-inner { padding: 12px 16px; gap: 14px; flex-wrap: wrap; }
.top-nav { gap: 12px; }
.page-title { flex-direction: column; align-items: flex-start; }
.page-title h1 { font-size: 24px; }
.count-strip { grid-template-columns: 1fr; gap: 10px; padding: 14px 16px; }
.meta-grid { grid-template-columns: 1fr; }
table.dt thead { display: none; }
table.dt tbody td { padding: 8px 14px; display: block; border-bottom: 0; }
table.dt tbody tr { display: block; padding: 10px 0; border-bottom: 1px solid var(--border); }
table.offer-tbl, table.offer-tbl thead, table.offer-tbl tbody, table.offer-tbl tr, table.offer-tbl th, table.offer-tbl td { display: block; }
table.offer-tbl thead { display: none; }
table.offer-tbl tbody tr { padding: 10px 14px; border-bottom: 1px solid var(--border); }
table.offer-tbl tbody td { padding: 4px 0; text-align: left !important; }
.advantages { grid-template-columns: 1fr; }
.offer-cta { flex-direction: column; align-items: stretch; }
.offer-cta-buttons { justify-content: stretch; }
.offer-cta-buttons .btn { flex: 1; }
.card-head { flex-direction: column; align-items: flex-start; }
.card-head .card-meta { text-align: left; }
.offer-benefit .ob-value { font-size: 24px; }
}
/* ── Stage 4b: IMV benchmark + house info card ── */
.imv-benchmark {
margin: 16px 0;
padding: 16px;
border: 1px solid #e5e7eb;
border-radius: 12px;
background: #fafbfc;
}
.imv-benchmark--loading {
display: flex;
gap: 8px;
align-items: center;
color: #6b7280;
}
.imv-benchmark__head {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 12px;
}
.imv-benchmark__title {
font-weight: 600;
font-size: 14px;
}
.imv-benchmark__source {
font-size: 11px;
color: #6b7280;
text-decoration: none;
}
.imv-benchmark__source:hover {
text-decoration: underline;
}
.imv-benchmark__row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 16px;
}
.imv-benchmark__metric-label {
font-size: 12px;
color: #6b7280;
margin-bottom: 2px;
}
.imv-benchmark__metric-value {
font-size: 16px;
font-weight: 600;
color: #111827;
}
.imv-benchmark__compare {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #e5e7eb;
font-size: 13px;
color: #374151;
}
/* Cian valuation card */
.cian-valuation {
margin: 16px 0;
padding: 16px;
border: 1px solid #e5e7eb;
border-radius: 12px;
background: #fff;
}
.cian-valuation__head {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
}
.cian-valuation__title {
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #6b7280;
}
.cian-valuation__badge {
font-size: 12px;
font-weight: 600;
padding: 2px 7px;
border-radius: 6px;
}
.cian-valuation__badge--up {
background: #d1fae5;
color: #065f46;
}
.cian-valuation__badge--down {
background: #fee2e2;
color: #991b1b;
}
.cian-valuation__badge--neutral {
background: #f3f4f6;
color: #374151;
}
.cian-valuation__row {
display: flex;
gap: 24px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.cian-valuation__metric-label {
font-size: 12px;
color: #6b7280;
margin-bottom: 2px;
}
.cian-valuation__metric-value {
font-size: 15px;
font-weight: 500;
color: #111827;
}
.cian-valuation__metric-value--primary {
font-size: 20px;
font-weight: 700;
}
.cian-valuation__sparkline {
color: #2563eb;
}
.cian-valuation__sparkline-dates {
display: flex;
justify-content: space-between;
font-size: 11px;
color: #6b7280; /* #834 a11y: было #9ca3af (2.5:1) → AA 4.83:1 на белом */
width: 240px;
margin-top: 2px;
}
/* House info card */
.house-info-card {
margin: 16px 0;
padding: 16px;
border: 1px solid #e5e7eb;
border-radius: 12px;
background: #fff;
}
.house-info-card__head {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 12px;
}
.house-info-card__title {
font-size: 16px;
font-weight: 600;
margin: 0;
}
.house-info-card__addr {
font-size: 13px;
color: #6b7280;
margin: 0;
}
.house-info-card__rating {
display: flex;
gap: 8px;
align-items: baseline;
margin-bottom: 12px;
}
.house-info-card__rating strong {
font-size: 24px;
color: #b45309; /* #834 a11y: было #f59e0b (2.14:1) → AA large-text 5.0:1 на белом */
}
.house-info-card__rating-label {
font-size: 12px;
color: #6b7280;
}
.house-info-card__features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 8px 16px;
margin: 0;
}
.house-info-card__feature {
display: flex;
justify-content: space-between;
font-size: 13px;
padding: 4px 0;
border-bottom: 1px dotted #e5e7eb;
}
.house-info-card__feature dt {
color: #6b7280;
}
.house-info-card__feature dd {
margin: 0;
font-weight: 500;
color: #111827;
}
.house-info-card__more {
margin-top: 12px;
font-size: 12px;
color: #6b7280;
font-style: italic;
}
/* ── Stage 4c: scrapers admin pages ── */
.scraper-page {
max-width: 960px;
margin: 0 auto;
padding: 24px 20px 60px;
}
.scraper-h1 {
font-size: 22px;
font-weight: 600;
margin: 0 0 4px;
color: var(--fg-primary, #111111);
}
.scraper-subtitle {
font-size: 14px;
color: var(--fg-secondary, #5b6066);
margin: 0 0 24px;
}
.scraper-section {
margin-bottom: 24px;
padding: 20px;
background: var(--bg-card, #ffffff);
border: 1px solid var(--border-card, #e6e8ec);
border-radius: 12px;
}
.scraper-section h2 {
font-size: 16px;
font-weight: 600;
margin: 0 0 8px;
color: var(--fg-primary, #111111);
}
.scraper-hint {
font-size: 13px;
color: var(--fg-secondary, #5b6066);
margin: 0 0 16px;
}
.scraper-form {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px;
align-items: end;
}
.scraper-form label {
display: flex;
flex-direction: column;
font-size: 12px;
color: var(--fg-secondary, #5b6066);
gap: 4px;
}
.scraper-form label.full {
grid-column: 1 / -1;
}
.scraper-form label.checkbox {
flex-direction: row;
align-items: center;
gap: 6px;
font-size: 13px;
color: var(--fg-primary, #111111);
}
.scraper-form input,
.scraper-form select {
padding: 8px 10px;
border: 1px solid var(--border-strong, #d1d5db);
border-radius: 6px;
font-size: 14px;
background: #fff;
color: var(--fg-primary, #111111);
}
.scraper-form input:focus,
.scraper-form select:focus {
outline: 2px solid var(--accent, #1d4ed8);
outline-offset: -1px;
border-color: var(--accent, #1d4ed8);
}
.scraper-form button {
padding: 8px 16px;
background: var(--accent, #1d4ed8);
color: #fff;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
grid-column: 1 / -1;
justify-self: start;
}
.scraper-form button:hover:not(:disabled) {
background: var(--accent-hover, #1e40af);
}
.scraper-form button:disabled {
background: var(--border-strong, #d1d5db);
cursor: not-allowed;
}
.scraper-result {
margin-top: 16px;
padding: 12px;
background: var(--bg-card-alt, #fafbfc);
border: 1px solid var(--border-soft, #eef0f3);
border-radius: 8px;
font-size: 12px;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
max-height: 320px;
overflow: auto;
}
.scraper-result--error {
background: var(--danger-soft, #fee2e2);
border-color: var(--danger, #b3261e);
color: var(--danger, #b3261e);
font-family: inherit;
}
.scraper-result pre {
margin: 0;
white-space: pre-wrap;
word-break: break-all;
}
/* ── Stage 4d: city sweep section ── */
.scraper-section--highlight {
border-color: var(--accent, #1d4ed8);
background: var(--accent-soft, #dbeafe);
}
.scraper-section--highlight h2 {
color: var(--accent, #1d4ed8);
}
.city-sweep-runs {
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid var(--border-card, #e6e8ec);
}
.city-sweep-runs h3 {
font-size: 14px;
font-weight: 600;
margin: 0 0 12px;
color: var(--fg-primary, #111111);
}
.runs-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
background: #ffffff;
border-radius: 6px;
overflow: hidden;
}
.runs-table thead {
background: var(--bg-card-alt, #fafbfc);
}
.runs-table th {
padding: 8px 10px;
text-align: left;
font-weight: 600;
font-size: 11px;
color: var(--fg-secondary, #5b6066);
text-transform: uppercase;
letter-spacing: 0.04em;
border-bottom: 1px solid var(--border-card, #e6e8ec);
}
.runs-table td {
padding: 8px 10px;
border-bottom: 1px solid var(--border-soft, #eef0f3);
font-variant-numeric: tabular-nums;
}
.runs-table .run-muted {
color: var(--fg-secondary, #5b6066);
font-size: 11px;
}
.run-badge {
padding: 3px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 500;
}
.run-badge--running {
background: var(--accent-soft, #dbeafe);
color: var(--accent, #1d4ed8);
}
.run-badge--done {
background: var(--success-soft, #dcfce7);
color: var(--success, #0a7a3a);
}
.run-badge--failed,
.run-badge--zombie {
background: var(--danger-soft, #fee2e2);
color: var(--danger, #b3261e);
}
.run-badge--cancelled {
background: var(--warn-soft, #fef3c7);
color: var(--warn, #9a6700);
}
.cancel-btn {
padding: 4px 10px;
background: var(--danger, #b3261e);
color: #ffffff;
border: none;
border-radius: 4px;
font-size: 11px;
cursor: pointer;
}
.cancel-btn:hover:not(:disabled) {
background: #8b1d18;
}
.cancel-btn:disabled {
background: var(--border-strong, #d1d5db);
cursor: not-allowed;
}
/* ── Stage 4e: scheduler section ── */
.scraper-section--schedule {
border-color: var(--success, #0a7a3a);
background: var(--success-soft, #dcfce7);
}
.scraper-section--schedule h2 {
color: var(--success, #0a7a3a);
}
.schedule-status {
margin-bottom: 16px;
padding: 12px;
background: #ffffff;
border-radius: 6px;
border: 1px solid var(--border-card, #e6e8ec);
}
.schedule-status__row {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
padding: 4px 0;
}
.schedule-status__label {
min-width: 140px;
color: var(--fg-secondary, #5b6066);
font-weight: 500;
}
/* ── Росреестр deeplink button ── */
.rosreestr-btn {
background: transparent;
border: 1px solid var(--border);
color: var(--muted);
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
cursor: pointer;
white-space: nowrap;
}
.rosreestr-btn:hover {
color: var(--fg);
border-color: var(--fg);
}
/* ── PR L — StreetDealsCard with paired listings (sales-vs-listings) ── */
/* Подсказка о покрытии (linkage_rate_pct) в card-head */
.linkage-hint {
margin-top: 6px;
font-size: 12px;
color: var(--muted);
line-height: 1.5;
}
.linkage-hint b {
color: var(--fg-2);
font-weight: 500;
}
/* Знак discount_pct — отрицательный = торг (success), положительный = редко (danger). */
.discount-neg { color: var(--success); }
.discount-pos { color: var(--danger); }
/* #835 a11y: визуально-скрытый текст для скринридеров (sr-only) — для пустых
th-заголовков (empty-table-header требует ТЕКСТ, не aria-label). */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Ячейка listing-колонки */
.listing-cell {
min-width: 200px;
max-width: 280px;
}
.listing-empty {
color: var(--muted-2);
font-family: var(--font-mono);
font-size: 12px;
}
/* Container link / static — inline-flex с baseline alignment */
.listing-link {
display: inline-flex;
flex-wrap: wrap;
align-items: baseline;
gap: 6px;
max-width: 100%;
color: var(--fg);
text-decoration: none;
line-height: 1.4;
}
.listing-link:hover:not(.is-static) .listing-price { color: var(--accent); }
.listing-link.is-static { cursor: default; }
/* Source badge (AVITO / CIAN / YANDEX / N1 / DOMKLIK) — per ui-tokens accent-soft + accent text uppercase 11px */
.listing-source-badge {
display: inline-block;
padding: 1px 6px;
background: var(--accent-soft);
color: var(--accent-ink);
border-radius: 4px;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
white-space: nowrap;
}
.listing-price {
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
font-size: 13px;
color: var(--fg);
font-weight: 500;
white-space: nowrap;
}
.listing-meta {
font-size: 11px;
color: var(--muted);
letter-spacing: 0.01em;
white-space: nowrap;
}
/* Mobile (≤720px) — listing column stacks; tables уже становятся block per existing rules */
@media (max-width: 720px) {
.listing-cell { min-width: 0; max-width: none; }
.listing-link { flex-direction: column; align-items: flex-start; gap: 2px; }
}
/* ── PR M (#564 Phase 3) — Tier badge for rosreestr deals ────────────────── */
.tier-badge {
display: inline-block;
margin-left: 6px;
padding: 1px 6px;
border-radius: 4px;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
vertical-align: middle;
}
.tier-t0 {
/* T0_per_house — точный кадастровый match (high confidence) */
background: var(--success-soft);
color: var(--success);
}
.tier-t1 {
/* T1_per_street — улица-уровень (open dataset default) */
background: var(--bg-card-alt);
color: #666970; /* #834 a11y: было var(--fg-tertiary)=#73767e (4.4:1) → AA 5.3:1 на #fafbfc */
}
/* ── Address precision badge (DaData qc_geo) ─────────────────────────────── */
.precision-badge {
display: inline-block;
margin-left: 6px;
padding: 1px 6px;
border-radius: 4px;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
vertical-align: middle;
}
.precision-house {
/* qc_geo=0 — дом определён точно (high confidence) */
background: var(--success-soft);
color: var(--success);
}
.precision-street {
/* qc_geo=1 — до улицы (neutral, как tier-t1) */
background: var(--bg-card-alt);
color: #666970; /* #834 a11y: было var(--fg-tertiary)=#73767e (4.4:1) → AA 5.3:1 на #fafbfc */
}
.precision-approx {
/* qc_geo≥2 — приблизительно. Amber-токены под «Адрес распознан неточно» disclaimer */
background: var(--accent-2-soft);
color: var(--accent-2);
}
/* ── #2002: бейдж премиального дома (рядом с адресом) ────────────────────────
Та же форма, что precision-badge; цвет — brand-accent (синий, prestige),
отличается от amber-warn и green-success. Только существующие токены. */
.premium-badge {
display: inline-block;
align-self: flex-start;
margin-bottom: 2px;
padding: 2px 8px;
border-radius: 4px;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
background: var(--accent-soft);
color: var(--accent-ink);
border: 1px solid color-mix(in oklch, var(--accent) 25%, var(--border));
}
/* ── #2002: баннер «рекомендуется ручная оценка» (amber, non-alarming) ───────
Под headline-числом. Те же amber-токены, что mock-address disclaimer выше. */
.manual-review {
margin: 16px 22px 0;
padding: 12px 16px;
border: 1px solid var(--accent-2);
background: var(--accent-2-soft);
border-radius: var(--radius);
color: var(--fg);
font-size: 13px;
line-height: 1.5;
}
.manual-review__head {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: var(--accent-2);
}
.manual-review__icon {
display: grid;
place-items: center;
width: 18px;
height: 18px;
flex-shrink: 0;
}
.manual-review__title {
font-size: 14px;
}
.manual-review__lead {
margin: 6px 0 0;
color: var(--fg-2);
}
.manual-review__reasons {
margin: 8px 0 0;
padding-left: 18px;
display: flex;
flex-direction: column;
gap: 3px;
color: var(--fg-2);
}
.manual-review__reasons li {
line-height: 1.45;
}
/* ── «Что-если» интерактивный пересчёт (web-преимущество над PDF) ──
Компактная панель над HeroSummary: segmented ремонт + stepper/slider этаж +
numeric площадь + балкон. Делает тот же POST /estimate (debounced) и диффит
к baseline. Все цвета — через brand-токены (работает в .brand-active). */
.whatif-card {
border: 1px solid var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft);
}
.whatif-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
padding: 16px 22px 10px;
}
.whatif-title {
font-size: 15px;
font-weight: 600;
margin: 2px 0 0;
color: var(--fg);
}
.whatif-auto {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--muted);
cursor: pointer;
white-space: nowrap;
}
.whatif-auto input { width: 14px; height: 14px; cursor: pointer; }
.whatif-body {
padding: 4px 22px 14px;
display: flex;
flex-direction: column;
gap: 14px;
}
.whatif-field { display: flex; flex-direction: column; gap: 6px; }
.whatif-label {
font-size: 12px;
font-weight: 600;
color: var(--fg-2);
display: flex;
align-items: baseline;
gap: 6px;
}
.whatif-hint { font-size: 11px; font-weight: 400; color: var(--muted); }
.whatif-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
.whatif-segmented {
display: flex;
gap: 4px;
background: var(--surface-2);
padding: 4px;
border-radius: var(--radius);
border: 1px solid var(--border);
flex-wrap: wrap;
}
.whatif-seg {
flex: 1 1 auto;
min-width: 88px;
padding: 7px 8px;
font-size: 12px;
font-weight: 500;
border: 1px solid transparent;
border-radius: calc(var(--radius) - 4px);
background: transparent;
color: var(--fg-2);
cursor: pointer;
transition: background .12s, color .12s, border-color .12s;
}
.whatif-seg:hover { background: var(--surface); }
.whatif-seg.is-active {
background: var(--accent);
color: var(--surface);
border-color: var(--accent);
}
.whatif-stepper { display: flex; align-items: center; gap: 8px; }
.whatif-step-btn {
flex: 0 0 auto;
width: 30px;
height: 30px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 18px;
line-height: 1;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--surface);
color: var(--accent);
cursor: pointer;
}
.whatif-step-btn:hover:not(:disabled) { background: var(--accent-soft); }
.whatif-step-btn:disabled { opacity: .4; cursor: not-allowed; }
.whatif-slider { flex: 1 1 auto; accent-color: var(--accent); cursor: pointer; }
.whatif-num { width: 84px; text-align: center; padding: 6px 8px; }
.whatif-balcony {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--fg);
cursor: pointer;
}
.whatif-balcony input { width: 14px; height: 14px; cursor: pointer; }
.whatif-foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
padding: 14px 22px;
border-top: 1px solid var(--border);
background: var(--surface-2);
border-radius: 0 0 var(--radius) var(--radius);
}
.whatif-result { display: flex; flex-direction: column; gap: 2px; min-height: 34px; justify-content: center; }
.whatif-delta { font-size: 18px; font-weight: 700; color: var(--muted); }
.whatif-delta.up { color: var(--success); }
.whatif-delta.down { color: var(--danger); }
.whatif-newprice { font-size: 12px; color: var(--muted); }
.whatif-baseline { font-size: 13px; color: var(--fg-2); }
.whatif-loading { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--muted); }
.whatif-actions { display: flex; align-items: center; gap: 8px; }
.whatif-recalc { padding: 8px 16px; }
.whatif-error {
margin: 0 22px 16px;
padding: 10px 12px;
background: var(--danger-soft);
border: 1px solid var(--danger);
border-radius: 6px;
color: var(--danger);
font-size: 12px;
}
@media (max-width: 560px) {
.whatif-row { grid-template-columns: 1fr; }
}
@keyframes ti-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
/* ============================================================
#692 — Mobile horizontal-overflow guard (≤390px)
Root causes: recharts ResponsiveContainer / Leaflet tiles /
wide comp-tables / top-nav, все push width когда flex/grid
ребёнок имеет дефолтный min-width:auto и не может ужаться.
Эти правила desktop-neutral: на ≥1100px ничего не меняют
(layout уже single-col и контент влезает), только страхуют
узкие вьюпорты от бокового скролла страницы.
============================================================ */
/* Последняя линия обороны: страница никогда не скроллится вбок.
clip (не hidden) — не создаёт scroll-container, не ломает sticky. */
html, body { overflow-x: clip; }
.page { max-width: 100%; overflow-x: clip; }
/* Каждая прямая карточка/секция результат-колонки должна уметь
ужиматься (flex-ребёнок по умолчанию min-width:auto). .result-col
уже min-width:0; добавляем то же его детям + клип переполнения. */
.result-col > * { min-width: 0; max-width: 100%; }
/* Recharts: контейнер графика всегда 100% ширины родителя и
не шире её — ResponsiveContainer мерит этот box. */
.recharts-responsive-container,
.recharts-wrapper { max-width: 100%; }
/* Leaflet-обёртка карты (MapCard) — не даём тайлам растянуть карточку. */
.card > [aria-label="Карта аналогов и сделок"] { max-width: 100%; }
/* Comp/analog-таблицы со «склейкой» listing-ячейки шире остального:
на узких вьюпортах горизонтальный скролл ВНУТРИ карточки, не страницы. */
.card > table.dt,
.card > table.offer-tbl { max-width: 100%; }
@media (max-width: 720px) {
/* Topbar: nav переносится и не выпирает за ширину бара. */
.top-nav { flex-wrap: wrap; min-width: 0; margin-left: auto; }
.topbar-inner { max-width: 100%; }
}