gendesign/tradein-mvp/frontend/src/components/trade-in/trade-in.css
Light1YT 7d749f4080
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 1m47s
Deploy Trade-In / deploy (push) Successful in 36s
feat(tradein): dual-display asking vs expected-sold in HeroSummary (#648 S5a) (#664)
2026-05-29 14:00:49 +00:00

1814 lines
48 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.

: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: oklch(50% 0.012 250);
--muted-2: oklch(62% 0.010 250);
/* 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: oklch(56% 0.14 150);
--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;
/* 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-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;
}
.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: 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: 16px 18px 8px;
overflow-y: auto;
flex: 1;
}
.field { margin-bottom: 12px; }
.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: 12px 18px 16px;
border-top: 1px solid var(--border);
background: linear-gradient(to bottom, transparent, color-mix(in oklch, var(--surface-2) 60%, transparent) 40%);
}
.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));
}
.price-figure-label {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
font-weight: 500;
letter-spacing: 0.02em;
color: var(--muted);
}
.price-figure[data-kind="sold"] .price-figure-label { color: var(--accent-ink); }
.price-figure-delta {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.02em;
padding: 1px 6px;
border-radius: var(--radius-sm);
background: color-mix(in oklch, var(--accent) 14%, transparent);
color: var(--accent-ink);
}
.price-figure-value {
font-size: 28px;
font-weight: 600;
line-height: 1.1;
letter-spacing: -0.02em;
color: var(--fg);
}
.price-figure-range {
font-size: 12px;
color: var(--muted);
letter-spacing: 0.01em;
}
.price-figure-perm2 {
font-size: 11px;
color: var(--muted-2);
letter-spacing: 0.04em;
}
.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;
}
/* ============================================================
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)
============================================================ */
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 h4 {
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-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; }
}
/* ── 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: #9ca3af;
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: #f59e0b;
}
.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); }
/* Ячейка 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: var(--fg-tertiary);
}
/* ── 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: var(--fg-tertiary);
}
.precision-approx {
/* qc_geo≥2 — приблизительно. Amber-токены под «Адрес распознан неточно» disclaimer */
background: var(--accent-2-soft);
color: var(--accent-2);
}