Two coordinated polish moves:
1. **<PulseLink> / <PulseButton>** — new `apps/web/components/pulse.tsx`.
Click anywhere on a wrapped link or button and a small indigo dot
detonates from the click point, scaling 1x→80x over 650ms before
fading to transparent. Same visual language as the hero load-in
glow — the click effectively says "this is the brand reaching back."
The dot lives in a `pointer-events: none` overlay, so it never
blocks the underlying navigation. `overflow-hidden + relative` are
added to the host so the bloom stays inside the rounded shape.
`glow-pulse` keyframe sits in globals.css next to the existing
`pulse-dot` / `shimmer` / `fade-in` definitions; reduced-motion
suppresses the animation to instant-opacity-0 so the click flow
is preserved without the bloom.
Wired into the highest-conversion CTAs only — the user explicitly
asked "wo's Sinn macht":
- Hero "Start building free" + "Read the docs"
- Marketing header Login / Dashboard button
- Dashboard header "+ New server" pill
Deliberately NOT applied to dashboard nav links, logout, destructive
buttons, form internals, carousel dots — pulse on every click would
be noise.
2. **Hero fills 100svh − nav** (`min-height: calc(100svh - 3rem)`).
`svh` (small viewport height) instead of `vh` so the hero doesn't
jump when the mobile address bar hides/shows. The 3rem subtracts
the sticky marketing nav (h-12 = 48px), so the hero ends right at
the loadscreen's natural bottom edge.
`flex items-center` plus the inner grid's existing `md:items-center`
keep the content vertically centred inside the tall section. The
ParticleHero background now has cinematic-scale room and the indigo
radial-glow + dot-mask read as the dominant background motif —
which is the effect the user loved at load-in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Top header on mobile was cramped: Logo + 5 icon-only nav buttons + avatar
crammed into a 48px-tall row. Felt like a desktop nav shrunk down.
Pivot to native-mobile-app pattern:
- Top mobile: just Logo (left) + UserMenu avatar (right). Desktop top nav
is `hidden sm:flex` so it disappears on phones.
- Bottom: full tab bar replacing the single-button MobileActionBar.
Five destinations: Overview · Servers · Create (FAB-style center) ·
Market · Settings.
- "Create" is a raised FAB-style button (round accent fill, -mt-3 to
overlap the bar border) — same prominent-action pattern as Instagram /
Notion mobile.
- Active tab gets accent color + aria-current=page.
- Audit demoted from primary nav on mobile (low frequency); still
reachable via direct /audit URL.
Desktop unchanged — top nav stays.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User-facing identity:
- UserMenu component in dashboard header: avatar (deterministic colour from
email hash), email + name, current plan badge, dropdown to Profile /
Billing / Support / Your data / (Admin panel if isAdmin) / Sign out
- /settings/profile: editable display name; email + phone shown read-only
(changing them requires support ticket — magic-link flow assumed)
- GET + PATCH /v1/account/profile
In-app subscription management (no more Stripe Portal redirect for the
common flows — cancellation, plan switch, invoice viewing all in-app):
- Billing status now combines DB state with a live Stripe lookup of the
subscription details + last 5 invoices. Single roundtrip.
- POST /v1/billing/cancel → schedules cancel_at_period_end
- POST /v1/billing/reactivate → undo scheduled cancel
- POST /v1/billing/change-plan → prorated swap between any tier+cycle
- /settings/billing rewritten: current plan card with renew/cancel date,
big cancel button + reactivate flow, plan-switcher grid, invoice list with
PDF + hosted-invoice links
- Stripe portal still linked at the bottom as the escape hatch for rare
actions (payment-method update, address change). New-subscription Checkout
still uses Stripe-hosted Checkout (industry standard for PCI).
Stripe SDK v22 / API 2024-09 fix: current_period_end moved to subscription
items; updated read paths accordingly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On phones the dashboard top bar is tight with the nav icons + the primary
action crammed alongside. Move the action into a sticky bottom bar in the
thumb zone, leave the top bar to navigation. Hidden on the create-wizard
route since that page owns its own action.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Hero h1 was a fixed text-[44px] — overflowed narrow phones. Now
text-[30px] sm:text-[40px] md:text-[56px].
- Hero grid children get min-w-0 so the code blocks' overflow-x-auto
actually constrains instead of widening the page.
- Marketing nav: the inline links were hidden below md with no fallback.
Added a hamburger MobileMenu; "Sign in" collapses into it on the
smallest screens.
- Section vertical padding is now responsive (py-14 sm:py-20).
- globals.css: overflow-x: clip on <html> as a safety net.
- docs: the 240px sidebar is hidden below lg, article gets min-w-0.
- dashboard header: nav labels collapse to icons on small screens.
Verified: next build passes (40/40 pages).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The logged-in user can now reach the marketplace and filter to their own
templates.
Dashboard nav:
- Added 'Marketplace' item (Overview · Servers · Marketplace · Audit · Settings).
/templates page — login-aware:
- Detects session via /v1/auth/me. Logged-in users get a 'Dashboard' + '+ New
server' header instead of 'Home' + 'Start building'.
- New [All templates | My templates] scope toggle, shown only when logged in.
- 'My templates' loads GET /v1/templates/mine and shows EVERY status the user
owns (public / hidden / draft / takedown) with a colored status badge on each
card — so a template you unshared doesn't appear to have vanished.
- Sort tabs (trending/top/newest) hide in 'mine' scope — meaningless for a
handful of own templates. Category filter + search still apply (client-side).
- Takedown cards link to the source server's Publish tab instead of the detail
route (which 410s); everything else opens the detail page.
Backend:
- GET /v1/templates/mine (requireAuth) — all own templates, any status,
registered before /:slug so the static route always wins the match.
- GET /v1/templates/:slug — now does an optional session check: the OWNER can
view their own hidden/draft template (so a 'My templates' card click never
dead-ends in a 404). takedown stays 410 for everyone, owner included — that's
an admin decision, not the owner's to reverse.
Detail page:
- Fork CTA is gated on status === 'public'. For a non-public template the owner
sees an amber 'not forkable — re-share from the Publish tab' notice plus a
'Manage in server' link, instead of a Fork button that would fail silently.
Verified:
- GET /v1/templates/mine → marco's 1 template; 401 without auth
- Owner GET of a hidden template → 200 status:hidden; anon → 404
- Dashboard nav shows Marketplace (screenshot)
- /templates 'My templates' toggle → only own template, public badge, sort tabs
hidden (screenshot)