Refine SEO, uploads, and deploy handling

This commit is contained in:
2026-05-02 10:48:08 +02:00
parent b6be6ed2ac
commit a9dfa6ea11
97 changed files with 373 additions and 327 deletions

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'
import { Link, usePage } from '@inertiajs/react'
import SeoHead from '../components/seo/SeoHead'
import NovaSelect from '../components/ui/NovaSelect'
import { studioModule, studioSurface, trackStudioEvent } from '../utils/studioEvents'
@@ -268,6 +269,13 @@ export default function StudioLayout({ children, title, subtitle, actions }) {
const [mobileOpen, setMobileOpen] = useState(false)
const [createOpen, setCreateOpen] = useState(false)
const pathname = url.split('?')[0]
const pageTitle = title ? `${title} — Creator Studio` : 'Creator Studio'
const pageDescription = subtitle || 'Manage your creator workspace from one shared Creator Studio surface.'
const pageSeo = {
title: pageTitle,
description: pageDescription,
robots: 'noindex,nofollow',
}
const studioGroups = Array.isArray(props.studio_groups) ? props.studio_groups : []
const currentGroup = props.studioGroup || null
const userLabel = props.auth?.user?.name || props.auth?.user?.username || 'Your creator workspace'
@@ -391,6 +399,7 @@ export default function StudioLayout({ children, title, subtitle, actions }) {
return (
<div className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(14,165,233,0.12),_transparent_30%),radial-gradient(circle_at_bottom_right,_rgba(34,197,94,0.12),_transparent_35%),linear-gradient(180deg,_#06101d_0%,_#020617_45%,_#02040a_100%)]">
<SeoHead seo={pageSeo} />
<div className="sticky top-16 z-30 border-b border-white/10 bg-slate-950/80 backdrop-blur-xl lg:hidden">
<div className="flex items-center justify-between px-4 py-3">
<div>

View File

@@ -66,7 +66,7 @@ export default function CollectionAnalytics() {
return (
<>
<Head>
<title>{seo.title || `${collection.title || 'Collection'} Analytics — Skinbase Nova`}</title>
<title>{seo.title || `${collection.title || 'Collection'} Analytics — Skinbase`}</title>
<meta name="description" content={seo.description || 'Collection analytics overview.'} />
{seo.canonical ? <link rel="canonical" href={seo.canonical} /> : null}
<meta name="robots" content={seo.robots || 'noindex,follow'} />

View File

@@ -527,7 +527,7 @@ export default function CollectionDashboard() {
return (
<>
<Head>
<title>{seo.title || 'Collections Dashboard — Skinbase Nova'}</title>
<title>{seo.title || 'Collections Dashboard — Skinbase'}</title>
<meta name="description" content={seo.description || 'Collection lifecycle and performance dashboard.'} />
{seo.canonical ? <link rel="canonical" href={seo.canonical} /> : null}
<meta name="robots" content={seo.robots || 'noindex,follow'} />

View File

@@ -255,7 +255,7 @@ export default function CollectionFeaturedIndex() {
const seo = props.seo || {}
const eyebrow = props.eyebrow || 'Discovery'
const title = props.title || 'Featured collections'
const description = props.description || 'A rotating set of standout galleries from across Skinbase Nova. Some are meticulously hand-sequenced. Others are smart collections that stay fresh as the creator publishes new work.'
const description = props.description || 'A rotating set of standout galleries from across Skinbase. Some are meticulously hand-sequenced. Others are smart collections that stay fresh as the creator publishes new work.'
const collections = Array.isArray(props.collections) ? props.collections : []
const communityCollections = Array.isArray(props.communityCollections) ? props.communityCollections : []
const editorialCollections = Array.isArray(props.editorialCollections) ? props.editorialCollections : []
@@ -288,7 +288,7 @@ export default function CollectionFeaturedIndex() {
return (
<>
<SeoHead seo={seo} title={seo?.title || `${title} — Skinbase Nova`} description={seo?.description || description} jsonLd={listSchema} />
<SeoHead seo={seo} title={seo?.title || `${title} — Skinbase`} description={seo?.description || description} jsonLd={listSchema} />
<div className="relative min-h-screen overflow-hidden pb-16">
<div

View File

@@ -88,7 +88,7 @@ export default function CollectionHistory() {
return (
<>
<Head>
<title>{seo.title || `${collection.title || 'Collection'} History — Skinbase Nova`}</title>
<title>{seo.title || `${collection.title || 'Collection'} History — Skinbase`}</title>
<meta name="description" content={seo.description || 'Collection audit history.'} />
{seo.canonical ? <link rel="canonical" href={seo.canonical} /> : null}
<meta name="robots" content={seo.robots || 'noindex,follow'} />

View File

@@ -1766,7 +1766,7 @@ export default function CollectionManage() {
return (
<>
<Head>
<title>{mode === 'create' ? 'Create Collection — Skinbase Nova' : `${collectionState?.title || 'Collection'} — Manage Collection`}</title>
<title>{mode === 'create' ? 'Create Collection — Skinbase' : `${collectionState?.title || 'Collection'} — Manage Collection`}</title>
<meta name="robots" content="noindex,nofollow" />
</Head>

View File

@@ -19,14 +19,14 @@ export default function CollectionSeriesShow() {
const { props } = usePage()
const seo = props.seo || {}
const title = props.title || `Collection Series: ${props.seriesKey || ''}`
const description = props.description || 'A connected sequence of public collections on Skinbase Nova.'
const description = props.description || 'A connected sequence of public collections on Skinbase.'
const collections = Array.isArray(props.collections) ? props.collections : []
const leadCollection = props.leadCollection || null
const stats = props.stats || {}
return (
<>
<SeoHead seo={seo} title={seo.title || `${title} — Skinbase Nova`} description={seo.description || description} />
<SeoHead seo={seo} title={seo.title || `${title} — Skinbase`} description={seo.description || description} />
<div className="relative min-h-screen overflow-hidden pb-16">
<div aria-hidden="true" className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[36rem] opacity-95" style={{ background: 'radial-gradient(circle at 10% 15%, rgba(59,130,246,0.18), transparent 28%), radial-gradient(circle at 84% 18%, rgba(34,197,94,0.16), transparent 24%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)' }} />

View File

@@ -552,7 +552,7 @@ export default function CollectionShow() {
const enabledModuleKeys = new Set(enabledModules.map((module) => module?.key).filter(Boolean))
const showIntroBlock = enabledModuleKeys.size === 0 || enabledModuleKeys.has('intro_block')
const metaOwnerName = owner?.name || owner?.username || collection?.owner?.name || 'Skinbase Curator'
const metaTitle = seo?.title || `${collection?.title} — Skinbase Nova`
const metaTitle = seo?.title || `${collection?.title} — Skinbase`
const metaDescription = seo?.description || collection?.summary || collection?.description || ''
const collectionSchema = seo?.canonical ? {
'@context': 'https://schema.org',
@@ -563,7 +563,7 @@ export default function CollectionShow() {
image: seo?.og_image || collection?.cover_image || undefined,
isPartOf: {
'@type': 'WebSite',
name: 'Skinbase Nova',
name: 'Skinbase',
url: typeof window !== 'undefined' ? window.location.origin : undefined,
},
author: owner ? {
@@ -772,7 +772,7 @@ export default function CollectionShow() {
setCollection((current) => ({ ...current, shares_count: payload?.shares_count ?? current.shares_count }))
await share({
title: collection?.title,
text: collection?.summary || collection?.description || `Explore ${collection?.title} on Skinbase Nova.`,
text: collection?.summary || collection?.description || `Explore ${collection?.title} on Skinbase.`,
url: collection?.public_url,
})
} catch (error) {

View File

@@ -458,7 +458,7 @@ export default function CollectionStaffProgramming() {
return (
<>
<Head>
<title>{seo.title || 'Collection Programming — Skinbase Nova'}</title>
<title>{seo.title || 'Collection Programming — Skinbase'}</title>
<meta name="description" content={seo.description || 'Staff programming tools for collections.'} />
{seo.canonical ? <link rel="canonical" href={seo.canonical} /> : null}
<meta name="robots" content={seo.robots || 'noindex,follow'} />

View File

@@ -374,7 +374,7 @@ export default function CollectionStaffSurfaces() {
return (
<>
<Head>
<title>{seo.title || 'Collection Surfaces — Skinbase Nova'}</title>
<title>{seo.title || 'Collection Surfaces — Skinbase'}</title>
<meta name="description" content={seo.description || 'Staff tools for collection surfaces.'} />
{seo.canonical ? <link rel="canonical" href={seo.canonical} /> : null}
<meta name="robots" content={seo.robots || 'noindex,follow'} />

View File

@@ -144,7 +144,7 @@ export default function SavedCollections() {
'@context': 'https://schema.org',
'@type': 'CollectionPage',
name: 'Saved collections',
description: seo?.description || 'Your saved collections on Skinbase Nova.',
description: seo?.description || 'Your saved collections on Skinbase.',
url: seo.canonical,
mainEntity: {
'@type': 'ItemList',
@@ -328,7 +328,7 @@ export default function SavedCollections() {
return (
<>
<SeoHead seo={seo} title={seo?.title || 'Saved Collections — Skinbase Nova'} description={seo?.description || 'Your saved collections on Skinbase Nova.'} jsonLd={listSchema} />
<SeoHead seo={seo} title={seo?.title || 'Saved Collections — Skinbase'} description={seo?.description || 'Your saved collections on Skinbase.'} jsonLd={listSchema} />
<div className="relative min-h-screen overflow-hidden pb-16">
<div aria-hidden="true" className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[34rem] opacity-95" style={{ background: 'radial-gradient(circle at 15% 14%, rgba(245,158,11,0.16), transparent 26%), radial-gradient(circle at 82% 18%, rgba(56,189,248,0.16), transparent 24%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)' }} />

View File

@@ -146,7 +146,7 @@ export default function GroupHelpPage() {
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80">Groups documentation</p>
<h1 className="mt-3 max-w-4xl text-4xl font-semibold tracking-[-0.04em] text-white md:text-5xl xl:text-6xl">Build, manage, and publish through Groups without losing personal credit.</h1>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">Groups on Skinbase Nova are shared creative identities for studios, collectives, release teams, and long-term collaborations. This guide explains when to use them, how to structure roles, how publishing works, and how to keep the public page clear, trustworthy, and easy to maintain.</p>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">Groups on Skinbase are shared creative identities for studios, collectives, release teams, and long-term collaborations. This guide explains when to use them, how to structure roles, how publishing works, and how to keep the public page clear, trustworthy, and easy to maintain.</p>
<div className="mt-6 flex flex-wrap gap-3">
<a href={links.create_group} className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-300/18">Create a Group</a>
<a href={links.group_studio} className="rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.07]">Open Group Studio</a>

View File

@@ -117,7 +117,7 @@ export default function AuthHelpPage() {
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80">Signup and login help</p>
<h1 className="mt-3 max-w-4xl text-4xl font-semibold tracking-[-0.04em] text-white md:text-5xl xl:text-6xl">Account access should feel clear, fixable, and much less stressful than it often does.</h1>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">This page explains how signup, login, password recovery, and account verification basics work on Skinbase Nova so you can get into your account, recover it when needed, and separate true access problems from workflow or permission confusion.</p>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">This page explains how signup, login, password recovery, and account verification basics work on Skinbase so you can get into your account, recover it when needed, and separate true access problems from workflow or permission confusion.</p>
<div className="mt-6 flex flex-wrap gap-3">
<a href={signedIn ? links.open_studio : links.login} className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-300/18">{signedIn ? 'Open Studio' : 'Open login'}</a>
<a href={links.register} className="rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.07]">Create account</a>

View File

@@ -121,7 +121,7 @@ export default function CardsHelpPage() {
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80">Cards help</p>
<h1 className="mt-3 max-w-4xl text-4xl font-semibold tracking-[-0.04em] text-white md:text-5xl xl:text-6xl">Cards are for ideas that need design, presentation, and message to work together.</h1>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">This page explains what Cards are on Skinbase Nova, how they differ from artworks, posts, and collections, how to create and publish them, and how to use them well in both personal and Group workflows.</p>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">This page explains what Cards are on Skinbase, how they differ from artworks, posts, and collections, how to create and publish them, and how to use them well in both personal and Group workflows.</p>
<div className="mt-6 flex flex-wrap gap-3">
<a href={links.create_card} className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-300/18">Create a Card</a>
<a href={links.studio_cards} className="rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.07]">Open Cards workspace</a>

View File

@@ -119,9 +119,9 @@ export default function HelpCenterPage() {
<section id="introduction" className="rounded-[38px] border border-white/10 bg-[linear-gradient(140deg,rgba(15,23,42,0.94),rgba(15,23,42,0.72)),radial-gradient(circle_at_top_right,rgba(125,211,252,0.18),transparent_26%)] p-6 shadow-[0_32px_100px_rgba(2,6,23,0.34)] md:p-8 lg:p-10">
<div className="grid gap-8 xl:grid-cols-[minmax(0,1fr)_360px]">
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80">Skinbase Nova Help Center</p>
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80">Skinbase Help Center</p>
<h1 className="mt-3 max-w-4xl text-4xl font-semibold tracking-[-0.04em] text-white md:text-5xl xl:text-6xl">Find the right guide, quickstart, FAQ, or fix without digging through scattered help.</h1>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">This is the central help hub for Skinbase Nova. Use it to get started, find module-specific guidance, open the live Groups documentation set, and move quickly toward the next useful answer.</p>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">This is the central help hub for Skinbase. Use it to get started, find module-specific guidance, open the live Groups documentation set, and move quickly toward the next useful answer.</p>
<div className="mt-6 flex flex-wrap gap-3">
<a href="#featured-guides" className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-300/18">Browse help topics</a>

View File

@@ -119,7 +119,7 @@ export default function ProfileHelpPage() {
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80">Profile help</p>
<h1 className="mt-3 max-w-4xl text-4xl font-semibold tracking-[-0.04em] text-white md:text-5xl xl:text-6xl">Your profile is the personal identity people remember when they discover your work.</h1>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">This page explains what a profile is on Skinbase Nova, how it differs from a Group, how to set it up well, and how to build a stronger public creator presence without turning the page into noise.</p>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">This page explains what a profile is on Skinbase, how it differs from a Group, how to set it up well, and how to build a stronger public creator presence without turning the page into noise.</p>
<div className="mt-6 flex flex-wrap gap-3">
<a href={links.profile_settings} className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-300/18">Open profile settings</a>
<a href={links.groups_help} className="rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.07]">Read Groups help</a>

View File

@@ -145,7 +145,7 @@ export default function StudioHelpPage() {
<div className="grid gap-8 xl:grid-cols-[minmax(0,1fr)_360px]">
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80">Studio help</p>
<h1 className="mt-3 max-w-4xl text-4xl font-semibold tracking-[-0.04em] text-white md:text-5xl xl:text-6xl">Studio is the creative control center of Skinbase Nova.</h1>
<h1 className="mt-3 max-w-4xl text-4xl font-semibold tracking-[-0.04em] text-white md:text-5xl xl:text-6xl">Studio is the creative control center of Skinbase.</h1>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">Use Studio to manage drafts, uploads, publishing, artworks, cards, collections, and collaborative work before and after it goes public. This page explains how Studio fits into the platform, how personal and Group contexts differ, and how to use the workspace without creating avoidable confusion.</p>
<div className="mt-6 flex flex-wrap gap-3">
<a href={links.open_studio} className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-300/18">Open Studio</a>

View File

@@ -122,7 +122,7 @@ export default function WorldsHelpPage() {
<div className="grid gap-8 xl:grid-cols-[minmax(0,1fr)_360px]">
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80">Worlds help</p>
<h1 className="mt-3 max-w-4xl text-4xl font-semibold tracking-[-0.04em] text-white md:text-5xl xl:text-6xl">Worlds are where Skinbase Nova turns curated content into a live campaign surface.</h1>
<h1 className="mt-3 max-w-4xl text-4xl font-semibold tracking-[-0.04em] text-white md:text-5xl xl:text-6xl">Worlds are where Skinbase turns curated content into a live campaign surface.</h1>
<p className="mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg">This guide explains what Worlds are, how attached content works, how section visibility and order shape the result, and how to preview, publish, promote, and reuse Worlds for recurring campaigns.</p>
<div className="mt-6 flex flex-wrap gap-3">
<a href={links.create_world} className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-300/18">Create a World</a>

View File

@@ -14,7 +14,7 @@ export const HERO_METRICS = [
{
label: 'What this unlocks',
value: 'Identity and workspace access',
note: 'Signup and login are how you reach your profile, enter Studio, and manage the rest of your creator workflow on Skinbase Nova.',
note: 'Signup and login are how you reach your profile, enter Studio, and manage the rest of your creator workflow on Skinbase.',
},
{
label: 'Most common blocker',

View File

@@ -73,7 +73,7 @@ export const HIGHLIGHTED_GUIDES = [
{
eyebrow: 'Live now',
title: 'Profile help',
description: 'A creator-friendly guide to personal identity, profile setup, profile-versus-Group clarity, and stronger public presentation on Skinbase Nova.',
description: 'A creator-friendly guide to personal identity, profile setup, profile-versus-Group clarity, and stronger public presentation on Skinbase.',
status: 'Guide',
tone: 'white',
primaryLinkKey: 'help_profile',
@@ -292,7 +292,7 @@ export const HELP_CATEGORIES = [
id: 'creation-and-publishing',
label: 'Creation & publishing',
title: 'Creation and publishing',
summary: 'The main surfaces creators use to make, edit, organize, and publish work on Skinbase Nova.',
summary: 'The main surfaces creators use to make, edit, organize, and publish work on Skinbase.',
topics: [
{
eyebrow: 'Workspace',

View File

@@ -173,7 +173,7 @@ export const COMMON_MISTAKES = [
export const FAQ_ITEMS = [
{
question: 'What is Studio?',
answer: 'Studio is the private creator workspace on Skinbase Nova. It is where you manage drafts, uploads, publishing, cards, collections, settings, and other operational parts of your creative work.',
answer: 'Studio is the private creator workspace on Skinbase. It is where you manage drafts, uploads, publishing, cards, collections, settings, and other operational parts of your creative work.',
},
{
question: 'Why do Personal Studio and Group Studio look different?',

View File

@@ -146,7 +146,7 @@ export const SECTION_ITEMS_DETAIL = [
{ title: 'Challenge spotlight', body: 'Challenges attached to the campaign or recent participation around it.' },
{ title: 'Related events', body: 'Upcoming or recent sessions, launches, and live moments.' },
{ title: 'Release spotlights', body: 'Projects and releases that belong in the campaign space.' },
{ title: 'Themed cards', body: 'Nova Cards that extend the World identity into designed communication surfaces.' },
{ title: 'Themed cards', body: 'Cards that extend the World identity into designed communication surfaces.' },
]
export const RELATION_TYPE_ITEMS = [
@@ -210,7 +210,7 @@ export const COMMON_MISTAKES = [
export const FAQ_ITEMS = [
{
question: 'What is a World on Skinbase Nova?',
question: 'What is a World on Skinbase?',
answer: 'A World is a curated editorial destination for a seasonal moment, event, tribute, or campaign. It combines one strong hero with a controlled set of attached modules and optional promotion across public surfaces.',
},
{

View File

@@ -10,7 +10,7 @@ export default function HomeHero({ artwork }) {
<div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-nova-900 via-nova-900/60 to-transparent" />
<div className="relative z-10 w-full px-6 pb-7 sm:px-10 lg:px-16">
<h1 className="text-2xl font-bold tracking-tight text-white sm:text-4xl">
Skinbase Nova
Skinbase
</h1>
<p className="mt-2 max-w-xl text-sm text-soft">
Discover. Create. Inspire.

View File

@@ -1,5 +1,6 @@
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'
import { usePage } from '@inertiajs/react'
import SeoHead from '../../components/seo/SeoHead'
import TagInput from '../../components/tags/TagInput'
import UploadWizard from '../../components/upload/UploadWizard'
import Checkbox from '../../Components/ui/Checkbox'
@@ -620,6 +621,8 @@ function useUploadMachine({ draftId, filesCdnUrl, chunkSize, chunkRequestTimeout
export default function UploadPage({ draftId, filesCdnUrl, chunkSize, chunkRequestTimeoutMs }) {
const { props } = usePage()
const pageTitle = 'Upload Artwork — Creator Studio'
const pageDescription = 'Submit a new artwork, complete the required metadata, and publish it from Skinbase Creator Studio.'
const windowFlags = window?.SKINBASE_FLAGS || {}
const propFlagRaw = props?.feature_flags?.uploads_v2
@@ -640,6 +643,7 @@ export default function UploadPage({ draftId, filesCdnUrl, chunkSize, chunkReque
if (uploadsV2Enabled) {
return (
<section className="min-h-[calc(100vh-4rem)] bg-[#07111c] text-slate-100">
<SeoHead seo={{ title: pageTitle, description: pageDescription, robots: 'noindex, nofollow' }} />
<div className="relative isolate">
<div className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[420px] bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.22),_transparent_32%),radial-gradient(circle_at_top_right,_rgba(251,146,60,0.16),_transparent_30%),linear-gradient(180deg,_rgba(8,17,28,0.98),_rgba(7,17,28,1))]" />
<div className="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
@@ -825,6 +829,7 @@ export default function UploadPage({ draftId, filesCdnUrl, chunkSize, chunkReque
)}
<div className="grid gap-8 lg:grid-cols-[1.1fr,0.9fr]">
<div className="space-y-6">
<SeoHead seo={{ title: pageTitle, description: pageDescription, robots: 'noindex, nofollow' }} />
<div className="rounded-2xl border border-white/10 bg-white/5 p-6 shadow-[0_0_50px_rgba(59,130,246,0.15)]">
<div className="flex items-center justify-between">
<h1 className="text-3xl font-semibold tracking-tight">Upload artwork</h1>

View File

@@ -12,11 +12,11 @@ export default function WorldIndex() {
return (
<main className="min-h-screen bg-[radial-gradient(circle_at_top_left,_rgba(249,115,22,0.12),_transparent_28%),radial-gradient(circle_at_top_right,_rgba(56,189,248,0.12),_transparent_32%),linear-gradient(180deg,_#020617_0%,_#02040a_100%)] px-4 py-10 sm:px-6 lg:px-8">
<SeoHead title={props.seo?.title || 'Worlds - Skinbase Nova'} description={props.seo?.description || props.description} image={props.seo?.image} />
<SeoHead title={props.seo?.title || 'Worlds - Skinbase'} description={props.seo?.description || props.description} image={props.seo?.image} />
<div className="mx-auto max-w-7xl">
<section className="rounded-[36px] border border-white/10 bg-white/[0.03] p-6 sm:p-8">
<div className="max-w-4xl">
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/70">Skinbase Nova Worlds</p>
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/70">Skinbase Worlds</p>
<h1 className="mt-4 text-4xl font-semibold tracking-[-0.05em] text-white sm:text-5xl">Curated spaces for seasonal culture, scene moments, and editorial campaigns.</h1>
<p className="mt-5 max-w-3xl text-base leading-7 text-slate-300">Worlds bundle together artworks, collections, creators, groups, cards, releases, events, challenges, and newsroom context into a single themed destination. They are not filters. They are editorial environments.</p>
</div>
@@ -50,7 +50,7 @@ export default function WorldIndex() {
<WorldsIndexSection
title="Active Worlds"
description="Live worlds and currently running campaign surfaces across Skinbase Nova."
description="Live worlds and currently running campaign surfaces across Skinbase."
items={props.activeWorlds}
emptyMessage="No worlds are currently live. Check upcoming programming below."
sourceSurface="worlds_index"

View File

@@ -227,7 +227,7 @@ export default function WorldShow() {
return (
<main ref={rootRef} className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(56,189,248,0.12),_transparent_28%),linear-gradient(180deg,_#020617_0%,_#02040a_100%)] px-4 py-10 sm:px-6 lg:px-8">
<SeoHead title={props.seo?.title || `${world?.title || 'World'} - Skinbase Nova`} description={props.seo?.description || world?.summary} image={props.seo?.image} />
<SeoHead title={props.seo?.title || `${world?.title || 'World'} - Skinbase`} description={props.seo?.description || world?.summary} image={props.seo?.image} />
<div className="mx-auto max-w-7xl">
{previewMode ? (
<section className="mb-6 rounded-[28px] border border-amber-300/20 bg-amber-400/10 px-5 py-4 text-sm text-amber-50">

View File

@@ -5,7 +5,7 @@ import { cx, formatCompactNumber } from './groupStyles'
export default function GroupDiscoveryCard({ group, className = '', compact = false }) {
if (!group) return null
const primarySummary = group.headline || group.bio_excerpt || 'Collaborative publishing identity on Skinbase Nova.'
const primarySummary = group.headline || group.bio_excerpt || 'Collaborative publishing identity on Skinbase.'
return (
<a

View File

@@ -143,7 +143,7 @@ export default function HomepageAnnouncement({ announcement, mode = 'live' }) {
className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.06] px-4 py-2 text-sm font-semibold text-white/90 transition hover:border-white/20 hover:bg-white/[0.1]"
>
<span aria-hidden="true"></span>
<span>Show Skinbase Nova announcement</span>
<span>Show Skinbase announcement</span>
</button>
</div>
</section>

View File

@@ -59,7 +59,7 @@ export default function NovaConfirmDialog({
className="w-full max-w-md overflow-hidden rounded-3xl border border-white/10 bg-[linear-gradient(180deg,rgba(16,22,34,0.98),rgba(8,12,19,0.98))] shadow-[0_30px_80px_rgba(0,0,0,0.55)]"
>
<div className="border-b border-white/[0.06] bg-white/[0.02] px-6 py-5">
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-white/35">Skinbase Nova</p>
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-white/35">Skinbase</p>
<h3 id="nova-confirm-title" className="mt-2 text-lg font-semibold text-white">
{title}
</h3>

View File

@@ -1157,7 +1157,7 @@ export default function DashboardPage({ username, isCreator, level, rank, receiv
<div className="absolute inset-0 bg-[linear-gradient(135deg,_rgba(56,189,248,0.12),_transparent_40%,_rgba(245,158,11,0.10)_100%)]" />
<div className="relative grid gap-8 xl:grid-cols-[1.35fr_0.95fr] xl:items-start">
<div>
<p className="text-[11px] uppercase tracking-[0.28em] text-sky-200/80">Skinbase Nova Dashboard</p>
<p className="text-[11px] uppercase tracking-[0.28em] text-sky-200/80"> Dashboard</p>
<h1 className="mt-3 max-w-3xl text-3xl font-semibold tracking-tight text-white sm:text-4xl">
Welcome back, {username}
</h1>