UI: slab brand mark replacing checkmark, header aligned to page column, flatter controls

This commit is contained in:
Barely Removable 2026-08-22 12:20:56 -07:00
parent fa5467e55d
commit 527558bef2
4 changed files with 73 additions and 46 deletions

View file

@ -173,7 +173,7 @@ function renderGradeBlock(g, opts = {}) {
}).join(''); }).join('');
return ` return `
<div class="note">A photo can't show surface scratches, print lines, or light edge wear the <div class="note note-warn">A photo can't show surface scratches, print lines, or light edge wear the
way a grader's raking light does — and a seller's listing photo is often lit to hide them. way a grader's raking light does — and a seller's listing photo is often lit to hide them.
Treat this as a rough screen, not a prediction of what it comes back as.</div> Treat this as a rough screen, not a prediction of what it comes back as.</div>
<div style="margin:14px 0">${renderSlab(g, opts)}</div> <div style="margin:14px 0">${renderSlab(g, opts)}</div>

View file

@ -1,6 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<rect width="128" height="128" rx="24" fill="#16224a"/> <!-- Matches the in-app brand mark: a graded slab — case, label, and the
<rect x="34" y="24" width="60" height="80" rx="8" fill="#2f6fd0" stroke="#35c6a8" stroke-width="3"/> card sealed inside. Kept in step with .brand-mark in style.css. -->
<path d="M48 66 L60 78 L84 50" fill="none" stroke="#ffffff" stroke-width="7" <rect width="128" height="128" rx="26" fill="#0e1522"/>
stroke-linecap="round" stroke-linejoin="round"/> <rect x="38.5" y="20.5" width="51" height="87" rx="9" fill="none"
stroke="#4d9bff" stroke-width="5"/>
<rect x="48" y="31" width="32" height="9" rx="3.5" fill="#3ddcc0"/>
<rect x="48" y="49" width="32" height="45" rx="4.5" fill="#4d9bff" opacity="0.34"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 367 B

After

Width:  |  Height:  |  Size: 548 B

Before After
Before After

View file

@ -23,14 +23,24 @@
<body> <body>
<header class="topbar"> <header class="topbar">
<div class="topbar-inner">
<div class="brand"> <div class="brand">
<span class="brand-mark"></span> <!-- A graded slab: the case, its label, and the card sealed inside.
This is the literal object the whole app estimates, so it says
what this is without belonging to any one card game — which a
checkmark (or any single game's iconography) doesn't. -->
<svg class="brand-mark" viewBox="0 0 24 30" fill="none" aria-hidden="true" focusable="false">
<rect class="mark-case" x="1.15" y="1.15" width="21.7" height="27.7" rx="3.4" stroke-width="1.7"/>
<rect class="mark-label" x="5.2" y="5.3" width="13.6" height="3.3" rx="1.3"/>
<rect class="mark-card" x="5.2" y="11.9" width="13.6" height="12.9" rx="1.6"/>
</svg>
<h1>Card Grader</h1> <h1>Card Grader</h1>
</div> </div>
<div class="topbar-actions"> <div class="topbar-actions">
<button id="btn-install" class="btn" hidden>Install app</button> <button id="btn-install" class="btn" hidden>Install app</button>
<button id="btn-settings" class="btn btn-quiet">Settings</button> <button id="btn-settings" class="btn btn-quiet">Settings</button>
</div> </div>
</div>
</header> </header>
<div id="banner" class="banner" hidden></div> <div id="banner" class="banner" hidden></div>

View file

@ -90,24 +90,34 @@ main {
.topbar { .topbar {
position: sticky; top: 0; z-index: 20; position: sticky; top: 0; z-index: 20;
display: flex; align-items: center; justify-content: space-between;
gap: 16px; flex-wrap: wrap;
padding: 14px 24px;
background: rgba(8, 12, 20, .82); background: rgba(8, 12, 20, .82);
-webkit-backdrop-filter: blur(14px) saturate(1.4); -webkit-backdrop-filter: blur(14px) saturate(1.4);
backdrop-filter: blur(14px) saturate(1.4); backdrop-filter: blur(14px) saturate(1.4);
border-bottom: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline);
} }
/* 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
panels floated in a narrow centre column was the single thing making
this read as unfinished rather than designed. */
.topbar-inner {
max-width: 940px; margin: 0 auto;
display: flex; align-items: center; justify-content: space-between;
gap: 16px; flex-wrap: wrap;
padding: 13px 24px;
}
/* Holo-foil edge: the rainbow sweep you get tilting a refractor. Purely /* Holo-foil edge: the rainbow sweep you get tilting a refractor. Purely
decorative and it encodes nothing, so it's free to be vivid here where no decorative and it encodes nothing, so it's free to be vivid here where no
number lives. Kept from the light theme it's the app's signature. */ number lives but dialled back from the original full-strength stripe,
which was the loudest thing on an otherwise restrained page. */
.topbar::after { .topbar::after {
content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1.5px;
background: linear-gradient(90deg, background: linear-gradient(90deg,
#ff5f8f, #ffb347, #ffe66d, #3ddcc0, #4d9bff, #a97bff, #ff5f8f); #ff5f8f, #ffb347, #ffe66d, #3ddcc0, #4d9bff, #a97bff, #ff5f8f);
opacity: .9; opacity: .5;
}
.topbar h1 {
color: #fff; font-size: 16px; font-weight: 600; letter-spacing: -.005em;
} }
.topbar h1 { color: #fff; letter-spacing: .005em; }
.topbar .btn { .topbar .btn {
background: var(--accent-2); color: #04231d; background: var(--accent-2); color: #04231d;
@ -120,41 +130,34 @@ main {
} }
.topbar .btn-quiet:hover { background: rgba(255, 255, 255, .13); filter: none; } .topbar .btn-quiet:hover { background: rgba(255, 255, 255, .13); filter: none; }
.brand { display: flex; align-items: center; gap: 12px; } .brand { display: flex; align-items: center; gap: 11px; }
/* Brand mark: a graded slab, drawn in plain CSS. A rounded card outline with /* Brand mark: a graded slab case, label, and the card sealed inside.
a check "this one's been looked at" rather than any single game's Drawn as flat two-tone geometry rather than the gradient-blob-with-a-
iconography, since this grades anything PSA does. */ checkmark it replaced: that read as a generic app badge and said nothing
.brand-mark { about grading. Thin strokes and no glow keep it legible at 22px. */
position: relative; flex: none; .brand-mark { flex: none; width: 21px; height: 26px; display: block; }
width: 30px; height: 30px; border-radius: 8px; .brand-mark .mark-case { stroke: var(--accent); }
background: linear-gradient(150deg, var(--accent-2), var(--accent) 70%, #a97bff); .brand-mark .mark-label { fill: var(--accent-2); }
box-shadow: .brand-mark .mark-card { fill: var(--accent); opacity: .34; }
inset 0 0 0 1.5px rgba(255, 255, 255, .34),
0 2px 10px rgba(77, 155, 255, .45);
}
.brand-mark::after {
content: ''; position: absolute; left: 8px; top: 8px;
width: 9px; height: 5.5px;
border-left: 2.2px solid #04231d; border-bottom: 2.2px solid #04231d;
transform: rotate(-45deg);
}
.topbar-actions { display: flex; gap: 9px; flex-wrap: wrap; } .topbar-actions { display: flex; gap: 9px; flex-wrap: wrap; }
/* ------------------------------------------------------------ controls */ /* ------------------------------------------------------------ controls */
/* Flat accent fill with a plain shadow. The previous gradient-plus-coloured-
glow made every button feel like the page's main event, which flattened
the hierarchy the grade result should be the loudest thing here, not
the controls. */
.btn { .btn {
font: inherit; font-weight: 550; font-size: 14.5px; font: inherit; font-weight: 600; font-size: 14.5px;
padding: 9px 17px; border-radius: 10px; cursor: pointer; padding: 9px 17px; border-radius: 9px; cursor: pointer;
background: linear-gradient(180deg, background: var(--accent);
color-mix(in srgb, var(--accent) 92%, #fff),
var(--accent));
color: #04121f; color: #04121f;
border: 1px solid transparent; border: 1px solid transparent;
text-decoration: none; display: inline-block; line-height: 1.4; text-decoration: none; display: inline-block; line-height: 1.4;
white-space: nowrap; white-space: nowrap;
box-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(77, 155, 255, .22); box-shadow: 0 1px 2px rgba(0, 0, 0, .35);
transition: filter .13s ease, transform .13s ease, background .13s ease; transition: filter .13s ease, transform .13s ease, background .13s ease;
} }
.btn:hover { filter: brightness(1.08); } .btn:hover { filter: brightness(1.08); }
@ -217,9 +220,15 @@ textarea { resize: vertical; min-height: 64px; }
box-shadow: var(--shadow); box-shadow: var(--shadow);
} }
/* A rule under the heading gives each panel a real header band instead of
a title floating above loose content the cheapest structural cue there
is, and what most separates "designed" from "stacked divs". Suppressed
inside modals, which set their own tighter header treatment below. */
.panel-head { .panel-head {
display: flex; align-items: baseline; justify-content: space-between; display: flex; align-items: baseline; justify-content: space-between;
gap: 12px; flex-wrap: wrap; margin-bottom: 6px; gap: 12px; flex-wrap: wrap;
padding-bottom: 13px; margin-bottom: 4px;
border-bottom: 1px solid var(--hairline);
} }
.hint { font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; } .hint { font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }
.search-status { .search-status {
@ -341,7 +350,7 @@ textarea { resize: vertical; min-height: 64px; }
line under the title. Pin it: title takes the slack, × stays top-right. */ line under the title. Pin it: title takes the slack, × stays top-right. */
.modal .panel-head { .modal .panel-head {
flex-wrap: nowrap; align-items: flex-start; gap: 16px; flex-wrap: nowrap; align-items: flex-start; gap: 16px;
margin-bottom: 14px; padding-bottom: 0; margin-bottom: 12px; border-bottom: none;
} }
.modal .panel-head h2 { flex: 1; min-width: 0; } .modal .panel-head h2 { flex: 1; min-width: 0; }
.modal .close { flex: none; margin-top: 1px; } .modal .close { flex: none; margin-top: 1px; }
@ -365,12 +374,17 @@ textarea { resize: vertical; min-height: 64px; }
} }
.spacer { flex: 1; } .spacer { flex: 1; }
/* Informational by default accent, not amber. The shooting guidance
("front straight-on is the minimum") is instruction, not a caution, and
colouring it like a warning spent the page's only alarm colour on
something that never needed it. Genuine cautions opt into .note-warn. */
.note { .note {
font-size: 13px; line-height: 1.55; color: var(--ink-2); font-size: 13px; line-height: 1.55; color: var(--ink-2);
background: var(--surface-2); border: 1px solid var(--hairline); background: var(--surface-2); border: 1px solid var(--hairline);
border-left: 3px solid var(--warning); border-left: 3px solid var(--accent);
border-radius: 8px; padding: 12px 14px; margin-top: 14px; border-radius: 8px; padding: 12px 14px; margin-top: 14px;
} }
.note-warn { border-left-color: var(--warning); }
.note-flag { .note-flag {
border-left-color: var(--critical); border-left-color: var(--critical);
color: var(--critical); color: var(--critical);