Fix card print colors + clarify Nostr verification language
Print fixes: - Add -webkit-print-color-adjust: exact + print-color-adjust: exact to ALL card theme classes so dark backgrounds/gradients actually print (browsers strip them by default unless forced) - Increase chip/muted opacity values slightly for print legibility - Fix multiple-card issue: hide body > * on print, show only .print-only-card - Remove complex repeating-linear-gradient from wood theme (simplify for print) Language fixes: - Remove "on-chain" terminology (Nostr is not a blockchain) - Replace with plain explanation: "signed message on the Nostr network" - Park access note now explains exactly what gets verified and how - Park card info box now lists exactly what shows on a scan: who issued it, who it was issued to, access level, expiry, timestamp - Clarifies why a rider can't forge the card (would need your private key) Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
e2582aa013
commit
3b99392f3b
2 changed files with 108 additions and 78 deletions
147
src/index.css
147
src/index.css
|
|
@ -427,143 +427,152 @@ html, body {
|
|||
mix-blend-mode: screen;
|
||||
}
|
||||
|
||||
/* ── Force background graphics to print on all card themes ──
|
||||
Browsers suppress backgrounds by default. -webkit-print-color-adjust: exact
|
||||
overrides this for Chrome/Safari. color-adjust: exact covers Firefox/Edge. */
|
||||
.rider-card,
|
||||
.rider-card *,
|
||||
[class^="card-theme-"],
|
||||
[class^="card-theme-"] * {
|
||||
-webkit-print-color-adjust: exact !important;
|
||||
print-color-adjust: exact !important;
|
||||
color-adjust: exact !important;
|
||||
}
|
||||
|
||||
/* ── Theme: THUG (default — black + neon red) ── */
|
||||
.card-theme-thug {
|
||||
background: linear-gradient(135deg, #0a0a0a 0%, #1a0008 60%, #0d0d0d 100%);
|
||||
background: linear-gradient(135deg, #0a0a0a 0%, #1a0008 60%, #0d0d0d 100%) !important;
|
||||
border: 1.5px solid rgba(255,0,64,0.5);
|
||||
box-shadow: 0 0 30px rgba(255,0,64,0.2), inset 0 1px 0 rgba(255,255,255,0.05);
|
||||
color: #fff;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-theme-thug .card-accent { color: #ff0040; }
|
||||
.card-theme-thug .card-accent2 { color: #ccff00; }
|
||||
.card-theme-thug .card-stripe { background: linear-gradient(90deg, #ff0040, #ff6600); }
|
||||
.card-theme-thug .card-muted { color: rgba(255,255,255,0.45); }
|
||||
.card-theme-thug .card-bg-chip { background: rgba(255,0,64,0.12); border: 1px solid rgba(255,0,64,0.3); }
|
||||
.card-theme-thug .card-accent { color: #ff0040 !important; }
|
||||
.card-theme-thug .card-accent2 { color: #ccff00 !important; }
|
||||
.card-theme-thug .card-stripe { background: linear-gradient(90deg, #ff0040, #ff6600) !important; }
|
||||
.card-theme-thug .card-muted { color: rgba(255,255,255,0.6) !important; }
|
||||
.card-theme-thug .card-bg-chip { background: rgba(255,0,64,0.25) !important; border: 1px solid rgba(255,0,64,0.5) !important; }
|
||||
|
||||
/* ── Theme: NEON LIME ── */
|
||||
.card-theme-lime {
|
||||
background: linear-gradient(135deg, #050f00 0%, #0a1a00 60%, #050f00 100%);
|
||||
background: linear-gradient(135deg, #050f00 0%, #0a1a00 60%, #050f00 100%) !important;
|
||||
border: 1.5px solid rgba(204,255,0,0.5);
|
||||
box-shadow: 0 0 30px rgba(204,255,0,0.15), inset 0 1px 0 rgba(255,255,255,0.05);
|
||||
color: #fff;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-theme-lime .card-accent { color: #ccff00; }
|
||||
.card-theme-lime .card-accent2 { color: #ff6600; }
|
||||
.card-theme-lime .card-stripe { background: linear-gradient(90deg, #ccff00, #88ff00); }
|
||||
.card-theme-lime .card-muted { color: rgba(255,255,255,0.45); }
|
||||
.card-theme-lime .card-bg-chip { background: rgba(204,255,0,0.10); border: 1px solid rgba(204,255,0,0.3); }
|
||||
.card-theme-lime .card-accent { color: #ccff00 !important; }
|
||||
.card-theme-lime .card-accent2 { color: #ff6600 !important; }
|
||||
.card-theme-lime .card-stripe { background: linear-gradient(90deg, #ccff00, #88ff00) !important; }
|
||||
.card-theme-lime .card-muted { color: rgba(255,255,255,0.6) !important; }
|
||||
.card-theme-lime .card-bg-chip { background: rgba(204,255,0,0.2) !important; border: 1px solid rgba(204,255,0,0.5) !important; }
|
||||
|
||||
/* ── Theme: CHROME (silver/steel) ── */
|
||||
.card-theme-chrome {
|
||||
background: linear-gradient(135deg, #1a1a1a 0%, #2e2e2e 40%, #111 100%);
|
||||
background: linear-gradient(135deg, #1a1a1a 0%, #2e2e2e 40%, #111 100%) !important;
|
||||
border: 1.5px solid rgba(200,200,200,0.4);
|
||||
box-shadow: 0 0 20px rgba(180,180,180,0.1), inset 0 1px 0 rgba(255,255,255,0.15);
|
||||
color: #e8e8e8;
|
||||
color: #e8e8e8 !important;
|
||||
}
|
||||
.card-theme-chrome .card-accent { color: #c8c8c8; }
|
||||
.card-theme-chrome .card-accent2 { color: #ff6600; }
|
||||
.card-theme-chrome .card-stripe { background: linear-gradient(90deg, #888, #ccc, #888); }
|
||||
.card-theme-chrome .card-muted { color: rgba(232,232,232,0.45); }
|
||||
.card-theme-chrome .card-bg-chip { background: rgba(200,200,200,0.08); border: 1px solid rgba(200,200,200,0.2); }
|
||||
.card-theme-chrome .card-accent { color: #c8c8c8 !important; }
|
||||
.card-theme-chrome .card-accent2 { color: #ff6600 !important; }
|
||||
.card-theme-chrome .card-stripe { background: linear-gradient(90deg, #888, #ccc, #888) !important; }
|
||||
.card-theme-chrome .card-muted { color: rgba(232,232,232,0.6) !important; }
|
||||
.card-theme-chrome .card-bg-chip { background: rgba(200,200,200,0.15) !important; border: 1px solid rgba(200,200,200,0.3) !important; }
|
||||
|
||||
/* ── Theme: WOODGRAIN ── */
|
||||
.card-theme-wood {
|
||||
background:
|
||||
repeating-linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
rgba(0,0,0,0.03) 2px,
|
||||
rgba(0,0,0,0.03) 4px
|
||||
),
|
||||
linear-gradient(160deg, #3d2008 0%, #5c3210 30%, #2e1804 60%, #4a2a0e 100%);
|
||||
background: linear-gradient(160deg, #3d2008 0%, #5c3210 30%, #2e1804 60%, #4a2a0e 100%) !important;
|
||||
border: 1.5px solid rgba(160,100,40,0.6);
|
||||
box-shadow: 0 0 20px rgba(120,70,20,0.2), inset 0 1px 0 rgba(255,200,100,0.1);
|
||||
color: #f5e6c8;
|
||||
color: #f5e6c8 !important;
|
||||
}
|
||||
.card-theme-wood .card-accent { color: #f5c842; }
|
||||
.card-theme-wood .card-accent2 { color: #ff6600; }
|
||||
.card-theme-wood .card-stripe { background: linear-gradient(90deg, #8b5e3c, #d4a05a, #8b5e3c); }
|
||||
.card-theme-wood .card-muted { color: rgba(245,230,200,0.5); }
|
||||
.card-theme-wood .card-bg-chip { background: rgba(160,100,40,0.15); border: 1px solid rgba(160,100,40,0.4); }
|
||||
.card-theme-wood .card-accent { color: #f5c842 !important; }
|
||||
.card-theme-wood .card-accent2 { color: #ff6600 !important; }
|
||||
.card-theme-wood .card-stripe { background: linear-gradient(90deg, #8b5e3c, #d4a05a, #8b5e3c) !important; }
|
||||
.card-theme-wood .card-muted { color: rgba(245,230,200,0.65) !important; }
|
||||
.card-theme-wood .card-bg-chip { background: rgba(160,100,40,0.3) !important; border: 1px solid rgba(160,100,40,0.6) !important; }
|
||||
|
||||
/* ── Theme: MIDNIGHT BLUE ── */
|
||||
.card-theme-midnight {
|
||||
background: linear-gradient(135deg, #020818 0%, #050e2a 60%, #030b1a 100%);
|
||||
background: linear-gradient(135deg, #020818 0%, #050e2a 60%, #030b1a 100%) !important;
|
||||
border: 1.5px solid rgba(100,160,255,0.4);
|
||||
box-shadow: 0 0 30px rgba(60,120,255,0.15), inset 0 1px 0 rgba(100,160,255,0.08);
|
||||
color: #d0e4ff;
|
||||
color: #d0e4ff !important;
|
||||
}
|
||||
.card-theme-midnight .card-accent { color: #64a0ff; }
|
||||
.card-theme-midnight .card-accent2 { color: #ccff00; }
|
||||
.card-theme-midnight .card-stripe { background: linear-gradient(90deg, #3060cc, #64a0ff); }
|
||||
.card-theme-midnight .card-muted { color: rgba(208,228,255,0.45); }
|
||||
.card-theme-midnight .card-bg-chip { background: rgba(100,160,255,0.08); border: 1px solid rgba(100,160,255,0.25); }
|
||||
.card-theme-midnight .card-accent { color: #64a0ff !important; }
|
||||
.card-theme-midnight .card-accent2 { color: #ccff00 !important; }
|
||||
.card-theme-midnight .card-stripe { background: linear-gradient(90deg, #3060cc, #64a0ff) !important; }
|
||||
.card-theme-midnight .card-muted { color: rgba(208,228,255,0.6) !important; }
|
||||
.card-theme-midnight .card-bg-chip { background: rgba(100,160,255,0.15) !important; border: 1px solid rgba(100,160,255,0.4) !important; }
|
||||
|
||||
/* ── Theme: SUNSET (orange/pink) ── */
|
||||
.card-theme-sunset {
|
||||
background: linear-gradient(135deg, #120006 0%, #1a0808 40%, #0e0810 100%);
|
||||
background: linear-gradient(135deg, #120006 0%, #1a0808 40%, #0e0810 100%) !important;
|
||||
border: 1.5px solid rgba(255,80,0,0.5);
|
||||
box-shadow: 0 0 30px rgba(255,60,0,0.2), inset 0 1px 0 rgba(255,150,50,0.08);
|
||||
color: #ffd0a0;
|
||||
color: #ffd0a0 !important;
|
||||
}
|
||||
.card-theme-sunset .card-accent { color: #ff6020; }
|
||||
.card-theme-sunset .card-accent2 { color: #ff0080; }
|
||||
.card-theme-sunset .card-stripe { background: linear-gradient(90deg, #ff0040, #ff6020, #ffaa00); }
|
||||
.card-theme-sunset .card-muted { color: rgba(255,208,160,0.5); }
|
||||
.card-theme-sunset .card-bg-chip { background: rgba(255,80,0,0.10); border: 1px solid rgba(255,80,0,0.3); }
|
||||
.card-theme-sunset .card-accent { color: #ff6020 !important; }
|
||||
.card-theme-sunset .card-accent2 { color: #ff0080 !important; }
|
||||
.card-theme-sunset .card-stripe { background: linear-gradient(90deg, #ff0040, #ff6020, #ffaa00) !important; }
|
||||
.card-theme-sunset .card-muted { color: rgba(255,208,160,0.65) !important; }
|
||||
.card-theme-sunset .card-bg-chip { background: rgba(255,80,0,0.2) !important; border: 1px solid rgba(255,80,0,0.5) !important; }
|
||||
|
||||
/* ── Print-only card: invisible on screen, appears on print ── */
|
||||
/* ── Print-only card: hidden on screen, shown only when printing ── */
|
||||
.print-only-card {
|
||||
display: none;
|
||||
display: none !important;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* ── Print styles ── */
|
||||
@media print {
|
||||
/* Hide absolutely everything */
|
||||
* {
|
||||
visibility: hidden !important;
|
||||
/* Step 1: hide the entire page */
|
||||
html, body {
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
/* Show only the print-only card and all its children */
|
||||
.print-only-card,
|
||||
.print-only-card * {
|
||||
visibility: visible !important;
|
||||
body > * {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Position the card at top-left of the page */
|
||||
/* Step 2: show only the print-only-card container */
|
||||
.print-only-card {
|
||||
display: block !important;
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 85.6mm !important;
|
||||
height: 54mm !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
z-index: 99999 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* Reset page to card size */
|
||||
@page {
|
||||
size: 85.6mm 54mm;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Card at exact print dimensions */
|
||||
/* Step 3: size the card to exact credit-card dimensions */
|
||||
.print-only-card .rider-card {
|
||||
width: 85.6mm !important;
|
||||
height: 54mm !important;
|
||||
border-radius: 4mm !important;
|
||||
page-break-inside: avoid;
|
||||
box-shadow: none !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* Reset page margins to zero */
|
||||
@page {
|
||||
size: 85.6mm 54mm;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Kill holographic overlay on print */
|
||||
.rider-card-holo::after {
|
||||
display: none !important;
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
/* Kill all animations */
|
||||
/* Kill animations */
|
||||
*, *::before, *::after {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
|
|
|
|||
|
|
@ -875,10 +875,21 @@ export default function RiderCardPage() {
|
|||
{cardType === 'park' && (
|
||||
<div className="flex items-start gap-2 p-3 rounded-xl bg-blue-500/5 border border-blue-500/20 text-xs text-gray-400">
|
||||
<Info className="h-4 w-4 text-blue-400 flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<span className="font-bold text-blue-400">Park owners only.</span>{' '}
|
||||
Park Access Cards should only be issued by the park's Nostr key.
|
||||
For private parks this is enforced on-chain — anyone can verify the issuer matches the park owner.
|
||||
<div className="space-y-1.5">
|
||||
<div>
|
||||
<span className="font-bold text-blue-400">Park owners only.</span>{' '}
|
||||
Park Access Cards should only be issued by the park's own Nostr key — the same key that published the park listing on SkateSpots.
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-bold text-gray-300">How verification works:</span>{' '}
|
||||
The card is a signed message on the Nostr network (like a public post only you could have written).
|
||||
Anyone can scan the QR, look up the event on any Nostr relay, and see which key signed it —
|
||||
if that key matches the park's registered identity, the card is genuine.
|
||||
No central authority needed.
|
||||
</div>
|
||||
<div className="text-gray-500">
|
||||
Self-issued cards for private parks will show a mismatch warning — the park owner's key won't match.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -1058,11 +1069,21 @@ export default function RiderCardPage() {
|
|||
placeholder="e.g. 2026-12-31" className="bg-black/50 border-gray-700" />
|
||||
</div>
|
||||
|
||||
<div className="p-3 rounded-xl bg-[#ccff00]/5 border border-[#ccff00]/20 text-xs text-gray-400">
|
||||
<div className="font-bold text-[#ccff00] mb-1">🔐 Nostr-verified access</div>
|
||||
This card is published as a signed Nostr event from <em>your</em> key.
|
||||
Anyone scanning the QR code can verify the card is genuine and was issued by
|
||||
the park's Nostr identity.
|
||||
<div className="p-3 rounded-xl bg-[#ccff00]/5 border border-[#ccff00]/20 text-xs text-gray-400 space-y-2">
|
||||
<div className="font-bold text-[#ccff00]">🔐 How the park verifies it's real</div>
|
||||
<div>
|
||||
When you publish this card, it's signed with your Nostr private key and broadcast to relays.
|
||||
The park's staff can scan the QR on any printed card and instantly see:
|
||||
</div>
|
||||
<ul className="list-disc list-inside space-y-1 text-gray-500">
|
||||
<li>Who issued it (your npub)</li>
|
||||
<li>Who it was issued to (the rider's npub)</li>
|
||||
<li>The access level and expiry</li>
|
||||
<li>The exact timestamp it was signed</li>
|
||||
</ul>
|
||||
<div>
|
||||
A rider can't fake this card — they'd need your private key to forge your signature.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue