127 lines
5.8 KiB
HTML
127 lines
5.8 KiB
HTML
<!doctype html>
|
|
<html lang="en" data-base="__BASE__">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Card Grader</title>
|
|
<link rel="manifest" href="__BASE__/static/manifest.json">
|
|
<link rel="icon" href="__BASE__/static/icon.svg">
|
|
<meta name="theme-color" content="#16224a">
|
|
<!-- iOS ignores the web app manifest for install behaviour and needs its own
|
|
meta tags. Without these, "Add to Home Screen" on an iPhone produces a
|
|
bookmark that opens in Safari with the address bar showing, rather than
|
|
something that looks and behaves like an installed app. -->
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
<meta name="apple-mobile-web-app-title" content="Card Grader">
|
|
<link rel="apple-touch-icon" href="__BASE__/static/icon-180.png">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap">
|
|
<link rel="stylesheet" href="__BASE__/static/style.css">
|
|
</head>
|
|
<body>
|
|
|
|
<!-- Header and banner stick as one unit, so the banner sits under the header
|
|
by stacking rather than by a hardcoded top offset — that offset silently
|
|
went stale (and overlapped) the moment the header's height changed. -->
|
|
<div class="chrome">
|
|
<header class="topbar">
|
|
<div class="topbar-inner">
|
|
<div class="brand">
|
|
<!-- 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. -->
|
|
<!-- Proportioned to a real slab (18:28 ≈ 0.64, against PSA's ~0.62)
|
|
with restrained corners. Squarer, rounder earlier drafts read as
|
|
a phone icon at topbar size, which is the failure mode for any
|
|
small rounded rectangle. -->
|
|
<svg class="brand-mark" viewBox="0 0 20 30" fill="none" aria-hidden="true" focusable="false">
|
|
<rect class="mark-case" x="1" y="1" width="18" height="28" rx="2.6" stroke-width="1.6"/>
|
|
<rect class="mark-label" x="4.2" y="4.6" width="11.6" height="3" rx="1.2"/>
|
|
<rect class="mark-card" x="4.2" y="10.6" width="11.6" height="14.2" rx="1.4"/>
|
|
</svg>
|
|
<h1>Card Grader</h1>
|
|
</div>
|
|
<div class="topbar-actions">
|
|
<button id="btn-install" class="btn" hidden>Install app</button>
|
|
<button id="btn-settings" class="btn btn-quiet">Settings</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div id="banner" class="banner" hidden></div>
|
|
</div><!-- /.chrome -->
|
|
|
|
<main>
|
|
|
|
<!-- ------------------------------------------------------------- grade -->
|
|
<section class="panel">
|
|
<div class="panel-head">
|
|
<h2>Grade a card</h2>
|
|
<span class="hint" id="model-cost-hint"></span>
|
|
</div>
|
|
<div class="note">Front straight-on is the minimum. Adding the back lets it judge
|
|
back centering, and it can only tell a print line from a crease — about five PSA
|
|
grades apart — if it can check both sides.</div>
|
|
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:12px">
|
|
<button id="btn-camera" class="btn">Take photo…</button>
|
|
<button id="btn-grade" class="btn btn-quiet">Choose from library…</button>
|
|
<input id="grade-label" class="input" placeholder="Card name (optional, for your history)" style="flex:1 1 220px">
|
|
</div>
|
|
<input id="grade-file" type="file" accept="image/*" multiple hidden>
|
|
<!-- capture="environment" is what skips straight to the rear camera
|
|
instead of showing the OS's photo-library picker — the library
|
|
input above already offers a camera option buried in that picker
|
|
on most phones, but a dedicated button is one tap instead of two,
|
|
and makes "take a fresh photo" the obvious default rather than
|
|
something you have to notice. Desktop browsers just ignore
|
|
`capture` and fall back to a normal file picker. -->
|
|
<input id="grade-camera" type="file" accept="image/*" capture="environment" hidden>
|
|
<div id="grade-status" class="search-status" hidden></div>
|
|
<div id="grade-review"></div>
|
|
</section>
|
|
|
|
<!-- Fallback photo picker for Regrade on a card saved before this feature
|
|
existed (or otherwise missing its stored photo) — regradeTargetId in
|
|
app.js tracks which row this fires for. -->
|
|
<input id="regrade-file" type="file" accept="image/*" multiple hidden>
|
|
|
|
<!-- ----------------------------------------------------------- history -->
|
|
<section class="panel">
|
|
<div class="panel-head">
|
|
<h2>History</h2>
|
|
<span class="hint" id="history-count"></span>
|
|
</div>
|
|
<div class="table-scroll">
|
|
<table class="grid" id="history-table">
|
|
<thead>
|
|
<tr>
|
|
<th class="col-card">Card</th>
|
|
<th>Grade</th>
|
|
<th>Confidence</th>
|
|
<th>When</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="history-body"></tbody>
|
|
</table>
|
|
</div>
|
|
<p id="history-empty" class="empty" hidden>No cards graded yet — every estimate you run
|
|
gets saved here, with the full category breakdown.</p>
|
|
</section>
|
|
|
|
</main>
|
|
|
|
<!-- ----------------------------------------------------------- settings -->
|
|
<div id="modal-scrim" class="scrim" hidden></div>
|
|
<div id="settings-modal" class="modal" hidden role="dialog" aria-label="Settings"></div>
|
|
|
|
<!-- ------------------------------------------------------- history entry -->
|
|
<div id="entry-scrim" class="scrim" hidden></div>
|
|
<div id="entry-modal" class="modal" hidden role="dialog" aria-label="Grade detail"></div>
|
|
|
|
<script src="__BASE__/static/app.js"></script>
|
|
</body>
|
|
</html>
|