contents(app-shop): move the free printables into the 'Sticker packs' entry

The sticker + poster were their own 'Free to print' section, which made the shop
read as two lists. They now live inside the 'Sticker packs' card in the 'In the
works' grid (full-width, FREE badge, compact previews + Print/Save), between
Decks and Shop tools — so a category shows what it already offers.

Verified: no 'Free to print' heading remains, both printables still render, and
the page reads hero -> in the works (5 entries) -> how buying works.
This commit is contained in:
SkateSpots 2026-09-28 11:21:03 +07:00
parent 263ce11073
commit 5b770dc79b
2 changed files with 96 additions and 71 deletions

View file

@ -361,7 +361,9 @@ session with **no other memory of this project**. So:
'nothing is for sale' note — removed on request). **Do not add prices/fake urgency/buy buttons until
there is real stock**; when items exist, model them as listings and take
Lightning/Cashu payment. The page reads the live stock strip as 0/None/Nothing
— keep it honest if that changes. It also serves two **free printables**
— keep it honest if that changes. It also serves two **free printables** (rendered INSIDE the 'Sticker packs'
entry of the 'In the works' grid — deliberately not a separate section, per the
owner: the free sticker + poster belong under their merch category)
(`public/images/print/skatespots-sticker.svg` 55×86 mm, `…-poster.svg` A3),
regenerated with `node scripts/make-print-art.mjs` (output is committed). Those
SVGs are self-contained: brand fonts + logo embedded as data URIs and a real

View file

@ -58,7 +58,8 @@ const FREE_PRINT = [
const PLANNED = [
{ icon: Shirt, label: 'Tees & hoodies' },
{ icon: Package, label: 'Decks' },
{ icon: Sticker, label: 'Sticker packs' },
// 'Sticker packs' is rendered separately in the grid, because it is the one
// entry that already contains something real: the free printables.
{ icon: Hammer, label: 'Shop tools' },
];
@ -157,7 +158,97 @@ export default function AppShopPage() {
</div>
<div className="grid gap-3 sm:grid-cols-2">
{PLANNED.map(({ icon: Icon, label }) => (
{PLANNED.slice(0, 2).map(({ icon: Icon, label }) => (
<div
key={label}
className="group relative overflow-hidden rounded-2xl border-2 border-dashed border-white/12 bg-white/[0.02] p-4 transition-colors hover:border-[#ccff00]/30"
>
<div className="flex items-start gap-3">
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-[#ccff00]/10 text-[#ccff00]">
<Icon className="h-5 w-5" />
</span>
<p className="min-w-0 self-center font-black uppercase tracking-wide text-white">
{label}
</p>
</div>
<span className="absolute right-3 top-3 rounded-full border border-white/12 px-2 py-0.5 text-[9px] font-black uppercase tracking-widest text-gray-500">
soon
</span>
</div>
))}
{/* Sticker packs — the only thing you can actually GET today: the free
printables. Kept inside its category (not a separate section) so the
shop reads as one list of merch, free stuff included. */}
<div className="rounded-2xl border-2 border-dashed border-[#ccff00]/35 bg-[#ccff00]/[0.04] p-4 sm:col-span-2">
<div className="flex items-start gap-3">
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-[#ccff00]/10 text-[#ccff00]">
<Sticker className="h-5 w-5" />
</span>
<div className="min-w-0 flex-1">
<p className="font-black uppercase tracking-wide text-white">Sticker packs</p>
<p className="mt-0.5 text-xs leading-relaxed text-gray-400">
The packs are coming. The designs are not — print these two right now, free.
</p>
</div>
<span className="shrink-0 rounded-full bg-[#ccff00] px-2 py-0.5 text-[9px] font-black uppercase tracking-widest text-black">
Free
</span>
</div>
<div className="mt-3 grid gap-3 sm:grid-cols-2">
{FREE_PRINT.map((item) => (
<div
key={item.title}
className="overflow-hidden rounded-xl border border-white/10 bg-black/40"
>
<div className="flex h-36 items-center justify-center p-3">
<img
src={item.file}
alt={`SkateSpots ${item.title} — printable`}
className="max-h-full max-w-full drop-shadow-[0_4px_14px_rgba(0,0,0,0.6)]"
/>
</div>
<div className="border-t border-white/8 p-3">
<div className="flex items-baseline justify-between gap-2">
<p className="font-bold uppercase tracking-wide text-white">{item.title}</p>
<span className="shrink-0 text-[10px] font-bold uppercase tracking-widest text-gray-500">
{item.size}
</span>
</div>
<p className="mt-1 flex items-start gap-1.5 text-[11px] leading-relaxed text-gray-500">
<Scissors className="mt-0.5 h-3 w-3 shrink-0 text-gray-600" />
{item.tip}
</p>
<div className="mt-2.5 flex flex-wrap gap-2">
<Button
asChild
size="sm"
className="gap-1.5 bg-[#ccff00] font-bold text-black hover:bg-[#ccff00]/90"
>
<a href={item.file} target="_blank" rel="noopener noreferrer">
<Printer className="h-3.5 w-3.5" /> Print
</a>
</Button>
<Button asChild size="sm" variant="outline" className="gap-1.5 border-white/15">
<a href={item.file} download>
<Download className="h-3.5 w-3.5" /> Save
</a>
</Button>
</div>
</div>
</div>
))}
</div>
<p className="mt-3 text-[11px] leading-relaxed text-gray-600">
Print at <strong className="text-gray-400">100% / “Actual size”</strong> so the size is
exact and the QR stays sharp — it points at skate-spots.org. Vector files, so they
scale without going fuzzy.
</p>
</div>
{PLANNED.slice(2).map(({ icon: Icon, label }) => (
<div
key={label}
className="group relative overflow-hidden rounded-2xl border-2 border-dashed border-white/12 bg-white/[0.02] p-4 transition-colors hover:border-[#ccff00]/30"
@ -178,74 +269,6 @@ export default function AppShopPage() {
</div>
</section>
{/* ── Free to print ────────────────────────────────────────────────── */}
<section className="mt-10">
<div className="mb-4 flex items-center gap-3">
<h3 className="flex items-center gap-2 text-sm font-black uppercase tracking-widest text-white">
<Printer className="h-4 w-4 text-[#ccff00]" />
Free to print
</h3>
<div className="h-px flex-1 bg-white/10" />
<span className="text-[10px] font-bold uppercase tracking-widest text-[#ccff00]">
no sats needed
</span>
</div>
<div className="grid gap-3 sm:grid-cols-2">
{FREE_PRINT.map((item) => (
<div
key={item.title}
className="overflow-hidden rounded-2xl border border-white/8 bg-white/[0.03]"
>
<div className="relative flex h-56 items-center justify-center bg-black/40 p-4">
<img
src={item.file}
alt={`SkateSpots ${item.title} — printable`}
className="max-h-full max-w-full drop-shadow-[0_6px_20px_rgba(0,0,0,0.6)]"
/>
<span className="absolute left-3 top-3 rounded-full bg-[#ccff00] px-2 py-0.5 text-[9px] font-black uppercase tracking-widest text-black">
Free
</span>
</div>
<div className="p-4">
<div className="flex items-baseline justify-between gap-2">
<p className="font-black uppercase tracking-wide text-white">{item.title}</p>
<span className="shrink-0 text-[10px] font-bold uppercase tracking-widest text-gray-500">
{item.size}
</span>
</div>
<p className="mt-1 flex items-start gap-1.5 text-xs leading-relaxed text-gray-400">
<Scissors className="mt-0.5 h-3.5 w-3.5 shrink-0 text-gray-600" />
{item.tip}
</p>
<div className="mt-3 flex flex-wrap gap-2">
<Button
asChild
size="sm"
className="gap-2 bg-[#ccff00] font-bold text-black hover:bg-[#ccff00]/90"
>
<a href={item.file} target="_blank" rel="noopener noreferrer">
<Printer className="h-3.5 w-3.5" /> Open &amp; print
</a>
</Button>
<Button asChild size="sm" variant="outline" className="gap-2 border-white/15">
<a href={item.file} download>
<Download className="h-3.5 w-3.5" /> Save file
</a>
</Button>
</div>
</div>
</div>
))}
</div>
<p className="mt-3 text-[11px] leading-relaxed text-gray-600">
Print at <strong className="text-gray-400">100% / “Actual size”</strong> so the size is
exact and the QR stays sharp — it points at skate-spots.org. These are vector files, so
they scale to any size without going fuzzy.
</p>
</section>
{/* ── How it'll work ───────────────────────────────────────────────── */}
<section className="mt-10">
<div className="mb-4 flex items-center gap-3">