diff --git a/static/icon.svg b/static/icon.svg
index bb5ae46..32fc46a 100644
--- a/static/icon.svg
+++ b/static/icon.svg
@@ -1,9 +1,12 @@
diff --git a/static/index.html b/static/index.html
index aa253d9..580165e 100644
--- a/static/index.html
+++ b/static/index.html
@@ -6,13 +6,13 @@
Card Grader
-
+
-
+
diff --git a/static/manifest.json b/static/manifest.json
index ed155c0..25bc78b 100644
--- a/static/manifest.json
+++ b/static/manifest.json
@@ -6,8 +6,8 @@
"scope": "__BASE__/",
"display": "standalone",
"orientation": "portrait",
- "background_color": "#0a0f1c",
- "theme_color": "#16224a",
+ "background_color": "#eef0f4",
+ "theme_color": "#ffffff",
"icons": [
{ "src": "__BASE__/static/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
{ "src": "__BASE__/static/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
diff --git a/static/style.css b/static/style.css
index 06e61a6..937af26 100644
--- a/static/style.css
+++ b/static/style.css
@@ -1,56 +1,64 @@
-/* Card Grader — "collector-grade" dark theme.
+/* Card Grader — slab-house theme: silver ground, red accent, black text.
Card-type agnostic (Pokemon, sports, TCG).
- Committed dark, not a dark *mode*. The light palette was removed rather
- than kept as a fallback: this is one deliberate look, and a half-tended
- second palette is how a design drifts out of sync with itself. Phones in
- light mode get this same design, which is the intent.
+ Committed light, not a light *mode*. A single deliberate look; a
+ half-tended second palette is how a design drifts out of sync with
+ itself. Phones in dark mode get this same design, which is the intent.
- Colour discipline (carried over from the light theme, still true):
+ Colour discipline:
- Grade severities ride a lightness ladder (none -> minor -> moderate ->
major), so the table still reads as escalating severity in greyscale.
- - Red is reserved for "you should distrust this number" (unmeasurable
- edges, a low grade) — never decorative.
+ - Red is the BRAND here, so it can no longer also mean "distrust this
+ number". Severity red is therefore a distinctly darker, browner red
+ (--critical) than the brand's (--brand), and severity is never carried
+ by colour alone — the pills and chips carry a word too.
- Foil/holo treatment is reserved for a 10. Every card shimmering would
say nothing. */
:root {
- color-scheme: dark;
+ color-scheme: light;
- /* Ground is nearly black with a blue cast — a neutral grey ground makes
- the card thumbnails look washed, since most slabs and holos are cool. */
- --ground: #06090f;
- --surface-1: #0e1522;
- --surface-2: #141d2e; /* raised: modal, slab body, hover */
- --ink: #eef3fb;
- --ink-2: #aab8cf;
- --muted: #7e8da6;
- --hairline: #1c2740;
- --rule: #2a3852;
+ /* Ground is a faint cool grey rather than pure white — slab labels, card
+ thumbnails and the panels all sit ON something, and pure white leaves
+ nothing for a white label to separate from. */
+ --ground: #eef0f4;
+ --surface-1: #ffffff;
+ --surface-2: #f4f6f9; /* raised: modal insets, slab body, hover */
+ --ink: #10141c;
+ --ink-2: #47505f;
+ --muted: #6b7484;
+ --hairline: #e2e6ec;
+ --rule: #ccd2dc;
- --accent: #4d9bff;
- --accent-2: #3ddcc0;
- --accent-soft: rgba(77, 155, 255, .14);
+ /* PSA-style red. Used for brand chrome and primary actions. */
+ --brand: #c8102e;
+ --brand-dark: #a20d25;
+ --brand-soft: rgba(200, 16, 46, .09);
- /* Label chrome only — the red band on a slab label, never a severity.
- Deliberately deeper than --critical (#f2555a) so the two don't read as
- the same signal: this one is decoration on a physical-looking object,
- that one means "distrust this number". */
- --slab-red: #c8102e;
- --slab-face: #eceef2;
+ --accent: var(--brand);
+ --accent-2: #1c2b45; /* deep navy — the label's text/secondary tone */
+ --accent-soft: var(--brand-soft);
+
+ /* Slab label tokens. On a light ground the label is near-white and
+ separates by its border and shadow rather than by being lighter than
+ everything else, which is how a real label sits on a page. */
+ --slab-red: var(--brand);
+ --slab-face: #f7f8fa;
--slab-ink: #0c111a;
--slab-ink-2: #5a6474;
- --slab-rule: #b9c0cc;
+ --slab-rule: #c2c8d2;
- --good: #23c55e;
- --good-text: #4ade80;
- --warning: #f5a33c;
- --critical: #f2555a;
+ --good: #16853f;
+ --good-text: #127034;
+ --warning: #a86200;
+ /* Deliberately darker and browner than --brand so a severity red never
+ reads as the brand red sitting next to it. */
+ --critical: #8f1d1d;
- --ring: rgba(255, 255, 255, .12);
- --shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
- --shadow: 0 2px 6px rgba(0, 0, 0, .5), 0 16px 40px rgba(0, 0, 0, .55);
- --shadow-lg: 0 4px 12px rgba(0, 0, 0, .55), 0 28px 70px rgba(0, 0, 0, .65);
+ --ring: rgba(16, 20, 28, .12);
+ --shadow-sm: 0 1px 2px rgba(16, 20, 28, .08);
+ --shadow: 0 1px 2px rgba(16, 20, 28, .06), 0 6px 18px rgba(16, 20, 28, .08);
+ --shadow-lg: 0 2px 6px rgba(16, 20, 28, .08), 0 18px 44px rgba(16, 20, 28, .14);
--radius: 14px;
--radius-lg: 18px;
@@ -65,13 +73,13 @@ body {
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
- /* Two broad, very low-opacity glows instead of the old dot grid. A tiled
- pattern reads as "utility dashboard"; soft light on a dark ground reads
- as depth. Fixed so it doesn't slide around under the content. */
+ /* A single very faint warm-red wash at the top, so the ground isn't a
+ flat slab of grey but doesn't compete with the content either. The
+ dark theme's two coloured glows were far too strong to survive on a
+ light ground — they read as stains rather than depth. */
background-color: var(--ground);
background-image:
- radial-gradient(900px 600px at 12% -8%, rgba(77, 155, 255, .10), transparent 60%),
- radial-gradient(760px 520px at 92% 4%, rgba(61, 220, 192, .07), transparent 62%);
+ radial-gradient(1100px 520px at 50% -12%, rgba(200, 16, 46, .06), transparent 70%);
background-attachment: fixed;
background-repeat: no-repeat;
min-height: 100vh;
@@ -105,10 +113,10 @@ main {
.topbar {
position: relative; /* containing block for the holo edge below */
- background: rgba(8, 12, 20, .82);
- -webkit-backdrop-filter: blur(14px) saturate(1.4);
- backdrop-filter: blur(14px) saturate(1.4);
- border-bottom: 1px solid var(--hairline);
+ background: rgba(255, 255, 255, .88);
+ -webkit-backdrop-filter: blur(14px) saturate(1.3);
+ backdrop-filter: blur(14px) saturate(1.3);
+ border-bottom: 1px solid var(--rule);
}
/* The bar spans the viewport, but its CONTENTS line up with the page
column — the logo sitting hard against a 3440px screen edge while the
@@ -125,25 +133,20 @@ main {
number lives — but dialled back from the original full-strength stripe,
which was the loudest thing on an otherwise restrained page. */
.topbar::after {
- content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1.5px;
+ content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
background: linear-gradient(90deg,
#ff5f8f, #ffb347, #ffe66d, #3ddcc0, #4d9bff, #a97bff, #ff5f8f);
- opacity: .5;
+ opacity: .8; /* light ground needs more of it than dark did to register */
}
.topbar h1 {
- color: #fff; font-size: 16px; font-weight: 600; letter-spacing: -.005em;
+ color: var(--ink); font-size: 16px; font-weight: 700; letter-spacing: -.005em;
}
-.topbar .btn {
- background: var(--accent-2); color: #04231d;
- border-color: transparent; font-weight: 650;
-}
-.topbar .btn:hover { filter: brightness(1.08); }
-.topbar .btn-quiet {
- background: rgba(255, 255, 255, .07); color: var(--ink);
- border-color: rgba(255, 255, 255, .16); font-weight: 550;
-}
-.topbar .btn-quiet:hover { background: rgba(255, 255, 255, .13); filter: none; }
+/* The header's buttons inherit the page's button styling now. On the dark
+ theme they needed their own palette to stay legible against the bar;
+ against a white bar they don't, and overriding them only made the header
+ inconsistent with every other control. */
+.topbar .btn-quiet:hover { background: var(--surface-2); filter: none; }
.brand { display: flex; align-items: center; gap: 11px; }
@@ -152,12 +155,15 @@ main {
checkmark it replaced: that read as a generic app badge and said nothing
about grading. Thin strokes and no glow keep it legible at 22px. */
.brand-mark { flex: none; width: 18px; height: 27px; display: block; }
-.brand-mark .mark-case { stroke: var(--accent); }
-.brand-mark .mark-label { fill: var(--accent-2); }
+/* Case in the label's navy, label band in brand red — the same two-tone
+ relationship a real slab label has, and it keeps the mark legible
+ against a white bar where an all-red mark would shout. */
+.brand-mark .mark-case { stroke: var(--accent-2); }
+.brand-mark .mark-label { fill: var(--brand); }
/* .48, not the .34 this started at — below roughly .45 the card window
stops separating from the case interior at topbar size and the mark
collapses into an empty outline. Checked by rendering it at 42px. */
-.brand-mark .mark-card { fill: var(--accent); opacity: .48; }
+.brand-mark .mark-card { fill: var(--accent-2); opacity: .48; }
.topbar-actions { display: flex; gap: 9px; flex-wrap: wrap; }
@@ -170,15 +176,15 @@ main {
.btn {
font: inherit; font-weight: 600; font-size: 14.5px;
padding: 9px 17px; border-radius: 9px; cursor: pointer;
- background: var(--accent);
- color: #04121f;
+ background: var(--brand);
+ color: #fff;
border: 1px solid transparent;
text-decoration: none; display: inline-block; line-height: 1.4;
white-space: nowrap;
- box-shadow: 0 1px 2px rgba(0, 0, 0, .35);
+ box-shadow: var(--shadow-sm);
transition: filter .13s ease, transform .13s ease, background .13s ease;
}
-.btn:hover { filter: brightness(1.08); }
+.btn:hover { background: var(--brand-dark); filter: none; }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .45; cursor: default; filter: none; box-shadow: none; }
.btn:focus-visible, .close:focus-visible {
@@ -186,10 +192,10 @@ main {
}
.btn-quiet {
- background: var(--surface-2); color: var(--ink);
+ background: var(--surface-1); color: var(--ink);
border-color: var(--rule); box-shadow: var(--shadow-sm);
}
-.btn-quiet:hover { background: #1a2439; filter: none; }
+.btn-quiet:hover { background: var(--surface-2); filter: none; }
.btn-danger {
background: transparent; color: var(--critical);
@@ -205,7 +211,7 @@ main {
padding: 9px 12px;
border: 1px solid var(--rule);
border-radius: 10px;
- background: #0a1120;
+ background: var(--surface-1);
color: var(--ink);
width: 100%;
transition: border-color .13s ease, box-shadow .13s ease;
@@ -213,8 +219,8 @@ main {
.input::placeholder, textarea::placeholder { color: var(--muted); }
.input:focus, select:focus, textarea:focus {
outline: none;
- border-color: var(--accent);
- box-shadow: 0 0 0 3px var(--accent-soft);
+ border-color: var(--brand);
+ box-shadow: 0 0 0 3px var(--brand-soft);
}
textarea { resize: vertical; min-height: 64px; }
@@ -227,11 +233,12 @@ textarea { resize: vertical; min-height: 64px; }
/* -------------------------------------------------------------- panels */
+/* The dark theme lit each panel with a top white-gradient highlight; on a
+ white surface that's invisible, so panels separate from the ground by
+ border and shadow instead. */
.panel {
position: relative;
- background:
- linear-gradient(180deg, rgba(255, 255, 255, .035), transparent 120px),
- var(--surface-1);
+ background: var(--surface-1);
border: 1px solid var(--hairline);
border-radius: var(--radius-lg);
padding: 22px 24px 24px;
@@ -277,7 +284,7 @@ textarea { resize: vertical; min-height: 64px; }
.cardcell img, .cardcell .thumb-blank {
width: 46px; height: 64px; object-fit: cover;
border-radius: 6px; background: var(--surface-2); flex: none;
- box-shadow: 0 2px 8px rgba(0, 0, 0, .55), inset 0 0 0 1px rgba(255, 255, 255, .07);
+ box-shadow: 0 1px 3px rgba(16, 20, 28, .18), inset 0 0 0 1px rgba(16, 20, 28, .08);
}
.thumb-blank {
display: inline-block;
@@ -345,7 +352,7 @@ textarea { resize: vertical; min-height: 64px; }
.scrim {
position: fixed; inset: 0; z-index: 30;
- background: rgba(3, 5, 10, .68);
+ background: rgba(16, 20, 28, .42);
-webkit-backdrop-filter: blur(3px);
backdrop-filter: blur(3px);
}
@@ -354,9 +361,7 @@ textarea { resize: vertical; min-height: 64px; }
top: 50%; left: 50%; transform: translate(-50%, -50%);
width: min(600px, calc(100vw - 28px));
max-height: min(88vh, 940px); overflow-y: auto;
- background:
- linear-gradient(180deg, rgba(255, 255, 255, .04), transparent 140px),
- var(--surface-1);
+ background: var(--surface-1);
border: 1px solid var(--rule);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
@@ -399,7 +404,9 @@ textarea { resize: vertical; min-height: 64px; }
.note {
font-size: 13px; line-height: 1.55; color: var(--ink-2);
background: var(--surface-2); border: 1px solid var(--hairline);
- border-left: 3px solid var(--accent);
+ /* Navy, not the brand red: this box is instruction, and now that red is
+ the brand colour an informational red bar would read as an alert. */
+ border-left: 3px solid var(--accent-2);
border-radius: 8px; padding: 12px 14px; margin-top: 14px;
}
.note-warn { border-left-color: var(--warning); }
@@ -413,13 +420,15 @@ textarea { resize: vertical; min-height: 64px; }
.banner {
padding: 12px 24px; font-size: 13.5px;
- background: color-mix(in srgb, var(--accent) 22%, var(--ground));
+ background: var(--brand-soft);
color: var(--ink);
- border-bottom: 1px solid var(--hairline);
+ border-bottom: 1px solid var(--rule);
+ box-shadow: inset 3px 0 0 var(--brand);
}
.banner.err {
- background: color-mix(in srgb, var(--critical) 82%, #000);
+ background: var(--critical);
color: #fff;
+ box-shadow: none;
}
.banner ul { margin: 5px 0 0; padding-left: 18px; }