Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 33 additions & 8 deletions components/FloatingNavbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function DiscordIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
)
}

type ProductId = 'concepta' | 'mix' | 'remix' | 'naked-ui' | 'ack' | 'stargate' | 'code-analysis'
type ProductId = 'concepta' | 'mix' | 'remix' | 'naked-ui' | 'ack' | 'stargate' | 'code-analysis' | 'voyager'

function getActiveProduct(pathname: string | null): ProductId {
if (!pathname) return 'concepta'
Expand All @@ -43,6 +43,7 @@ function getActiveProduct(pathname: string | null): ProductId {
if (pathname === '/ack' || pathname.startsWith('/ack/')) return 'ack'
if (pathname === '/stargate' || pathname.startsWith('/stargate/')) return 'stargate'
if (pathname === '/code-analysis' || pathname.startsWith('/code-analysis/')) return 'code-analysis'
if (pathname === '/voyager' || pathname.startsWith('/voyager/')) return 'voyager'
return 'mix'
}

Expand Down Expand Up @@ -143,6 +144,22 @@ const PRODUCTS: Product[] = [
},
]

// Voyager is intentionally available only by direct URL while its positioning
// is being tested. It needs a real shell identity without appearing in either
// desktop or mobile product menus.
const VOYAGER_PRODUCT: Product = {
id: 'voyager',
label: 'Voyager',
href: '/voyager',
logo: '/assets/logo_voyager_mark.svg',
showLabel: true,
}

function getProductMeta(id: ProductId) {
if (id === 'voyager') return VOYAGER_PRODUCT
return PRODUCTS.find((product) => product.id === id)
}

type DocsEntry =
| { label: string; href: string }
| { label: string; pages: { label: string; href: string }[] }
Expand Down Expand Up @@ -340,7 +357,7 @@ function ProductMenu() {
const pathname = usePathname()
const active = getActiveProduct(pathname)
// Undefined on the Concepta home — the trigger falls back to "Projects".
const activeProduct = PRODUCTS.find((p) => p.id === active)
const activeProduct = getProductMeta(active)

const [open, setOpen] = useState(false)
const ref = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -447,12 +464,16 @@ function MobileDrawer({
onClose,
docsHref,
githubHref,
productCtaHref,
productCtaLabel,
activeProduct,
isDocsActive,
}: {
onClose: () => void
docsHref: string | null
githubHref: string | null
productCtaHref: string
productCtaLabel: string
activeProduct: ProductId
isDocsActive: boolean
}) {
Expand Down Expand Up @@ -560,11 +581,11 @@ function MobileDrawer({
</>
) : (
<Link
href="#waitlist"
href={productCtaHref}
onClick={onClose}
className="px-3 py-2 text-white/90 hover:bg-white/5 rounded mt-2"
>
Join waitlist
{productCtaLabel}
</Link>
)}
{hasVersionMenu(activeProduct) && (
Expand Down Expand Up @@ -625,11 +646,13 @@ export default function FloatingNavbar() {
const isDocsActive = pathname?.startsWith('/documentation') ?? false
const isReportsActive = pathname?.startsWith('/reports') ?? false
const activeProduct = getActiveProduct(pathname)
const homeHref =
PRODUCTS.find((p) => p.id === activeProduct)?.href ?? '/'
const homeHref = getProductMeta(activeProduct)?.href ?? '/'
const isHomeActive = pathname === homeHref
const docsHref = getDocsHref(activeProduct)
const githubHref = getGithubHref(activeProduct)
const isVoyagerReadiness = pathname?.startsWith('/voyager/readiness') ?? false
const productCtaHref = isVoyagerReadiness ? '#request' : '#waitlist'
const productCtaLabel = isVoyagerReadiness ? 'Request assessment' : 'Join waitlist'

useEffect(() => {
setDrawerOpen(false)
Expand Down Expand Up @@ -716,10 +739,10 @@ export default function FloatingNavbar() {
</Link>
) : (
<Link
href="#waitlist"
href={productCtaHref}
className="px-3 py-1.5 text-sm text-white/80 hover:text-white transition-colors"
>
Join waitlist
{productCtaLabel}
</Link>
)}
<span className="h-4 w-px bg-white/10" />
Expand Down Expand Up @@ -839,6 +862,8 @@ export default function FloatingNavbar() {
onClose={() => setDrawerOpen(false)}
docsHref={docsHref}
githubHref={githubHref}
productCtaHref={productCtaHref}
productCtaLabel={productCtaLabel}
activeProduct={activeProduct}
isDocsActive={isDocsActive}
/>
Expand Down
21 changes: 18 additions & 3 deletions components/ProductFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,17 @@ function isMixPath(pathname: string | null) {
|| pathname?.startsWith('/documentation/mix')
}

function isVoyagerPath(pathname: string | null) {
return pathname === '/voyager' || pathname?.startsWith('/voyager/')
}

function isProductWithoutPublicRepo(pathname: string | null) {
return pathname === '/stargate'
|| pathname?.startsWith('/stargate/')
|| pathname === '/code-analysis'
|| pathname?.startsWith('/code-analysis/')
|| pathname === '/voyager'
|| pathname?.startsWith('/voyager/')
}

function getGithubHref(pathname: string | null) {
Expand All @@ -48,6 +54,7 @@ function getPubDevHref(pathname: string | null) {

export default function ProductFooter() {
const pathname = usePathname()
const isVoyager = isVoyagerPath(pathname)

if (isAckPath(pathname)) {
return (
Expand Down Expand Up @@ -83,10 +90,18 @@ export default function ProductFooter() {
return (
<div className="flex flex-col sm:flex-row items-center justify-between w-full gap-4 text-sm text-[var(--mix-text-muted)]">
<span className="flex items-center gap-2.5">
<Link href="/" aria-label="Concepta home" className="opacity-80 hover:opacity-100 transition-opacity">
<img src="/assets/logo_concepta.svg" alt="Concepta" className="h-3.5 w-auto" />
<Link
href={isVoyager ? '/voyager' : '/'}
aria-label={isVoyager ? 'Voyager home' : 'Concepta home'}
className="opacity-80 hover:opacity-100 transition-opacity"
>
<img
src={isVoyager ? '/assets/logo_voyager_mark.svg' : '/assets/logo_concepta.svg'}
alt={isVoyager ? 'Voyager' : 'Concepta'}
className={isVoyager ? 'h-4 w-auto' : 'h-3.5 w-auto'}
/>
</Link>
<span>&copy; 2026 Concepta Tech.</span>
<span>&copy; 2026 {isVoyager ? 'Voyager.' : 'Concepta Tech.'}</span>
</span>
<div className="flex items-center gap-5">
<Link href="/reports" className="hover:text-white transition-colors">Reports</Link>
Expand Down
Loading
Loading