diff --git a/frontend/src/app/site-finder/analysis/[cad]/ptica/ptica.module.css b/frontend/src/app/site-finder/analysis/[cad]/ptica/ptica.module.css index c1f4bd48..b87003c1 100644 --- a/frontend/src/app/site-finder/analysis/[cad]/ptica/ptica.module.css +++ b/frontend/src/app/site-finder/analysis/[cad]/ptica/ptica.module.css @@ -1322,6 +1322,40 @@ line-height: 1.5; color: var(--text-soft); } + +/* Recommended Product — honest advisory verdict (degenerate-mix case: every + формат «избегать», deficit_index −1.0 → затоварка рынка). Tokens resolve + under .pticaRoot[data-theme="dark"]. */ +.verdictPanel { + display: flex; + flex-direction: column; + gap: 6px; + margin-top: 4px; +} +.signalBadge { + display: inline-flex; + align-self: flex-start; + padding: 2px 9px; + border-radius: 999px; + font-size: 9px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.signalBadgeBad { + border: var(--line) solid var(--accent-red); + background: rgba(210, 101, 91, 0.08); + color: var(--accent-red); +} +.verdictReason { + font-size: 11px; + line-height: 1.4; + color: var(--text); +} +.verdictCaveat { + font-size: 10px; + line-height: 1.5; + color: var(--text-soft); +} .emptyPanel { display: grid; place-items: center; @@ -1898,6 +1932,23 @@ .massingViewport canvas { display: block; } +/* Compact always-on preview canvas in the «Инсоляционная матрица» lower-grid + card — autorotating mini massing, hands-off (canvas pointer-events:none so it + never steals page scroll/clicks). Tokens resolve under .pticaRoot[data-theme="dark"]. */ +.massingPreviewViewport { + position: relative; + width: 100%; + height: 150px; + margin: 2px 0 10px; + border: var(--line) solid var(--border); + border-radius: var(--radius-md); + background: var(--surface-inset); + overflow: hidden; +} +.massingPreviewViewport canvas { + display: block; + pointer-events: none; +} .massingSkeleton, .massingFallback { position: absolute; @@ -2511,58 +2562,9 @@ margin-bottom: 1px; } -/* Инсоляционная матрица — faint preview massing + ПРЕВЬЮ placeholder */ -/* Ambient «turntable» rotation of the massing-preview cube — gives a sense of - the 3D module without opening the drawer. A constant slight rotateX keeps a - sliver of depth visible as it passes edge-on. */ -@keyframes pticaIsoSpin { - from { - transform: rotateX(14deg) rotateY(0deg); - } - to { - transform: rotateX(14deg) rotateY(360deg); - } -} -.isoBoxPreview { - height: 74px; - display: grid; - place-items: center; - margin: 2px 0 10px; - opacity: 0.55; - perspective: 540px; -} -.isoBoxPreview svg { - width: 100%; - height: 100%; - transform-style: preserve-3d; - transform-origin: 50% 50%; - will-change: transform; - animation: pticaIsoSpin 8s linear infinite; -} -@media (prefers-reduced-motion: reduce) { - .isoBoxPreview svg { - animation: none; - } -} -.isoPreviewLeft { - fill: rgba(150, 192, 214, 0.1); - stroke: var(--border-strong); - stroke-width: 1; -} -.isoPreviewTop { - fill: rgba(150, 192, 214, 0.06); - stroke: var(--border-strong); - stroke-width: 1; -} -.isoPreviewRight { - fill: rgba(150, 192, 214, 0.14); - stroke: var(--border-strong); - stroke-width: 1; -} -.isoPreviewEdge { - stroke: var(--border); - stroke-width: 0.6; -} +/* Инсоляционная матрица — live preview massing (.massingPreviewViewport above) + + ПРЕВЬЮ placeholder. The former faint SVG iso-cube (.isoBoxPreview / + @keyframes pticaIsoSpin) was replaced by the real autorotating 3D model. */ .placeholder { display: flex; flex-direction: column; diff --git a/frontend/src/components/site-finder/ptica/cockpit/InsolationCard.tsx b/frontend/src/components/site-finder/ptica/cockpit/InsolationCard.tsx index 868007d2..9db236a2 100644 --- a/frontend/src/components/site-finder/ptica/cockpit/InsolationCard.tsx +++ b/frontend/src/components/site-finder/ptica/cockpit/InsolationCard.tsx @@ -1,19 +1,29 @@ "use client"; /** - * InsolationCard — lower-grid «Инсоляционная матрица» preview card. Ports the - * prototype's faint isometric massing visual + «ПРЕВЬЮ» state; «Открыть 3D» - * opens the insolation / future3d drawer (the 3D massing + shadow module). + * InsolationCard — lower-grid «Инсоляционная матрица» preview card. Renders a + * LIVE compact autorotating 3D massing model (a hands-off mini version of the + * «Открыть 3D» drawer) instead of the old abstract SVG cube, fed by the REAL + * parcel area (geometry) + НСПД КСИТ (max_far). «Открыть 3D» opens the + * insolation drawer (3D massing + shadow module). */ import styles from "@/app/site-finder/analysis/[cad]/ptica/ptica.module.css"; +import type { ParcelAnalysis } from "@/types/site-finder"; +import { parcelAreaM2 } from "@/components/site-finder/ptica/ptica-adapt"; +import { MassingViewer } from "@/components/site-finder/ptica/massing/MassingViewer"; import type { DrawerKey } from "@/components/site-finder/ptica/drawers/drawer-keys"; interface Props { + analysis: ParcelAnalysis; onOpenDrawer: (key: DrawerKey) => void; } -export function InsolationCard({ onOpenDrawer }: Props) { +export function InsolationCard({ analysis, onOpenDrawer }: Props) { + const areaM2 = parcelAreaM2(analysis) ?? undefined; + const maxFar = analysis.nspd_zoning?.max_far ?? undefined; + const farIsReal = maxFar != null; + return (
+ {capitalize(deficitWord(verdict.deficitIndex))} рынка · индекс + дефицита {fmtNum(verdict.deficitIndex, 2)} +
+ )} + {gateCaveat &&{gateCaveat}
}