All checks were successful
Deploy to Production / deploy (push) Successful in 57s
Two related polish items:
1. Remove the global blue Preview banner from app/layout.tsx and delete
the SiteBanner component. The component's own comment said "Remove
once the service is open for production use" — Stripe live billing,
OAuth, and per-runner TLS are all wired now, so the pre-launch notice
is misleading.
2. Mobile-responsive treatment for the standalone /templates page (it
lives outside (dashboard) layout, so it didn't inherit the new
mobile chrome from the dashboard pass):
- Top header tightened: "/templates" breadcrumb + Dashboard link +
"+ New server" pill all hidden on mobile (the avatar UserMenu +
bottom MobileActionBar cover those paths).
- Logged-in users now get the same MobileActionBar tab-bar at the
bottom (Market tab active), giving consistent app-shell across
dashboard pages.
- Filter row stacks vertically on mobile with search on top (thumb
reach), then a horizontally-scrollable chip row for scope / sort /
category so segmented controls don't squeeze below their min-width.
- h1 scales 32px → 24px on mobile; padding tightened to px-4 py-8.
- main gets pb-24 when logged in so cards clear the tab bar.
Logged-out marketplace browsing keeps the simpler marketing chrome
(Logo + "Start building" CTA) — no tab-bar, since visitors don't have
a dashboard to navigate into yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
70 lines
1.5 KiB
TypeScript
70 lines
1.5 KiB
TypeScript
import { JsonLd } from '@/components/json-ld';
|
|
import {
|
|
SEO_KEYWORDS,
|
|
SITE_DESCRIPTION,
|
|
SITE_NAME,
|
|
SITE_TAGLINE,
|
|
SITE_URL,
|
|
siteJsonLd,
|
|
} from '@/lib/seo';
|
|
import { GeistMono } from 'geist/font/mono';
|
|
import { GeistSans } from 'geist/font/sans';
|
|
import type { Metadata } from 'next';
|
|
import './globals.css';
|
|
|
|
const TITLE = `${SITE_NAME} — ${SITE_TAGLINE}`;
|
|
|
|
export const metadata: Metadata = {
|
|
metadataBase: new URL(SITE_URL),
|
|
title: {
|
|
default: TITLE,
|
|
template: `%s | ${SITE_NAME}`,
|
|
},
|
|
description: SITE_DESCRIPTION,
|
|
applicationName: SITE_NAME,
|
|
keywords: SEO_KEYWORDS,
|
|
authors: [{ name: SITE_NAME }],
|
|
creator: SITE_NAME,
|
|
publisher: SITE_NAME,
|
|
alternates: { canonical: '/' },
|
|
openGraph: {
|
|
type: 'website',
|
|
locale: 'en_US',
|
|
url: SITE_URL,
|
|
siteName: SITE_NAME,
|
|
title: TITLE,
|
|
description: SITE_DESCRIPTION,
|
|
},
|
|
twitter: {
|
|
card: 'summary_large_image',
|
|
title: TITLE,
|
|
description: SITE_DESCRIPTION,
|
|
},
|
|
robots: {
|
|
index: true,
|
|
follow: true,
|
|
googleBot: {
|
|
index: true,
|
|
follow: true,
|
|
'max-image-preview': 'large',
|
|
'max-snippet': -1,
|
|
'max-video-preview': -1,
|
|
},
|
|
},
|
|
};
|
|
|
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<html
|
|
lang="en"
|
|
className={`${GeistSans.variable} ${GeistMono.variable}`}
|
|
suppressHydrationWarning
|
|
>
|
|
<body>
|
|
<JsonLd data={siteJsonLd()} />
|
|
{children}
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|