Redesign dashboard: refined-minimal layout with product thumbnails

Move the dashboard from boxed glass cards to an airy, hairline-separated
layout where type and whitespace carry the page, then surface listing
images as the visual anchor.

- Solid surfaces (drop backdrop-filter glass, per spec); calm flat field
  with a single cool top-glow replacing the drifting aurora and dot-grid.
- Recent results / recent alerts are now product feeds with 52px
  thumbnails, listing titles, and price; image_url threaded through
  ResultRow/AlertRow (data already stored, just unsurfaced).
- ListResults gains an OnlyAlerted filter so the alerts panel reuses its
  decryption path instead of a raw query.
- Dashboard recent results capped at 8 for a balanced snapshot.
This commit is contained in:
prosolis
2026-06-20 12:42:00 -07:00
parent ed03494edf
commit 9a7f8b47a3
8 changed files with 647 additions and 392 deletions

View File

@@ -1,9 +1,9 @@
/* Veola — Sega-blue palette and component overrides for Tailwind play CDN. */
:root {
--bg: #1a2b6d;
--surface: #1f3380;
--surface-2: #243a93;
--bg: #16224f;
--surface: #243a93;
--surface-2: #2c47ad;
--accent: #00a4e4;
--yellow: #f5c400;
--text: #ffffff;
@@ -25,59 +25,175 @@ html, body {
a { color: var(--accent); }
a:hover { text-decoration: underline; }
/* Solid surfaces, per the spec's "blue mode": no glassmorphism, no blur. A
clear value gap between --bg and --surface is what lifts cards off the
page; a 1px top highlight + soft blue shadow gives the cartridge edge. */
.v-card {
position: relative;
isolation: isolate;
background:
linear-gradient(180deg, rgba(36, 58, 147, 0.82), rgba(31, 51, 128, 0.82));
backdrop-filter: blur(10px) saturate(140%);
-webkit-backdrop-filter: blur(10px) saturate(140%);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
box-shadow: var(--shadow);
border-radius: 12px;
box-shadow: 0 1px 2px rgba(0, 0, 40, 0.25);
transition:
transform 180ms ease,
box-shadow 180ms ease,
border-color 180ms ease;
}
.v-card::before {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(135deg,
rgba(0, 164, 228, 0.65) 0%,
rgba(245, 196, 0, 0.30) 45%,
rgba(255, 255, 255, 0.04) 100%);
-webkit-mask:
linear-gradient(#000 0 0) content-box,
linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
z-index: -1;
}
.v-card:hover {
transform: translateY(-2px);
box-shadow:
0 10px 28px rgba(0, 0, 80, 0.55),
0 0 0 1px rgba(0, 164, 228, 0.30);
border-color: rgba(255, 255, 255, 0.2);
box-shadow: 0 2px 8px rgba(0, 0, 40, 0.3);
}
.v-card-flat {
position: relative;
isolation: isolate;
background:
linear-gradient(180deg, rgba(36, 58, 147, 0.70), rgba(31, 51, 128, 0.70));
backdrop-filter: blur(8px) saturate(130%);
-webkit-backdrop-filter: blur(8px) saturate(130%);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
}
.v-divider { border-top: 1px solid var(--border); }
/* --- Console readout / page header system -------------------------------
The dashboard's signature: a mono "terminal" kicker over a heavy display
title, and the Spend/Saved pair styled as an arcade scoreboard readout.
JetBrains Mono is the machine voice here (labels, counts); Outfit carries
the title. */
.v-kicker {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 0.72rem;
font-weight: 500;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--accent);
}
.v-kicker .v-kicker-sep { color: var(--text-2); opacity: 0.6; }
.v-kicker .v-kicker-dim { color: var(--text-2); }
/* --- Refined header + metric system -------------------------------------
Calm and spacious: type and whitespace carry the page. No glow, no
uppercase-mono labels, no accent rules. Mono is reserved for the numbers
themselves; Outfit carries everything else. */
.v-page-title {
font-family: 'Outfit', system-ui, sans-serif;
font-size: 1.9rem;
font-weight: 600;
letter-spacing: -0.015em;
line-height: 1.1;
text-shadow: none;
}
.v-page-sub {
color: var(--text-2);
font-size: 0.95rem;
margin-top: 0.3rem;
}
/* Hero metric: a small quiet label over a large clean number. Presented in
open space with a hairline, not a boxed card. */
.v-metric-label {
color: var(--text-2);
font-size: 0.82rem;
letter-spacing: 0.01em;
}
.v-metric-value {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: clamp(2rem, 3.8vw, 2.75rem);
font-weight: 600;
line-height: 1.05;
margin-top: 0.45rem;
}
.v-metric-value.v-price-deal { color: var(--success); }
.v-metric-sub {
color: var(--text-2);
font-size: 0.82rem;
margin-top: 0.5rem;
}
/* Secondary stat: smaller label/number pair for the supporting counts. */
.v-stat-label {
color: var(--text-2);
font-size: 0.78rem;
letter-spacing: 0.01em;
}
.v-stat-value {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 1.5rem;
font-weight: 600;
line-height: 1;
margin-top: 0.3rem;
}
/* Understated section heading for list panels — sized to sit below the page
title, sentence case, no glow. */
.v-section-title {
font-family: 'Outfit', system-ui, sans-serif;
font-size: 0.95rem;
font-weight: 600;
letter-spacing: 0;
color: var(--text);
text-shadow: none;
}
/* Hairline used to separate open sections instead of boxing them. */
.v-rule {
border: 0;
border-top: 1px solid var(--border);
}
/* Quiet empty-state line. */
.v-empty {
color: var(--text-2);
font-size: 0.9rem;
padding: 0.4rem 0;
}
/* --- Product feed (thumbnails) ------------------------------------------
Listing thumbnails are what give the data life. A square cropped image,
the listing title as the line that earns its space, price on the right. */
.v-thumb {
width: 52px;
height: 52px;
flex: none;
border-radius: 8px;
object-fit: cover;
background: var(--surface-2);
border: 1px solid var(--border);
display: block;
}
.v-thumb-empty {
display: flex;
align-items: center;
justify-content: center;
color: var(--text-2);
font-size: 1.1rem;
font-weight: 600;
text-transform: uppercase;
}
.v-feed { display: block; }
.v-feed-row {
display: flex;
align-items: center;
gap: 0.85rem;
padding: 0.7rem 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.v-feed-row:last-child { border-bottom: 0; }
.v-feed-name {
display: block;
font-weight: 500;
color: var(--text);
}
a.v-feed-name:hover { color: var(--accent); text-decoration: none; }
.v-feed-meta {
color: var(--text-2);
font-size: 0.8rem;
margin-top: 0.15rem;
}
.v-feed-price { flex: none; white-space: nowrap; }
.v-btn {
background: var(--accent);
color: white;
@@ -212,21 +328,22 @@ a:hover { text-decoration: underline; }
table.v-table { width: 100%; border-collapse: collapse; }
table.v-table th {
text-align: left;
font-size: 0.75rem;
letter-spacing: 0.05em;
text-transform: uppercase;
font-size: 0.74rem;
font-weight: 500;
letter-spacing: 0.01em;
color: var(--text-2);
padding: 0.6rem 0.75rem;
padding: 0.5rem 0.75rem 0.7rem;
border-bottom: 1px solid var(--border);
}
table.v-table td {
padding: 0.7rem 0.75rem;
border-bottom: 1px solid var(--border);
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
vertical-align: middle;
}
/* No zebra — hairline rows and a faint hover keep it calm but scannable. */
table.v-table td { transition: background 140ms ease; }
table.v-table tr { transition: transform 140ms ease; }
table.v-table tbody tr:hover td { background: rgba(0, 164, 228, 0.08); }
table.v-table tbody tr:last-child td { border-bottom: 0; }
table.v-table tbody tr:hover td { background: rgba(255, 255, 255, 0.04); }
.v-error-text { color: var(--danger); font-size: 0.85rem; }
.v-muted { color: var(--text-2); }