+
+
+
Аналитика · Распределение
+
Где ваша квартира в локальном рынке
+
+
+
+ По {n} аналогам
+
+
₽/м² · гистограмма
+
+
+
+
+
+
+
+
+
+ [`${value} лот.`, "В диапазоне"]}
+ labelFormatter={(mid: number) => {
+ const b = bins.find((x) => x.mid === mid);
+ return b
+ ? `${Math.round(b.start).toLocaleString("ru-RU")}–${Math.round(
+ b.end,
+ ).toLocaleString("ru-RU")} ₽/м²`
+ : `${Math.round(mid).toLocaleString("ru-RU")} ₽/м²`;
+ }}
+ />
+
+ {bins.map((b, i) => (
+ |
+ ))}
+
+
+
+
+
+
+
+
+ Ваша оценка:{" "}
+ {subject.toLocaleString("ru-RU")} ₽/м² ·
+ оранжевый столбец — ваш диапазон
+
+
+
+ );
+}
diff --git a/tradein-mvp/frontend/src/components/trade-in/ExposureCard.tsx b/tradein-mvp/frontend/src/components/trade-in/ExposureCard.tsx
new file mode 100644
index 00000000..a5f13941
--- /dev/null
+++ b/tradein-mvp/frontend/src/components/trade-in/ExposureCard.tsx
@@ -0,0 +1,133 @@
+"use client";
+
+/**
+ * ExposureCard — ликвидность: scatter цена (₽) × срок экспозиции (дней) по
+ * аналогам. Дешевле → быстрее продаётся. Цена оценки отмечена reference-линией.
+ * Reuses recharts (как PriceHistoryChart.tsx). Degrade gracefully: рендерим
+ * только если ≥3 аналогов имеют непустой days_on_market.
+ */
+import { useMemo } from "react";
+import {
+ ScatterChart,
+ Scatter,
+ XAxis,
+ YAxis,
+ ZAxis,
+ Tooltip,
+ ResponsiveContainer,
+ CartesianGrid,
+ ReferenceLine,
+} from "recharts";
+
+import type { AggregatedEstimate } from "@/types/trade-in";
+
+interface Props {
+ estimate: AggregatedEstimate;
+}
+
+const fmtM = (n: number) => `${(n / 1_000_000).toFixed(1)}`;
+
+interface Point {
+ price: number; // ₽
+ days: number; // срок экспозиции
+ address: string;
+}
+
+export function ExposureCard({ estimate }: Props) {
+ const points = useMemo