diff --git a/pages/index.tsx b/pages/index.tsx
index b01a345..f63c2b2 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -1,6 +1,7 @@
import { useEffect } from 'react'
import Seo from '@/components/Seo'
import { SITE_URL } from '@/lib/site'
+import dynamic from 'next/dynamic'
import Nav from '@/components/Nav'
import Hero from '@/components/Hero'
import Capabilities from '@/components/Capabilities'
@@ -11,6 +12,9 @@ import OpenSource from '@/components/OpenSource'
import Partners from '@/components/Partners'
import Footer from '@/components/Footer'
+// Client-only: its visibility depends on localStorage and the current date.
+const AnnouncementBar = dynamic(() => import('@/components/AnnouncementBar'), { ssr: false })
+
export default function Home() {
// Set up scroll-reveal observer (mirrors the original HTML script)
useEffect(() => {
@@ -35,6 +39,7 @@ export default function Home() {
return (
<>
+
diff --git a/pages/inference-day.tsx b/pages/inference-day.tsx
index f48ddc4..6974a5d 100644
--- a/pages/inference-day.tsx
+++ b/pages/inference-day.tsx
@@ -1,7 +1,8 @@
import Seo from '@/components/Seo'
import Footer from '@/components/Footer'
+import Nav from '@/components/Nav'
import { SITE_URL } from '@/lib/site'
-import EventNav from '@/components/inference-day/EventNav'
+import EventSubNav from '@/components/inference-day/EventSubNav'
import EventHero from '@/components/inference-day/EventHero'
import RegisterSection from '@/components/inference-day/RegisterSection'
import Agenda from '@/components/inference-day/Agenda'
@@ -54,7 +55,8 @@ export default function InferenceDay() {
-
+
+
diff --git a/src/components/AnnouncementBar.tsx b/src/components/AnnouncementBar.tsx
new file mode 100644
index 0000000..29800c3
--- /dev/null
+++ b/src/components/AnnouncementBar.tsx
@@ -0,0 +1,57 @@
+import { useState } from 'react'
+import Link from 'next/link'
+
+// Homepage announcement bar for the Inference Day event. Links to the event
+// landing page, can be dismissed (remembered per browser), and hides itself once
+// the event date has passed so it never needs to be removed by hand.
+//
+// Rendered client-only (next/dynamic ssr:false in the homepage), so the initial
+// visibility is read straight from localStorage/the clock in the useState
+// initializer — no effect, and no server/client markup to mismatch.
+const EVENT_URL = '/inference-day'
+const HIDE_AFTER = new Date('2026-11-06T00:00:00Z') // day after the event
+const DISMISS_KEY = 'mp-inference-day-bar'
+
+function shouldShow(): boolean {
+ if (Date.now() >= HIDE_AFTER.getTime()) return false
+ try {
+ return localStorage.getItem(DISMISS_KEY) !== 'dismissed'
+ } catch {
+ // private mode / blocked storage — treat as not dismissed
+ return true
+ }
+}
+
+export default function AnnouncementBar() {
+ const [show, setShow] = useState(shouldShow)
+
+ function dismiss() {
+ setShow(false)
+ try {
+ localStorage.setItem(DISMISS_KEY, 'dismissed')
+ } catch {
+ // storage unavailable — nothing to persist
+ }
+ }
+
+ if (!show) return null
+
+ return (
+
+
+ Inference Day
+
+ Nov 5 · Virtual · Free
+
+ Hear where AI infrastructure is going from the engineers building it.
+
+ Register →
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/Nav.tsx b/src/components/Nav.tsx
index e34ea96..2b5bf3d 100644
--- a/src/components/Nav.tsx
+++ b/src/components/Nav.tsx
@@ -55,6 +55,7 @@ export default function Nav({ hero = false }: NavProps) {
Blog
Manifesto
Slack
+ Inference Day
★ Star the Project
Get started →
@@ -75,6 +76,7 @@ export default function Nav({ hero = false }: NavProps) {
setOpen(false)}>Blog
setOpen(false)}>Manifesto
setOpen(false)}>Slack
+ setOpen(false)}>Inference Day
setOpen(false)}>
setOpen(false)}>★ Star the Project
setOpen(false)}>Get started →
diff --git a/src/components/inference-day/EventHero.tsx b/src/components/inference-day/EventHero.tsx
index 9208047..2f205f1 100644
--- a/src/components/inference-day/EventHero.tsx
+++ b/src/components/inference-day/EventHero.tsx
@@ -23,14 +23,14 @@ export default function EventHero() {
+
+
-
-
)
}
diff --git a/src/components/inference-day/EventNav.tsx b/src/components/inference-day/EventNav.tsx
deleted file mode 100644
index 921edc3..0000000
--- a/src/components/inference-day/EventNav.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { useEffect, useState } from 'react'
-
-// Event-specific top bar for /inference-day. Separate from the site Nav so the
-// links are the on-page anchors rather than the marketing nav. Sticky on a
-// midnight background; collapses to a toggle menu on small screens.
-const DOCS_URL = 'https://docs.modelplane.ai'
-
-const LINKS = [
- { label: 'Register', href: '#register' },
- { label: 'Agenda', href: '#agenda' },
- { label: 'Speakers', href: '#speakers' },
- { label: 'Docs', href: DOCS_URL },
-]
-
-export default function EventNav() {
- const [open, setOpen] = useState(false)
-
- // Close the mobile menu when the hash changes (an anchor link was followed).
- useEffect(() => {
- const close = () => setOpen(false)
- window.addEventListener('hashchange', close)
- return () => window.removeEventListener('hashchange', close)
- }, [])
-
- return (
-
-
-
-
-
-
-
-
-
setOpen((v) => !v)}
- aria-label="Toggle menu"
- aria-expanded={open}
- >
-
-
-
-
-
-
- )
-}
diff --git a/src/components/inference-day/EventSubNav.tsx b/src/components/inference-day/EventSubNav.tsx
new file mode 100644
index 0000000..b86b842
--- /dev/null
+++ b/src/components/inference-day/EventSubNav.tsx
@@ -0,0 +1,20 @@
+// Event sub-nav for the Inference Day landing page. Sits directly below the main
+// site and sticks under it, carrying only the on-page jump links plus a
+// Register CTA. The "Inference Day" label marks which section of the site you're
+// in, since the main nav shows the Modelplane logo. All links are in-page anchors,
+// so they stay plain (next/link is for route navigation).
+export default function EventSubNav() {
+ return (
+
+
+
+ )
+}
diff --git a/src/styles/globals.css b/src/styles/globals.css
index a3337d5..7c6d27e 100644
--- a/src/styles/globals.css
+++ b/src/styles/globals.css
@@ -1798,3 +1798,74 @@ a.post-tag:hover { border-color: var(--border-hi); color: var(--text); }
@media (max-width: 560px) {
.post-related .post-grid { grid-template-columns: 1fr 1fr; }
}
+
+/* ── ANNOUNCEMENT BAR (Inference Day, homepage) ── */
+.announce {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ align-items: stretch;
+ background:
+ radial-gradient(460px 160px at 14% 50%, rgba(0, 220, 232, 0.16), transparent 72%),
+ #001C2E;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+}
+.announce-link {
+ flex: 1;
+ min-height: 48px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 18px;
+ padding: 9px 56px 9px 24px;
+ text-decoration: none;
+ color: #fff;
+}
+.announce-wordmark {
+ font-family: var(--display);
+ font-weight: 600;
+ font-size: 18px;
+ letter-spacing: -0.01em;
+ background: linear-gradient(90deg, #9A5EFC, #00DCE8);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ color: transparent;
+ white-space: nowrap;
+}
+.announce-sep { width: 1px; height: 16px; background: rgba(255, 255, 255, 0.18); flex: none; }
+.announce-tag {
+ font-size: 12px; font-weight: 500; letter-spacing: 0.10em; text-transform: uppercase;
+ color: var(--cyan); white-space: nowrap;
+}
+.announce-copy { font-size: 14px; color: rgba(255, 255, 255, 0.86); }
+.announce-cta {
+ display: inline-flex; align-items: center; gap: 6px;
+ background: var(--purple); color: #fff; font-size: 13px; font-weight: 600;
+ padding: 7px 16px; border-radius: 999px; white-space: nowrap;
+ transition: background 0.2s;
+}
+.announce-link:hover .announce-cta { background: linear-gradient(90deg, #9A5EFC, #00DCE8); }
+.announce-close {
+ position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
+ display: inline-flex; align-items: center; justify-content: center;
+ width: 32px; height: 32px;
+ background: none; border: 0; color: rgba(255, 255, 255, 0.55);
+ cursor: pointer; border-radius: 8px; transition: color 0.2s, background 0.2s;
+}
+.announce-close:hover { color: #fff; background: rgba(255, 255, 255, 0.08); }
+
+@media (max-width: 760px) {
+ .announce-copy, .announce-sep { display: none; }
+ .announce-link { gap: 12px; justify-content: space-between; padding: 8px 48px 8px 16px; }
+}
+@media (max-width: 420px) {
+ .announce-tag { display: none; }
+}
+
+/* Inference Day event link in the main nav — a subtle cyan accent so the
+ time-sensitive event stands out next to the standard nav items. */
+.nav-links .nav-event,
+.nav-dropdown .nav-event { color: var(--cyan); }
+.nav-links .nav-event:hover,
+.nav-dropdown .nav-event:hover { color: var(--purple-hi); }
diff --git a/src/styles/inference-day.css b/src/styles/inference-day.css
index 6d1b1df..b3698e1 100644
--- a/src/styles/inference-day.css
+++ b/src/styles/inference-day.css
@@ -48,7 +48,7 @@ body:has(.id-page) { background: #001C2E; }
padding-inline: var(--id-gutter);
}
-.id-page section { scroll-margin-top: 84px; } /* clear the sticky nav on anchor jumps */
+.id-page section { scroll-margin-top: 128px; } /* clear the main nav + event sub-nav on anchor jumps */
/* ── Shared type ──────────────────────────────────────────────────────────── */
.id-wordmark {
@@ -94,85 +94,90 @@ body:has(.id-page) { background: #001C2E; }
height: 48px;
}
-/* ── 3.1 Nav ──────────────────────────────────────────────────────────────── */
-.id-nav {
+/* ── Event sub-nav ──────────────────────────────────────────────────────────
+ Sits directly below the main site and sticks under it, carrying the
+ on-page jump links. The main nav is sticky at top:0 (z 100); this pins just
+ below it. .id-subnav is a , so it overrides the global nav rules. */
+.id-subnav {
position: sticky;
- top: 0;
- z-index: 100;
- background: rgba(0, 28, 46, .82);
+ top: 65px;
+ z-index: 90;
+ background: rgba(3, 57, 74, .85);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-bottom: 1px solid var(--id-line);
}
-.id-nav-inner {
+.id-subnav-inner {
display: flex;
align-items: center;
- justify-content: space-between;
gap: 24px;
- padding-block: 16px;
+ min-height: 44px;
+ padding-block: 8px;
}
-.id-nav-logo { display: inline-flex; align-items: center; }
-.id-nav-logo img { height: 26px; width: auto; display: block; }
-.id-nav-links {
- display: flex;
- align-items: center;
- gap: 28px;
- margin: 0;
- padding: 0;
- list-style: none;
+.id-subnav-label {
+ font-family: var(--font-display);
+ font-weight: 500;
+ font-size: 15px;
+ letter-spacing: -0.01em;
+ color: #fff;
+ text-decoration: none;
+ white-space: nowrap;
}
-.id-nav-links a {
+.id-subnav-links { display: flex; gap: 22px; }
+.id-subnav-links a {
font: 400 14px/1 var(--font-body);
color: var(--id-white-72);
text-decoration: none;
transition: color .18s;
}
-.id-nav-links a:hover { color: #fff; }
-.id-nav-links .btn-primary { color: #fff; }
-.id-nav-toggle {
- display: none;
- background: none;
- border: 0;
- padding: 8px;
- cursor: pointer;
+.id-subnav-links a:hover { color: #fff; }
+.id-page .id-subnav-cta {
+ margin-left: auto;
+ font-size: 13px;
+ padding: 8px 18px;
}
-.id-nav-toggle span {
- display: block;
- width: 22px;
- height: 2px;
- margin: 4px 0;
- background: #fff;
- transition: transform .2s, opacity .2s;
+@media (max-width: 640px) {
+ .id-subnav-label { display: none; }
+ .id-page .id-subnav-cta { display: none; }
+ .id-subnav-inner { gap: 0; }
+ .id-subnav-links { width: 100%; justify-content: space-around; gap: 12px; }
}
-.id-nav-mobile { display: none; }
/* ── 3.2 Hero ─────────────────────────────────────────────────────────────── */
.id-hero {
position: relative;
overflow: hidden;
background: var(--id-hero);
- padding-block: clamp(56px, 7vw, 96px) clamp(72px, 8vw, 120px);
-}
+ /* Compact, roomy hero like the design (≈800px at 1440): the copy sits in the
+ upper-left and the min-height leaves open space below/right for the wave. */
+ min-height: clamp(560px, 52vw, 780px);
+ padding-block: clamp(48px, 5vw, 80px) clamp(64px, 7vw, 104px);
+}
+/* The contrail is full-bleed behind the content, as the design composes it:
+ anchored right and bled above the top (y -133 at 1440), sweeping from the
+ upper-right down to the lower-left. The copy sits compact in the upper-left
+ (z 1), so the wave's dense sweep falls in the open right and lower area. */
.id-hero-art {
position: absolute;
- top: -133px;
+ top: -9.2vw;
right: 0;
- width: min(1200px, 115%);
- max-width: none;
+ width: 108.5vw;
+ min-width: 1040px;
height: auto;
pointer-events: none;
z-index: 0;
}
-.id-hero-inner { position: relative; z-index: 1; max-width: 760px; }
+.id-hero-inner { position: relative; z-index: 1; max-width: none; }
.id-hero h1 {
- font-size: clamp(56px, 9.7vw, 140px);
+ font-size: clamp(44px, 7.2vw, 108px);
+ white-space: nowrap;
margin: 0;
}
.id-hero-intro {
- font: 400 clamp(18px, 1.7vw, 24px)/1.35 var(--font-body);
+ font: 400 clamp(17px, 1.5vw, 22px)/1.4 var(--font-body);
color: #fff;
- margin: 22px 0 0;
- max-width: 620px;
+ margin: 20px 0 0;
+ max-width: 900px;
}
.id-hero-intro span { display: block; }
.id-date {
@@ -180,23 +185,23 @@ body:has(.id-page) { background: #001C2E; }
letter-spacing: .10em;
text-transform: uppercase;
color: var(--id-cyan);
- margin: 26px 0 0;
+ margin: 22px 0 0;
}
.id-hero-actions {
display: flex;
flex-wrap: wrap;
gap: 16px;
- margin-top: 34px;
+ margin-top: 28px;
}
+/* "Hosted by / Sponsored by" lockup: a normal flow element left-aligned under
+ the buttons, so it sits on clean dark below the copy rather than centred over
+ the wave pattern (where it got lost). Places predictably on every breakpoint. */
.id-hero-lockup {
- position: absolute;
- right: var(--id-gutter);
- bottom: 28px;
- width: 612px;
- max-width: 48%;
+ display: block;
+ width: 440px;
+ max-width: 100%;
height: auto;
- z-index: 1;
- pointer-events: none;
+ margin: 44px 0 0;
}
/* ── 3.3 About + Register ─────────────────────────────────────────────────── */
@@ -459,8 +464,10 @@ body:has(.id-page) { background: #001C2E; }
.id-cta-art {
position: absolute;
right: 0;
- bottom: -40px;
- width: min(1100px, 110%);
+ top: 50%;
+ transform: translateY(-50%);
+ width: 108.5vw;
+ min-width: 1040px;
height: auto;
opacity: .85;
pointer-events: none;
@@ -502,28 +509,6 @@ body:has(.id-page) { background: #001C2E; }
.id-hero-inner { max-width: none; }
}
@media (max-width: 860px) {
- .id-nav-links { display: none; }
- .id-nav-toggle { display: block; }
- .id-nav-mobile.open {
- display: flex;
- flex-direction: column;
- gap: 4px;
- padding: 8px var(--id-gutter) 20px;
- border-top: 1px solid var(--id-line);
- }
- .id-nav-mobile a {
- font: 400 16px/1 var(--font-body);
- color: #fff;
- text-decoration: none;
- padding: 12px 0;
- }
- .id-hero-lockup {
- position: static;
- display: block;
- width: 70%;
- max-width: 420px;
- margin: 40px 0 0;
- }
.id-featured--large,
.id-featured--small { flex-basis: 100%; }
}
@@ -544,3 +529,15 @@ body:has(.id-page) { background: #001C2E; }
@media (prefers-reduced-motion: reduce) {
.id-page * { animation: none !important; transition: none !important; }
}
+
+/* On phones/small tablets the hero and CTA copy is full-width, so the full-bleed
+ wave can only sit behind the text and clutter it. Hide the decorative art at
+ these sizes and keep the clean solid-dark hero/CTA instead. */
+@media (max-width: 768px) {
+ /* Phones/small tablets: hide the decorative wave (no room beside full-width
+ copy), let the wordmark wrap, and drop the desktop min-height. */
+ .id-hero-art,
+ .id-cta-art { display: none; }
+ .id-hero { min-height: 0; }
+ .id-hero h1 { white-space: normal; }
+}