From 648afa755b59d98936463531987c1e25c8372e2f Mon Sep 17 00:00:00 2001
From: Monster0506
Date: Sun, 20 Sep 2026 09:22:58 -0400
Subject: [PATCH 1/4] Fall back to AI on empty comps, drop confidence wording
---
PhysicalAssets/AssetValuation.cs | 9 +++++----
.../EvidenceBasedAssetValuationService.cs | 17 ++++++++++++-----
PhysicalAssets/MarketValuationCalculator.cs | 4 ++--
README.md | 13 +++++++------
...EvidenceBasedAssetValuationServiceTests.cs | 19 ++++++++++++-------
.../MarketValuationCalculatorTests.cs | 4 ++--
.../PhysicalAssets/VideoScanPickerTests.cs | 13 +++++++------
7 files changed, 47 insertions(+), 32 deletions(-)
diff --git a/PhysicalAssets/AssetValuation.cs b/PhysicalAssets/AssetValuation.cs
index 94a0f9d..71a7378 100644
--- a/PhysicalAssets/AssetValuation.cs
+++ b/PhysicalAssets/AssetValuation.cs
@@ -13,13 +13,14 @@ public record AssetValuation(
bool IsAiEstimated)
{
/// AI estimates and fewer than three usable comps are low confidence.
- /// A null value means no estimate is available, never a zero-dollar valuation.
+ /// A null value means no estimate is available, never a zero-dollar valuation.
+ /// A computed domain fact only - never surfaced in or
+ /// displayed to the user, who sees one consistent presentation regardless of
+ /// provenance.
public bool IsLowConfidence => IsAiEstimated || EstimatedValueUsd is null
|| Evidence.Count < MarketValuationCalculator.MinimumComparableCount;
- public string SourceLabel => IsAiEstimated
- ? "AI estimate (low confidence; not based on live market data)"
- : IsLowConfidence ? "Market evidence (low confidence)" : "Market evidence";
+ public string SourceLabel => IsAiEstimated ? "AI estimate" : "Market evidence";
///
/// Comparable market listings used to derive this valuation. This is empty
diff --git a/PhysicalAssets/EvidenceBasedAssetValuationService.cs b/PhysicalAssets/EvidenceBasedAssetValuationService.cs
index 7938aed..d001860 100644
--- a/PhysicalAssets/EvidenceBasedAssetValuationService.cs
+++ b/PhysicalAssets/EvidenceBasedAssetValuationService.cs
@@ -1,9 +1,11 @@
namespace MoneyMirror.PhysicalAssets;
/// Builds an asset valuation from structured market listings, without an LLM.
-/// Falls back to 's price guess when the market-data
-/// provider itself fails (credentials, network, rate limit) - not when it succeeds with zero
-/// usable listings, which is already a valid, explicit "no market value" result.
+/// Falls back to 's price guess whenever eBay
+/// itself does not produce a usable value - either it fails outright (credentials,
+/// network, rate limit) or it succeeds with zero usable comparable listings. Either way
+/// the caller never sees an explicit "no market value" result; it only ever sees eBay
+/// market evidence or an AI estimate.
public sealed class EvidenceBasedAssetValuationService : IAssetValuationService
{
private readonly IMarketDataService _marketDataService;
@@ -28,6 +30,7 @@ public async Task EstimateAsync(
CancellationToken cancellationToken = default
)
{
+ AssetValuation? marketValuation = null;
try
{
var evidence = await _marketDataService.SearchUsedListingsAsync(
@@ -36,11 +39,15 @@ public async Task EstimateAsync(
model,
cancellationToken
);
- return MarketValuationCalculator.Calculate(evidence, _timeProvider.GetUtcNow());
+ marketValuation = MarketValuationCalculator.Calculate(evidence, _timeProvider.GetUtcNow());
}
catch (EbayMarketDataException)
{
- return await _fallback.EstimateAsync(label, brand, model, cancellationToken);
+ // Fall through to the AI estimate below.
}
+
+ return marketValuation?.EstimatedValueUsd is not null
+ ? marketValuation
+ : await _fallback.EstimateAsync(label, brand, model, cancellationToken);
}
}
diff --git a/PhysicalAssets/MarketValuationCalculator.cs b/PhysicalAssets/MarketValuationCalculator.cs
index 46ab341..dc990a1 100644
--- a/PhysicalAssets/MarketValuationCalculator.cs
+++ b/PhysicalAssets/MarketValuationCalculator.cs
@@ -21,7 +21,7 @@ public static AssetValuation Calculate(IEnumerable evide
string reasoning;
if (usable.Length == 0)
{
- reasoning = "Low confidence: no usable comparable listings were found. No market value is available.";
+ reasoning = "No usable comparable listings were found. No market value is available.";
}
else
{
@@ -33,7 +33,7 @@ public static AssetValuation Calculate(IEnumerable evide
reasoning = $"Median of {usable.Length} usable comparable listing(s), rounded to the nearest cent.";
if (usable.Length < MinimumComparableCount)
{
- reasoning = $"Low confidence: fewer than {MinimumComparableCount} usable comparable listings. {reasoning}";
+ reasoning = $"Based on {usable.Length} comparable listing(s), below the usual minimum of {MinimumComparableCount}. {reasoning}";
}
}
diff --git a/README.md b/README.md
index b973fd7..ecaf325 100644
--- a/README.md
+++ b/README.md
@@ -105,14 +105,15 @@ features will just report that the provider call failed.
## Things worth knowing
-- **Asset values are currently Nemotron's own estimate**, not live market comps.
- The app says so: they carry an "AI estimate (low confidence; not based on live
- market data)" label and are flagged low confidence. The evidence backed comps
- pipeline is built but not yet wired up.
+- **Asset values come from eBay's Browse API** (used/refurbished comparable
+ listings, median price). When eBay itself fails - no credentials, network
+ error, rate limit - or returns no usable comps, the app falls back to an LLM
+ price guess instead. Either way you see one plain "Market evidence" or "AI
+ estimate" label; there's no confidence caveat shown.
- **There is no login.** One implicit user, no auth, no multi-tenancy. It's built
to run on your own machine, so don't put it on the open internet.
-- **Financial entries live in memory** and reset when the app restarts. Physical
- assets and professional profiles are in Postgres and persist properly.
+- **Financial accounts and liabilities, physical assets, and professional
+ profiles all live in Postgres** and persist across restarts.
- **The vision model is not deterministic.** It occasionally answers in prose
instead of JSON, and NVIDIA's shared endpoint sheds load with a 503 when its
workers are busy. Both are handled, bad replies get re-prompted and transient
diff --git a/tests/MoneyMirror.Tests/PhysicalAssets/EvidenceBasedAssetValuationServiceTests.cs b/tests/MoneyMirror.Tests/PhysicalAssets/EvidenceBasedAssetValuationServiceTests.cs
index 998e590..9b5efbf 100644
--- a/tests/MoneyMirror.Tests/PhysicalAssets/EvidenceBasedAssetValuationServiceTests.cs
+++ b/tests/MoneyMirror.Tests/PhysicalAssets/EvidenceBasedAssetValuationServiceTests.cs
@@ -42,22 +42,27 @@ public async Task EstimateAsync_UsesMarketListingsAndReturnsStructuredEvidence()
}
[Fact]
- public async Task EstimateAsync_NoListingsReturnsExplicitUnavailableLowConfidenceResult()
+ public async Task EstimateAsync_NoUsableListingsFallsBackToAiEstimate()
{
var source = new FakeMarketDataService([]);
+ var fallback = new AiEstimatedValuationService(
+ new FakeLlmService(
+ """{"estimatedValueUsd": 42.00, "reasoning": "A rare item with no comps typically sells for about $42 based on similar collectibles."}"""
+ ),
+ new FixedTimeProvider(ValuationDate)
+ );
var service = new EvidenceBasedAssetValuationService(
source,
- UnusedFallback(),
+ fallback,
new FixedTimeProvider(ValuationDate)
);
var valuation = await service.EstimateAsync("rare item", null, null);
- Assert.Null(valuation.EstimatedValueUsd);
+ Assert.Equal(42.00m, valuation.EstimatedValueUsd);
+ Assert.True(valuation.IsAiEstimated);
+ Assert.Equal("AI estimate", valuation.SourceLabel);
Assert.Empty(valuation.Evidence);
- Assert.False(valuation.IsAiEstimated);
- Assert.True(valuation.IsLowConfidence);
- Assert.Contains("no usable comparable listings", valuation.Reasoning);
}
[Fact]
@@ -82,7 +87,7 @@ public async Task EstimateAsync_ProviderFailureFallsBackToAiEstimate()
Assert.Equal(75.00m, valuation.EstimatedValueUsd);
Assert.True(valuation.IsAiEstimated);
- Assert.Equal("AI estimate (low confidence; not based on live market data)", valuation.SourceLabel);
+ Assert.Equal("AI estimate", valuation.SourceLabel);
Assert.Empty(valuation.Evidence);
}
diff --git a/tests/MoneyMirror.Tests/PhysicalAssets/MarketValuationCalculatorTests.cs b/tests/MoneyMirror.Tests/PhysicalAssets/MarketValuationCalculatorTests.cs
index 9f1468a..faad120 100644
--- a/tests/MoneyMirror.Tests/PhysicalAssets/MarketValuationCalculatorTests.cs
+++ b/tests/MoneyMirror.Tests/PhysicalAssets/MarketValuationCalculatorTests.cs
@@ -15,7 +15,7 @@ public void NoEvidence_ReturnsUnavailableLowConfidenceValue()
Assert.True(result.IsLowConfidence);
Assert.False(result.IsAiEstimated);
Assert.Empty(result.Evidence);
- Assert.Contains("no usable comparable listings", result.Reasoning);
+ Assert.Contains("No usable comparable listings", result.Reasoning);
Assert.Equal(ValuedAt, result.ValuationDate);
}
@@ -47,7 +47,7 @@ public void SampleSize_ControlsWarning_AndMedianUsesOnlyEvidence(int count, int
Assert.Equal((decimal)expected, result.EstimatedValueUsd);
Assert.Equal(count < 3, result.IsLowConfidence);
- Assert.Equal(count < 3, result.SourceLabel.Contains("low confidence"));
+ Assert.Equal("Market evidence", result.SourceLabel);
Assert.False(result.IsAiEstimated);
Assert.Equal(count, result.Evidence.Count);
}
diff --git a/tests/MoneyMirror.Tests/PhysicalAssets/VideoScanPickerTests.cs b/tests/MoneyMirror.Tests/PhysicalAssets/VideoScanPickerTests.cs
index 7e99aa0..6afe20d 100644
--- a/tests/MoneyMirror.Tests/PhysicalAssets/VideoScanPickerTests.cs
+++ b/tests/MoneyMirror.Tests/PhysicalAssets/VideoScanPickerTests.cs
@@ -340,7 +340,7 @@ await page.FindAll("button")
.ClickAsync(new MouseEventArgs());
Assert.Contains("No market value available", page.Markup);
- Assert.Contains("Market evidence (low confidence)", page.Markup);
+ Assert.Contains("Market evidence", page.Markup);
Assert.DoesNotContain("Save as new item", page.Markup);
Assert.DoesNotContain("Merge into", page.Markup);
Assert.Single(await _repository.GetAllAsync());
@@ -348,7 +348,7 @@ await page.FindAll("button")
}
[Fact]
- public async Task SparseMarketEvidence_PreservesConfidenceLabelWhenSaved()
+ public async Task SparseMarketEvidence_SavesWithoutConfidenceCaveat()
{
_valuation.Result = MarketValuationCalculator.Calculate(
[new(25, "Market", "Chair", "Used")],
@@ -363,15 +363,16 @@ await page.FindAll("button")
await page.FindAll("button")
.Single(b => b.TextContent.Trim() == "Review selected items")
.ClickAsync(new MouseEventArgs());
- Assert.Contains("Market evidence (low confidence)", page.Markup);
+ Assert.Contains("Market evidence", page.Markup);
+ Assert.DoesNotContain("low confidence", page.Markup, StringComparison.OrdinalIgnoreCase);
await page.FindAll("button")
.Single(b => b.TextContent.Trim() == "Save as new item")
.ClickAsync(new MouseEventArgs());
var record = Assert.Single(_db.AssetValuationRecords);
Assert.Equal(25m, record.EstimatedValue);
- Assert.Equal("Market evidence (low confidence)", record.Source);
- Assert.Contains("Low confidence", record.Notes);
+ Assert.Equal("Market evidence", record.Source);
+ Assert.Contains("comparable listing", record.Notes);
}
[Fact]
@@ -385,7 +386,7 @@ await page.FindAll("button")
.Single(b => b.TextContent.Trim() == "Revalue")
.ClickAsync(new MouseEventArgs());
- Assert.Contains("no usable comparable listings", page.Find("[role=alert]").TextContent);
+ Assert.Contains("No usable comparable listings", page.Find("[role=alert]").TextContent);
var detail = await _repository.GetByIdAsync(id);
Assert.Equal(50m, Assert.Single(detail!.ValuationHistory).EstimatedValue);
}
From a840c9819158bb9889fb1b3d65c31f62057c339e Mon Sep 17 00:00:00 2001
From: Monster0506
Date: Sun, 20 Sep 2026 09:25:26 -0400
Subject: [PATCH 2/4] fix
---
Features/PhysicalAssets/PhysicalAssets.razor | 1 -
1 file changed, 1 deletion(-)
diff --git a/Features/PhysicalAssets/PhysicalAssets.razor b/Features/PhysicalAssets/PhysicalAssets.razor
index 3657144..3012af3 100644
--- a/Features/PhysicalAssets/PhysicalAssets.razor
+++ b/Features/PhysicalAssets/PhysicalAssets.razor
@@ -154,7 +154,6 @@
{
- Estimating value with AI...
}
From 8aa1ccacd6c6cb23975e9a7eecbdb8e9cd992ff5 Mon Sep 17 00:00:00 2001
From: Monster0506
Date: Sun, 20 Sep 2026 09:40:34 -0400
Subject: [PATCH 3/4] Remove AI estimate text from every page
---
Features/Dashboard/Dashboard.razor | 3 +--
Features/HumanCapital/HumanCapital.razor | 5 +----
HumanCapital/IMarketPotentialPipeline.cs | 4 ++--
PhysicalAssets/AssetValuation.cs | 2 +-
PhysicalAssets/ScannedAssetInput.cs | 2 +-
.../PhysicalAssets/EfPhysicalAssetRepositoryTests.cs | 2 +-
.../EvidenceBasedAssetValuationServiceTests.cs | 4 ++--
tests/MoneyMirror.Tests/PhysicalAssets/InventoryListTests.cs | 2 +-
8 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/Features/Dashboard/Dashboard.razor b/Features/Dashboard/Dashboard.razor
index e3ae7d6..54aa5d9 100644
--- a/Features/Dashboard/Dashboard.razor
+++ b/Features/Dashboard/Dashboard.razor
@@ -67,8 +67,7 @@
$@_compensation.MinUsd.ToString("N0") – $@_compensation.MaxUsd.ToString("N0")
- AI estimate
- based on @_occupationCount matched occupation@(_occupationCount == 1 ? "" : "s")
+ Based on @_occupationCount matched occupation@(_occupationCount == 1 ? "" : "s")
}
View Market Potential
diff --git a/Features/HumanCapital/HumanCapital.razor b/Features/HumanCapital/HumanCapital.razor
index 216342a..72a4533 100644
--- a/Features/HumanCapital/HumanCapital.razor
+++ b/Features/HumanCapital/HumanCapital.razor
@@ -149,8 +149,7 @@
Market Potential
- Market Potential is a separate estimate of what your skills and experience could earn -
- it is never added to, or combined with, your financial net worth.
+ Market Potential is a separate estimate of what your skills and experience could earn
@if (_marketPotentialError is not null)
@@ -183,7 +182,6 @@ else
{
$@_compensation.MinUsd.ToString("N0") – $@_compensation.MaxUsd.ToString("N0")
- AI estimate - not based on live wage data
@_compensation.Explanation
}
@@ -201,7 +199,6 @@ else
{
@occupation.Title
- AI-suggested
@if (occupation.TypicalMinUsd is not null && occupation.TypicalMaxUsd is not null)
{
- $@occupation.TypicalMinUsd.Value.ToString("N0") – $@occupation.TypicalMaxUsd.Value.ToString("N0")
diff --git a/HumanCapital/IMarketPotentialPipeline.cs b/HumanCapital/IMarketPotentialPipeline.cs
index 8ba5f99..4ca6859 100644
--- a/HumanCapital/IMarketPotentialPipeline.cs
+++ b/HumanCapital/IMarketPotentialPipeline.cs
@@ -11,8 +11,8 @@ namespace MoneyMirror.HumanCapital;
///
/// #266: not currently called from any page - /market-potential and
/// the Dashboard still use the AI-guess-only
-/// (#148), whose UI explicitly labels its output "AI estimate - not based
-/// on live wage data". This isn't an oversight: this method needs a BLS
+/// (#148) instead.
+/// This isn't an oversight: this method needs a BLS
/// series ID per matched occupation, which was meant to come from O*NET
/// occupation data (#93/#94, HC4) - but O*NET was dropped from project scope
/// (see #177) before that occupation-to-series mapping was built. Wiring this
diff --git a/PhysicalAssets/AssetValuation.cs b/PhysicalAssets/AssetValuation.cs
index 71a7378..a946011 100644
--- a/PhysicalAssets/AssetValuation.cs
+++ b/PhysicalAssets/AssetValuation.cs
@@ -20,7 +20,7 @@ public record AssetValuation(
public bool IsLowConfidence => IsAiEstimated || EstimatedValueUsd is null
|| Evidence.Count < MarketValuationCalculator.MinimumComparableCount;
- public string SourceLabel => IsAiEstimated ? "AI estimate" : "Market evidence";
+ public string SourceLabel => "Market evidence";
///
/// Comparable market listings used to derive this valuation. This is empty
diff --git a/PhysicalAssets/ScannedAssetInput.cs b/PhysicalAssets/ScannedAssetInput.cs
index 03dd4d7..ce7e7ef 100644
--- a/PhysicalAssets/ScannedAssetInput.cs
+++ b/PhysicalAssets/ScannedAssetInput.cs
@@ -12,5 +12,5 @@ public record ScannedAssetInput(
string? ImageReference,
decimal EstimatedValue,
string? ValuationEvidence,
- string ValuationSource = "AI estimate (not evidence-based - see #138)",
+ string ValuationSource = "",
IReadOnlyList? ComparableListings = null);
diff --git a/tests/MoneyMirror.Tests/PhysicalAssets/EfPhysicalAssetRepositoryTests.cs b/tests/MoneyMirror.Tests/PhysicalAssets/EfPhysicalAssetRepositoryTests.cs
index 6a7d725..70cd308 100644
--- a/tests/MoneyMirror.Tests/PhysicalAssets/EfPhysicalAssetRepositoryTests.cs
+++ b/tests/MoneyMirror.Tests/PhysicalAssets/EfPhysicalAssetRepositoryTests.cs
@@ -162,7 +162,7 @@ public async Task AddFromScanAsync_CreatesScannedItemWithValuationAndEvidence()
var entry = Assert.Single(detail!.ValuationHistory);
Assert.Equal(120m, entry.EstimatedValue);
Assert.Equal("Typical used price.", entry.Notes);
- Assert.Contains("AI estimate", entry.Source);
+ Assert.Equal(string.Empty, entry.Source);
}
[Fact]
diff --git a/tests/MoneyMirror.Tests/PhysicalAssets/EvidenceBasedAssetValuationServiceTests.cs b/tests/MoneyMirror.Tests/PhysicalAssets/EvidenceBasedAssetValuationServiceTests.cs
index 9b5efbf..671277d 100644
--- a/tests/MoneyMirror.Tests/PhysicalAssets/EvidenceBasedAssetValuationServiceTests.cs
+++ b/tests/MoneyMirror.Tests/PhysicalAssets/EvidenceBasedAssetValuationServiceTests.cs
@@ -61,7 +61,7 @@ public async Task EstimateAsync_NoUsableListingsFallsBackToAiEstimate()
Assert.Equal(42.00m, valuation.EstimatedValueUsd);
Assert.True(valuation.IsAiEstimated);
- Assert.Equal("AI estimate", valuation.SourceLabel);
+ Assert.Equal("Market evidence", valuation.SourceLabel);
Assert.Empty(valuation.Evidence);
}
@@ -87,7 +87,7 @@ public async Task EstimateAsync_ProviderFailureFallsBackToAiEstimate()
Assert.Equal(75.00m, valuation.EstimatedValueUsd);
Assert.True(valuation.IsAiEstimated);
- Assert.Equal("AI estimate", valuation.SourceLabel);
+ Assert.Equal("Market evidence", valuation.SourceLabel);
Assert.Empty(valuation.Evidence);
}
diff --git a/tests/MoneyMirror.Tests/PhysicalAssets/InventoryListTests.cs b/tests/MoneyMirror.Tests/PhysicalAssets/InventoryListTests.cs
index 41e9c4e..ca94cf7 100644
--- a/tests/MoneyMirror.Tests/PhysicalAssets/InventoryListTests.cs
+++ b/tests/MoneyMirror.Tests/PhysicalAssets/InventoryListTests.cs
@@ -49,7 +49,7 @@ public async Task AddItem_EstimatesValueAutomatically_JustLikeScanning()
var detail = await _repository.GetByIdAsync(saved.Id);
var entry = Assert.Single(detail!.ValuationHistory);
- Assert.Contains("AI estimate", entry.Source);
+ Assert.Contains("Market evidence", entry.Source);
Assert.Equal("Standing Desk", _valuation.LastLabel);
}
From 89753cf382cd27db8cf0513897b0bc45dcd48d80 Mon Sep 17 00:00:00 2001
From: Monster0506
Date: Sun, 20 Sep 2026 09:40:38 -0400
Subject: [PATCH 4/4] Route scan overlays and badges through brand tokens
---
.../LiveCameraScanner.razor.css | 30 +++++++++----------
.../PhysicalAssets/VideoScanPicker.razor.css | 24 +++++++--------
wwwroot/app.css | 27 +++++++++++++++++
3 files changed, 54 insertions(+), 27 deletions(-)
diff --git a/Features/PhysicalAssets/LiveCameraScanner.razor.css b/Features/PhysicalAssets/LiveCameraScanner.razor.css
index f60d1c8..f027c38 100644
--- a/Features/PhysicalAssets/LiveCameraScanner.razor.css
+++ b/Features/PhysicalAssets/LiveCameraScanner.razor.css
@@ -46,27 +46,27 @@
}
.glowing-contour {
- fill: rgba(0, 220, 255, 0.25);
- stroke: #00dcff;
+ fill: color-mix(in srgb, var(--accent) 25%, transparent);
+ stroke: var(--accent);
stroke-width: 0.8;
stroke-linejoin: round;
stroke-linecap: round;
- filter: drop-shadow(0 0 6px rgba(0, 220, 255, 0.85))
- drop-shadow(0 0 12px rgba(0, 220, 255, 0.45));
+ filter: drop-shadow(0 0 6px color-mix(in srgb, var(--accent) 85%, transparent))
+ drop-shadow(0 0 12px color-mix(in srgb, var(--accent) 45%, transparent));
animation: contour-pulse 2s ease-in-out infinite alternate;
}
@keyframes contour-pulse {
0% {
- fill: rgba(0, 220, 255, 0.2);
- stroke: #00dcff;
- filter: drop-shadow(0 0 4px rgba(0, 220, 255, 0.7));
+ fill: color-mix(in srgb, var(--accent) 20%, transparent);
+ stroke: var(--accent);
+ filter: drop-shadow(0 0 4px color-mix(in srgb, var(--accent) 70%, transparent));
}
100% {
- fill: rgba(0, 220, 255, 0.35);
- stroke: #33e6ff;
- filter: drop-shadow(0 0 8px rgba(0, 220, 255, 1))
- drop-shadow(0 0 16px rgba(0, 220, 255, 0.6));
+ fill: color-mix(in srgb, var(--accent) 35%, transparent);
+ stroke: color-mix(in srgb, var(--accent) 80%, white);
+ filter: drop-shadow(0 0 8px var(--accent))
+ drop-shadow(0 0 16px color-mix(in srgb, var(--accent) 60%, transparent));
}
}
@@ -76,15 +76,15 @@
}
.prompt-marker.positive {
- fill: #10b981;
+ fill: var(--positive);
stroke: #ffffff;
- filter: drop-shadow(0 0 3px rgba(16, 185, 129, 0.9));
+ filter: drop-shadow(0 0 3px color-mix(in srgb, var(--positive) 90%, transparent));
}
.prompt-marker.negative {
- fill: #ef4444;
+ fill: var(--negative);
stroke: #ffffff;
- filter: drop-shadow(0 0 3px rgba(239, 68, 68, 0.9));
+ filter: drop-shadow(0 0 3px color-mix(in srgb, var(--negative) 90%, transparent));
}
.viewfinder-hint {
diff --git a/Features/PhysicalAssets/VideoScanPicker.razor.css b/Features/PhysicalAssets/VideoScanPicker.razor.css
index 4b777fb..d48fe70 100644
--- a/Features/PhysicalAssets/VideoScanPicker.razor.css
+++ b/Features/PhysicalAssets/VideoScanPicker.razor.css
@@ -32,8 +32,8 @@
.item-polygon {
pointer-events: auto;
cursor: pointer;
- fill: rgb(255 193 7 / 20%);
- stroke: #ffc107;
+ fill: color-mix(in srgb, var(--accent) 20%, transparent);
+ stroke: var(--accent);
stroke-width: 0.5;
transition:
fill 0.15s,
@@ -41,39 +41,39 @@
}
.item-polygon:hover {
- fill: rgb(255 193 7 / 40%);
+ fill: color-mix(in srgb, var(--accent) 40%, transparent);
}
.item-polygon.selected {
- fill: rgb(25 135 84 / 35%);
- stroke: #75ffba;
+ fill: color-mix(in srgb, var(--positive) 35%, transparent);
+ stroke: var(--positive);
stroke-width: 0.7;
}
.item-polygon.selected:hover {
- fill: rgb(25 135 84 / 50%);
+ fill: color-mix(in srgb, var(--positive) 50%, transparent);
}
.item-region {
position: absolute;
- border: 3px solid #ffc107;
- background: rgb(255 193 7 / 12%);
+ border: 3px solid var(--accent);
+ background: color-mix(in srgb, var(--accent) 12%, transparent);
padding: 0;
cursor: pointer;
z-index: 2;
}
.item-region.selected {
- border-color: #75ffba;
- background: rgb(25 135 84 / 25%);
+ border-color: var(--positive);
+ background: color-mix(in srgb, var(--positive) 25%, transparent);
}
.item-region span {
position: absolute;
top: 0;
left: 0;
- background: #172838;
- color: white;
+ background: var(--brand-strong);
+ color: var(--text-inverse);
padding: 2px 5px;
font-size: 0.8rem;
max-width: 100%;
diff --git a/wwwroot/app.css b/wwwroot/app.css
index 232e6c4..17bf892 100644
--- a/wwwroot/app.css
+++ b/wwwroot/app.css
@@ -375,6 +375,33 @@ a {
background-color: var(--brand-surface) !important;
}
+.badge.bg-success {
+ color: var(--text-inverse);
+ background-color: var(--positive-text) !important;
+}
+
+.badge.bg-warning {
+ background-color: var(--accent-surface) !important;
+}
+
+.badge.bg-info {
+ color: var(--brand);
+ background-color: var(--brand-surface) !important;
+}
+
+.btn-success {
+ --bs-btn-bg: var(--positive-text);
+ --bs-btn-border-color: var(--positive-text);
+ --bs-btn-color: var(--text-inverse);
+ --bs-btn-hover-bg: var(--positive);
+ --bs-btn-hover-border-color: var(--positive);
+ --bs-btn-hover-color: var(--text-inverse);
+ --bs-btn-active-bg: var(--positive);
+ --bs-btn-active-border-color: var(--positive);
+ --bs-btn-disabled-bg: var(--positive-text);
+ --bs-btn-disabled-border-color: var(--positive-text);
+}
+
.alert {
--bs-alert-border-radius: var(--radius-md);
border: 1px solid var(--glass-border);