From da50f3b5ead2aebc394230a82168ef94447ce86f Mon Sep 17 00:00:00 2001 From: Benjamin Sutter Date: Tue, 9 Jun 2026 22:26:30 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20premium=20redesign=20=E2=80=94=20DM=20S?= =?UTF-8?q?erif,=20refined=20palette,=20flat=20score=20badges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Design tokens (ds.ts, theme.ts, scoreTheme.ts): new warm palette (#152642 navy, #f9f8f6 warm white, #e8e7e4 borders, #b8975a gold accent), flat score tier badges replacing CSS gradients, Inter + DM Serif Display typography - Card components: white-background cards, DM Serif score numbers, max-2 badge chips with +N overflow tooltip, editorial score badge positioning - Layout shell: gold left-accent nav active state, 64px top bar, outlined workspace chip, DM Serif page titles - Shared atoms: GenericBadge (outlined/solid variants), ResultFilterBar (simplified chip styles), DecisionContextPanel (dot metrics, no left accent) - Global replacement (118 files): #1e3a5f→#152642, #e2e8f0→#e8e7e4, #f4f6f9→#f9f8f6 — all handled via Node.js for proper UTF-8 safety Co-Authored-By: Claude Sonnet 4.6 --- .../ai-monitoring/AIOutputTable.tsx | 2 +- .../ai-monitoring/AIReviewActionToolbar.tsx | 2 +- .../anfragencenter/ActiveInquiriesTab.tsx | 6 +- src/components/anfragencenter/InquiryCard.tsx | 4 +- src/components/anfragencenter/InquiryChat.tsx | 2 +- .../anfragencenter/InquiryDetailPanel.tsx | 2 +- .../anfragencenter/InquiryListRow.tsx | 2 +- .../anfragencenter/InquiryMessageBubble.tsx | 2 +- .../anfragencenter/InquiryReplyComposer.tsx | 2 +- .../anfragencenter/LatentInquiriesTab.tsx | 2 +- .../anfragencenter/MockPdfPreview.tsx | 4 +- .../anfragencenter/OfferChatComposer.tsx | 2 +- .../anfragencenter/OfferCreationPanel.tsx | 2 +- .../anfragencenter/OfferCreationWizard.tsx | 6 +- .../OfferFieldSelectionStep.tsx | 2 +- .../anfragencenter/OfferPdfReviewStep.tsx | 2 +- .../OfferPropertySelectionStep.tsx | 2 +- .../anfragencenter/OfferWizardPdfStep.tsx | 10 +- .../anfragencenter/OwnPropertyMatchList.tsx | 4 +- .../anfragencenter/PreparationWizard.tsx | 10 +- .../anfragencenter/PublicNeedCard.tsx | 4 +- .../anfragencenter/PublicNeedDetail.tsx | 8 +- .../anfragencenter/PublicNeedList.tsx | 2 +- .../RelatedPropertyCardPanel.tsx | 4 +- .../SelectablePropertyMatchCard.tsx | 6 +- .../anfragencenter/latentNeedUtils.ts | 2 +- .../assistant/AssistantActionCards.tsx | 2 +- .../assistant/AssistantMessageList.tsx | 2 +- .../assistant/GlobalAIAssistantDrawer.tsx | 2 +- src/components/auth/DemoRoleSwitcher.tsx | 4 +- src/components/auth/SessionExpired.tsx | 2 +- src/components/cards/MetricCard.tsx | 2 +- src/components/cards/SourceTypeBadge.tsx | 2 +- .../compare/CompareColumnHeader.tsx | 2 +- .../compare/CompareCriteriaCard.tsx | 2 +- src/components/compare/CompareEmptyState.tsx | 2 +- src/components/compare/CompareTableBody.tsx | 2 +- .../data-quality/DataQualityPanel.tsx | 2 +- .../data-quality/DataQualityProgress.tsx | 4 +- .../data-quality/ProvenancePanel.tsx | 2 +- src/components/demand/AISearchActionBar.tsx | 2 +- src/components/demand/AISearchSavePreview.tsx | 4 +- src/components/demand/FollowUpPanel.tsx | 2 +- .../demand/NeedBuilderErrorState.tsx | 2 +- src/components/demand/NeedCardPreview.tsx | 6 +- src/components/demand/NeedInput.tsx | 12 +- src/components/demand/VoiceNeedInput.tsx | 10 +- src/components/demand/WeightingEditor.tsx | 2 +- src/components/forms/PriorityChipGroup.tsx | 2 +- .../future-signals/FutureSignalFilterBar.tsx | 4 +- src/components/layout/AppShellSidebar.tsx | 22 +- src/components/layout/AppShellTopBar.tsx | 9 +- src/components/layout/CompareTray.tsx | 4 +- .../layout/OrganizationContextBadge.tsx | 2 +- src/components/layout/PageHeader.tsx | 4 +- src/components/layout/UserMenu.tsx | 2 +- src/components/layout/appShellConfig.ts | 2 +- .../match-card/FutureAvailabilityCard.tsx | 1 - .../match-card/IntelligenceMatchCard.tsx | 152 ++++++------ .../match-card/MatchActionToolbar.tsx | 2 +- .../match-card/MatchCardCompact.tsx | 229 ++++++++---------- .../match-card/MatchCardCompareMini.tsx | 2 +- src/components/match-card/MatchCardHeader.tsx | 4 +- .../match-card/ScoreInlineBreakdown.tsx | 8 +- .../match-card/SignalQualityDots.tsx | 2 +- src/components/match-center/MatchListCard.tsx | 2 +- .../match-center/MatchStatusBadge.tsx | 2 +- .../match-detail/ExecutiveSummaryPanel.tsx | 2 +- .../match-detail/InquiryQuickDialog.tsx | 2 +- .../match-detail/MatchDetailHeader.tsx | 4 +- .../match-detail/MatchDetailHero.tsx | 4 +- .../match-detail/NextActionsPanel.tsx | 2 +- .../match-detail/scoreBreakdownConstants.ts | 2 +- src/components/onboarding/WelcomeDialog.tsx | 4 +- src/components/ops/DataCategoryBadgeList.tsx | 2 +- .../ops/MarketSignalDetailPanel.tsx | 2 +- src/components/ops/MarketSignalFilterBar.tsx | 4 +- src/components/ops/ReviewGatePanel.tsx | 2 +- src/components/ops/SignalConversionPanel.tsx | 2 +- src/components/ops/SignalInbox.tsx | 2 +- src/components/ops/SignalPipelineStepper.tsx | 8 +- src/components/ops/SignalPipelineView.tsx | 2 +- .../ops/SignalToMatchAuditTrail.tsx | 2 +- src/components/ops/SourceCard.tsx | 2 +- src/components/ops/SourceDetailPanel.tsx | 6 +- src/components/ops/SourceList.tsx | 2 +- src/components/pipeline/PipelineCard.tsx | 12 +- src/components/results/ResultFilterBar.tsx | 46 ++-- src/components/review/ReviewDetailPanel.tsx | 2 +- .../review/ReviewEntityTypeBadge.tsx | 2 +- src/components/review/ReviewTaskCard.tsx | 2 +- src/components/scores/ScoreBar.tsx | 2 +- src/components/shared/GenericBadge.tsx | 32 +-- src/components/shared/LocationPreview.tsx | 2 +- src/components/shared/PropertyMap.tsx | 2 +- src/components/shared/ViewToggle.tsx | 4 +- .../shortlist/AddToPipelineDialog.tsx | 2 +- .../shortlist/AddToShortlistDialog.tsx | 2 +- .../shortlist/DecisionBriefDraftPanel.tsx | 2 +- src/components/shortlist/ShortlistCard.tsx | 2 +- src/components/shortlist/ShortlistDetail.tsx | 4 +- .../shortlist/ShortlistItemCard.tsx | 8 +- src/components/shortlist/ShortlistList.tsx | 4 +- .../shortlist/ShortlistStatusBadge.tsx | 2 +- src/components/supply/BerichtDialog.tsx | 2 +- src/components/supply/MarketKpiPanel.tsx | 6 +- .../supply/PropertyActivityLogPanel.tsx | 6 +- .../supply/PropertyDetailOverview.tsx | 6 +- src/components/supply/PropertyDetailView.tsx | 2 +- src/components/supply/PropertyFilterBar.tsx | 2 +- .../supply/PropertyIntelligenceCard.tsx | 87 ++++--- .../supply/PropertyMarketSignalsTab.tsx | 2 +- .../supply/ReminderDetailDrawer.tsx | 4 +- src/components/supply/ReminderFeed.tsx | 2 +- src/components/supply/ReminderTypeBadge.tsx | 2 +- src/components/supply/SignalCard.tsx | 4 +- src/components/supply/propertyHelpers.ts | 2 +- .../supply/reminderDetailHelpers.tsx | 2 +- src/components/tables/TableToolbar.tsx | 2 +- src/components/ui/DecisionContextPanel.tsx | 47 ++-- src/index.css | 2 + src/lib/ds.ts | 185 +++++++------- src/lib/scoreTheme.ts | 42 ++-- src/lib/theme.ts | 68 +++--- src/lib/utils.ts | 28 +-- src/pages/auth/LoginScreen.tsx | 6 +- src/pages/demand/PropertyDetail.tsx | 2 +- src/pages/demand/Results.tsx | 4 +- src/pages/supply/DataQuality.tsx | 2 +- src/pages/supply/MarketIntelligence.tsx | 36 +-- src/pages/supply/MyListings.tsx | 4 +- src/pages/supply/Properties.tsx | 2 +- 132 files changed, 659 insertions(+), 711 deletions(-) diff --git a/src/components/ai-monitoring/AIOutputTable.tsx b/src/components/ai-monitoring/AIOutputTable.tsx index 1a6e9a0..f6b3672 100644 --- a/src/components/ai-monitoring/AIOutputTable.tsx +++ b/src/components/ai-monitoring/AIOutputTable.tsx @@ -23,7 +23,7 @@ const TYPE_LABELS: Record = { } const TYPE_COLORS: Record = { - NEED_PARSE: '#1e3a5f', + NEED_PARSE: '#152642', FOLLOW_UP_QUESTIONS: '#0891b2', MATCH_EXPLANATION: '#4f46e5', COMPARE_SUMMARY: '#1a7a4a', diff --git a/src/components/ai-monitoring/AIReviewActionToolbar.tsx b/src/components/ai-monitoring/AIReviewActionToolbar.tsx index 8ab9a59..1e8ae69 100644 --- a/src/components/ai-monitoring/AIReviewActionToolbar.tsx +++ b/src/components/ai-monitoring/AIReviewActionToolbar.tsx @@ -61,7 +61,7 @@ export function AIReviewActionToolbar({ output, onUpdateStatus, onCopyJson, isSu variant="outlined" onClick={onCopyJson} startIcon={} - sx={{ textTransform: 'none', fontSize: '0.75rem', color: '#64748b', borderColor: '#e2e8f0', '&:hover': { bgcolor: '#f8fafc' } }} + sx={{ textTransform: 'none', fontSize: '0.75rem', color: '#64748b', borderColor: '#e8e7e4', '&:hover': { bgcolor: '#f8fafc' } }} > JSON diff --git a/src/components/anfragencenter/ActiveInquiriesTab.tsx b/src/components/anfragencenter/ActiveInquiriesTab.tsx index dab929c..7d39749 100644 --- a/src/components/anfragencenter/ActiveInquiriesTab.tsx +++ b/src/components/anfragencenter/ActiveInquiriesTab.tsx @@ -93,18 +93,18 @@ export function ActiveInquiriesTab() { Anfragen - + {inquiries.length} - setView('list')} sx={{ bgcolor: view === 'list' ? '#e0e7ff' : 'transparent', color: view === 'list' ? '#1e3a5f' : '#64748b' }}> + setView('list')} sx={{ bgcolor: view === 'list' ? '#e0e7ff' : 'transparent', color: view === 'list' ? '#152642' : '#64748b' }}> - setView('grid')} sx={{ bgcolor: view === 'grid' ? '#e0e7ff' : 'transparent', color: view === 'grid' ? '#1e3a5f' : '#64748b' }}> + setView('grid')} sx={{ bgcolor: view === 'grid' ? '#e0e7ff' : 'transparent', color: view === 'grid' ? '#152642' : '#64748b' }}> diff --git a/src/components/anfragencenter/InquiryCard.tsx b/src/components/anfragencenter/InquiryCard.tsx index 8395fee..348100d 100644 --- a/src/components/anfragencenter/InquiryCard.tsx +++ b/src/components/anfragencenter/InquiryCard.tsx @@ -20,12 +20,12 @@ export function InquiryCard({ inquiry, selected, onClick }: InquiryCardProps) { p: 2, borderRadius: 1.5, border: '1px solid', - borderColor: selected ? '#1e3a5f' : '#e2e8f0', + borderColor: selected ? '#152642' : '#e8e7e4', bgcolor: selected ? '#f1f5f9' : 'white', cursor: 'pointer', transition: 'all 0.15s', '&:hover': { - borderColor: selected ? '#1e3a5f' : '#94a3b8', + borderColor: selected ? '#152642' : '#94a3b8', boxShadow: '0 2px 6px rgba(15,23,42,0.06)', }, display: 'flex', diff --git a/src/components/anfragencenter/InquiryChat.tsx b/src/components/anfragencenter/InquiryChat.tsx index d3c1872..88bd2ae 100644 --- a/src/components/anfragencenter/InquiryChat.tsx +++ b/src/components/anfragencenter/InquiryChat.tsx @@ -42,7 +42,7 @@ export function InquiryChat({ fontSize: '0.75rem', borderColor: '#cbd5e1', color: '#475569', - '&:hover': { borderColor: '#1e3a5f', color: '#1e3a5f', bgcolor: '#f0f4f8' }, + '&:hover': { borderColor: '#152642', color: '#152642', bgcolor: '#f0f4f8' }, }} > Angebot erstellen diff --git a/src/components/anfragencenter/InquiryDetailPanel.tsx b/src/components/anfragencenter/InquiryDetailPanel.tsx index cea0dcd..7336ea5 100644 --- a/src/components/anfragencenter/InquiryDetailPanel.tsx +++ b/src/components/anfragencenter/InquiryDetailPanel.tsx @@ -85,7 +85,7 @@ export function InquiryDetailPanel({ inquiryId }: InquiryDetailPanelProps) { variant="outlined" startIcon={} onClick={() => setPreparationOpen(true)} - sx={{ textTransform: 'none', whiteSpace: 'nowrap', borderColor: '#1e3a5f', color: '#1e3a5f', '&:hover': { bgcolor: '#f0f4f8' } }} + sx={{ textTransform: 'none', whiteSpace: 'nowrap', borderColor: '#152642', color: '#152642', '&:hover': { bgcolor: '#f0f4f8' } }} > Vorbereitung starten diff --git a/src/components/anfragencenter/InquiryListRow.tsx b/src/components/anfragencenter/InquiryListRow.tsx index 775eb53..d462237 100644 --- a/src/components/anfragencenter/InquiryListRow.tsx +++ b/src/components/anfragencenter/InquiryListRow.tsx @@ -20,7 +20,7 @@ export function InquiryListRow({ inquiry, selected, onClick }: InquiryListRowPro py: 1.5, borderBottom: '1px solid #e2e8f0', borderLeft: '3px solid', - borderLeftColor: selected ? '#1e3a5f' : hasUnread ? '#2563eb' : 'transparent', + borderLeftColor: selected ? '#152642' : hasUnread ? '#2563eb' : 'transparent', bgcolor: selected ? '#f1f5f9' : 'white', cursor: 'pointer', transition: 'background-color 0.15s, border-color 0.15s', diff --git a/src/components/anfragencenter/InquiryMessageBubble.tsx b/src/components/anfragencenter/InquiryMessageBubble.tsx index 7398050..170ae91 100644 --- a/src/components/anfragencenter/InquiryMessageBubble.tsx +++ b/src/components/anfragencenter/InquiryMessageBubble.tsx @@ -38,7 +38,7 @@ export function InquiryMessageBubble({ message }: InquiryMessageBubbleProps) { Antwort senden diff --git a/src/components/anfragencenter/LatentInquiriesTab.tsx b/src/components/anfragencenter/LatentInquiriesTab.tsx index c2e03f4..c448599 100644 --- a/src/components/anfragencenter/LatentInquiriesTab.tsx +++ b/src/components/anfragencenter/LatentInquiriesTab.tsx @@ -47,7 +47,7 @@ export function LatentInquiriesTab() { setMobileView('properties')} - sx={{ p: 1.25, bgcolor: '#1e3a5f', color: 'white', borderRadius: 1, textAlign: 'center', cursor: 'pointer', fontSize: '0.85rem', fontWeight: 600 }} + sx={{ p: 1.25, bgcolor: '#152642', color: 'white', borderRadius: 1, textAlign: 'center', cursor: 'pointer', fontSize: '0.85rem', fontWeight: 600 }} > Eigene Objekte anzeigen → diff --git a/src/components/anfragencenter/MockPdfPreview.tsx b/src/components/anfragencenter/MockPdfPreview.tsx index 1e0bb76..a4d4415 100644 --- a/src/components/anfragencenter/MockPdfPreview.tsx +++ b/src/components/anfragencenter/MockPdfPreview.tsx @@ -164,11 +164,11 @@ export function MockPdfPreview({ fields, fallbackFields, fieldSelections }: Mock {hasPropertyPage && ( <> - + Seite 2 — Objektdetails - + - + {a.fileName} diff --git a/src/components/anfragencenter/OfferCreationPanel.tsx b/src/components/anfragencenter/OfferCreationPanel.tsx index 6277340..0421034 100644 --- a/src/components/anfragencenter/OfferCreationPanel.tsx +++ b/src/components/anfragencenter/OfferCreationPanel.tsx @@ -35,7 +35,7 @@ export function OfferCreationPanel({ selectedCount, needId, needTitle }: OfferCr onClick={() => open(needId, needTitle)} sx={{ textTransform: 'none', - bgcolor: '#1e3a5f', + bgcolor: '#152642', fontWeight: 600, '&:hover': { bgcolor: '#16304d' }, }} diff --git a/src/components/anfragencenter/OfferCreationWizard.tsx b/src/components/anfragencenter/OfferCreationWizard.tsx index 31db3e0..5101a7c 100644 --- a/src/components/anfragencenter/OfferCreationWizard.tsx +++ b/src/components/anfragencenter/OfferCreationWizard.tsx @@ -255,7 +255,7 @@ export function OfferCreationWizard({ inquiryId, propertyId, tenantName, onClose variant="contained" disabled={loading} onClick={() => setStep(1)} - sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }} + sx={{ textTransform: 'none', bgcolor: '#152642', '&:hover': { bgcolor: '#16304d' } }} > Weiter @@ -264,7 +264,7 @@ export function OfferCreationWizard({ inquiryId, propertyId, tenantName, onClose @@ -273,7 +273,7 @@ export function OfferCreationWizard({ inquiryId, propertyId, tenantName, onClose diff --git a/src/components/anfragencenter/OfferFieldSelectionStep.tsx b/src/components/anfragencenter/OfferFieldSelectionStep.tsx index 1d07224..af19a71 100644 --- a/src/components/anfragencenter/OfferFieldSelectionStep.tsx +++ b/src/components/anfragencenter/OfferFieldSelectionStep.tsx @@ -77,7 +77,7 @@ export function OfferFieldSelectionStep() { diff --git a/src/components/anfragencenter/OfferPdfReviewStep.tsx b/src/components/anfragencenter/OfferPdfReviewStep.tsx index ed9bd9a..ad20ded 100644 --- a/src/components/anfragencenter/OfferPdfReviewStep.tsx +++ b/src/components/anfragencenter/OfferPdfReviewStep.tsx @@ -179,7 +179,7 @@ export function OfferPdfReviewStep() { endIcon={markChecked.isPending ? : } onClick={handleNext} disabled={!pdfReady || markChecked.isPending} - sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }} + sx={{ textTransform: 'none', bgcolor: '#152642', '&:hover': { bgcolor: '#16304d' } }} > {markChecked.isPending ? 'Wird geprüft…' : 'Angebot senden →'} diff --git a/src/components/anfragencenter/OfferPropertySelectionStep.tsx b/src/components/anfragencenter/OfferPropertySelectionStep.tsx index 41b30b8..2e29dff 100644 --- a/src/components/anfragencenter/OfferPropertySelectionStep.tsx +++ b/src/components/anfragencenter/OfferPropertySelectionStep.tsx @@ -141,7 +141,7 @@ export function OfferPropertySelectionStep() { endIcon={createDraft.isPending ? : } onClick={handleNext} disabled={selectedIds.length === 0 || createDraft.isPending} - sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }} + sx={{ textTransform: 'none', bgcolor: '#152642', '&:hover': { bgcolor: '#16304d' } }} > Weiter diff --git a/src/components/anfragencenter/OfferWizardPdfStep.tsx b/src/components/anfragencenter/OfferWizardPdfStep.tsx index 91f7119..e03f1b4 100644 --- a/src/components/anfragencenter/OfferWizardPdfStep.tsx +++ b/src/components/anfragencenter/OfferWizardPdfStep.tsx @@ -18,8 +18,8 @@ export function OfferWizardPdfStep({ generating, progress, ready, draft, propert {generating ? ( <> - - + + PDF wird generiert… @@ -44,7 +44,7 @@ export function OfferWizardPdfStep({ generating, progress, ready, draft, propert overflow: 'hidden', }} > - + @@ -53,7 +53,7 @@ export function OfferWizardPdfStep({ generating, progress, ready, draft, propert - + Angebotsschreiben {draft?.editableFields.slice(0, 3).map(f => ( @@ -83,7 +83,7 @@ export function OfferWizardPdfStep({ generating, progress, ready, draft, propert variant="contained" startIcon={} onClick={onDownload} - sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }} + sx={{ textTransform: 'none', bgcolor: '#152642', '&:hover': { bgcolor: '#16304d' } }} > Herunterladen diff --git a/src/components/anfragencenter/OwnPropertyMatchList.tsx b/src/components/anfragencenter/OwnPropertyMatchList.tsx index 5beb133..87bd35a 100644 --- a/src/components/anfragencenter/OwnPropertyMatchList.tsx +++ b/src/components/anfragencenter/OwnPropertyMatchList.tsx @@ -62,7 +62,7 @@ export function OwnPropertyMatchList({ need }: OwnPropertyMatchListProps) { flexShrink: 0, }} > - + Eigene Objekte @@ -72,7 +72,7 @@ export function OwnPropertyMatchList({ need }: OwnPropertyMatchListProps) { px: 1, py: 0.125, borderRadius: 1, - bgcolor: '#1e3a5f', + bgcolor: '#152642', color: 'white', fontWeight: 600, fontSize: '0.7rem', diff --git a/src/components/anfragencenter/PreparationWizard.tsx b/src/components/anfragencenter/PreparationWizard.tsx index 9e1975e..2506c16 100644 --- a/src/components/anfragencenter/PreparationWizard.tsx +++ b/src/components/anfragencenter/PreparationWizard.tsx @@ -159,8 +159,8 @@ export function PreparationWizard({ inquiryId, inquiry, onClose }: Props) { {generating ? ( <> - - + + Bericht wird erstellt… @@ -234,7 +234,7 @@ export function PreparationWizard({ inquiryId, inquiry, onClose }: Props) { fullWidth startIcon={} onClick={() => showToast('PDF wird heruntergeladen…', 'info')} - sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }} + sx={{ textTransform: 'none', bgcolor: '#152642', '&:hover': { bgcolor: '#16304d' } }} > Herunterladen @@ -271,7 +271,7 @@ export function PreparationWizard({ inquiryId, inquiry, onClose }: Props) { variant="contained" disabled={selectedIds.length === 0} onClick={handleGenerate} - sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }} + sx={{ textTransform: 'none', bgcolor: '#152642', '&:hover': { bgcolor: '#16304d' } }} > Weiter @@ -282,7 +282,7 @@ export function PreparationWizard({ inquiryId, inquiry, onClose }: Props) { disabled={finalizing} onClick={handleFinalize} startIcon={finalizing ? : undefined} - sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }} + sx={{ textTransform: 'none', bgcolor: '#152642', '&:hover': { bgcolor: '#16304d' } }} > Finalisieren diff --git a/src/components/anfragencenter/PublicNeedCard.tsx b/src/components/anfragencenter/PublicNeedCard.tsx index 8998a37..b7d8798 100644 --- a/src/components/anfragencenter/PublicNeedCard.tsx +++ b/src/components/anfragencenter/PublicNeedCard.tsx @@ -19,12 +19,12 @@ export function PublicNeedCard({ need, selected, onClick }: PublicNeedCardProps) p: 1.5, borderRadius: 1.5, border: '1px solid', - borderColor: selected ? '#1e3a5f' : '#e2e8f0', + borderColor: selected ? '#152642' : '#e8e7e4', bgcolor: selected ? '#f1f5f9' : 'white', cursor: 'pointer', transition: 'all 0.15s', '&:hover': { - borderColor: selected ? '#1e3a5f' : '#94a3b8', + borderColor: selected ? '#152642' : '#94a3b8', boxShadow: '0 2px 6px rgba(15,23,42,0.06)', }, display: 'flex', diff --git a/src/components/anfragencenter/PublicNeedDetail.tsx b/src/components/anfragencenter/PublicNeedDetail.tsx index e7a7219..347fbc2 100644 --- a/src/components/anfragencenter/PublicNeedDetail.tsx +++ b/src/components/anfragencenter/PublicNeedDetail.tsx @@ -174,7 +174,7 @@ export function PublicNeedDetail({ need }: PublicNeedDetailProps) { variant="caption" sx={{ fontWeight: 700, - color: '#1e3a5f', + color: '#152642', fontSize: '0.75rem', bgcolor: '#e0e7ff', px: 1, @@ -189,7 +189,7 @@ export function PublicNeedDetail({ need }: PublicNeedDetailProps) { sx={{ height: 6, borderRadius: 3, - bgcolor: '#e2e8f0', + bgcolor: '#e8e7e4', overflow: 'hidden', }} > @@ -197,7 +197,7 @@ export function PublicNeedDetail({ need }: PublicNeedDetailProps) { sx={{ width: `${pct}%`, height: '100%', - bgcolor: '#1e3a5f', + bgcolor: '#152642', transition: 'width 0.3s', }} /> @@ -229,7 +229,7 @@ export function PublicNeedDetail({ need }: PublicNeedDetailProps) { disabled={disabled} sx={{ textTransform: 'none', - bgcolor: '#1e3a5f', + bgcolor: '#152642', fontWeight: 600, '&:hover': { bgcolor: '#16304d' }, }} diff --git a/src/components/anfragencenter/PublicNeedList.tsx b/src/components/anfragencenter/PublicNeedList.tsx index 9103435..16a2f87 100644 --- a/src/components/anfragencenter/PublicNeedList.tsx +++ b/src/components/anfragencenter/PublicNeedList.tsx @@ -47,7 +47,7 @@ export function PublicNeedList({ selectedNeedId, onSelect, fullWidth }: PublicNe px: 1, py: 0.125, borderRadius: 1, - bgcolor: '#1e3a5f', + bgcolor: '#152642', color: 'white', fontWeight: 600, fontSize: '0.7rem', diff --git a/src/components/anfragencenter/RelatedPropertyCardPanel.tsx b/src/components/anfragencenter/RelatedPropertyCardPanel.tsx index 5465151..572bdaf 100644 --- a/src/components/anfragencenter/RelatedPropertyCardPanel.tsx +++ b/src/components/anfragencenter/RelatedPropertyCardPanel.tsx @@ -60,7 +60,7 @@ export function RelatedPropertyCardPanel({ propertyId, inquiryId }: RelatedPrope height: 140, borderRadius: 1.5, overflow: 'hidden', - bgcolor: '#e2e8f0', + bgcolor: '#e8e7e4', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -203,7 +203,7 @@ function AdditionalMatchCard({ match }: { match: AdditionalPropertyMatch }) { size="small" endIcon={} onClick={() => navigate('/supply/properties')} - sx={{ textTransform: 'none', fontSize: '0.7rem', mt: 0.5, p: 0, minWidth: 0, color: '#1e3a5f' }} + sx={{ textTransform: 'none', fontSize: '0.7rem', mt: 0.5, p: 0, minWidth: 0, color: '#152642' }} > Ansehen diff --git a/src/components/anfragencenter/SelectablePropertyMatchCard.tsx b/src/components/anfragencenter/SelectablePropertyMatchCard.tsx index 20d02b0..3a9b8b5 100644 --- a/src/components/anfragencenter/SelectablePropertyMatchCard.tsx +++ b/src/components/anfragencenter/SelectablePropertyMatchCard.tsx @@ -31,12 +31,12 @@ export function SelectablePropertyMatchCard({ p: 1.25, borderRadius: 1.5, border: '1px solid', - borderColor: selected ? '#1e3a5f' : '#e2e8f0', + borderColor: selected ? '#152642' : '#e8e7e4', bgcolor: selected ? '#eff6ff' : 'white', cursor: 'pointer', transition: 'all 0.15s', '&:hover': { - borderColor: selected ? '#1e3a5f' : '#94a3b8', + borderColor: selected ? '#152642' : '#94a3b8', }, display: 'flex', gap: 1.25, @@ -57,7 +57,7 @@ export function SelectablePropertyMatchCard({ height: 40, flexShrink: 0, borderRadius: 1, - bgcolor: '#e2e8f0', + bgcolor: '#e8e7e4', display: 'flex', alignItems: 'center', justifyContent: 'center', diff --git a/src/components/anfragencenter/latentNeedUtils.ts b/src/components/anfragencenter/latentNeedUtils.ts index 7cc1a6c..b8a672e 100644 --- a/src/components/anfragencenter/latentNeedUtils.ts +++ b/src/components/anfragencenter/latentNeedUtils.ts @@ -21,7 +21,7 @@ export function latentStatusBadge(status: 'public' | 'paused' | 'expired'): { } { if (status === 'public') return { label: 'Öffentlich', bg: '#dcfce7', fg: '#166534' } if (status === 'paused') return { label: 'Pausiert', bg: '#fed7aa', fg: '#9a3412' } - return { label: 'Abgelaufen', bg: '#e2e8f0', fg: '#475569' } + return { label: 'Abgelaufen', bg: '#e8e7e4', fg: '#475569' } } // Deterministic pseudo-random match score from string IDs (range 60–96) diff --git a/src/components/assistant/AssistantActionCards.tsx b/src/components/assistant/AssistantActionCards.tsx index c2dfbeb..7db3520 100644 --- a/src/components/assistant/AssistantActionCards.tsx +++ b/src/components/assistant/AssistantActionCards.tsx @@ -9,7 +9,7 @@ interface Props { } const ACTION_COLORS: Record = { - NAVIGATE: '#1e3a5f', + NAVIGATE: '#152642', OPEN_REVIEW: '#7c3aed', ADD_TO_SHORTLIST: '#1a7a4a', REQUEST_DATA: '#d97706', diff --git a/src/components/assistant/AssistantMessageList.tsx b/src/components/assistant/AssistantMessageList.tsx index 858c493..4241341 100644 --- a/src/components/assistant/AssistantMessageList.tsx +++ b/src/components/assistant/AssistantMessageList.tsx @@ -25,7 +25,7 @@ function MessageBubble({ message }: { message: AssistantMessage }) { px: 1.5, py: 1, borderRadius: isUser ? '8px 8px 2px 8px' : '2px 8px 8px 8px', - bgcolor: isUser ? '#1e3a5f' : '#f1f5f9', + bgcolor: isUser ? '#152642' : '#f1f5f9', color: isUser ? 'white' : '#1e293b', }} > diff --git a/src/components/assistant/GlobalAIAssistantDrawer.tsx b/src/components/assistant/GlobalAIAssistantDrawer.tsx index c5e5f77..1f74608 100644 --- a/src/components/assistant/GlobalAIAssistantDrawer.tsx +++ b/src/components/assistant/GlobalAIAssistantDrawer.tsx @@ -237,7 +237,7 @@ export function GlobalAIAssistantDrawer() { color: 'white', flexShrink: 0, '&:hover': { bgcolor: '#4338ca' }, - '&:disabled': { bgcolor: '#e2e8f0', color: '#94a3b8' }, + '&:disabled': { bgcolor: '#e8e7e4', color: '#94a3b8' }, }} > diff --git a/src/components/auth/DemoRoleSwitcher.tsx b/src/components/auth/DemoRoleSwitcher.tsx index 9d168b3..f6fb01e 100644 --- a/src/components/auth/DemoRoleSwitcher.tsx +++ b/src/components/auth/DemoRoleSwitcher.tsx @@ -37,10 +37,10 @@ export function DemoRoleSwitcher() { sx={{ fontSize: '0.7rem', height: 22, - bgcolor: active ? '#1e3a5f' : 'transparent', + bgcolor: active ? '#152642' : 'transparent', color: active ? '#fff' : 'text.secondary', border: '1px solid', - borderColor: active ? '#1e3a5f' : 'divider', + borderColor: active ? '#152642' : 'divider', '&:hover': { bgcolor: active ? '#162d4a' : 'rgba(0,0,0,0.04)' }, }} /> diff --git a/src/components/auth/SessionExpired.tsx b/src/components/auth/SessionExpired.tsx index 0ede7c4..7755c97 100644 --- a/src/components/auth/SessionExpired.tsx +++ b/src/components/auth/SessionExpired.tsx @@ -51,7 +51,7 @@ export function SessionExpired() { diff --git a/src/components/cards/MetricCard.tsx b/src/components/cards/MetricCard.tsx index 461657f..5f4a1ae 100644 --- a/src/components/cards/MetricCard.tsx +++ b/src/components/cards/MetricCard.tsx @@ -17,7 +17,7 @@ interface MetricCardProps { sx?: SxProps } -export function MetricCard({ label, value, delta, icon, color = '#1e3a5f', sx }: MetricCardProps) { +export function MetricCard({ label, value, delta, icon, color = '#152642', sx }: MetricCardProps) { return ( diff --git a/src/components/cards/SourceTypeBadge.tsx b/src/components/cards/SourceTypeBadge.tsx index 9104367..e0af70a 100644 --- a/src/components/cards/SourceTypeBadge.tsx +++ b/src/components/cards/SourceTypeBadge.tsx @@ -9,7 +9,7 @@ interface SourceTypeBadgeProps { } const CONFIG: Record = { - VERIFIED_PORTFOLIO: { bg: 'rgba(30,58,95,0.1)', color: '#1e3a5f', Icon: ShieldCheck }, + VERIFIED_PORTFOLIO: { bg: 'rgba(30,58,95,0.1)', color: '#152642', Icon: ShieldCheck }, MAISON_WORK: { bg: 'rgba(3,105,161,0.1)', color: '#0369a1', Icon: Building2 }, FUTURE_AVAILABILITY: { bg: 'rgba(124,58,237,0.1)', color: '#6d28d9', Icon: Sparkles }, } diff --git a/src/components/compare/CompareColumnHeader.tsx b/src/components/compare/CompareColumnHeader.tsx index eeab57d..776d46f 100644 --- a/src/components/compare/CompareColumnHeader.tsx +++ b/src/components/compare/CompareColumnHeader.tsx @@ -67,7 +67,7 @@ export function CompareColumnHeader({ item, onRemove }: Props) { sx={{ fontWeight: 700, lineHeight: 1.3, mb: 0.25, cursor: 'pointer', - '&:hover': { color: '#1e3a5f' }, + '&:hover': { color: '#152642' }, }} onClick={() => navigate(`/demand/results/${item.matchId}`)} > diff --git a/src/components/compare/CompareCriteriaCard.tsx b/src/components/compare/CompareCriteriaCard.tsx index d53e8f4..5403113 100644 --- a/src/components/compare/CompareCriteriaCard.tsx +++ b/src/components/compare/CompareCriteriaCard.tsx @@ -38,7 +38,7 @@ export function CompareCriteriaCard({ compareItems, relevantCriteria, overallWin Kriterium {compareItems.map(item => ( - + {getTitle(item)} diff --git a/src/components/compare/CompareEmptyState.tsx b/src/components/compare/CompareEmptyState.tsx index de4f42e..bb85998 100644 --- a/src/components/compare/CompareEmptyState.tsx +++ b/src/components/compare/CompareEmptyState.tsx @@ -15,7 +15,7 @@ export function CompareEmptyState() { Fügen Sie 2–4 Ergebnisse aus dem Feed, Match Detail oder Match Center zum Vergleich hinzu. diff --git a/src/components/compare/CompareTableBody.tsx b/src/components/compare/CompareTableBody.tsx index 9a1b92e..ba8edef 100644 --- a/src/components/compare/CompareTableBody.tsx +++ b/src/components/compare/CompareTableBody.tsx @@ -208,7 +208,7 @@ export function CompareTableBody({ + {fitOutMonthlyLabel} CHF/Monat (Ausbau ÷ {FITOUT_AMORTIZATION_YEARS} J.) )} - + = {totalMonthly.toLocaleString('de-CH')} CHF/Monat {fitOutLabel && ( diff --git a/src/components/data-quality/DataQualityPanel.tsx b/src/components/data-quality/DataQualityPanel.tsx index 86e4867..5690c92 100644 --- a/src/components/data-quality/DataQualityPanel.tsx +++ b/src/components/data-quality/DataQualityPanel.tsx @@ -81,7 +81,7 @@ export function DataQualityPanel({ property }: DataQualityPanelProps) { Datenaktualisierung anfragen {q.missingCriticalFields.length > 0 && ( - )} diff --git a/src/components/data-quality/DataQualityProgress.tsx b/src/components/data-quality/DataQualityProgress.tsx index d1a97b4..42f6ebd 100644 --- a/src/components/data-quality/DataQualityProgress.tsx +++ b/src/components/data-quality/DataQualityProgress.tsx @@ -76,7 +76,7 @@ export function DataQualityProgress({ property, compact = false }: DataQualityPr sx={{ height: 4, borderRadius: 2, - bgcolor: '#e2e8f0', + bgcolor: '#e8e7e4', '& .MuiLinearProgress-bar': { bgcolor: d.color }, }} /> @@ -110,7 +110,7 @@ export function DataQualityProgress({ property, compact = false }: DataQualityPr sx={{ height: 5, borderRadius: 3, - bgcolor: '#e2e8f0', + bgcolor: '#e8e7e4', '& .MuiLinearProgress-bar': { bgcolor: d.color }, }} /> diff --git a/src/components/data-quality/ProvenancePanel.tsx b/src/components/data-quality/ProvenancePanel.tsx index a74b896..7d3a412 100644 --- a/src/components/data-quality/ProvenancePanel.tsx +++ b/src/components/data-quality/ProvenancePanel.tsx @@ -79,7 +79,7 @@ export function ProvenancePanel({ property: p }: ProvenancePanelProps) { sx={{ height: 5, borderRadius: 3, - bgcolor: '#e2e8f0', + bgcolor: '#e8e7e4', '& .MuiLinearProgress-bar': { bgcolor: color }, }} /> diff --git a/src/components/demand/AISearchActionBar.tsx b/src/components/demand/AISearchActionBar.tsx index 704a8e8..29aa41e 100644 --- a/src/components/demand/AISearchActionBar.tsx +++ b/src/components/demand/AISearchActionBar.tsx @@ -23,7 +23,7 @@ export function AISearchActionBar({ canProceed, isSearching, isSavingProfile, on sx={{ flex: 1, py: 1.5, - bgcolor: '#1e3a5f', + bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' }, fontSize: 15, fontWeight: 600, diff --git a/src/components/demand/AISearchSavePreview.tsx b/src/components/demand/AISearchSavePreview.tsx index c8b64b4..69cfd40 100644 --- a/src/components/demand/AISearchSavePreview.tsx +++ b/src/components/demand/AISearchSavePreview.tsx @@ -48,7 +48,7 @@ export function AISearchSavePreview({ @@ -86,7 +86,7 @@ export function AISearchSavePreview({ onClick={onSave} disabled={isSaving} endIcon={isSaving ? : } - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' }, textTransform: 'none' }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' }, textTransform: 'none' }} > {isSaving ? 'Wird gespeichert…' diff --git a/src/components/demand/FollowUpPanel.tsx b/src/components/demand/FollowUpPanel.tsx index 0f86fb6..e7bd678 100644 --- a/src/components/demand/FollowUpPanel.tsx +++ b/src/components/demand/FollowUpPanel.tsx @@ -66,7 +66,7 @@ export function FollowUpPanel({ questions, answers, onAnswer, onContinue, onRepa disabled={requiredUnanswered.length > 0} onClick={onContinue} endIcon={} - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' } }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' } }} > Weiter zur Gewichtung diff --git a/src/components/demand/NeedBuilderErrorState.tsx b/src/components/demand/NeedBuilderErrorState.tsx index 97a8105..5ed8083 100644 --- a/src/components/demand/NeedBuilderErrorState.tsx +++ b/src/components/demand/NeedBuilderErrorState.tsx @@ -22,7 +22,7 @@ export function NeedBuilderErrorState({ message, onRetry }: Props) { variant="contained" startIcon={} onClick={onRetry} - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' } }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' } }} > Erneut versuchen diff --git a/src/components/demand/NeedCardPreview.tsx b/src/components/demand/NeedCardPreview.tsx index 5615261..7cd0ab7 100644 --- a/src/components/demand/NeedCardPreview.tsx +++ b/src/components/demand/NeedCardPreview.tsx @@ -66,7 +66,7 @@ export function NeedCardPreview({ criteria: c, weights, confidenceByField, missi {/* Header */} {c.assetType && ( - + )} @@ -143,7 +143,7 @@ export function NeedCardPreview({ criteria: c, weights, confidenceByField, missi variant="determinate" value={overallConfidence * 100} sx={{ - height: 6, borderRadius: 3, bgcolor: '#e2e8f0', + height: 6, borderRadius: 3, bgcolor: '#e8e7e4', '& .MuiLinearProgress-bar': { bgcolor: overallConfidence >= 0.7 ? '#1a7a4a' : overallConfidence >= 0.5 ? '#d97706' : '#c0392b', }, @@ -171,7 +171,7 @@ export function NeedCardPreview({ criteria: c, weights, confidenceByField, missi {pct}% diff --git a/src/components/demand/NeedInput.tsx b/src/components/demand/NeedInput.tsx index 129600f..de28635 100644 --- a/src/components/demand/NeedInput.tsx +++ b/src/components/demand/NeedInput.tsx @@ -81,7 +81,7 @@ export function NeedInput({ criteria: c, onCriteriaChange: set }: Props) { clickable onClick={() => set({ ...c, assetType: c.assetType === opt.value ? undefined : opt.value })} sx={c.assetType === opt.value - ? { bgcolor: '#1e3a5f', color: 'white', '& .MuiChip-label': { color: 'white' } } + ? { bgcolor: '#152642', color: 'white', '& .MuiChip-label': { color: 'white' } } : {}} /> ))} @@ -139,7 +139,7 @@ export function NeedInput({ criteria: c, onCriteriaChange: set }: Props) { }) }} sx={c.preferredLocations?.includes(loc) - ? { fontSize: '0.68rem', height: 22, bgcolor: '#1e3a5f', color: 'white' } + ? { fontSize: '0.68rem', height: 22, bgcolor: '#152642', color: 'white' } : { fontSize: '0.68rem', height: 22, color: '#64748b', borderColor: '#cbd5e1' } } /> @@ -169,7 +169,7 @@ export function NeedInput({ criteria: c, onCriteriaChange: set }: Props) { Suchradius - + {c.searchRadius ?? 30} km @@ -181,7 +181,7 @@ export function NeedInput({ criteria: c, onCriteriaChange: set }: Props) { step={1} onChange={(_, v) => set({ ...c, searchRadius: v as number })} size="small" - sx={{ color: '#1e3a5f', '& .MuiSlider-markLabel': { fontSize: '0.62rem' } }} + sx={{ color: '#152642', '& .MuiSlider-markLabel': { fontSize: '0.62rem' } }} /> @@ -197,7 +197,7 @@ export function NeedInput({ criteria: c, onCriteriaChange: set }: Props) { clickable onClick={() => set({ ...c, budgetRange: { maxPerSqm: p.value, currency: 'CHF' } })} sx={c.budgetRange?.maxPerSqm === p.value - ? { fontSize: '0.68rem', height: 22, bgcolor: '#1e3a5f', color: 'white' } + ? { fontSize: '0.68rem', height: 22, bgcolor: '#152642', color: 'white' } : { fontSize: '0.68rem', height: 22, color: '#64748b', borderColor: '#cbd5e1' } } /> @@ -243,7 +243,7 @@ export function NeedInput({ criteria: c, onCriteriaChange: set }: Props) { )} {/* Anonymous search */} - + Anonyme Suche diff --git a/src/components/demand/VoiceNeedInput.tsx b/src/components/demand/VoiceNeedInput.tsx index c0f1219..7a2169c 100644 --- a/src/components/demand/VoiceNeedInput.tsx +++ b/src/components/demand/VoiceNeedInput.tsx @@ -77,7 +77,7 @@ export function VoiceNeedInput({ text, onTextChange, onAiSubmit, isAnalyzing, is sx={{ p: 3, border: '1px solid', - borderColor: isRecording ? '#dc2626' : '#e2e8f0', + borderColor: isRecording ? '#dc2626' : '#e8e7e4', transition: 'border-color 0.2s', bgcolor: isRecording ? '#fff5f5' : 'white', }} @@ -101,11 +101,11 @@ export function VoiceNeedInput({ text, onTextChange, onAiSubmit, isAnalyzing, is /> ) : isAnalyzing ? ( - - Analysiert… + + Analysiert… ) : isAutoGen && text ? ( - ⚡ auto-synchronisiert + ⚡ auto-synchronisiert ) : null} @@ -154,7 +154,7 @@ export function VoiceNeedInput({ text, onTextChange, onAiSubmit, isAnalyzing, is size="small" disabled={isAnalyzing || !isSpeechSupported} title={isSpeechSupported ? 'Spracheingabe starten' : 'Spracheingabe nicht verfügbar'} - sx={{ bgcolor: '#f1f5f9', color: '#475569', '&:hover': { bgcolor: '#e2e8f0' }, '&:disabled': { opacity: 0.35 } }} + sx={{ bgcolor: '#f1f5f9', color: '#475569', '&:hover': { bgcolor: '#e8e7e4' }, '&:disabled': { opacity: 0.35 } }} > diff --git a/src/components/demand/WeightingEditor.tsx b/src/components/demand/WeightingEditor.tsx index d881ca8..0947997 100644 --- a/src/components/demand/WeightingEditor.tsx +++ b/src/components/demand/WeightingEditor.tsx @@ -109,7 +109,7 @@ export function WeightingEditor({ weights, onChange, assetType }: Props) { - + diff --git a/src/components/forms/PriorityChipGroup.tsx b/src/components/forms/PriorityChipGroup.tsx index adfc430..7cf5ee0 100644 --- a/src/components/forms/PriorityChipGroup.tsx +++ b/src/components/forms/PriorityChipGroup.tsx @@ -39,7 +39,7 @@ export function PriorityChipGroup({ label, options, value, onChange, exclusive = {options.map(opt => { const selected = value.includes(opt.value) - const accent = opt.color ?? '#1e3a5f' + const accent = opt.color ?? '#152642' return ( set({ signalType: filters.signalType === t ? '' : t })} sx={{ fontWeight: filters.signalType === t ? 700 : 400, - bgcolor: filters.signalType === t ? '#1e3a5f' : undefined, + bgcolor: filters.signalType === t ? '#152642' : undefined, color: filters.signalType === t ? 'white' : undefined, }} /> @@ -131,7 +131,7 @@ export function FutureSignalFilterBar({ filters, onChange, totalCount, filteredC label={`${activeCount} Filter aktiv`} size="small" onDelete={() => onChange(DEFAULT_SIGNAL_FILTERS)} - sx={{ bgcolor: '#eff6ff', color: '#1e3a5f' }} + sx={{ bgcolor: '#eff6ff', color: '#152642' }} /> )} diff --git a/src/components/layout/AppShellSidebar.tsx b/src/components/layout/AppShellSidebar.tsx index 999a2d7..1566b14 100644 --- a/src/components/layout/AppShellSidebar.tsx +++ b/src/components/layout/AppShellSidebar.tsx @@ -13,7 +13,8 @@ export const SIDEBAR_BG = '#0f1923' export const DIVIDER_COLOR = 'rgba(255,255,255,0.08)' export const TEXT_MUTED = '#94a3b8' export const TEXT_WHITE = '#ffffff' -export const NAV_ACTIVE_BG = 'rgba(255,255,255,0.12)' +export const NAV_ACTIVE_BG = 'transparent' +export const NAV_ACTIVE_ACCENT = '#b8975a' export const NAV_HOVER_BG = 'rgba(255,255,255,0.06)' // --------------------------------------------------------------------------- @@ -188,16 +189,16 @@ export function Sidebar({ display: 'flex', alignItems: 'center', gap: 1.25, - px: collapsed ? 0 : 1.5, + pl: collapsed ? 0 : isActive ? 1.25 : 1.5, + pr: collapsed ? 0 : 1.5, py: 0.75, mx: 0.5, borderRadius: 1, justifyContent: collapsed ? 'center' : 'flex-start', - backgroundColor: isActive ? NAV_ACTIVE_BG : 'transparent', - '&:hover': { - backgroundColor: isActive ? NAV_ACTIVE_BG : NAV_HOVER_BG, - }, - transition: 'background-color 0.15s ease', + borderLeft: collapsed ? 'none' : `3px solid ${isActive ? NAV_ACTIVE_ACCENT : 'transparent'}`, + backgroundColor: 'transparent', + '&:hover': { backgroundColor: NAV_HOVER_BG }, + transition: 'border-color 0.15s ease', cursor: 'pointer', }} > @@ -208,8 +209,9 @@ export function Sidebar({ variant="body2" sx={{ color: isActive ? TEXT_WHITE : TEXT_MUTED, - fontWeight: isActive ? 500 : 400, - fontSize: '0.8125rem', + fontWeight: isActive ? 600 : 400, + fontSize: '0.875rem', + letterSpacing: '-0.01em', whiteSpace: 'nowrap', }} > @@ -263,7 +265,7 @@ export function Sidebar({ width: 32, height: 32, fontSize: '0.75rem', - bgcolor: '#1e3a5f', + bgcolor: '#b8975a', flexShrink: 0, }} > diff --git a/src/components/layout/AppShellTopBar.tsx b/src/components/layout/AppShellTopBar.tsx index 846049f..935ced8 100644 --- a/src/components/layout/AppShellTopBar.tsx +++ b/src/components/layout/AppShellTopBar.tsx @@ -31,10 +31,10 @@ export function TopBar({ activeWorkspace, pathname, onMenuClick, isMobile }: Top = { - VERIFIED_PORTFOLIO: '#1e3a5f', + VERIFIED_PORTFOLIO: '#152642', MAISON_WORK: '#0369a1', FUTURE_AVAILABILITY: '#7c3aed', } @@ -92,7 +92,7 @@ export function CompareTray() { variant="contained" size="small" onClick={() => navigate('/demand/compare')} - sx={{ bgcolor: '#1e3a5f', textTransform: 'none', flexShrink: 0, '&:hover': { bgcolor: '#162d4a' } }} + sx={{ bgcolor: '#152642', textTransform: 'none', flexShrink: 0, '&:hover': { bgcolor: '#162d4a' } }} > Vergleich starten diff --git a/src/components/layout/OrganizationContextBadge.tsx b/src/components/layout/OrganizationContextBadge.tsx index 0ca238c..bf0f83c 100644 --- a/src/components/layout/OrganizationContextBadge.tsx +++ b/src/components/layout/OrganizationContextBadge.tsx @@ -13,7 +13,7 @@ export function OrganizationContextBadge() { variant="outlined" icon={} label={currentUser.organizationName} - sx={{ fontSize: '0.7rem', height: 22, color: '#64748b', borderColor: '#e2e8f0' }} + sx={{ fontSize: '0.7rem', height: 22, color: '#64748b', borderColor: '#e8e7e4' }} /> ) } diff --git a/src/components/layout/PageHeader.tsx b/src/components/layout/PageHeader.tsx index 174f722..fa682d1 100644 --- a/src/components/layout/PageHeader.tsx +++ b/src/components/layout/PageHeader.tsx @@ -28,7 +28,7 @@ export function PageHeader({ sx, }: PageHeaderProps) { return ( - + {breadcrumbs && breadcrumbs.length > 0 && ( {breadcrumbs.map((crumb) => @@ -52,7 +52,7 @@ export function PageHeader({ - + {title} {badge} diff --git a/src/components/layout/UserMenu.tsx b/src/components/layout/UserMenu.tsx index 9f1b50f..e0cb38d 100644 --- a/src/components/layout/UserMenu.tsx +++ b/src/components/layout/UserMenu.tsx @@ -45,7 +45,7 @@ export function UserMenu() { return ( <> - + {initials} diff --git a/src/components/layout/appShellConfig.ts b/src/components/layout/appShellConfig.ts index 37f7d88..135f96e 100644 --- a/src/components/layout/appShellConfig.ts +++ b/src/components/layout/appShellConfig.ts @@ -44,7 +44,7 @@ export const WORKSPACE_CONFIG: Record = { abbreviation: 'VW', icon: Building2, firstPath: '/supply/dashboard', - chipColor: '#1e3a5f', + chipColor: '#152642', navItems: [ { path: '/supply/dashboard', label: 'Übersicht', icon: LayoutDashboard }, { path: '/supply/properties', label: 'Meine Objekte', icon: Building2 }, diff --git a/src/components/match-card/FutureAvailabilityCard.tsx b/src/components/match-card/FutureAvailabilityCard.tsx index bd2a3d5..4e03085 100644 --- a/src/components/match-card/FutureAvailabilityCard.tsx +++ b/src/components/match-card/FutureAvailabilityCard.tsx @@ -32,7 +32,6 @@ export const FutureAvailabilityCard = memo(function FutureAvailabilityCard({ vm borderRadius: 2, overflow: 'hidden', border: `1px solid ${borderColor}`, - borderLeft: `3px solid ${accentColor}`, background: 'white', display: 'flex', flexDirection: 'column', boxShadow: '0 1px 8px rgba(0,0,0,0.06)', diff --git a/src/components/match-card/IntelligenceMatchCard.tsx b/src/components/match-card/IntelligenceMatchCard.tsx index a45f14f..edb90da 100644 --- a/src/components/match-card/IntelligenceMatchCard.tsx +++ b/src/components/match-card/IntelligenceMatchCard.tsx @@ -1,19 +1,15 @@ -import { Box, Button, Chip, Divider, Typography } from '@mui/material' -import { - Building2, - CheckCircle2, -} from 'lucide-react' +import { Box, Button, Chip, Tooltip, Typography } from '@mui/material' +import { Building2, CheckCircle2 } from 'lucide-react' import { useSessionStore } from '../../stores/sessionStore' import { useInquiryStore } from '../../stores/inquiryStore' import { LocationPreview } from '../shared/LocationPreview' import { HeatBadge } from '../shared/HeatBadge' import { getScoreTier, SCORE_THEME } from '../shared/scoreTheme' -import { RESULT_TYPE_META } from '../../lib/ds' +import { RESULT_TYPE_META, DS_TEXT, DS_BORDER } from '../../lib/ds' +import { confidenceHex } from '../../lib/utils' import type { MatchCardViewModel } from './MatchCardViewModel' import { FutureAvailabilityCard } from './FutureAvailabilityCard' -// ── Main component ──────────────────────────────────────────────────────────── - interface Props { vm: MatchCardViewModel imageUrl?: string @@ -30,52 +26,64 @@ export function IntelligenceMatchCard({ vm, imageUrl, lat, lng, cityLabel, overl const { currentUser } = useSessionStore() const openInquiryDialog = useInquiryStore(s => s.openInquiryDialog) const isPortfolioOwner = currentUser?.role === 'PROPERTY_MANAGER' || currentUser?.role === 'ORGANIZATION_ADMIN' - const rt = RESULT_TYPE_META[vm.resultType] ?? { label: vm.resultType, color: '#64748b' } + const rt = RESULT_TYPE_META[vm.resultType] ?? { label: vm.resultType, color: '#9a9a9a' } const isFuture = vm.resultType === 'FUTURE_AVAILABILITY' + const confPct = Math.round(vm.confidenceScore * 100) - // Future availability cards use the new design if (isFuture) { return } + // secondary chips — max 2 visible + const chips: Array<{ label: string; color?: string }> = [] + if (vm.availabilityLabel) chips.push({ label: vm.availabilityLabel }) + if (vm.fitOutLabel) chips.push({ label: vm.fitOutLabel }) + if (vm.isDivisible && vm.minDivisibleUnitSqm) chips.push({ label: `Teilbar ab ${vm.minDivisibleUnitSqm} m²` }) + const visibleChips = chips.slice(0, 2) + const hiddenChips = chips.slice(2) + return ( {/* ── Hero zone ── */} - + + + {/* Score badge — bottom-left editorial */} - + {vm.matchScore}% - + + {/* Top-right badge strip */} + {vm.resultType === 'VERIFIED_PORTFOLIO' && isPortfolioOwner && ( } + icon={} label="Ihr Objekt" size="small" - sx={{ bgcolor: 'rgba(30,58,95,0.85)', color: 'white', fontWeight: 700, fontSize: 9, height: 18, '& .MuiChip-icon': { ml: 0.5 } }} + sx={{ bgcolor: 'rgba(21,38,66,0.85)', color: 'white', fontWeight: 700, fontSize: 9, height: 18, '& .MuiChip-icon': { ml: 0.5 } }} /> )} @@ -83,24 +91,24 @@ export function IntelligenceMatchCard({ vm, imageUrl, lat, lng, cityLabel, overl {/* ── Card body ── */} - - {/* Unit-first title (floor + street) */} - + + + {/* Title */} + {vm.title} - {/* Building name as subtitle when a unit or floor range is in the title */} {vm.propertySubtitle && ( - + {vm.propertySubtitle} )} - + {[vm.locationLabel, vm.availabilityLabel].filter(Boolean).join(' · ')} - {/* Unit area + rent when a specific unit is matched */} + {/* Unit details */} {vm.preMarketUnit && ( - + {vm.preMarketUnit.areaSqm.toLocaleString('de-CH')} m² {vm.preMarketUnit.rentPricePerSqm ? ` · CHF ${Math.round(vm.preMarketUnit.rentPricePerSqm / 12).toLocaleString('de-CH')}/m²/Mt.` @@ -108,43 +116,53 @@ export function IntelligenceMatchCard({ vm, imageUrl, lat, lng, cityLabel, overl )} - {/* Regular explainability summary */} + {/* Confidence + secondary chips */} + + + {visibleChips.map((c, i) => ( + + ))} + {hiddenChips.length > 0 && ( + c.label).join(' · ')} arrow> + + + )} + + + {/* Explainability */} {vm.explainabilitySummary && ( - <> - - - {vm.explainabilitySummary} - - + + {vm.explainabilitySummary} + )} {vm.reasons.length > 0 && ( - <> - - - {vm.reasons.slice(0, 3).map((r, i) => ( - - - - - {r.label} - - - {r.explanation} - - + + {vm.reasons.slice(0, 3).map((r, i) => ( + + + + + {r.label} + + + {r.explanation} + - ))} - - + + ))} + )} {/* Actions */} - + @@ -169,9 +187,7 @@ export function IntelligenceMatchCard({ vm, imageUrl, lat, lng, cityLabel, overl variant={a.variant === 'primary' ? 'contained' : 'outlined'} onClick={a.onClick} disabled={a.disabled} - sx={{ - textTransform: 'none', fontSize: '0.7rem', py: 0.375, px: 1, - }} + sx={{ textTransform: 'none', fontSize: '0.7rem', py: 0.375, px: 1 }} > {a.label} diff --git a/src/components/match-card/MatchActionToolbar.tsx b/src/components/match-card/MatchActionToolbar.tsx index d9161c2..f365f3e 100644 --- a/src/components/match-card/MatchActionToolbar.tsx +++ b/src/components/match-card/MatchActionToolbar.tsx @@ -26,7 +26,7 @@ export function MatchActionToolbar({ actions }: Props) { onClick={action.onClick} sx={ action.variant === 'primary' - ? { bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' } } + ? { bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' } } : action.variant === 'danger' ? { color: '#c0392b', borderColor: '#c0392b', '&:hover': { borderColor: '#c0392b' } } : {} diff --git a/src/components/match-card/MatchCardCompact.tsx b/src/components/match-card/MatchCardCompact.tsx index ed524a4..b9ca4ff 100644 --- a/src/components/match-card/MatchCardCompact.tsx +++ b/src/components/match-card/MatchCardCompact.tsx @@ -1,11 +1,11 @@ import { memo } from 'react' -import { Alert, Box, Button, Chip, Divider, Typography } from '@mui/material' +import { Alert, Box, Button, Chip, Divider, Tooltip, Typography } from '@mui/material' import { MapPin, Building2, CheckCircle2, AlertTriangle } from 'lucide-react' import { useSessionStore } from '../../stores/sessionStore' import { useInquiryStore } from '../../stores/inquiryStore' import { HeatBadge } from '../shared/HeatBadge' import { getScoreTier, SCORE_THEME } from '../shared/scoreTheme' -import { RESULT_TYPE_META } from '../../lib/ds' +import { RESULT_TYPE_META, DS_TEXT, DS_BORDER } from '../../lib/ds' import { confidenceHex } from '../../lib/utils' import { ScoreInlineBreakdown } from './ScoreInlineBreakdown' import { MatchCardRestrictedState } from './MatchCardRestrictedState' @@ -25,12 +25,20 @@ export const MatchCardCompact = memo(function MatchCardCompact({ vm, imageUrl, o const isPortfolioOwner = currentUser?.role === 'PROPERTY_MANAGER' || currentUser?.role === 'ORGANIZATION_ADMIN' const tier = getScoreTier(vm.matchScore) - const theme = SCORE_THEME[tier] - const rt = RESULT_TYPE_META[vm.resultType] ?? { label: vm.resultType, color: '#64748b' } + const scoreBadgeBg = SCORE_THEME[tier].gradient + const rt = RESULT_TYPE_META[vm.resultType] ?? { label: vm.resultType, color: '#9a9a9a' } const confPct = Math.round(vm.confidenceScore * 100) const topRisk = vm.risks.find(r => r.level === 'CRITICAL' || r.level === 'HIGH') - const borderColor = vm.isCompareSelected ? '#7c3aed' : vm.isSelected ? '#1e3a5f' : 'transparent' + const leftAccent = vm.isCompareSelected ? '#5b3f8a' : vm.isSelected ? '#152642' : 'transparent' + + const extraChips: string[] = [] + if (vm.availabilityLabel) extraChips.push(vm.availabilityLabel) + if (vm.fitOutLabel) extraChips.push(vm.fitOutLabel) + if (vm.isDivisible && vm.minDivisibleUnitSqm) extraChips.push(`Teilbar ab ${vm.minDivisibleUnitSqm} m²`) + if (topRisk) extraChips.push(topRisk.level === 'CRITICAL' ? 'Kritisch' : 'Hohes Risiko') + const visibleExtra = extraChips.slice(0, 2) + const hiddenExtra = extraChips.slice(2) return ( {/* ── Image strip ── */} - + {imageUrl ? ( ) : ( - + )} {/* Score badge */} - + {vm.matchScore}% - {/* Asset type label */} {overlayTypeLabel && ( - + {overlayTypeLabel} @@ -92,149 +103,113 @@ export const MatchCardCompact = memo(function MatchCardCompact({ vm, imageUrl, o {/* ── Content ── */} - + - {/* Alerts */} {vm.disclaimer && ( {vm.disclaimer} )} - {/* Badges row */} - - - - - {vm.resultType === 'VERIFIED_PORTFOLIO' && isPortfolioOwner && ( - } label="Ihr Objekt" size="small" - sx={{ bgcolor: 'rgba(30,58,95,0.10)', color: '#1e3a5f', fontWeight: 700, fontSize: 9, height: 18, '& .MuiChip-icon': { ml: 0.5 } }} /> - )} - - {vm.availabilityLabel && ( - - )} - {vm.fitOutLabel && ( - - )} - {vm.fitOutInvestment && !vm.fitOutInvestment.isFullyCovered && ( - 100000 ? '#d97706' : '#94a3b8', - color: vm.fitOutInvestment.netTotal.max > 100000 ? '#92400e' : '#64748b', - }} - /> - )} - {vm.isDivisible && vm.minDivisibleUnitSqm && ( - - )} - {topRisk && ( - - )} - + + + + {vm.resultType === 'VERIFIED_PORTFOLIO' && isPortfolioOwner && ( + } label="Ihr Objekt" size="small" + sx={{ bgcolor: 'rgba(21,38,66,0.10)', color: '#152642', fontWeight: 700, fontSize: 9, height: 18, '& .MuiChip-icon': { ml: 0.5 } }} /> + )} + + {visibleExtra.map((label, i) => ( + + ))} + {hiddenExtra.length > 0 && ( + + + + )} {/* Title + location */} - + {vm.title} {vm.propertySubtitle && ( - + {vm.propertySubtitle} )} - - + + {vm.locationLabel} - {/* Explainability summary */} {vm.explainabilitySummary && ( - {vm.explainabilitySummary} )} - - - {/* Score breakdown */} - {vm.scoreBreakdown && ( - - - - )} - - {/* Reasons — all 3, same style as grid card */} {vm.reasons.length > 0 && ( - - {vm.reasons.slice(0, 3).map((r, i) => ( - - - - - {r.label} - - - {r.explanation} - - + + {vm.reasons.slice(0, 2).map((r, i) => ( + + + + {r.label} + ))} )} - {/* Top tradeoff */} - {vm.tradeoffs.length > 0 && ( - - - - {vm.tradeoffs[0].concern} - - + {vm.scoreFactors.length > 0 && ( + <> + + + + )} + + {topRisk && topRisk.level === 'CRITICAL' && ( + } severity="error" + sx={{ mt: 0.75, py: 0.25, fontSize: '0.72rem' }}> + {topRisk.description} + )} {/* Actions */} - - {vm.actions.map(a => ( - ))} diff --git a/src/components/match-card/MatchCardCompareMini.tsx b/src/components/match-card/MatchCardCompareMini.tsx index d67aa41..e83798e 100644 --- a/src/components/match-card/MatchCardCompareMini.tsx +++ b/src/components/match-card/MatchCardCompareMini.tsx @@ -4,7 +4,7 @@ import { MatchScoreDisplay } from './MatchScoreDisplay' import type { MatchCardViewModel } from './MatchCardViewModel' const RESULT_TYPE_META: Record = { - VERIFIED_PORTFOLIO: { label: 'Verified', color: '#1e3a5f' }, + VERIFIED_PORTFOLIO: { label: 'Verified', color: '#152642' }, MAISON_WORK: { label: 'Maison Work', color: '#0369a1' }, FUTURE_AVAILABILITY: { label: 'Signal', color: '#7c3aed' }, } diff --git a/src/components/match-card/MatchCardHeader.tsx b/src/components/match-card/MatchCardHeader.tsx index 5d4629d..edadd40 100644 --- a/src/components/match-card/MatchCardHeader.tsx +++ b/src/components/match-card/MatchCardHeader.tsx @@ -38,10 +38,10 @@ export function MatchCardHeader({ vm, compact }: Props) { /> {vm.resultType === 'VERIFIED_PORTFOLIO' && isPortfolioOwner && ( } + icon={} label="Ihr Objekt" size="small" - sx={{ bgcolor: 'rgba(30,58,95,0.12)', color: '#1e3a5f', fontWeight: 700, fontSize: 11, '& .MuiChip-icon': { ml: 0.5 } }} + sx={{ bgcolor: 'rgba(30,58,95,0.12)', color: '#152642', fontWeight: 700, fontSize: 11, '& .MuiChip-icon': { ml: 0.5 } }} /> )} {vm.assetType && ( diff --git a/src/components/match-card/ScoreInlineBreakdown.tsx b/src/components/match-card/ScoreInlineBreakdown.tsx index f0e1878..f769b8b 100644 --- a/src/components/match-card/ScoreInlineBreakdown.tsx +++ b/src/components/match-card/ScoreInlineBreakdown.tsx @@ -25,7 +25,7 @@ const HARD_KEYS = new Set(['area', 'location', 'budget', 'timing']) function importanceLabel(weight: number, maxWeight: number): { label: string; color: string } { const ratio = maxWeight > 0 ? weight / maxWeight : 0 - if (ratio >= 0.85) return { label: 'Entscheidend', color: '#1e3a5f' } + if (ratio >= 0.85) return { label: 'Entscheidend', color: '#152642' } if (ratio >= 0.65) return { label: 'Sehr wichtig', color: '#1d4ed8' } if (ratio >= 0.40) return { label: 'Wichtig', color: '#475569' } if (ratio >= 0.20) return { label: 'Wenig wichtig',color: '#94a3b8' } @@ -93,15 +93,15 @@ export function ScoreInlineBreakdown({ scoreBreakdown: sb, allFactors, compact = return ( - + Hart-Kriterien {hardFactors.map((f, i) => )} 0 ? 2 : 1.5 }}> - + {sb.hardMatchScore}/100 × 60% - {hardContrib} Pkt + {hardContrib} Pkt {softFactors.length > 0 && ( diff --git a/src/components/match-card/SignalQualityDots.tsx b/src/components/match-card/SignalQualityDots.tsx index 3b037bf..9d42aea 100644 --- a/src/components/match-card/SignalQualityDots.tsx +++ b/src/components/match-card/SignalQualityDots.tsx @@ -10,7 +10,7 @@ export function SignalQualityDots({ quality }: { quality: 'HIGH' | 'MEDIUM' | 'L return ( {c.dots.map((filled, i) => ( - + ))} {c.label} diff --git a/src/components/match-center/MatchListCard.tsx b/src/components/match-center/MatchListCard.tsx index 6401c4f..006c85e 100644 --- a/src/components/match-center/MatchListCard.tsx +++ b/src/components/match-center/MatchListCard.tsx @@ -74,7 +74,7 @@ export function MatchListCard({ match, property, need, onSelect, onApprove }: Pr size="small" sx={need.isAnonymous ? { bgcolor: '#f5f3ff', color: '#6d28d9', fontSize: 11, height: 20, fontWeight: 600, border: '1px solid #ddd6fe' } - : { bgcolor: '#eff6ff', color: '#1e3a5f', fontSize: 11, height: 20, fontWeight: 500 } + : { bgcolor: '#eff6ff', color: '#152642', fontSize: 11, height: 20, fontWeight: 500 } } /> )} diff --git a/src/components/match-center/MatchStatusBadge.tsx b/src/components/match-center/MatchStatusBadge.tsx index f2afbc6..8fa1d09 100644 --- a/src/components/match-center/MatchStatusBadge.tsx +++ b/src/components/match-center/MatchStatusBadge.tsx @@ -5,7 +5,7 @@ const STATUS_META: Record = { PENDING_REVIEW: { label: 'Ausstehend', color: '#d97706' }, APPROVED: { label: 'Genehmigt', color: '#1a7a4a' }, REJECTED: { label: 'Abgelehnt', color: '#c0392b' }, - SHORTLISTED: { label: 'Shortlist', color: '#1e3a5f' }, + SHORTLISTED: { label: 'Shortlist', color: '#152642' }, } export function MatchStatusBadge({ status }: { status?: MatchStatus }) { diff --git a/src/components/match-detail/ExecutiveSummaryPanel.tsx b/src/components/match-detail/ExecutiveSummaryPanel.tsx index 0be9810..8e85e71 100644 --- a/src/components/match-detail/ExecutiveSummaryPanel.tsx +++ b/src/components/match-detail/ExecutiveSummaryPanel.tsx @@ -13,7 +13,7 @@ export function ExecutiveSummaryPanel({ match }: Props) { const rows: { icon: React.ReactNode; label: string; text: string }[] = [ { - icon: , + icon: , label: 'Gesamteignung', text: match.explainabilitySummary || `${match.matchStrength}-Match mit ${match.matchScore} Punkten`, }, diff --git a/src/components/match-detail/InquiryQuickDialog.tsx b/src/components/match-detail/InquiryQuickDialog.tsx index b268594..a9df729 100644 --- a/src/components/match-detail/InquiryQuickDialog.tsx +++ b/src/components/match-detail/InquiryQuickDialog.tsx @@ -82,7 +82,7 @@ export function InquiryQuickDialog() { size="small" disabled={!message.trim()} startIcon={} - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' } }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' } }} > Anfrage senden diff --git a/src/components/match-detail/MatchDetailHeader.tsx b/src/components/match-detail/MatchDetailHeader.tsx index a6b0beb..656bfa6 100644 --- a/src/components/match-detail/MatchDetailHeader.tsx +++ b/src/components/match-detail/MatchDetailHeader.tsx @@ -6,7 +6,7 @@ import type { Property } from '../../domain/property' import type { FutureSignal } from '../../domain/futureSignal' const RESULT_TYPE_META: Record = { - VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#1e3a5f' }, + VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#152642' }, MAISON_WORK: { label: 'Maison Work', color: '#0369a1' }, FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed' }, } @@ -101,7 +101,7 @@ export function MatchDetailHeader({ match, property, signal, onBack, onCompare, size="small" startIcon={} onClick={onCompare} - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' } }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' } }} > Vergleichen diff --git a/src/components/match-detail/MatchDetailHero.tsx b/src/components/match-detail/MatchDetailHero.tsx index 988d525..67698e7 100644 --- a/src/components/match-detail/MatchDetailHero.tsx +++ b/src/components/match-detail/MatchDetailHero.tsx @@ -46,7 +46,7 @@ export function MatchDetailHero({ {/* Hero: image first, map fallback */} {!isFuture && ( property?.images?.[0] ? ( - + {property.title}} onClick={onCompare} - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' }, textTransform: 'none' }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' }, textTransform: 'none' }} > Vergleichen diff --git a/src/components/match-detail/NextActionsPanel.tsx b/src/components/match-detail/NextActionsPanel.tsx index fb4befe..03ee604 100644 --- a/src/components/match-detail/NextActionsPanel.tsx +++ b/src/components/match-detail/NextActionsPanel.tsx @@ -28,7 +28,7 @@ export function NextActionsPanel({ match, onCompare, onPipeline, onInquire }: Pr variant="contained" startIcon={} onClick={onInquire} - sx={{ justifyContent: 'flex-start', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' } }} + sx={{ justifyContent: 'flex-start', bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' } }} > Anfrage senden diff --git a/src/components/match-detail/scoreBreakdownConstants.ts b/src/components/match-detail/scoreBreakdownConstants.ts index 37971e6..c6b77e7 100644 --- a/src/components/match-detail/scoreBreakdownConstants.ts +++ b/src/components/match-detail/scoreBreakdownConstants.ts @@ -20,7 +20,7 @@ export function factorLabel(criterion: string): string { // Convert normalised weight to 1-5 importance level relative to other factors in the same set export function importanceLabel(weight: number, maxWeight: number): { label: string; color: string } { const ratio = maxWeight > 0 ? weight / maxWeight : 0 - if (ratio >= 0.85) return { label: 'Entscheidend', color: '#1e3a5f' } + if (ratio >= 0.85) return { label: 'Entscheidend', color: '#152642' } if (ratio >= 0.65) return { label: 'Sehr wichtig', color: '#1d4ed8' } if (ratio >= 0.40) return { label: 'Wichtig', color: '#475569' } if (ratio >= 0.20) return { label: 'Wenig wichtig',color: '#94a3b8' } diff --git a/src/components/onboarding/WelcomeDialog.tsx b/src/components/onboarding/WelcomeDialog.tsx index b1f0276..8f28a3a 100644 --- a/src/components/onboarding/WelcomeDialog.tsx +++ b/src/components/onboarding/WelcomeDialog.tsx @@ -41,7 +41,7 @@ function Slide0() { return ( - + Willkommen bei Property Match @@ -74,7 +74,7 @@ function Slide1() { return ( - + Bedarf beschreiben — KI findet Matches diff --git a/src/components/ops/DataCategoryBadgeList.tsx b/src/components/ops/DataCategoryBadgeList.tsx index be25c05..60d0aa2 100644 --- a/src/components/ops/DataCategoryBadgeList.tsx +++ b/src/components/ops/DataCategoryBadgeList.tsx @@ -18,7 +18,7 @@ export function DataCategoryBadgeList({ categories, max }: DataCategoryBadgeList label={cat} sx={{ bgcolor: 'rgba(30,58,95,0.07)', - color: '#1e3a5f', + color: '#152642', border: 'none', fontSize: '0.68rem', }} diff --git a/src/components/ops/MarketSignalDetailPanel.tsx b/src/components/ops/MarketSignalDetailPanel.tsx index 4b1e21b..ff2a451 100644 --- a/src/components/ops/MarketSignalDetailPanel.tsx +++ b/src/components/ops/MarketSignalDetailPanel.tsx @@ -156,7 +156,7 @@ export function MarketSignalDetailPanel({ signal }: MarketSignalDetailPanelProps label={entity.value} sx={{ bgcolor: 'rgba(30,58,95,0.08)', - color: '#1e3a5f', + color: '#152642', border: 'none', fontSize: '0.75rem', cursor: 'default', diff --git a/src/components/ops/MarketSignalFilterBar.tsx b/src/components/ops/MarketSignalFilterBar.tsx index 560e805..3a34d6e 100644 --- a/src/components/ops/MarketSignalFilterBar.tsx +++ b/src/components/ops/MarketSignalFilterBar.tsx @@ -48,10 +48,10 @@ export function MarketSignalFilterBar({ filters, onChange }: MarketSignalFilterB sx={{ fontSize: '0.65rem', height: 20, - bgcolor: active ? '#1e3a5f' : 'transparent', + bgcolor: active ? '#152642' : 'transparent', color: active ? '#fff' : 'text.secondary', border: '1px solid', - borderColor: active ? '#1e3a5f' : 'divider', + borderColor: active ? '#152642' : 'divider', '&:hover': { bgcolor: active ? '#162d4a' : 'rgba(0,0,0,0.04)' }, }} /> diff --git a/src/components/ops/ReviewGatePanel.tsx b/src/components/ops/ReviewGatePanel.tsx index b70ae71..d3b0f90 100644 --- a/src/components/ops/ReviewGatePanel.tsx +++ b/src/components/ops/ReviewGatePanel.tsx @@ -50,7 +50,7 @@ export function ReviewGatePanel({ gate }: ReviewGatePanelProps) { {reviewerNotes.length > 0 && ( - + Reviewer-Informationen {reviewerNotes.map((note, i) => ( diff --git a/src/components/ops/SignalConversionPanel.tsx b/src/components/ops/SignalConversionPanel.tsx index 86834cf..5c96b9d 100644 --- a/src/components/ops/SignalConversionPanel.tsx +++ b/src/components/ops/SignalConversionPanel.tsx @@ -50,7 +50,7 @@ export function SignalConversionPanel({ signal }: SignalConversionPanelProps) { }} > - + In Future Availability überführen diff --git a/src/components/ops/SignalInbox.tsx b/src/components/ops/SignalInbox.tsx index f7a8875..3e6e3a3 100644 --- a/src/components/ops/SignalInbox.tsx +++ b/src/components/ops/SignalInbox.tsx @@ -43,7 +43,7 @@ export function SignalInbox({ diff --git a/src/components/ops/SignalPipelineStepper.tsx b/src/components/ops/SignalPipelineStepper.tsx index bfb400e..31d9d7b 100644 --- a/src/components/ops/SignalPipelineStepper.tsx +++ b/src/components/ops/SignalPipelineStepper.tsx @@ -16,14 +16,14 @@ export function SignalPipelineStepper({ pipelineState }: SignalPipelineStepperPr sx={{ mb: 1, '& .MuiStepIcon-root': { fontSize: '1.6rem', color: '#cbd5e1' }, - '& .MuiStepIcon-root.Mui-active': { color: '#1e3a5f' }, + '& .MuiStepIcon-root.Mui-active': { color: '#152642' }, '& .MuiStepIcon-root.Mui-completed': { color: '#15803d' }, '& .MuiStepLabel-label': { fontSize: '0.68rem', mt: 0.5, color: '#64748b' }, - '& .MuiStepLabel-label.Mui-active': { color: '#1e3a5f', fontWeight: 700 }, + '& .MuiStepLabel-label.Mui-active': { color: '#152642', fontWeight: 700 }, '& .MuiStepLabel-label.Mui-completed': { color: '#15803d' }, - '& .MuiStepConnector-line': { borderTopWidth: 2, borderColor: '#e2e8f0' }, + '& .MuiStepConnector-line': { borderTopWidth: 2, borderColor: '#e8e7e4' }, '& .MuiStepConnector-root.Mui-completed .MuiStepConnector-line': { borderColor: '#15803d' }, - '& .MuiStepConnector-root.Mui-active .MuiStepConnector-line': { borderColor: '#1e3a5f' }, + '& .MuiStepConnector-root.Mui-active .MuiStepConnector-line': { borderColor: '#152642' }, }} > {PIPELINE_STAGE_ORDER.map((stage) => ( diff --git a/src/components/ops/SignalPipelineView.tsx b/src/components/ops/SignalPipelineView.tsx index 6906514..63360c2 100644 --- a/src/components/ops/SignalPipelineView.tsx +++ b/src/components/ops/SignalPipelineView.tsx @@ -123,7 +123,7 @@ export function SignalPipelineView({ signal }: SignalPipelineViewProps) { sx={{ textTransform: 'none', fontSize: '0.8rem', - bgcolor: '#1e3a5f', + bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' }, }} > diff --git a/src/components/ops/SignalToMatchAuditTrail.tsx b/src/components/ops/SignalToMatchAuditTrail.tsx index 3a19dd1..355244e 100644 --- a/src/components/ops/SignalToMatchAuditTrail.tsx +++ b/src/components/ops/SignalToMatchAuditTrail.tsx @@ -10,7 +10,7 @@ function getStageColor(entry: AuditTrailEntry): string { const idx = PIPELINE_STAGE_ORDER.indexOf(entry.stage) if (idx <= 0) return '#94a3b8' if (idx >= 5) return '#15803d' - if (idx >= 3) return '#1e3a5f' + if (idx >= 3) return '#152642' if (idx >= 2) return '#4f46e5' return '#2563eb' } diff --git a/src/components/ops/SourceCard.tsx b/src/components/ops/SourceCard.tsx index 39f31a1..86b69bb 100644 --- a/src/components/ops/SourceCard.tsx +++ b/src/components/ops/SourceCard.tsx @@ -67,7 +67,7 @@ export function SourceCard({ source, selected, onClick }: SourceCardProps) { mt: 0.25, }} > - + - + @@ -166,7 +166,7 @@ export function SourceDetailPanel({ source }: SourceDetailPanelProps) { key={a} size="small" label={a} - sx={{ bgcolor: 'rgba(30,58,95,0.06)', color: '#1e3a5f', border: 'none', fontSize: '0.72rem' }} + sx={{ bgcolor: 'rgba(30,58,95,0.06)', color: '#152642', border: 'none', fontSize: '0.72rem' }} /> ))} @@ -194,7 +194,7 @@ export function SourceDetailPanel({ source }: SourceDetailPanelProps) { startIcon={isRunning ? : } disabled={isRunning} onClick={() => triggerRun(source.id)} - sx={{ bgcolor: '#1e3a5f', textTransform: 'none', fontSize: '0.8rem', '&:hover': { bgcolor: '#162d4a' } }} + sx={{ bgcolor: '#152642', textTransform: 'none', fontSize: '0.8rem', '&:hover': { bgcolor: '#162d4a' } }} > {isRunning ? 'Läuft...' : 'Demo-Run starten'} diff --git a/src/components/ops/SourceList.tsx b/src/components/ops/SourceList.tsx index 8e30956..7206d4b 100644 --- a/src/components/ops/SourceList.tsx +++ b/src/components/ops/SourceList.tsx @@ -40,7 +40,7 @@ export function SourceList({ diff --git a/src/components/pipeline/PipelineCard.tsx b/src/components/pipeline/PipelineCard.tsx index 6454606..dccb4a6 100644 --- a/src/components/pipeline/PipelineCard.tsx +++ b/src/components/pipeline/PipelineCard.tsx @@ -55,7 +55,7 @@ export function DraggableCard({ boxShadow: isDragOverlay ? '0 8px 24px rgba(0,0,0,0.18)' : '0 1px 3px rgba(0,0,0,0.08)', '&:hover': isDragOverlay ? {} : { boxShadow: '0 2px 8px rgba(0,0,0,0.12)', - borderColor: isSelected ? '#1e3a5f' : '#bfdbfe', + borderColor: isSelected ? '#152642' : '#bfdbfe', }, transition: isDragOverlay ? undefined : 'box-shadow 0.15s, border-color 0.15s', userSelect: 'none', @@ -83,7 +83,7 @@ export function DraggableCard({ {item.inquiryId && onChatClick && ( - + @@ -93,7 +93,7 @@ export function DraggableCard({ { e.stopPropagation(); navigate(path) }} - sx={{ p: 0.25, color: '#94a3b8', '&:hover': { color: '#1e3a5f', bgcolor: '#eff6ff' } }} + sx={{ p: 0.25, color: '#94a3b8', '&:hover': { color: '#152642', bgcolor: '#eff6ff' } }} > @@ -154,8 +154,8 @@ export function DraggableCard({ }} sx={{ fontSize: '0.7rem', py: 0.5, textTransform: 'none', fontWeight: 600, - borderColor: '#1e3a5f', color: '#1e3a5f', - '&:hover': { bgcolor: '#eff6ff', borderColor: '#1e3a5f' }, + borderColor: '#152642', color: '#152642', + '&:hover': { bgcolor: '#eff6ff', borderColor: '#152642' }, }} > Zur Konversation @@ -180,7 +180,7 @@ export function DraggableCard({ }} sx={{ fontSize: '0.7rem', py: 0.5, textTransform: 'none', fontWeight: 600, - bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' }, + bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' }, }} > Anfrage senden diff --git a/src/components/results/ResultFilterBar.tsx b/src/components/results/ResultFilterBar.tsx index dfd130e..76e3c54 100644 --- a/src/components/results/ResultFilterBar.tsx +++ b/src/components/results/ResultFilterBar.tsx @@ -16,10 +16,10 @@ interface Props { isPropertyManager?: boolean } -const FILTER_OPTIONS: { value: FilterSource; label: string; color: string }[] = [ - { value: 'ALL', label: 'Alle', color: '#1e3a5f' }, - { value: 'PLATFORM', label: 'Plattform', color: '#1e3a5f' }, - { value: 'MAISON_WORK', label: 'Maison Work', color: '#0369a1' }, +const FILTER_OPTIONS: { value: FilterSource; label: string }[] = [ + { value: 'ALL', label: 'Alle' }, + { value: 'PLATFORM', label: 'Plattform' }, + { value: 'MAISON_WORK', label: 'Maison Work' }, ] const SORT_OPTIONS: { value: SortBy; label: string }[] = [ @@ -28,6 +28,9 @@ const SORT_OPTIONS: { value: SortBy; label: string }[] = [ { value: 'rent', label: 'Mietpreis' }, ] +const ACTIVE_CHIP = { bgcolor: '#152642', color: 'white', border: '1px solid #152642', fontWeight: 600 } +const INACTIVE_CHIP = { bgcolor: 'transparent', color: 'text.secondary', border: '1px solid #e8e7e4', fontWeight: 400 } + export function ResultFilterBar({ filterSource, onFilterChange, @@ -45,7 +48,7 @@ export function ResultFilterBar({ {/* Source filter + toggles */} - {FILTER_OPTIONS.map(({ value, label, color }) => { + {FILTER_OPTIONS.map(({ value, label }) => { const active = filterSource === value return ( onFilterChange(value)} - sx={{ - bgcolor: active ? color : 'transparent', - color: active ? 'white' : 'text.secondary', - border: `1px solid ${active ? color : '#e2e8f0'}`, - fontWeight: active ? 600 : 400, - }} + sx={active ? ACTIVE_CHIP : INACTIVE_CHIP} /> ) })} @@ -67,36 +65,28 @@ export function ResultFilterBar({ - {/* Future Availability toggle */} } + icon={} label="Future Availability" onClick={() => onShowFutureAvailabilityChange(!showFutureAvailability)} sx={{ - bgcolor: showFutureAvailability ? '#f3e8ff' : 'transparent', - color: showFutureAvailability ? '#6d28d9' : 'text.disabled', - border: `1px solid ${showFutureAvailability ? '#c4b5fd' : '#e2e8f0'}`, - fontWeight: showFutureAvailability ? 600 : 400, - textDecoration: showFutureAvailability ? 'none' : 'line-through', + ...(showFutureAvailability ? ACTIVE_CHIP : INACTIVE_CHIP), + bgcolor: showFutureAvailability ? '#152642' : 'transparent', '& .MuiChip-icon': { ml: 0.75 }, }} /> - {/* Eigene Objekte toggle — only for Property Managers */} {isPropertyManager && ( } - label="Eigene Objekte einblenden" + icon={} + label="Eigene Objekte" onClick={() => onShowOwnPropertiesChange?.(!showOwnProperties)} sx={{ - bgcolor: showOwnProperties ? 'rgba(30,58,95,0.10)' : 'transparent', - color: showOwnProperties ? '#1e3a5f' : 'text.disabled', - border: `1px solid ${showOwnProperties ? '#1e3a5f' : '#e2e8f0'}`, - fontWeight: showOwnProperties ? 600 : 400, + ...(showOwnProperties ? ACTIVE_CHIP : INACTIVE_CHIP), '& .MuiChip-icon': { ml: 0.75 }, }} /> @@ -113,11 +103,7 @@ export function ResultFilterBar({ size="small" clickable onClick={() => onSortChange(value)} - sx={{ - bgcolor: sortBy === value ? '#1e3a5f' : 'transparent', - color: sortBy === value ? 'white' : 'text.secondary', - border: `1px solid ${sortBy === value ? '#1e3a5f' : '#e2e8f0'}`, - }} + sx={sortBy === value ? ACTIVE_CHIP : INACTIVE_CHIP} /> ))} diff --git a/src/components/review/ReviewDetailPanel.tsx b/src/components/review/ReviewDetailPanel.tsx index 40a15e4..590b5bc 100644 --- a/src/components/review/ReviewDetailPanel.tsx +++ b/src/components/review/ReviewDetailPanel.tsx @@ -121,7 +121,7 @@ export function ReviewDetailPanel({ sx={{ height: 4, borderRadius: 2, - bgcolor: '#e2e8f0', + bgcolor: '#e8e7e4', '& .MuiLinearProgress-bar': { bgcolor: task.confidenceScore >= 0.7 ? '#1a7a4a' : task.confidenceScore >= 0.5 ? '#d97706' : '#c0392b', }, diff --git a/src/components/review/ReviewEntityTypeBadge.tsx b/src/components/review/ReviewEntityTypeBadge.tsx index 918d64f..13fc9ea 100644 --- a/src/components/review/ReviewEntityTypeBadge.tsx +++ b/src/components/review/ReviewEntityTypeBadge.tsx @@ -3,7 +3,7 @@ import type { ReviewEntityType } from '../../domain/review' const CONFIG: Record = { FUTURE_SIGNAL: { label: 'Zukunftssignal', color: '#7c3aed' }, - MATCH_EXPLANATION: { label: 'Match-Begründung', color: '#1e3a5f' }, + MATCH_EXPLANATION: { label: 'Match-Begründung', color: '#152642' }, LOW_CONFIDENCE_MATCH: { label: 'Niedr. Konfidenz', color: '#ea580c' }, CONTACT_RELEASE: { label: 'Kontaktfreigabe', color: '#0891b2' }, AI_OUTPUT: { label: 'AI-Output', color: '#4f46e5' }, diff --git a/src/components/review/ReviewTaskCard.tsx b/src/components/review/ReviewTaskCard.tsx index 8e5d39b..d9342cd 100644 --- a/src/components/review/ReviewTaskCard.tsx +++ b/src/components/review/ReviewTaskCard.tsx @@ -76,7 +76,7 @@ export function ReviewTaskCard({ task, isSelected, onSelect }: ReviewTaskCardPro sx={{ height: 3, borderRadius: 2, - bgcolor: '#e2e8f0', + bgcolor: '#e8e7e4', '& .MuiLinearProgress-bar': { bgcolor: task.confidenceScore >= 0.7 ? '#1a7a4a' : task.confidenceScore >= 0.5 ? '#d97706' : '#c0392b', }, diff --git a/src/components/scores/ScoreBar.tsx b/src/components/scores/ScoreBar.tsx index f82523b..82fdda8 100644 --- a/src/components/scores/ScoreBar.tsx +++ b/src/components/scores/ScoreBar.tsx @@ -16,7 +16,7 @@ interface ScoreBarProps { } export function ScoreBar({ label, value, weight, color, maxWidth = 200 }: ScoreBarProps) { - const resolved = color ?? COLOR_MAP[matchScoreColor(value)] ?? '#1e3a5f' + const resolved = color ?? COLOR_MAP[matchScoreColor(value)] ?? '#152642' return ( diff --git a/src/components/shared/GenericBadge.tsx b/src/components/shared/GenericBadge.tsx index 1fea6f8..e68087c 100644 --- a/src/components/shared/GenericBadge.tsx +++ b/src/components/shared/GenericBadge.tsx @@ -6,26 +6,15 @@ export type BadgeSemanticVariant = keyof typeof BADGE_COLORS export interface GenericBadgeProps { label: string - /** - * Semantic variant key from BADGE_COLORS (preferred). - * Example: semanticVariant="confidenceHigh" instead of color="#1a7a4a" - */ + /** Semantic variant key from BADGE_COLORS (preferred). */ semanticVariant?: BadgeSemanticVariant - /** - * Raw hex color — use only when no semantic variant fits. - * Prefer semanticVariant to avoid hardcoded colors. - */ + /** Raw hex color — use only when no semantic variant fits. */ color?: string - /** transparent: semi-opaque bg (color + 18% opacity) | solid: filled bg, white text */ - variant?: 'transparent' | 'solid' - /** Adds a 1px border at color 25% opacity (transparent variant only) */ - showBorder?: boolean - /** fontWeight 700 instead of 600 */ + /** outlined: border + text, no bg | solid: filled bg, white text */ + variant?: 'outlined' | 'solid' bold?: boolean - /** Optional lucide / MUI icon element — sized by the caller */ icon?: ReactElement size?: 'small' | 'medium' - /** Wraps the badge in a Tooltip */ tooltip?: string ariaLabel?: string } @@ -34,15 +23,14 @@ export function GenericBadge({ label, semanticVariant, color: colorProp, - variant = 'transparent', - showBorder = false, + variant = 'outlined', bold = false, icon, size = 'small', tooltip, ariaLabel, }: GenericBadgeProps) { - const color = semanticVariant ? BADGE_COLORS[semanticVariant] : (colorProp ?? '#64748b') + const color = semanticVariant ? BADGE_COLORS[semanticVariant] : (colorProp ?? '#9a9a9a') const chip = ( diff --git a/src/components/shared/LocationPreview.tsx b/src/components/shared/LocationPreview.tsx index 1d753ce..1046ef9 100644 --- a/src/components/shared/LocationPreview.tsx +++ b/src/components/shared/LocationPreview.tsx @@ -114,7 +114,7 @@ export function LocationPreview({ imageUrl, lat, lng, address, cityLabel, height '&:hover': { bgcolor: 'white' }, }} > - + diff --git a/src/components/shared/PropertyMap.tsx b/src/components/shared/PropertyMap.tsx index 9d09233..3598ce4 100644 --- a/src/components/shared/PropertyMap.tsx +++ b/src/components/shared/PropertyMap.tsx @@ -111,7 +111,7 @@ export function PropertyMap({ lat, lng, label, height = 260, searchCenterLat, se - + Objekt diff --git a/src/components/shared/ViewToggle.tsx b/src/components/shared/ViewToggle.tsx index 4885a27..17c5b4d 100644 --- a/src/components/shared/ViewToggle.tsx +++ b/src/components/shared/ViewToggle.tsx @@ -17,7 +17,7 @@ export function ViewToggle({ view, onChange }: Props) { borderRadius: 0, width: 32, height: 32, - bgcolor: view === 'list' ? '#1e3a5f' : 'transparent', + bgcolor: view === 'list' ? '#152642' : 'transparent', color: view === 'list' ? 'white' : '#64748b', '&:hover': { bgcolor: view === 'list' ? '#162d4a' : '#f1f5f9' }, }} @@ -33,7 +33,7 @@ export function ViewToggle({ view, onChange }: Props) { borderRadius: 0, width: 32, height: 32, - bgcolor: view === 'grid' ? '#1e3a5f' : 'transparent', + bgcolor: view === 'grid' ? '#152642' : 'transparent', color: view === 'grid' ? 'white' : '#64748b', '&:hover': { bgcolor: view === 'grid' ? '#162d4a' : '#f1f5f9' }, }} diff --git a/src/components/shortlist/AddToPipelineDialog.tsx b/src/components/shortlist/AddToPipelineDialog.tsx index 2ce1de6..40135c3 100644 --- a/src/components/shortlist/AddToPipelineDialog.tsx +++ b/src/components/shortlist/AddToPipelineDialog.tsx @@ -58,7 +58,7 @@ export function AddToPipelineDialog() { onClick={handleConfirm} disabled={isPending} endIcon={isPending ? : undefined} - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' } }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' } }} > Merken diff --git a/src/components/shortlist/AddToShortlistDialog.tsx b/src/components/shortlist/AddToShortlistDialog.tsx index 8ea9258..23ff5e9 100644 --- a/src/components/shortlist/AddToShortlistDialog.tsx +++ b/src/components/shortlist/AddToShortlistDialog.tsx @@ -155,7 +155,7 @@ export function AddToShortlistDialog() { onClick={handleConfirm} disabled={!canConfirm} endIcon={isBusy ? : undefined} - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' } }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' } }} > Hinzufügen diff --git a/src/components/shortlist/DecisionBriefDraftPanel.tsx b/src/components/shortlist/DecisionBriefDraftPanel.tsx index 4c736fb..06c1e4d 100644 --- a/src/components/shortlist/DecisionBriefDraftPanel.tsx +++ b/src/components/shortlist/DecisionBriefDraftPanel.tsx @@ -80,7 +80,7 @@ export function DecisionBriefDraftPanel({ shortlistId }: Props) { size="small" startIcon={} onClick={handleGenerate} - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' } }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' } }} > Brief generieren diff --git a/src/components/shortlist/ShortlistCard.tsx b/src/components/shortlist/ShortlistCard.tsx index 577d12e..f0efa96 100644 --- a/src/components/shortlist/ShortlistCard.tsx +++ b/src/components/shortlist/ShortlistCard.tsx @@ -32,7 +32,7 @@ export function ShortlistCard({ shortlist }: Props) { diff --git a/src/components/shortlist/ShortlistDetail.tsx b/src/components/shortlist/ShortlistDetail.tsx index 2f8f4ad..31808d0 100644 --- a/src/components/shortlist/ShortlistDetail.tsx +++ b/src/components/shortlist/ShortlistDetail.tsx @@ -181,7 +181,7 @@ export function ShortlistDetail({ shortlist }: Props) { { setView('list'); localStorage.setItem('view-shortlist', 'list') }} - sx={{ color: view === 'list' ? '#1e3a5f' : '#94a3b8', bgcolor: view === 'list' ? '#eff6ff' : 'transparent' }} + sx={{ color: view === 'list' ? '#152642' : '#94a3b8', bgcolor: view === 'list' ? '#eff6ff' : 'transparent' }} > @@ -190,7 +190,7 @@ export function ShortlistDetail({ shortlist }: Props) { { setView('grid'); localStorage.setItem('view-shortlist', 'grid') }} - sx={{ color: view === 'grid' ? '#1e3a5f' : '#94a3b8', bgcolor: view === 'grid' ? '#eff6ff' : 'transparent' }} + sx={{ color: view === 'grid' ? '#152642' : '#94a3b8', bgcolor: view === 'grid' ? '#eff6ff' : 'transparent' }} > diff --git a/src/components/shortlist/ShortlistItemCard.tsx b/src/components/shortlist/ShortlistItemCard.tsx index fdc7cee..b6bd112 100644 --- a/src/components/shortlist/ShortlistItemCard.tsx +++ b/src/components/shortlist/ShortlistItemCard.tsx @@ -103,7 +103,7 @@ export function ShortlistItemCard({ item, shortlistId, isFinalized, view = 'list variant="contained" onClick={handleSendInquiry} disabled={!inquiryMsg.trim()} - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#1a3050' } }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#1a3050' } }} > Anfrage senden @@ -141,7 +141,7 @@ export function ShortlistItemCard({ item, shortlistId, isFinalized, view = 'list { e.stopPropagation(); setInquiryOpen(true) }} - sx={{ color: '#94a3b8', '&:hover': { color: '#1e3a5f' } }} + sx={{ color: '#94a3b8', '&:hover': { color: '#152642' } }} > @@ -192,7 +192,7 @@ export function ShortlistItemCard({ item, shortlistId, isFinalized, view = 'list > - {item.title} + {item.title} diff --git a/src/components/shortlist/ShortlistList.tsx b/src/components/shortlist/ShortlistList.tsx index 0251c7a..3a8bbb5 100644 --- a/src/components/shortlist/ShortlistList.tsx +++ b/src/components/shortlist/ShortlistList.tsx @@ -70,7 +70,7 @@ export function ShortlistList({ shortlists, isLoading }: Props) { {createShortlist.isPending ? : ( - ) @@ -82,7 +82,7 @@ export function ShortlistList({ shortlists, isLoading }: Props) { size="small" startIcon={} onClick={() => setCreating(true)} - sx={{ color: '#1e3a5f', justifyContent: 'flex-start' }} + sx={{ color: '#152642', justifyContent: 'flex-start' }} > Neue Shortlist diff --git a/src/components/shortlist/ShortlistStatusBadge.tsx b/src/components/shortlist/ShortlistStatusBadge.tsx index 5ef6ab7..2e1fb33 100644 --- a/src/components/shortlist/ShortlistStatusBadge.tsx +++ b/src/components/shortlist/ShortlistStatusBadge.tsx @@ -6,7 +6,7 @@ const STATUS_META: Record = { REVIEW_READY: { label: 'Prüfbereit', color: '#d97706' }, FINALIZED: { label: 'Finalisiert', color: '#1a7a4a' }, ARCHIVED: { label: 'Archiviert', color: '#475569' }, - ACTIVE: { label: 'Aktiv', color: '#1e3a5f' }, + ACTIVE: { label: 'Aktiv', color: '#152642' }, SHARED: { label: 'Geteilt', color: '#7c3aed' }, CONVERTED: { label: 'Konvertiert', color: '#0891b2' }, } diff --git a/src/components/supply/BerichtDialog.tsx b/src/components/supply/BerichtDialog.tsx index bb59842..039e7de 100644 --- a/src/components/supply/BerichtDialog.tsx +++ b/src/components/supply/BerichtDialog.tsx @@ -5,7 +5,7 @@ import type { MarketReport } from '../../domain/marketReport' import { DS_TEXT, DS_BG, DS_BORDER, DS_SURFACE } from '../../lib/ds' const SECTION_COLORS: Record = { - development: '#1e3a5f', + development: '#152642', demand: '#1a7a4a', supply: '#b45309', negotiation: '#7c3aed', diff --git a/src/components/supply/MarketKpiPanel.tsx b/src/components/supply/MarketKpiPanel.tsx index dfb4555..464f33e 100644 --- a/src/components/supply/MarketKpiPanel.tsx +++ b/src/components/supply/MarketKpiPanel.tsx @@ -147,10 +147,10 @@ export function MarketKpiPanel({ baseKpis, assetTypeLabel, radius, onRadiusChang border: '1px solid #e2e8f0', color: '#64748b', '&.Mui-selected': { - bgcolor: '#1e3a5f', + bgcolor: '#152642', color: '#ffffff', - borderColor: '#1e3a5f', - '&:hover': { bgcolor: '#1e3a5f' }, + borderColor: '#152642', + '&:hover': { bgcolor: '#152642' }, }, }, }} diff --git a/src/components/supply/PropertyActivityLogPanel.tsx b/src/components/supply/PropertyActivityLogPanel.tsx index f584cde..e36b5ee 100644 --- a/src/components/supply/PropertyActivityLogPanel.tsx +++ b/src/components/supply/PropertyActivityLogPanel.tsx @@ -43,8 +43,8 @@ const EVENT_LABELS: Record = { } const EVENT_COLORS: Record = { - PROPERTY_CREATED: '#1e3a5f', - PROPERTY_UPDATED: '#1e3a5f', + PROPERTY_CREATED: '#152642', + PROPERTY_UPDATED: '#152642', MATCH_APPROVED: '#1a7a4a', MATCH_REJECTED: '#c0392b', SIGNAL_VERIFIED: '#7c3aed', @@ -68,7 +68,7 @@ const CATEGORY_META: Record MATCHING: { label: 'Matching', color: '#1a7a4a' }, INTELLIGENCE: { label: 'Intelligence', color: '#7c3aed' }, REVIEW: { label: 'Review', color: '#d97706' }, - GOVERNANCE: { label: 'Governance', color: '#1e3a5f' }, + GOVERNANCE: { label: 'Governance', color: '#152642' }, } function getEventIcon(type: ActivityEventType) { diff --git a/src/components/supply/PropertyDetailOverview.tsx b/src/components/supply/PropertyDetailOverview.tsx index c677590..483c720 100644 --- a/src/components/supply/PropertyDetailOverview.tsx +++ b/src/components/supply/PropertyDetailOverview.tsx @@ -138,8 +138,8 @@ export function PropertyDetailOverview({ p, editing, draft, onDraftChange }: Pro {/* Mietvertrag */} - - + + Mietvertrag @@ -155,7 +155,7 @@ export function PropertyDetailOverview({ p, editing, draft, onDraftChange }: Pro href={p.leaseContractUrl} target="_blank" rel="noopener noreferrer" - sx={{ textTransform: 'none', fontSize: '0.7rem', py: 0.25, px: 1, borderColor: '#cbd5e1', color: '#1e3a5f', whiteSpace: 'nowrap', flexShrink: 0 }} + sx={{ textTransform: 'none', fontSize: '0.7rem', py: 0.25, px: 1, borderColor: '#cbd5e1', color: '#152642', whiteSpace: 'nowrap', flexShrink: 0 }} > Öffnen diff --git a/src/components/supply/PropertyDetailView.tsx b/src/components/supply/PropertyDetailView.tsx index 1a57b70..0651371 100644 --- a/src/components/supply/PropertyDetailView.tsx +++ b/src/components/supply/PropertyDetailView.tsx @@ -110,7 +110,7 @@ export function PropertyDetailView({ propertyId, onClose, hideTabs = [] }: Prope startIcon={saving ? : } onClick={saveEdit} disabled={saving} - sx={{ textTransform: 'none', fontSize: '0.75rem', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }} + sx={{ textTransform: 'none', fontSize: '0.75rem', bgcolor: '#152642', '&:hover': { bgcolor: '#16304d' } }} > Speichern diff --git a/src/components/supply/PropertyFilterBar.tsx b/src/components/supply/PropertyFilterBar.tsx index 2d68893..ffcae6a 100644 --- a/src/components/supply/PropertyFilterBar.tsx +++ b/src/components/supply/PropertyFilterBar.tsx @@ -79,7 +79,7 @@ export function PropertyFilterBar({ filters, onFiltersChange }: PropertyFilterBa onClick={() => toggleAssetType(t)} sx={{ cursor: 'pointer', - ...(selectedAssetTypes.includes(t) && { bgcolor: '#1e3a5f', color: 'white', borderColor: '#1e3a5f' }), + ...(selectedAssetTypes.includes(t) && { bgcolor: '#152642', color: 'white', borderColor: '#152642' }), }} /> ))} diff --git a/src/components/supply/PropertyIntelligenceCard.tsx b/src/components/supply/PropertyIntelligenceCard.tsx index f108913..978a19c 100644 --- a/src/components/supply/PropertyIntelligenceCard.tsx +++ b/src/components/supply/PropertyIntelligenceCard.tsx @@ -2,6 +2,7 @@ import { memo } from 'react' import { Box, Chip, LinearProgress, Tooltip, Typography } from '@mui/material' import { MapPin, Maximize2, TrendingUp, Calendar } from 'lucide-react' import { getAssetTypeColor, getAssetTypeLabel, getAvailabilityLabel } from './propertyHelpers' +import { DS_TEXT, DS_BORDER } from '../../lib/ds' import type { Property } from '../../domain/property' interface Props { @@ -10,14 +11,13 @@ interface Props { } function availabilityColor(status: string) { - if (status === 'AVAILABLE_NOW') return { bg: '#dcfce7', text: '#15803d' } + if (status === 'AVAILABLE_NOW') return { bg: '#dcfce7', text: '#1d6342' } if (status === 'AVAILABLE_SOON') return { bg: '#fef9c3', text: '#a16207' } - return { bg: '#f1f5f9', text: '#64748b' } + return { bg: '#f4f3f0', text: '#9a9a9a' } } export const PropertyIntelligenceCard = memo(function PropertyIntelligenceCard({ property: p, onSelect }: Props) { const confPct = Math.round(p.confidenceScore * 100) - const confColor = p.confidenceScore >= 0.75 ? '#15803d' : p.confidenceScore >= 0.55 ? '#d97706' : '#dc2626' const annualRent = Math.round(p.areaSqm * p.rentPricePerSqm / 1000) const avail = availabilityColor(p.availabilityStatus) const assetColor = getAssetTypeColor(p.assetType) @@ -29,22 +29,18 @@ export const PropertyIntelligenceCard = memo(function PropertyIntelligenceCard({ sx={{ borderRadius: 2, overflow: 'hidden', - border: '1px solid #e2e8f0', + border: `1px solid ${DS_BORDER.default}`, bgcolor: 'white', - boxShadow: '0 1px 4px rgba(0,0,0,0.06)', + boxShadow: '0 1px 3px rgba(0,0,0,0.06)', display: 'flex', flexDirection: 'column', cursor: 'pointer', - transition: 'box-shadow 0.15s, transform 0.12s', - '&:hover': { - boxShadow: '0 8px 24px rgba(0,0,0,0.12)', - transform: 'translateY(-2px)', - borderColor: '#cbd5e1', - }, + transition: 'border-color 0.15s', + '&:hover': { borderColor: '#c8c7c4' }, }} > {/* ── Image / header ── */} - + {hasImage ? ( )} - {/* Overlay gradient */} {/* Top badges */} @@ -85,11 +80,15 @@ export const PropertyIntelligenceCard = memo(function PropertyIntelligenceCard({ Jahresmiete ca. - + CHF {annualRent}k - + {p.rentPricePerSqm} /m²/J @@ -100,12 +99,12 @@ export const PropertyIntelligenceCard = memo(function PropertyIntelligenceCard({ {/* Title + location */} - + {p.title} - - + + {p.location.city}{p.location.district ? ` · ${p.location.district}` : ''} @@ -113,65 +112,61 @@ export const PropertyIntelligenceCard = memo(function PropertyIntelligenceCard({ {/* Key metrics row */} - + - - Fläche + + Fläche - + {p.areaSqm.toLocaleString('de-CH')} m² {p.availabilityDate && ( - + - - Ab + + Ab - + {new Date(p.availabilityDate).toLocaleDateString('de-CH', { month: 'short', year: '2-digit' })} )} {p.softFactors?.prestige != null && ( - + - - Prestige + + Prestige - + {p.softFactors.prestige} )} - {/* Soft factor chips */} + {/* Chips: fit-out + contract */} {p.softFactors && ( - {p.softFactors.publicTransportMinutes != null && ( - - )} {p.hardFacts?.fitOut && (() => { const FIT_OUT_META: Record = { - SHELL: { label: 'Rohbau', tip: 'Keine Einbauten — volle Ausbauinvestition durch Mieter erforderlich (Böden, Decken, Trennwände, TGA).' }, - BASIC: { label: 'Grundausbau', tip: 'Grundinfrastruktur vorhanden (Böden, Beleuchtung, WCs). Ausbau für Büro/Betrieb noch nötig.' }, - FULL: { label: 'Vollausbau', tip: 'Bezugsfertig ausgebaut — keine Ausbauinvestition nötig. Direkt einzugsbereit.' }, - PREMIUM: { label: 'Premium-Ausbau', tip: 'Hochwertig und repräsentativ ausgebaut. Sofort bezugsfertig ohne weiteren Ausbau.' }, + SHELL: { label: 'Rohbau', tip: 'Keine Einbauten — volle Ausbauinvestition durch Mieter.' }, + BASIC: { label: 'Grundausbau', tip: 'Grundinfrastruktur vorhanden, Ausbau noch nötig.' }, + FULL: { label: 'Vollausbau', tip: 'Bezugsfertig ausgebaut.' }, + PREMIUM: { label: 'Premium-Ausbau', tip: 'Hochwertig ausgebaut, sofort bezugsfertig.' }, } const meta = FIT_OUT_META[p.hardFacts!.fitOut!] ?? { label: p.hardFacts!.fitOut!, tip: '' } return ( + sx={{ height: 20, fontSize: '0.65rem', bgcolor: '#f4f3f0', color: DS_TEXT.secondary, cursor: 'help', border: `1px solid ${DS_BORDER.default}` }} /> ) })()} {p.contractDurationMonths && ( - + + sx={{ height: 20, fontSize: '0.65rem', bgcolor: '#f4f3f0', color: DS_TEXT.secondary, cursor: 'help', border: `1px solid ${DS_BORDER.default}` }} /> )} @@ -180,15 +175,15 @@ export const PropertyIntelligenceCard = memo(function PropertyIntelligenceCard({ {/* Confidence bar */} - Datenkonfidenz - {confPct}% + Datenkonfidenz + = 0.75 ? '#b8975a' : DS_TEXT.muted }}>{confPct}% diff --git a/src/components/supply/PropertyMarketSignalsTab.tsx b/src/components/supply/PropertyMarketSignalsTab.tsx index aa266ca..d13850e 100644 --- a/src/components/supply/PropertyMarketSignalsTab.tsx +++ b/src/components/supply/PropertyMarketSignalsTab.tsx @@ -12,7 +12,7 @@ interface Props { } const STATIC_SECTIONS: { category: SignalCategory; label: string; icon: React.ReactNode; color: string }[] = [ - { category: 'development', label: 'Entwicklungsnews', icon: , color: '#1e3a5f' }, + { category: 'development', label: 'Entwicklungsnews', icon: , color: '#152642' }, { category: 'negotiation', label: 'Verhandlungshinweise', icon: , color: '#7c3aed' }, ] diff --git a/src/components/supply/ReminderDetailDrawer.tsx b/src/components/supply/ReminderDetailDrawer.tsx index 6f87fa2..618d0d8 100644 --- a/src/components/supply/ReminderDetailDrawer.tsx +++ b/src/components/supply/ReminderDetailDrawer.tsx @@ -105,7 +105,7 @@ export function ReminderDetailDrawer() { Fläche: {reminder.areaSqm.toLocaleString('de-CH')} m² {reminderProperty?.leaseContractUrl ? ( - + diff --git a/src/components/supply/ReminderFeed.tsx b/src/components/supply/ReminderFeed.tsx index de57ed1..9d5c9f7 100644 --- a/src/components/supply/ReminderFeed.tsx +++ b/src/components/supply/ReminderFeed.tsx @@ -25,7 +25,7 @@ const HORIZON_CONFIG: { key: FilterHorizon; label: string; color: string; bg: st { key: 'OVERDUE', label: 'Überfällig', color: '#dc2626', bg: '#fef2f2', border: '#fecaca' }, { key: 'THIS_WEEK', label: 'Diese Woche', color: '#ea580c', bg: '#fff7ed', border: '#fed7aa' }, { key: 'THIS_MONTH', label: 'Dieser Monat', color: '#0369a1', bg: '#f0f9ff', border: '#bae6fd' }, - { key: 'LATER', label: 'Später', color: '#64748b', bg: '#f8fafc', border: '#e2e8f0' }, + { key: 'LATER', label: 'Später', color: '#64748b', bg: '#f8fafc', border: '#e8e7e4' }, ] const LIST_COLS = '100px 130px 1fr 140px 90px 100px 90px' diff --git a/src/components/supply/ReminderTypeBadge.tsx b/src/components/supply/ReminderTypeBadge.tsx index 5fcbe03..3d38760 100644 --- a/src/components/supply/ReminderTypeBadge.tsx +++ b/src/components/supply/ReminderTypeBadge.tsx @@ -13,7 +13,7 @@ import type { LucideIcon } from 'lucide-react' import type { ReminderType } from '../../domain/reminder' const CONFIG: Record = { - LEASE_EXPIRY: { Icon: FileText, label: 'Mietablauf', color: '#1e3a5f' }, + LEASE_EXPIRY: { Icon: FileText, label: 'Mietablauf', color: '#152642' }, BREAK_OPTION: { Icon: ArrowRightLeft, label: 'Break-Option', color: '#7c3aed' }, RENT_REVIEW: { Icon: TrendingUp, label: 'Mietanpassung', color: '#0369a1' }, INSPECTION: { Icon: ClipboardCheck, label: 'Inspektion', color: '#065f46' }, diff --git a/src/components/supply/SignalCard.tsx b/src/components/supply/SignalCard.tsx index 17112e2..62ad3d3 100644 --- a/src/components/supply/SignalCard.tsx +++ b/src/components/supply/SignalCard.tsx @@ -42,7 +42,7 @@ export function SignalCard({ signal }: { signal: PropertyMarketSignal }) { sx={{ height: 3, borderRadius: 2, - bgcolor: '#e2e8f0', + bgcolor: '#e8e7e4', '& .MuiLinearProgress-bar': { bgcolor: signal.confidence >= 0.8 ? '#1a7a4a' : signal.confidence >= 0.6 ? '#d97706' : '#64748b' }, }} /> @@ -66,7 +66,7 @@ export function SignalCard({ signal }: { signal: PropertyMarketSignal }) { alignItems: 'center', gap: 0.375, fontSize: '0.62rem', - color: signal.sourceUrl ? '#1e3a5f' : '#94a3b8', + color: signal.sourceUrl ? '#152642' : '#94a3b8', textDecoration: 'none', '&:hover': signal.sourceUrl ? { textDecoration: 'underline' } : {}, }} diff --git a/src/components/supply/propertyHelpers.ts b/src/components/supply/propertyHelpers.ts index 9270876..01e763e 100644 --- a/src/components/supply/propertyHelpers.ts +++ b/src/components/supply/propertyHelpers.ts @@ -18,7 +18,7 @@ export function getAssetTypeLabel(type: AssetType): string { export function getAssetTypeColor(type: AssetType): string { const colors: Record = { - OFFICE: '#1e3a5f', + OFFICE: '#152642', LOGISTICS: '#d97706', RETAIL: '#7c3aed', GASTRO: '#0d9488', diff --git a/src/components/supply/reminderDetailHelpers.tsx b/src/components/supply/reminderDetailHelpers.tsx index df454ff..3d61258 100644 --- a/src/components/supply/reminderDetailHelpers.tsx +++ b/src/components/supply/reminderDetailHelpers.tsx @@ -63,7 +63,7 @@ export function ActivityEntry({ entry }: { entry: ReminderActivity }) { - + diff --git a/src/components/tables/TableToolbar.tsx b/src/components/tables/TableToolbar.tsx index c7e2e7b..8b86ac2 100644 --- a/src/components/tables/TableToolbar.tsx +++ b/src/components/tables/TableToolbar.tsx @@ -36,7 +36,7 @@ export function TableToolbar({ title, count, filterSlot, actions, sx }: TableToo )} {filterSlot} diff --git a/src/components/ui/DecisionContextPanel.tsx b/src/components/ui/DecisionContextPanel.tsx index aa37e53..e215dd9 100644 --- a/src/components/ui/DecisionContextPanel.tsx +++ b/src/components/ui/DecisionContextPanel.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { Alert, Box, Button, Chip, Collapse, IconButton, Typography } from '@mui/material' +import { Alert, Box, Button, Collapse, IconButton, Typography } from '@mui/material' import { AlertTriangle, ChevronDown, ChevronUp, Target } from 'lucide-react' import type { ReactNode } from 'react' @@ -32,16 +32,16 @@ interface Props { children?: ReactNode } -const SEVERITY_COLOR: Record, string> = { - neutral: '#f1f5f9', - positive: '#f0fdf4', - warning: '#fef9c3', - critical: '#fef2f2', +const SEVERITY_DOT: Record, string> = { + neutral: '#9a9a9a', + positive: '#1d6342', + warning: '#a16207', + critical: '#b91c1c', } const SEVERITY_TEXT: Record, string> = { - neutral: '#475569', - positive: '#1a7a4a', + neutral: '#5c5c5c', + positive: '#1d6342', warning: '#92400e', critical: '#991b1b', } @@ -61,9 +61,8 @@ export function DecisionContextPanel({ return ( {/* Decision question */} - + - + {decision} {context && ( @@ -88,22 +87,16 @@ export function DecisionContextPanel({ {/* Metric chips */} {metrics.length > 0 && ( - + {metrics.map((m, i) => { const sev = m.severity ?? 'neutral' return ( - + + + + {m.value} {m.label} + + ) })} @@ -118,7 +111,7 @@ export function DecisionContextPanel({ variant={a.primary ? 'contained' : 'outlined'} onClick={a.onClick} sx={a.primary - ? { bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' }, textTransform: 'none', fontSize: '0.75rem', py: 0.375, px: 1.25 } + ? { bgcolor: '#152642', '&:hover': { bgcolor: '#0e1c30' }, textTransform: 'none', fontSize: '0.75rem', py: 0.375, px: 1.25 } : { textTransform: 'none', fontSize: '0.75rem', py: 0.375, px: 1.25 } } > diff --git a/src/index.css b/src/index.css index a5002d3..13edea7 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=Inter:wght@300;400;500;600;700&display=swap'); + @layer theme, base, components, utilities; @import "tailwindcss/theme.css" layer(theme); diff --git a/src/lib/ds.ts b/src/lib/ds.ts index 6932b31..ba4944f 100644 --- a/src/lib/ds.ts +++ b/src/lib/ds.ts @@ -2,129 +2,124 @@ import type { ConfidenceLevel, DataQualityLevel } from '../domain/enums' import { CONF_HIGH, CONF_MEDIUM, DQ_HIGH, DQ_MEDIUM } from './constants' // ── Semantic color tokens ───────────────────────────────────────────────────── -// Single source of truth for badge/score colors. All badge components read from here. export const DS_COLORS = { resultType: { - VERIFIED_PORTFOLIO: { bg: 'rgba(30,58,95,0.10)', fg: '#1e3a5f' }, - MAISON_WORK: { bg: 'rgba(3,105,161,0.10)', fg: '#0369a1' }, - FUTURE_AVAILABILITY: { bg: 'rgba(109,40,217,0.10)', fg: '#6d28d9' }, + VERIFIED_PORTFOLIO: { bg: 'rgba(21,38,66,0.10)', fg: '#152642' }, + MAISON_WORK: { bg: 'rgba(45,90,138,0.10)', fg: '#2d5a8a' }, + FUTURE_AVAILABILITY: { bg: 'rgba(91,63,138,0.10)', fg: '#5b3f8a' }, }, confidence: { - VERY_HIGH: { bg: 'rgba(26,122,74,0.12)', fg: '#1a7a4a' }, - HIGH: { bg: 'rgba(26,122,74,0.09)', fg: '#1a7a4a' }, - MEDIUM: { bg: 'rgba(30,58,95,0.10)', fg: '#1e3a5f' }, - LOW: { bg: 'rgba(217,119,6,0.12)', fg: '#d97706' }, - VERY_LOW: { bg: 'rgba(192,57,43,0.12)', fg: '#c0392b' }, + VERY_HIGH: { bg: 'rgba(29,99,66,0.12)', fg: '#1d6342' }, + HIGH: { bg: 'rgba(29,99,66,0.09)', fg: '#1d6342' }, + MEDIUM: { bg: 'rgba(21,38,66,0.10)', fg: '#152642' }, + LOW: { bg: 'rgba(161,98,7,0.12)', fg: '#a16207' }, + VERY_LOW: { bg: 'rgba(185,28,28,0.12)', fg: '#b91c1c' }, }, risk: { - LOW: { bg: 'rgba(26,122,74,0.10)', fg: '#1a7a4a' }, - MEDIUM: { bg: 'rgba(217,119,6,0.10)', fg: '#d97706' }, - HIGH: { bg: 'rgba(192,57,43,0.10)', fg: '#c0392b' }, - CRITICAL: { bg: 'rgba(127,0,0,0.12)', fg: '#7f1d1d' }, + LOW: { bg: 'rgba(29,99,66,0.10)', fg: '#1d6342' }, + MEDIUM: { bg: 'rgba(161,98,7,0.10)', fg: '#a16207' }, + HIGH: { bg: 'rgba(185,28,28,0.10)', fg: '#b91c1c' }, + CRITICAL: { bg: 'rgba(127,0,0,0.12)', fg: '#7f1d1d' }, }, availability: { - AVAILABLE_NOW: { bg: 'rgba(26,122,74,0.10)', fg: '#1a7a4a' }, - AVAILABLE_SOON: { bg: 'rgba(217,119,6,0.10)', fg: '#d97706' }, - FUTURE_SIGNAL: { bg: 'rgba(109,40,217,0.10)', fg: '#6d28d9' }, + AVAILABLE_NOW: { bg: 'rgba(29,99,66,0.10)', fg: '#1d6342' }, + AVAILABLE_SOON: { bg: 'rgba(161,98,7,0.10)', fg: '#a16207' }, + FUTURE_SIGNAL: { bg: 'rgba(91,63,138,0.10)', fg: '#5b3f8a' }, OCCUPIED: { bg: 'rgba(100,116,139,0.10)', fg: '#475569' }, - UNKNOWN: { bg: '#f1f5f9', fg: '#94a3b8' }, + UNKNOWN: { bg: '#f4f3f0', fg: '#9a9a9a' }, }, freshness: { - FRESH: { bg: 'rgba(26,122,74,0.10)', fg: '#1a7a4a' }, - STALE: { bg: 'rgba(217,119,6,0.10)', fg: '#d97706' }, - OUTDATED: { bg: 'rgba(192,57,43,0.10)', fg: '#c0392b' }, + FRESH: { bg: 'rgba(29,99,66,0.10)', fg: '#1d6342' }, + STALE: { bg: 'rgba(161,98,7,0.10)', fg: '#a16207' }, + OUTDATED: { bg: 'rgba(185,28,28,0.10)', fg: '#b91c1c' }, }, dataQuality: { - HIGH: { bg: 'rgba(26,122,74,0.10)', fg: '#1a7a4a' }, - MEDIUM: { bg: 'rgba(217,119,6,0.10)', fg: '#d97706' }, - LOW: { bg: 'rgba(192,57,43,0.10)', fg: '#c0392b' }, - INCOMPLETE: { bg: 'rgba(127,0,0,0.12)', fg: '#7f1d1d' }, + HIGH: { bg: 'rgba(29,99,66,0.10)', fg: '#1d6342' }, + MEDIUM: { bg: 'rgba(161,98,7,0.10)', fg: '#a16207' }, + LOW: { bg: 'rgba(185,28,28,0.10)', fg: '#b91c1c' }, + INCOMPLETE: { bg: 'rgba(127,0,0,0.12)', fg: '#7f1d1d' }, }, heat: { - VERY_HOT: { bg: '#fef3c7', border: '#fcd34d', icon: '#d97706', text: '#92400e' }, - HOT: { bg: '#fff7ed', border: '#fed7aa', icon: '#ea580c', text: '#c2410c' }, + VERY_HOT: { bg: '#fef3c7', border: '#fcd34d', icon: '#a16207', text: '#92400e' }, + HOT: { bg: '#fff7ed', border: '#fed7aa', icon: '#a16207', text: '#92400e' }, }, futureCard: { controlled: { - accent: '#7c3aed', - accentHover: '#6d28d9', - headerBg: '#faf5ff', + accent: '#5b3f8a', + accentHover: '#4a3070', + headerBg: '#faf7ff', border: '#e9d5ff', badgeBg: '#ede9fe', badgeText: '#5b21b6', }, signal: { - accent: '#1d4ed8', - accentHover: '#1e40af', - headerBg: '#eff6ff', + accent: '#2d5a8a', + accentHover: '#1e4068', + headerBg: '#f0f6ff', border: '#bfdbfe', badgeBg: '#dbeafe', badgeText: '#1e40af', - indicator: '#1d4ed8', + indicator: '#2d5a8a', }, }, } as const // ── Text color tokens ───────────────────────────────────────────────────────── -// For sx={{ color: DS_TEXT.secondary }} instead of hardcoded '#475569' export const DS_TEXT = { - primary: '#1e293b', - secondary: '#475569', - muted: '#64748b', - disabled: '#94a3b8', + primary: '#141414', + secondary: '#5c5c5c', + muted: '#9a9a9a', + disabled: '#c4c4c4', inverted: '#ffffff', - success: '#1a7a4a', - warning: '#d97706', - error: '#c0392b', - danger: '#dc2626', - info: '#0369a1', - brand: '#1e3a5f', - signal: '#6d28d9', - // Dark variants — use for text on light-tinted surface boxes + success: '#1d6342', + warning: '#a16207', + error: '#b91c1c', + danger: '#b91c1c', + info: '#2d5a8a', + brand: '#152642', + signal: '#5b3f8a', successDark: '#166534', warningDark: '#92400e', signalDark: '#4c1d95', infoDark: '#0c4a6e', - brandDark: '#162d4a', + brandDark: '#0e1c30', } as const // ── Background tokens ───────────────────────────────────────────────────────── export const DS_BG = { - page: '#f8fafc', + page: '#f9f8f6', surface: '#ffffff', - subtle: '#f1f5f9', - muted: '#e2e8f0', + subtle: '#f4f3f0', + muted: '#e8e7e4', } as const // ── Border tokens ───────────────────────────────────────────────────────────── export const DS_BORDER = { - default: '#e2e8f0', - muted: '#f1f5f9', - strong: '#cbd5e1', + default: '#e8e7e4', + muted: '#f2f1ee', + strong: '#c8c7c4', } as const -// ── Surface tokens — bg + border pairs for highlighted/tinted boxes ─────────── -// Use as: sx={{ bgcolor: DS_SURFACE.success.bg, border: `1px solid ${DS_SURFACE.success.border}` }} +// ── Surface tokens ──────────────────────────────────────────────────────────── export const DS_SURFACE = { - success: { bg: '#f0fdf4', border: '#bbf7d0' }, // green tint - warning: { bg: '#fffbeb', border: '#fde68a' }, // amber tint - error: { bg: '#fef2f2', border: '#fecaca' }, // red tint - info: { bg: '#f0f9ff', border: '#bae6fd' }, // sky tint - indigo: { bg: '#eef2ff', border: '#e0e7ff' }, // indigo tint (hard-criteria rows) - purple: { bg: '#faf5ff', border: '#e9d5ff' }, // purple tint (pre-market / future) - orange: { bg: '#fff7ed', border: '#fed7aa' }, // orange tint (HOT heat badge bg) - blue: { bg: '#eff6ff', border: '#bfdbfe' }, // blue tint (market signal) - neutral: { bg: '#f8fafc', border: '#e2e8f0' }, // neutral/slate tint (formula rows) - slate: { bg: '#f1f5f9', border: '#e2e8f0' }, // slightly darker neutral + success: { bg: '#f0fdf4', border: '#bbf7d0' }, + warning: { bg: '#fffbeb', border: '#fde68a' }, + error: { bg: '#fef2f2', border: '#fecaca' }, + info: { bg: '#f0f6ff', border: '#bfdbfe' }, + indigo: { bg: '#eef2ff', border: '#e0e7ff' }, + purple: { bg: '#faf7ff', border: '#e9d5ff' }, + orange: { bg: '#fff7ed', border: '#fed7aa' }, + blue: { bg: '#f0f6ff', border: '#bfdbfe' }, + neutral: { bg: '#f9f8f6', border: '#e8e7e4' }, + slate: { bg: '#f4f3f0', border: '#e8e7e4' }, } as const -// ── Match tier surface — bg tint keyed by score tier ───────────────────────── -// Use with SCORE_STRONG / SCORE_MODERATE thresholds from constants.ts +// ── Match tier surface ──────────────────────────────────────────────────────── export const DS_MATCH_TIER = { strong: DS_SURFACE.success, @@ -132,49 +127,45 @@ export const DS_MATCH_TIER = { weak: DS_SURFACE.error, } as const -// ── Pre-market & market signal shorthand aliases ────────────────────────────── -// DS_COLORS.futureCard.controlled / signal are the full token sets. -// These aliases make imports more readable in supply-side components. +// ── Pre-market & market signal aliases ──────────────────────────────────────── export const DS_PRE_MARKET = DS_COLORS.futureCard.controlled export const DS_MARKET_SIGNAL = DS_COLORS.futureCard.signal // ── Badge color presets ─────────────────────────────────────────────────────── -// Pass to GenericBadge.color instead of raw hex strings. -// BADGE_COLORS.confidenceHigh replaces '#1a7a4a' at call sites. export const BADGE_COLORS = { - confidenceHigh: '#1a7a4a', - confidenceMedium: '#d97706', - confidenceLow: '#c0392b', - riskHigh: '#c0392b', - riskMedium: '#d97706', - riskLow: '#1a7a4a', - preMarket: '#7c3aed', - marketSignal: '#1d4ed8', - verified: '#1e3a5f', - info: '#0369a1', - muted: '#64748b', - gold: '#d97706', - silver: '#64748b', - bronze: '#b45309', + confidenceHigh: '#1d6342', + confidenceMedium: '#a16207', + confidenceLow: '#b91c1c', + riskHigh: '#b91c1c', + riskMedium: '#a16207', + riskLow: '#1d6342', + preMarket: '#5b3f8a', + marketSignal: '#2d5a8a', + verified: '#152642', + info: '#2d5a8a', + muted: '#9a9a9a', + gold: '#b8975a', + silver: '#7a8a9a', + bronze: '#7a6050', } as const // ── Shadow tokens ───────────────────────────────────────────────────────────── export const DS_SHADOW = { - card: '0 1px 3px 0 rgba(0,0,0,0.08), 0 1px 2px -1px rgba(0,0,0,0.04)', - panel: '0 4px 6px -1px rgba(0,0,0,0.08), 0 2px 4px -2px rgba(0,0,0,0.04)', - dialog: '0 20px 25px -5px rgba(0,0,0,0.10), 0 8px 10px -6px rgba(0,0,0,0.06)', + card: '0 1px 3px rgba(0,0,0,0.06)', + panel: '0 3px 6px rgba(0,0,0,0.07)', + dialog: '0 16px 24px rgba(0,0,0,0.10)', } as const // ── Inquiry status metadata ─────────────────────────────────────────────────── export const INQUIRY_STATUS_META: Record = { - new: { label: 'Neu', fg: '#dc2626', bg: '#fef2f2' }, - in_progress: { label: 'Aktiv', fg: '#d97706', bg: '#fffbeb' }, - answered: { label: 'Beantwortet', fg: '#1a7a4a', bg: '#f0fdf4' }, - archived: { label: 'Archiviert', fg: '#64748b', bg: '#f8fafc' }, + new: { label: 'Neu', fg: '#b91c1c', bg: '#fef2f2' }, + in_progress: { label: 'Aktiv', fg: '#a16207', bg: '#fffbeb' }, + answered: { label: 'Beantwortet', fg: '#1d6342', bg: '#f0fdf4' }, + archived: { label: 'Archiviert', fg: '#9a9a9a', bg: '#f9f8f6' }, } // ── Score → qualitative level helpers ──────────────────────────────────────── @@ -194,10 +185,10 @@ export function scoreToDataQualityLevel(score: number): DataQualityLevel { return 'INCOMPLETE' } -// ── Result type meta — single source for labels + colors ───────────────────── +// ── Result type meta ────────────────────────────────────────────────────────── export const RESULT_TYPE_META: Record = { - VERIFIED_PORTFOLIO: { label: 'Verifiziertes Objekt', color: '#1e3a5f', bg: 'rgba(30,58,95,0.10)' }, - MAISON_WORK: { label: 'Maison Work', color: '#0369a1', bg: 'rgba(3,105,161,0.10)' }, - FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed', bg: 'rgba(109,40,217,0.10)' }, + VERIFIED_PORTFOLIO: { label: 'Verifiziertes Objekt', color: '#152642', bg: 'rgba(21,38,66,0.10)' }, + MAISON_WORK: { label: 'Maison Work', color: '#2d5a8a', bg: 'rgba(45,90,138,0.10)' }, + FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#5b3f8a', bg: 'rgba(91,63,138,0.10)' }, } diff --git a/src/lib/scoreTheme.ts b/src/lib/scoreTheme.ts index 35c7886..2595f30 100644 --- a/src/lib/scoreTheme.ts +++ b/src/lib/scoreTheme.ts @@ -8,30 +8,30 @@ export function getScoreTier(score: number): ScoreTier { export const SCORE_THEME = { gold: { - gradient: 'linear-gradient(135deg,#f8e642 0%,#d4920e 100%)', - border: '#c9900c', - glow: 'rgba(212,146,14,0.35)', - text: '#7a4f00', - label: 'Top Match', - cardBorder: '#fbbf24', - cardBg: 'linear-gradient(160deg,#fffbeb,#fef3c7)', + gradient: '#b8975a', + border: '#a07a3f', + glow: 'transparent', + text: '#ffffff', + label: 'Top Match', + cardBorder: '#b8975a', + cardBg: '#ffffff', }, silver: { - gradient: 'linear-gradient(135deg,#f1f5f9 0%,#cbd5e1 100%)', - border: '#94a3b8', - glow: 'rgba(148,163,184,0.30)', - text: '#334155', - label: 'Starkes Match', - cardBorder: '#cbd5e1', - cardBg: 'linear-gradient(160deg,#f8fafc,#f1f5f9)', + gradient: '#7a8a9a', + border: '#6b7a8a', + glow: 'transparent', + text: '#ffffff', + label: 'Starkes Match', + cardBorder: '#7a8a9a', + cardBg: '#ffffff', }, bronze: { - gradient: 'linear-gradient(135deg,#fde8c8 0%,#d4956a 100%)', - border: '#c07a46', - glow: 'rgba(192,122,70,0.25)', - text: '#7c3d0c', - label: 'Gutes Match', - cardBorder: '#f5d0a9', - cardBg: 'linear-gradient(160deg,#fdf6f0,#fef3e8)', + gradient: '#7a6050', + border: '#6a5040', + glow: 'transparent', + text: '#ffffff', + label: 'Gutes Match', + cardBorder: '#7a6050', + cardBg: '#ffffff', }, } as const diff --git a/src/lib/theme.ts b/src/lib/theme.ts index be22b6c..a167138 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -4,9 +4,9 @@ export const theme = createTheme({ palette: { mode: 'light', primary: { - main: '#1e3a5f', - light: '#2d5290', - dark: '#142a45', + main: '#152642', + light: '#1e3a5f', + dark: '#0e1c30', contrastText: '#ffffff', }, secondary: { @@ -16,18 +16,18 @@ export const theme = createTheme({ contrastText: '#ffffff', }, background: { - default: '#f4f6f9', + default: '#f9f8f6', paper: '#ffffff', }, text: { - primary: '#1a2332', - secondary: '#4a5568', + primary: '#141414', + secondary: '#5c5c5c', }, - error: { main: '#c0392b' }, - warning: { main: '#d97706' }, - success: { main: '#1a7a4a' }, + error: { main: '#b91c1c' }, + warning: { main: '#a16207' }, + success: { main: '#1d6342' }, info: { main: '#2563eb' }, - divider: '#e2e8f0', + divider: '#e8e7e4', }, typography: { fontFamily: '"Inter", "Segoe UI", system-ui, -apple-system, sans-serif', @@ -41,37 +41,37 @@ export const theme = createTheme({ subtitle2: { fontSize: '0.8125rem', fontWeight: 600, lineHeight: 1.4 }, body1: { fontSize: '0.875rem', lineHeight: 1.6 }, body2: { fontSize: '0.8125rem', lineHeight: 1.5 }, - caption: { fontSize: '0.75rem', lineHeight: 1.4, color: '#64748b' }, + caption: { fontSize: '0.75rem', lineHeight: 1.4, color: '#9a9a9a' }, overline: { fontSize: '0.6875rem', fontWeight: 600, letterSpacing: '0.08em', textTransform: 'uppercase' }, button: { fontSize: '0.8125rem', fontWeight: 600, textTransform: 'none' }, }, - shape: { borderRadius: 6 }, + shape: { borderRadius: 8 }, shadows: [ 'none', - '0 1px 2px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.08)', - '0 1px 4px rgba(0,0,0,0.06), 0 2px 6px rgba(0,0,0,0.08)', - '0 2px 6px rgba(0,0,0,0.07), 0 4px 10px rgba(0,0,0,0.08)', - '0 3px 8px rgba(0,0,0,0.08), 0 6px 14px rgba(0,0,0,0.08)', - '0 4px 10px rgba(0,0,0,0.08), 0 8px 18px rgba(0,0,0,0.08)', - '0 5px 12px rgba(0,0,0,0.09)', '0 6px 14px rgba(0,0,0,0.09)', - '0 7px 16px rgba(0,0,0,0.09)', '0 8px 18px rgba(0,0,0,0.09)', - '0 9px 20px rgba(0,0,0,0.09)', '0 10px 22px rgba(0,0,0,0.10)', - '0 11px 24px rgba(0,0,0,0.10)', '0 12px 26px rgba(0,0,0,0.10)', - '0 13px 28px rgba(0,0,0,0.10)', '0 14px 30px rgba(0,0,0,0.10)', - '0 15px 32px rgba(0,0,0,0.10)', '0 16px 34px rgba(0,0,0,0.10)', - '0 17px 36px rgba(0,0,0,0.10)', '0 18px 38px rgba(0,0,0,0.10)', - '0 19px 40px rgba(0,0,0,0.10)', '0 20px 42px rgba(0,0,0,0.10)', - '0 21px 44px rgba(0,0,0,0.10)', '0 22px 46px rgba(0,0,0,0.10)', - '0 24px 50px rgba(0,0,0,0.12)', + '0 1px 2px rgba(0,0,0,0.05)', + '0 1px 3px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.06)', + '0 3px 6px rgba(0,0,0,0.07)', + '0 4px 8px rgba(0,0,0,0.07)', + '0 5px 10px rgba(0,0,0,0.08)', '0 6px 12px rgba(0,0,0,0.08)', + '0 7px 14px rgba(0,0,0,0.08)', '0 8px 16px rgba(0,0,0,0.08)', + '0 9px 18px rgba(0,0,0,0.08)', '0 10px 20px rgba(0,0,0,0.09)', + '0 11px 22px rgba(0,0,0,0.09)', '0 12px 24px rgba(0,0,0,0.09)', + '0 13px 26px rgba(0,0,0,0.09)', '0 14px 28px rgba(0,0,0,0.09)', + '0 15px 30px rgba(0,0,0,0.09)', '0 16px 32px rgba(0,0,0,0.09)', + '0 17px 34px rgba(0,0,0,0.09)', '0 18px 36px rgba(0,0,0,0.09)', + '0 19px 38px rgba(0,0,0,0.09)', '0 20px 40px rgba(0,0,0,0.09)', + '0 21px 42px rgba(0,0,0,0.09)', '0 22px 44px rgba(0,0,0,0.09)', + '0 24px 48px rgba(0,0,0,0.10)', ], components: { MuiCard: { defaultProps: { elevation: 1 }, styleOverrides: { root: { - border: '1px solid #e2e8f0', - '&:hover': { boxShadow: '0 2px 8px rgba(0,0,0,0.10)' }, - transition: 'box-shadow 0.15s ease', + border: '1px solid #e8e7e4', + '&:hover': { borderColor: '#c8c7c4' }, + transition: 'border-color 0.15s ease', }, }, }, @@ -81,20 +81,20 @@ export const theme = createTheme({ root: { borderRadius: 6, padding: '6px 16px' }, contained: { '&.MuiButton-containedPrimary': { - background: '#1e3a5f', - '&:hover': { background: '#142a45' }, + background: '#152642', + '&:hover': { background: '#0e1c30' }, }, }, }, }, MuiChip: { styleOverrides: { - root: { borderRadius: 4, fontSize: '0.75rem', fontWeight: 600, height: 22 }, + root: { borderRadius: 6, fontSize: '0.75rem', fontWeight: 500, height: 22 }, }, }, MuiTableCell: { styleOverrides: { - head: { fontWeight: 600, fontSize: '0.75rem', color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em', padding: '8px 12px' }, + head: { fontWeight: 600, fontSize: '0.75rem', color: '#9a9a9a', textTransform: 'uppercase', letterSpacing: '0.05em', padding: '8px 12px' }, body: { fontSize: '0.8125rem', padding: '10px 12px' }, }, }, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 315138d..b3d1b4d 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -64,29 +64,29 @@ export function probabilityColor(prob: number): 'success' | 'warning' | 'error' return 'error' } -// Hex color variants for use in sx (when MUI color tokens aren't enough) +// Hex color variants for use in sx props export function dataQualityHex(score: number): string { - if (score >= DQ_HIGH) return '#1a7a4a' - if (score >= DQ_MEDIUM) return '#d97706' - return '#c0392b' + if (score >= DQ_HIGH) return '#1d6342' + if (score >= DQ_MEDIUM) return '#a16207' + return '#b91c1c' } export function confidenceHex(score: number): string { - if (score >= CONF_HIGH) return '#1a7a4a' - if (score >= CONF_MEDIUM) return '#1e3a5f' - return '#d97706' + if (score >= CONF_HIGH) return '#1d6342' + if (score >= CONF_MEDIUM) return '#152642' + return '#a16207' } export function probabilityHex(prob: number): string { - if (prob >= PROB_HIGH) return '#1a7a4a' - if (prob >= PROB_MEDIUM) return '#d97706' - return '#c0392b' + if (prob >= PROB_HIGH) return '#1d6342' + if (prob >= PROB_MEDIUM) return '#a16207' + return '#b91c1c' } export function matchScoreHex(score: number): string { - if (score >= SCORE_STRONG) return '#1a7a4a' - if (score >= SCORE_MODERATE) return '#d97706' - return '#c0392b' + if (score >= SCORE_STRONG) return '#1d6342' + if (score >= SCORE_MODERATE) return '#a16207' + return '#b91c1c' } export function criterionScoreColor(score: number): 'success' | 'warning' | 'error' { @@ -94,7 +94,7 @@ export function criterionScoreColor(score: number): 'success' | 'warning' | 'err } export function criterionScoreTextColor(score: number): string { - return score >= 70 ? '#1a7a4a' : score >= 50 ? '#d97706' : '#c0392b' + return score >= 70 ? '#1d6342' : score >= 50 ? '#a16207' : '#b91c1c' } // ── Misc ────────────────────────────────────────────────────────────────────── diff --git a/src/pages/auth/LoginScreen.tsx b/src/pages/auth/LoginScreen.tsx index 0695256..d50b2ec 100644 --- a/src/pages/auth/LoginScreen.tsx +++ b/src/pages/auth/LoginScreen.tsx @@ -134,7 +134,7 @@ export default function LoginScreen() { variant="contained" fullWidth disabled={loading} - sx={{ bgcolor: '#1e3a5f', textTransform: 'none', fontWeight: 600, '&:hover': { bgcolor: '#162d4a' } }} + sx={{ bgcolor: '#152642', textTransform: 'none', fontWeight: 600, '&:hover': { bgcolor: '#162d4a' } }} > {loading ? : 'Anmelden'} @@ -146,7 +146,7 @@ export default function LoginScreen() { - + Demo-Zugänge @@ -169,7 +169,7 @@ export default function LoginScreen() { border: '1px solid #e2e8f0', cursor: 'pointer', transition: 'border-color 0.15s', - '&:hover': { borderColor: '#1e3a5f', bgcolor: 'rgba(30,58,95,0.03)' }, + '&:hover': { borderColor: '#152642', bgcolor: 'rgba(30,58,95,0.03)' }, }} > diff --git a/src/pages/demand/PropertyDetail.tsx b/src/pages/demand/PropertyDetail.tsx index 142420d..720a67f 100644 --- a/src/pages/demand/PropertyDetail.tsx +++ b/src/pages/demand/PropertyDetail.tsx @@ -83,7 +83,7 @@ export default function PropertyDetail() { - + CHF {monthlyPerSqm.toLocaleString('de-CH')}/m²/Mt. {property.areaSqm.toLocaleString('de-CH')} m² total diff --git a/src/pages/demand/Results.tsx b/src/pages/demand/Results.tsx index 77007ef..15c2617 100644 --- a/src/pages/demand/Results.tsx +++ b/src/pages/demand/Results.tsx @@ -138,7 +138,7 @@ export default function Results() { - + Aktive Suche: {activeNeed.companyName} @@ -203,7 +203,7 @@ export default function Results() { size="small" variant="text" onClick={() => navigate('/demand/ai-search')} - sx={{ color: '#1e3a5f', flexShrink: 0 }} + sx={{ color: '#152642', flexShrink: 0 }} > Suche ändern diff --git a/src/pages/supply/DataQuality.tsx b/src/pages/supply/DataQuality.tsx index 9ee3c4f..6a30bb2 100644 --- a/src/pages/supply/DataQuality.tsx +++ b/src/pages/supply/DataQuality.tsx @@ -170,7 +170,7 @@ const PropertyRow = memo(function PropertyRow({ onEdit(property.id)} - sx={{ color: '#94a3b8', '&:hover': { color: '#1e3a5f' } }} + sx={{ color: '#94a3b8', '&:hover': { color: '#152642' } }} > diff --git a/src/pages/supply/MarketIntelligence.tsx b/src/pages/supply/MarketIntelligence.tsx index f00102c..80868e1 100644 --- a/src/pages/supply/MarketIntelligence.tsx +++ b/src/pages/supply/MarketIntelligence.tsx @@ -125,7 +125,7 @@ const LeadListItem = memo(function LeadListItem({ sx={{ px: 2, py: 1.5, borderBottom: '1px solid #f1f5f9', - borderLeft: `3px solid ${selected ? '#1e3a5f' : 'transparent'}`, + borderLeft: `3px solid ${selected ? '#152642' : 'transparent'}`, bgcolor: selected ? '#f0f9ff' : 'white', cursor: 'pointer', '&:hover': { bgcolor: selected ? '#f0f9ff' : '#f8fafc' }, @@ -181,7 +181,7 @@ function ContactRow({ contact }: { contact: ExtractedContact }) { href={href} target="_blank" rel="noopener noreferrer" - sx={{ color: '#1e3a5f', fontWeight: 500, fontSize: '0.8125rem', textDecoration: 'none', '&:hover': { textDecoration: 'underline' }, display: 'flex', alignItems: 'center', gap: 0.5 }} + sx={{ color: '#152642', fontWeight: 500, fontSize: '0.8125rem', textDecoration: 'none', '&:hover': { textDecoration: 'underline' }, display: 'flex', alignItems: 'center', gap: 0.5 }} > {contact.value} @@ -241,7 +241,7 @@ function PropertyMatchCard({ }} onClick={() => navigate('/supply/properties')} > - + {p.title} @@ -271,8 +271,8 @@ function PropertyMatchCard({ onClick={() => setOpen(v => !v)} sx={{ textTransform: 'none', fontSize: '0.75rem', - bgcolor: open ? '#1e3a5f' : undefined, - borderColor: open ? '#1e3a5f' : undefined, + bgcolor: open ? '#152642' : undefined, + borderColor: open ? '#152642' : undefined, '&:hover': { bgcolor: open ? '#162d4a' : undefined }, }} > @@ -302,7 +302,7 @@ function PropertyMatchCard({ onClick={handleCopy} sx={{ textTransform: 'none', fontSize: '0.72rem', - color: copied ? '#16a34a' : '#1e3a5f', + color: copied ? '#16a34a' : '#152642', }} > {copied ? 'Kopiert!' : 'Kopieren'} @@ -370,7 +370,7 @@ function LeadDetail({ lead }: { lead: MarketLead }) { href={signal.source.url} target="_blank" rel="noopener noreferrer" - sx={{ display: 'flex', alignItems: 'center', gap: 0.5, ml: 'auto', color: '#1e3a5f', fontSize: '0.75rem', fontWeight: 600, textDecoration: 'none', '&:hover': { textDecoration: 'underline' } }} + sx={{ display: 'flex', alignItems: 'center', gap: 0.5, ml: 'auto', color: '#152642', fontSize: '0.75rem', fontWeight: 600, textDecoration: 'none', '&:hover': { textDecoration: 'underline' } }} > Quelle öffnen @@ -403,7 +403,7 @@ function LeadDetail({ lead }: { lead: MarketLead }) { {signal.marketIndicators!.map((ind, i) => ( - + {ind} ))} @@ -531,7 +531,7 @@ const HinweisPropertyCard = memo(function HinweisPropertyCard({ cursor: 'default', }} > - + {p.title} @@ -561,8 +561,8 @@ const HinweisPropertyCard = memo(function HinweisPropertyCard({ onClick={() => setOpen(v => !v)} sx={{ textTransform: 'none', fontSize: '0.75rem', - bgcolor: open ? '#1e3a5f' : undefined, - borderColor: open ? '#1e3a5f' : undefined, + bgcolor: open ? '#152642' : undefined, + borderColor: open ? '#152642' : undefined, '&:hover': { bgcolor: open ? '#162d4a' : undefined }, }} > @@ -581,7 +581,7 @@ const HinweisPropertyCard = memo(function HinweisPropertyCard({ size="small" startIcon={copied ? : } onClick={handleCopy} - sx={{ textTransform: 'none', fontSize: '0.72rem', color: copied ? '#16a34a' : '#1e3a5f' }} + sx={{ textTransform: 'none', fontSize: '0.72rem', color: copied ? '#16a34a' : '#152642' }} > {copied ? 'Kopiert!' : 'Kopieren'} @@ -644,7 +644,7 @@ const HinweisListItem = memo(function HinweisListItem({ sx={{ px: 2, py: 1.5, borderBottom: '1px solid #f1f5f9', - borderLeft: `3px solid ${selected ? '#1e3a5f' : 'transparent'}`, + borderLeft: `3px solid ${selected ? '#152642' : 'transparent'}`, bgcolor: selected ? '#f0f9ff' : 'white', cursor: 'pointer', '&:hover': { bgcolor: selected ? '#f0f9ff' : '#f8fafc' }, @@ -654,7 +654,7 @@ const HinweisListItem = memo(function HinweisListItem({ {hinweis.direction === 'SUCHE' - ? + ? : } @@ -1003,7 +1003,7 @@ function HinweisErfassenDialog({ variant="contained" onClick={handleSave} disabled={mutation.isPending || !isValid} - sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' } }} + sx={{ textTransform: 'none', bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' } }} > Speichern @@ -1075,7 +1075,7 @@ export default function MarketIntelligence() { Erkannte Signale - + {leadsLoading @@ -1130,7 +1130,7 @@ export default function MarketIntelligence() { onClick={() => setErfassenOpen(true)} sx={{ textTransform: 'none', fontSize: '0.72rem', - bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' }, + bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' }, minWidth: 'auto', }} > @@ -1147,7 +1147,7 @@ export default function MarketIntelligence() { onClick={() => setHinweisFilter(f)} sx={{ height: 22, fontSize: '0.68rem', cursor: 'pointer', - bgcolor: hinweisFilter === f ? '#1e3a5f' : '#f1f5f9', + bgcolor: hinweisFilter === f ? '#152642' : '#f1f5f9', color: hinweisFilter === f ? 'white' : '#475569', }} /> diff --git a/src/pages/supply/MyListings.tsx b/src/pages/supply/MyListings.tsx index 6e01820..7333828 100644 --- a/src/pages/supply/MyListings.tsx +++ b/src/pages/supply/MyListings.tsx @@ -104,7 +104,7 @@ function MatchListItem({ sx={{ textTransform: 'none', fontSize: '0.75rem', - bgcolor: '#1e3a5f', + bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' }, }} > @@ -351,7 +351,7 @@ export default function MyListings() { variant="contained" startIcon={} onClick={() => navigate('/supply/new-listing')} - sx={{ bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' }, textTransform: 'none' }} + sx={{ bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' }, textTransform: 'none' }} > Neues Inserat diff --git a/src/pages/supply/Properties.tsx b/src/pages/supply/Properties.tsx index 7388430..80d6be4 100644 --- a/src/pages/supply/Properties.tsx +++ b/src/pages/supply/Properties.tsx @@ -177,7 +177,7 @@ export default function Properties() { variant={criticalGaps.length > 0 || staleOrOutdated.length > 0 ? 'contained' : 'outlined'} onClick={() => navigate('/supply/data-quality')} sx={criticalGaps.length > 0 || staleOrOutdated.length > 0 - ? { bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#162d4a' }, textTransform: 'none', fontSize: '0.75rem', py: 0.375, px: 1.5, flexShrink: 0 } + ? { bgcolor: '#152642', '&:hover': { bgcolor: '#162d4a' }, textTransform: 'none', fontSize: '0.75rem', py: 0.375, px: 1.5, flexShrink: 0 } : { textTransform: 'none', fontSize: '0.75rem', py: 0.375, px: 1.5, flexShrink: 0 } } >