- useLabDraft persists the form snapshot to localStorage (content-debounced)
and only offers a saved draft as resumable when it differs from the current
state, so opening an existing profile never shows a spurious resume banner and
a draft can't silently clobber a freshly loaded profile.
- LabDraftBanner offers Resume / Discard; wired into CraftProfileForm (8 crafts)
and the coach + filmer/editor/photographer/builder pages.
- The draft clears on a successful publish.
Filling the long form was blind until publish. LabProfilePreview renders a
faithful profile card from the current draft — title, bio, image, location,
skills, structured rate, languages/availability/payments and gallery — with a
not-published-yet note.
- Wired into CraftProfileForm (8 crafts) and the coach + filmer/editor/
photographer/builder pages via a Preview button next to Submit.
- parseLabExtras now accepts any { tags } so a draft tag list can be previewed
without a published event.
- i18n for the preview copy (EN/ES).
Still open: draft/autosave and a multi-step layout.
- After publishing, land with ?published=1 and show a one-time "You're live —
share it" banner on the profile (with a Share button), dismissible.
- /lab gains a "Your Lab profiles" strip listing the viewer's own profiles,
each deep-linking to view/edit — so multi-craft providers aren't hunting
through the list.
The offer → publish loop had two dead ends: a provider's own page only offered
Delete (no Edit), and there was no way to share a listing (the whole point of
announcing a service).
- Owner "Manage" card gains an "Edit profile" button deep-linking to
/become/<craft> (prefills the existing profile). Admins still get only
take-down, not a misleading self-edit link.
- A Share button on every provider page opens ShareProfileDialog (QR + copy +
native share) with the service URL /coach/<pubkey>/<identifier>.
- ShareProfileDialog accepts an optional `path` to override the default
/<npub> share URL.
- main.tsx clean-link shim now also rewrites /coach/<pubkey>/<d> to the hash
route, so shared /coach/... links resolve on the live site.
Follow-ups (not done): live preview before publishing, draft/autosave for the
long forms, a multi-step/progress layout, and a "your Lab profiles" manage
strip on /lab.
The coach portfolio/experience/payments treatment is now a shared layer instead
of 12 copies:
- src/lib/labExtras.ts: parseLabExtras + shared option sets (languages,
availability, structured rate, payment methods, booking fee, gallery/reel).
- components/LabExtrasFields.tsx: the self-contained form block — prefills from
the existing event, owns its state, and reports the managed tags via onChange.
- components/LabExtrasSection.tsx: the detail block (all crafts).
Wired into CraftProfileForm (the 8 data-driven crafts) and the four bespoke
pages (filmer/editor/photographer/builder); CoachDetailPage renders the shared
section for every craft. Per-craft buying units (rateUnits) are set per craft in
becomeRoles.tsx (e.g. barber 'per cut', musician 'per gig', photographer 'per
shoot'). /lab facet filters now read parseLabExtras, so availability/languages
filter across crafts.
Coach-specific formats/levels/ages/credentials remain coach-only. Money stays
custody-free: declared methods + a booking fee paid directly to the provider.
- Rename 'Federation coach' -> 'National federation coach'.
- Add 'Coaching qualification' and 'Competition judge'.
- Add a free-form cert_note so a coach can name an exact cert/issuer.
The core safety chips (first aid, CPR, safeguarding, background check,
insured) stay - they're the load-bearing trust signals. cert_note is
displayed under the chips and included in the same self-attested disclaimer.
Keep Cash on site / Lightning / Cashu / Bank transfer / Other. Existing
profiles that already carry a 'paypal' payment_method still render it (raw
fallback), so no data is lost. NIP.md + AGENTS.md updated.
Coaching payments, kept honest and custody-free:
- payment_method (cash/lightning/cashu/bank/paypal/other) + payment_note:
a declaration of what the coach accepts, not an integration. 'Cash on site'
lets a walletless coach list without being nagged.
- pay_what_you_can flag (sliding scale).
- booking_fee + booking_fee_currency: a small fee to confirm a booking, paid
DIRECTLY to the coach's kind-0 lud16 via the existing PayWithSats rail
(mounted only while the sheet is open). Only offered for sats amounts when
the coach has a Lightning address; copy states plainly that SkateSpots does
not hold or refund it.
Deliberately NOT added: fiat/card rails (no KYC/custody) and a refundable
deposit (needs the native-only, unverified Cashu escrow). NIP.md documents the
model; AGENTS.md records the decisions.
The coach option chips rendered raw 'labUi.coachForm.…' keys because the 29
per-option translation keys were referenced dynamically (t(option.key)) and had
never been added — the literal-only i18n guard missed them. Add every option
key (EN/ES) and extend scripts/check-i18n.mjs to also scan data tables for
key/titleKey/labelKey/badgeKey entries so this can't recur.
Also fix the chip layout: the global mobile 'button { min-height:44px }' rule
turned every chip into a 44px slab; add min-h-0 to the coach option chips,
language chips, Lab facet chips and Lab active-filter chips.
Remaining nice-to-haves:
- Structured rate: rate_amount + rate_currency (sats/USD/EUR/GBP) + rate_unit
(hour/session/half-day/day/camp), rendered on the detail page; legacy
freeform 'rate' still shows and acts as an optional note.
- Section the long coach form with a FormSection divider.
- Restyle the coach identity block from a red error-looking box to a neutral
avatar card.
Enrich the coach (kind 35001) beyond the shared base schema with the
experience/portfolio fields that actually win a booking:
- Session formats, levels, age groups (repeated tags, client-filtered)
- Self-attested credentials/safety (first-aid, safeguarding, …) with an
honest 'not verified' label
- Languages spoken (free-form) and availability
- Optional booking link
- Portfolio photo gallery (up to 12) reusing PhotoDropzone + ImageLightbox
- Highlight-reel video link (YouTube/Instagram)
Option sets live in src/lib/coachOptions.ts with EN/ES keys. parseSkateCoach
exposes the new fields; NIP.md documents the tags.
Also fix the pre-existing ErrorBoundary test (ErrorFallback uses
useTranslation but the test rendered no LanguageProvider).
- New `useBatchSpotCommunityScores(spots)` — ONE query for many spots (same
signals/weights as the single-spot hook; counts distinct accounts, drops the
mapper, caps per account; skip WoT/activity weighting at this scale).
- SpotCard takes an optional `community` prop and shows a small "Community"
(green) or "Seen" chip in the footer.
- MapPage scores the visible spot cards (shops excluded — different kind) and
passes each card its score.
- useSpotCommunityScore now also reads crews (35006, `a` ref, weight 3) and Lab
profile links (35001-35014, `a` ref, weight 2) as effortful signals — a whole
crew listing the spot, or a Lab pro linking it, is strong evidence. A crew
ref alone reaches "community". (Tours/events use geohash, not a spot coord,
so they're not counted.)
- Spot page: on an unconfirmed spot, a logged-in non-owner now sees a "Be the
first to confirm this spot" nudge with Add-photo + "I've skated here".
- Breakdown line/tooltip include crew + Lab counts.
New `useSpotCommunityScore(spot)` — ONE batched relay query covering photos,
status reports, comments, reactions, zaps, collections and tag suggestions for
a spot, then a distinct-account, tier-weighted score:
- counts DISTINCT accounts and never the mapper's own signals;
- weights costly signals higher (zap 3, photo/status 2, comment/reaction/
collection/tag 1) and caps each account's contribution at 3;
- multiplies by the actor's web-of-trust + real-activity (reusing
useBatchTrustScores/useBatchActivity) so a ring of empty accounts can't move it;
- returns level (unverified | seen | community) + a breakdown.
SpotDetailPage now derives the Community-verified badge from this (not just
photos+status) and shows a small "why" line (e.g. "2 photos · 1 zap · 3
comments") under the badges. No new event kinds.
Website/band/social inputs stored whatever was typed, so a bare "crew.com"
wasn't clickable. Now they run through normalizeUrlInput + sanitizeUrl (bare
domains become https://), with an inline "enter a valid link" hint on website
and band URL. A non-URL social (e.g. a @handle) is kept as typed so nothing is
dropped. Matches the profile/craft forms.
The Extras step stacked 9 ungrouped blocks. Now:
- Identity (NIP-05, links) — open by default.
- Sasquatch AI — open when the agent is on.
- Showcase (video, sponsor, band).
- Payments & treasury (treasury + the CLINK tip jar, moved out of "Links").
- Integrations (Lab link).
- Advanced (Coordinator) — collapsed + last, so a new founder isn't asked
about experimental plumbing.
Other fixes:
- The crew AI agent is now labelled "Sasquatch · Pro" (it runs on the same
Pro-gated Sasquatch proxy, falling back to a free model), with an honest note.
- crew founded year is clamped to 1954..current on save and bounded in the input.
- "Skip to end" is no longer hidden on mobile.
- Removed the coordinator copy keys left unused by the previous pass.
- Coordinator block: shorter copy, a Remove (✕) button, and an honest
"pointer only — real Cordn MLS isn't wired yet" note (the old paragraph was a
wall of jargon).
- Save now blocks on a non-empty but unparseable coordinator (was silently
dropped) instead of publishing a crew with no coordinator.
- One consistent block order for create (wizard "Extras") and edit, grouped:
core -> squad/places -> identity (NIP-05, links, band) -> agent -> showcase
(video, sponsor) -> treasury -> integrations (Lab link, coordinator).
A NIP-05 name maps to one key, so "who owns it" decides where it lives:
- Crew: repeatable nip05 tags (up to 5), verified against the founder key.
- Shop: same on kind 30017, verified against the owner key (new field).
- Profile: kind-0 has ONE standard nip05; extra personal handles live in a
separate NIP-78 record (kind 30078, d=skatespots:profile-handles) so another
client re-saving the profile can't wipe them.
Shared pieces:
- components/Nip05Badges.tsx — generic verify-and-render chip row (any owner).
- components/Nip05HandlesEditor.tsx — shared add/remove editor with live
verified/mismatch status; used by profile, crew and shop forms.
- CrewNip05Badge now delegates to Nip05Badges; CrewForm uses the shared editor.
- Profile page shows primary + extra handles, and each crew/shop chips its own
verified handle.
- lib/profileHandles.ts + hooks/useProfileHandles.ts (read/publish the record).
- types: SkateShop gains nip05/nip05s; parseSkateShop parses repeatable nip05s.
- NIP.md + AGENTS.md + i18n (EN/ES) updated.
A crew is our own tag convention (unlike a kind-0 profile, whose nip05 is a
single string), so it can now advertise up to 5 handles, each verified
independently against the founder key.
- parseSkateCrew: collect all nip05 tags -> SkateCrew.nip05s (normalised,
deduped, regex-validated, cap 5); nip05 stays = first (back-compat).
- CrewForm: add/remove handle rows with a live verified/mismatch/loading status
(useCrewNip05 per row); format validation + max-5 hint; Enter adds.
- CrewNip05Badge renders every handle; CrewHero passes nip05s.
- NIP.md: nip05 documented as repeatable on kind 35006. i18n EN/ES.
- Export dialog shows a 4-segment strength meter (length + character classes)
with a Weak/Fair/Good/Strong label as you type the password.
- Shows the filename that will be saved; encrypted exports get an
'-encrypted' suffix so the file is self-describing at a glance.
The stash export was plaintext JSON, so a leaked/shared file exposed the whole
gear list. Export now wraps the payload in a version-2 JSON envelope:
- password mode: scrypt (NIP-49 params, 2^16/r8/p1) -> AES-256-GCM, with the
header (mode+cipher+KDF) bound as AAD so it can't be downgraded.
- nip44 mode: NIP-44-encrypted to your own key (no password to remember; needs
the matching key to open).
- plaintext v1 exports still import unchanged.
New src/lib/stashBackup.ts (verified round-trip, wrong-password, tamper and
KDF-downgrade rejection) + StashBackupExportDialog/StashBackupUnlockDialog.
The import picker only prompts for a password on password-encrypted files.
Password mode needs no signer (Amber-friendly); 'My key' is hidden unless the
signer supports NIP-44.
- StashPage: Friends only / Add to Wishlist / Add Item / Edit Item / Public
stash / Uploading… / Add Photos / placeholders / item count / list-grid aria
labels now go through t() (t added to the sub-components).
- Manual Pro copy no longer claims the pass is already active: it says the pass
activates once the provider confirms the payment.
- Bumped low-contrast gray-600 guidance text to gray-400 (DM empty state, E2EE
note, stash item count).
H14 (NWC double-pay): after the 15s pay() timeout, useNWC now polls
lookupInvoice to confirm settlement before reporting failure; if the status
stays unknown it throws a coded error (PAYMENT_STATUS_UNKNOWN) and the callers
that used to fall through to another rail (useZaps, usePurchasePro,
PlatformFeePayment) now STOP and tell the user to check their wallet — no
automatic second payment.
H1 (spot claim): claim-from now stores the ORIGINAL COORDINATE
(30818:pubkey:d) rather than an ambiguous bare pubkey; SkateSpot parses
claimedFrom/claimReason; a claimed listing shows a 'Claimed listing' notice so
the duplicate is understandable. NIP.md updated. Full reader-side supersede is
documented as the remaining design item (a wrong rule could hide real spots).
- VenueAccessCard now has a visitor branch: a public 'this venue issues access
passes' hint (count only, never a holder list) shown only when the venue has
active passes. Rendered for everyone on shops and gated spots.
- New shared, cached useVenuesWithPasses() hook resolves active pass coords +
owners in one query.
- SpotCard badges shops that issue passes; MarketplaceItemCard flags sellers
whose venue issues passes (title explains it is the venue, not the listing).
- i18n: venueAccess.publicTitle/publicDesc/badge/sellerHint (EN + ES).
- New owner-only VenueAccessCard on shop and gated spot pages: shows active/
revoked pass counts for that venue and links into the card studio with the
venue preselected (/card?venue=<kind>:<pubkey>:<d>).
- RiderCardPage reads the venue search param and opens the park editor with the
matching venue + type selected.
- i18n: new venueAccess.* keys (EN + ES).
- U2: CardPreview is zoomable (click for a full-screen overlay); used in the
studio, 'My passes' and the public card.
- U4: the page title is translated (Rider Card / Carné de Rider) — was hardcoded
English.
- L4: removed the legacy style/skills from RiderCardConfig + CardData (the card
derives style/level and shows only witnessed badges).
- I2: cardVerifyUrl/cardPublicUrl are clean (/verify/<naddr>, /card/<npub>) and a
startup shim rewrites those paths to the hash (old /#/ links still work).
- B1: Share's clipboard fallback copied https://…/<npub> (a path HashRouter
ignores → homepage). Now /#/card/<npub> (native share text too).
- B4: useIssuedPasses didn't dedupe by d, so a revoked pass showed twice;
now newest-per-d like useReceivedPasses.
- B2/L5: the redesign dropped the stats row, so Spots/Zaps/Events toggles +
useRiderStats + spotsAdded/zapsReceived/eventsCreated/memberYear were dead.
Removed across lib/riderCard, CardData, CanvasCardData, both pages.
- B3: PublicCardPage ignored derived style/level (config.style is '' now) — it
now derives them from the Skater profile like the studio; holographic honors
the saved config.
- B6: holographic wasn't persisted — added to the card config/tags/parse.
- B5: 'Save PNG' is front-only while Print is both sides → label it
'Save front (PNG)' when a back exists.
- lib/nfc.ts: isNfcSupported() + writeNfcUrl() via Web NFC (Chrome/Android),
writing ONLY the verify URL (the same pointer the QR carries).
- NfcWriteButton component (renders nothing where Web NFC is unavailable — iOS,
desktop, Capacitor WebView — so there's no dead button).
- Wired into: the venue's issued-pass list (issuer copies a tag per holder), the
organiser's issued list, and the holder's 'My passes' view (+ Print).
- i18n nfc.* EN/ES. Tags are passive and clonable like a QR; trust still comes
from the relay signature, and /verify already handles taps.
Redesigned around the ONE question a verifier needs in half a second:
- Big verdict hero (✅ Valid / ❌ Expired / Revoked / Invalid / ⚠ Unverified
issuer / Not bound / No RSVP / Admitted / Authentic) with a plain-language line.
- Facts grid (holder, venue/organiser, valid-thru, access level) large and legible.
- Technical detail moved into a collapsed 'Details' section below.
- Combined verdict for access passes (signature + not revoked + not expired +
issuer owns the venue) and event tickets (organiser's event + holder RSVP).
No account/app needed; NFC tap and QR both land here.
The event organiser can see who they've issued check-in passes to (holder +
revoked state) and revoke them — same affordance the park panel has. (Skipped
listing a holder's passes on their public profile: a pass is between a venue
and a holder, so publishing it would leak where someone has access.)
A holder previously had no way to see a pass a venue/organiser issued them.
New useReceivedPasses (kind 35679 #p=me) + a fourth /card tab listing them;
selecting one renders it as a card (type park) with a Print button, shows the
access level / valid-thru, and flags revoked passes. Completes the
issue -> receive -> display -> verify loop.
- Duotone: desaturate + theme-accent tint the background photo so any image
reads as a branded SkateSpots card (text stays legible). Verified visually.
- Revocation: the venue owner can revoke an issued pass from the studio list;
revoke re-publishes under the same d with status=revoked (addressable
supersede — works on every relay). /verify shows a red 'Pass revoked'.
- Organiser check-in: an event's organiser can issue a signed access pass (kind
35679) bound to the event (a=31923:...); /verify confirms the issuer OWNS the
event (generalised the venue check to spots OR events) and labels it Organiser.
- useIssuedPasses/useRevokePass; i18n EN/ES.
- Attendee card = 'MY TICKET', organiser card = 'EVENT · HOST' — never present a
self-made card as organiser-issued.
- A ticket's QR now carries ?h=<holder-npub>; /verify fetches that holder's RSVP
(kind 31925, #a = the event) and shows Ticket holder + RSVP status, so a door
has a real check with no server.
- NIP.md + AGENTS updated; i18n EN/ES.
- Park Access is only offered if you own a venue (a spot you authored), and
publishing REQUIRES a venue binding — only the venue owner's key can issue it.
The pass names the holder; /verify shows holder + venue match + expiry.
- Card types are now gated by what you can actually issue/derive (Park/ Crew /
Event), with a note explaining how to unlock more.
- Park panel shows a 'map a spot you own' prompt when you have no venue; the
Publish button is disabled until a venue + holder are set.
- NIP.md: venue binding is required for new passes. i18n EN/ES.
- QR 150->178px and moved up (bottom-right, above the identity block).
- Bottom scrim now fades into the THEME's dark background + an accent glow
(was plain black) so the card reads as branded.
- Studio shows an 'add a profile photo' nudge when the account has no kind-0
picture (the card's placeholder name was the tell).
- Custom accent colour (native picker) overrides the theme accent via
--ss-accent in CSS + the canvas palette.
- Optional personal logo/crest (Blossom upload) shown next to the name.
- Printable back side (RiderCardBack React + drawCardBack canvas); Print renders
both sides, and the preview + public card can flip.
- Park passes enforce valid-thru: passExpiry() drives an EXPIRED badge on the
card and verify page; the input is now a date picker.
- Card schema -> v:3 (accent/logo/back). i18n; NIP.md + AGENTS.
- Two-pane studio: sticky live preview (desktop) + inspector column; mobile
stacks preview-first.
- Actions decluttered to a single primary Share + a '...' menu (Save PNG /
Print / Copy ID); removed the competing big buttons.
- Unsaved-changes state + manual Save changes button (no autosave, so an
interactive signer like Amber isn't prompted per keystroke).
- Card type is now a compact 4-up chip row; the two toggle grids merged into
ONE 'Show on card' list with live values.
- Skills: removed the self-claimed skill chips (marginal/unverifiable). The card
shows witnessed badges (kind 1293) only; style + level are now DERIVED from the
Skater profile (kind 35000) instead of a manual 'Skate Style' select.
- React card + canvas export updated; i18n cardUi.studio.*; NIP.md + AGENTS.
- /card gains a fourth type: Event. Renders an event (kind 31923) you organise
or RSVP'd going/interested to (useMyRsvpCoords, kind 31925) as a passport/
wristband: title, date/time, venue, cover, who it admits, going count.
Derived from the event's own signed event — its QR verifies that event;
/verify/:naddr renders kind 31923 too. React + canvas renderers; i18n.
- /review gains a dedicated Claims tab (pending private-spot ownership claims),
replacing the banner that sat above the tabs.
Private spots can't be domain-verified like shops, so ownership is a gated
two-step flow — never an instant takeover:
- A NIP-05-verified user files a claim (kind 1985 label in the separate
org.skatespots.spotclaim namespace) on a spot marked access=private.
- A moderator approves by publishing claim-approved (moderation namespace,
a=coord + p=claimant); surfaced in a pending-claims list atop /review.
- The claimant then finishes by re-publishing the spot with
claim-reason=private-approved (existing claim mechanism).
Adds useSpotClaims/useApprovedSpotClaims/useAllSpotClaims/useSubmitSpotClaim,
useAdminApproveClaim, the private branch in SpotOwnership, i18n, and docs.
Non-private spots keep the abandoned-claim flow. Nothing is auto-transferred.
- /card gains a third type: Crew. Pick one of your crews and render a printable
crew card (name, logo, home turf, founded, members, spots, vibes, Open call).
- No new event: the card is derived from the crew's own signed kind 35006, so its
QR verifies that event (the founder's key IS the signature).
- Rendered in the React card + canvas export; /verify/:naddr now renders kind
35006 with a link to the crew page.
- i18n cardUi.crew.* / verify.kindCrew; NIP.md + AGENTS.md updated.
- Card shows crew crests (kind 35006 memberships), a local badge (skater
profile location/home_spot), and Lab role chips (kinds 35001-35014) — all
DERIVED LIVE, so a card can't claim a role/crew the skater doesn't have.
- Rendered in the React card + the canvas export (print/PNG); local/crews as
text lines, roles as blue chips.
- Per-facet show toggles in the editor; card schema bumped to v2 so the new
flags default ON for cards saved before them.
- Also fixed: verifyUrl wasn't passed to the canvas renderer (QR fell back to
the profile path).
- i18n cardUi.facets.* / cardUi.roles.*; NIP.md + AGENTS.md updated.
- Publish the Rider Card as an addressable event (kind 32271, d=rider-card) and
hydrate the editor from it, so customization persists across devices.
- New routes: /card/:npub (public read-only card) and /verify/:naddr
(signature check + park venue binding).
- Park Access Cards can bind to the issuer's own spot (a tag 30818:...) and the
verify page confirms the issuer owns the venue.
- QR on the card (+ canvas export) now encodes the verify URL; the real QR
renders in the React card too (was a placeholder / broken profile path).
- Extracted shared card visuals to src/components/RiderCard.tsx; helpers in
src/lib/riderCard.ts; hooks useRiderCard/useRiderBadges.
- Documented in NIP.md; i18n cardUi namespace (EN/ES); smoke test covers the
new routes.
- First-visit language auto-detect (Spanish devices land in es; stored choice wins).
- Sasquatch AI replies in the app language (skate trick names kept English).
- i18n safety net: scripts/check-i18n.mjs wired into npm test — fails on any t()
key missing from en/es or any en/es key drift.
- ~1,680 new keys (2,297 total) across mapUi / spotUi / marketUi / labUi and
~65 files wired to t(): Map, spots (detail/create/edit/chat), marketplace,
trades, offers, escrow, shipping, The Lab, /become/* forms, coaches, skaters.
- Policy: only skate TRICK names stay English; user-generated content untouched.
'Shops & Spots' wrapped to two lines and pushed its icon out of line with the
rest of the bar. Shorten nav.map to 'Spots' (en/es) and render every tab label
single-line (w-full truncate text-center), so long customised labels can't wrap
and misalign the bar either.
Closes the real safety hole: "Remind me later" used to set a PERMANENT
localStorage flag, silently becoming "never remind again" — so users who
never backed up were never reminded again.
- BackupReminder: "Later" now snoozes for the current session only
(sessionStorage), so it returns each visit until backup is actually
done. Only `backup-confirmed` stops it for good. Never blocks the user.
- Downloading OR copying the recovery code (in SignupDialog's done step
and in SecuritySettings) now marks backup-confirmed — genuinely safe
users stop being nagged automatically.
- Plain-language copy pass (EN + ES): "Save your login" / "recovery code"
instead of "nsec / recovery phrase" jargon.
- Non-blocking and identical UX on both phone and desktop (bottom sheet).
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- vite.config.ts: mark nostr-signer-capacitor-plugin as external so
Rollup doesn't try to bundle it (it's resolved at runtime by Capacitor)
- en.ts / es.ts: remove duplicate lab key that was causing esbuild warnings
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
The entire skate-spots codebase was accidentally copied into src/src/ and
public/public/ instead of src/ and public/ directly. This left the real
src/ directory as blank MKStack template stubs, explaining why the landing
page showed the default template instead of SkateSpots.
Changes made:
- Copied all files from src/src/ up to src/ (App.tsx, AppRouter.tsx, main.tsx,
all pages, components, hooks, contexts, lib, i18n, test)
- Copied all files from public/public/ up to public/ (images, manifest, etc.)
- Deleted src/src/ and public/public/ nested directories
- Removed HelloWorld.tsx template stub from pages/
- Retained the previously fixed index.css (Tailwind v3 directives)
Full skate-spots feature set is now active:
- All 30+ routes (map, events, spots, coaches, marketplace, wallet, etc.)
- NWCProvider, LanguageProvider, PWAInstallPrompt, AmberCallbackHandler
- Skate community relays (goskatespots.nostr1.com, wotr.relatr.xyz, etc.)
- Outfit Variable font, Amber pre-check in main.tsx
- All hooks (useSkateSpots, useCoaches, useZaps, etc.)
- i18n support (en/es)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>