diff --git a/tradein-mvp/frontend/src/app/mera-public/__tests__/coverage-copy.test.ts b/tradein-mvp/frontend/src/app/mera-public/__tests__/coverage-copy.test.ts
index 60ea1434..6e9bd162 100644
--- a/tradein-mvp/frontend/src/app/mera-public/__tests__/coverage-copy.test.ts
+++ b/tradein-mvp/frontend/src/app/mera-public/__tests__/coverage-copy.test.ts
@@ -45,6 +45,24 @@ describe("describeCoverage", () => {
expect(ageTile.label).not.toMatch(/продаётся|продаются|срок продажи|уходят/i);
});
+ it("подпись под возрастом не показывает число, спорящее с плиткой выше", () => {
+ // На проде 27.08: сверху «30 похожих квартир», под ней «из этих 6».
+ // Слово «этих» указывало на 30, и человек читал два разных числа об
+ // одном и том же. Долю называем явно — либо не называем вовсе.
+ const [cohortTile, ageTile] = describeCoverage(
+ probe({ n_listings: 30, n_with_age: 6 }),
+ ).tiles;
+ expect(cohortTile.value).toBe("30");
+ expect(ageTile.label).toContain("6 из 30");
+ expect(ageTile.label).not.toMatch(/этих/);
+ });
+
+ it("когда дата известна у всех — доля не упоминается вовсе", () => {
+ // «дата известна у 30 из 30» — шум: доля есть, но она ничего не сообщает.
+ const [, ageTile] = describeCoverage(probe({ n_listings: 30, n_with_age: 30 })).tiles;
+ expect(ageTile.label).toBe("столько в среднем висит объявление");
+ });
+
it("город вне покрытия — говорим прямо и не предлагаем платный шаг", () => {
const verdict = describeCoverage(probe({ status: "not_covered", city: "", n_listings: 0 }));
expect(verdict.offerPaidStep).toBe(false);
diff --git a/tradein-mvp/frontend/src/app/mera-public/_components/estimate/EstimateFlow.tsx b/tradein-mvp/frontend/src/app/mera-public/_components/estimate/EstimateFlow.tsx
index bcf6dcdf..491ed98b 100644
--- a/tradein-mvp/frontend/src/app/mera-public/_components/estimate/EstimateFlow.tsx
+++ b/tradein-mvp/frontend/src/app/mera-public/_components/estimate/EstimateFlow.tsx
@@ -345,7 +345,14 @@ export function EstimateFlow() {
onClick={() => choose(item)}
>
{item.label}
- {item.full_address}
+ {/* Вторая строка — только когда она что-то добавляет.
+ Подсказчик отдаёт `label` и `full_address` порознь,
+ но для дома они сплошь и рядом совпадают буква в
+ букву, и человек видел один и тот же адрес дважды в
+ каждой строке списка. Найдено на проде 27.08. */}
+ {item.full_address !== item.label && (
+ {item.full_address}
+ )}
))}
diff --git a/tradein-mvp/frontend/src/app/mera-public/coverage-copy.ts b/tradein-mvp/frontend/src/app/mera-public/coverage-copy.ts
index aa0e09f6..6df3344b 100644
--- a/tradein-mvp/frontend/src/app/mera-public/coverage-copy.ts
+++ b/tradein-mvp/frontend/src/app/mera-public/coverage-copy.ts
@@ -64,10 +64,18 @@ function tilesFor(probe: CoverageProbe): CoverageTile[] {
];
if (probe.median_listing_age_days !== null) {
+ // Медиана считается НЕ по всей когорте, а только по тем объявлениям, у
+ // которых известна дата публикации. Прежняя подпись — «из этих N» —
+ // указывала словом «этих» на число в плитке выше (в проде: 30 квартир,
+ // а рядом «из этих 6»), и человек читал два спорящих числа. Называем
+ // долю явно, когда она меньше целого. Найдено на проде 27.08.
+ const partial = probe.n_with_age < probe.n_listings;
tiles.push({
value: `${probe.median_listing_age_days} дн.`,
// Формулировка запинена тестом — см. шапку модуля.
- label: `столько в среднем висит объявление из этих ${probe.n_with_age}`,
+ label: partial
+ ? `столько в среднем висит объявление; дата известна у ${probe.n_with_age} из ${probe.n_listings}`
+ : "столько в среднем висит объявление",
});
}