diff --git a/tradein-mvp/frontend/src/app/v2/page.tsx b/tradein-mvp/frontend/src/app/v2/page.tsx
index 1bc3617e..4917d454 100644
--- a/tradein-mvp/frontend/src/app/v2/page.tsx
+++ b/tradein-mvp/frontend/src/app/v2/page.tsx
@@ -289,12 +289,63 @@ function EmptyResultPanel() {
);
}
-function InsufficientPanel() {
+// Named predicate for the "no price at all" state — deliberately NOT
+// `!estimate.median_price_rub` (a bare zero/price check): that would also
+// misfire on a still-loading `estimate` or on legitimate falsy-but-present
+// values. The one reliable signal is the backend's own `insufficient_data`
+// flag, which is defined (see AggregatedEstimate in types/trade-in.ts /
+// backend @computed_field) to mean "this IS a successful response, but
+// median_price_rub <= 0 — there was nothing to price with". Only fires on a
+// present, successfully-returned estimate.
+function isInsufficientEstimate(estimate: AggregatedEstimate | null): boolean {
+ return estimate != null && estimate.insufficient_data === true;
+}
+
+// Карточка состояния «цены нет». Показывает объяснение, которое прислал сам
+// бэкенд (confidence_explanation — текст написан специально под это состояние),
+// и адрес, который геокодер всё-таки нашёл: target_address и координаты
+// заполнены даже когда цены нет. Плюс понятное следующее действие.
+// Цена, диапазон, «−N% к рынку» и аналоги здесь не рендерятся вовсе — на
+// проводе они 0/null, и показать их значило бы выдать сломанный экран «0 ₽»
+// вместо честного «дом нашли, цену — нет».
+function InsufficientPanel({
+ estimate,
+ onNew,
+}: {
+ estimate: AggregatedEstimate;
+ onNew: () => void;
+}) {
return (