Meeting Scheduler
Day strip, slot grid and a time zone the visitor can change — because a slot list with no zone on it is how this pattern wastes an hour of everyone involved.
Time down the side, tracks across, breaks spanning the width — the one comparison a month view cannot make. Falls to a linear list on a phone, from the same array.
components/event-agenda-grid.tsxnpx hoverlab add event-agenda-grid
Or over MCP, from your editor's agent — no account needed.
Free to read, copy and install, for personal and non-commercial projects. Shipping it in client work or a paid product needs Pro ($79 once). The source lands in your repo and stops being ours — no attribution, nothing to upgrade.
Start a page with this section — add more, order them, and leave with the page source.
Thursday 14 May
All times CEST (Berlin)
08:30 – 09:30
Registration and coffee
Foyer
09:30 – 10:15
10:30 – 11:10
11:20 – 12:00
12:00 – 13:30
Lunch, and the unconference board opens
Foyer
13:30 – 15:00
15:20 – 16:00
16:15 – 17:00
Rendered live in your current theme — this is the same component whose source is below, not a screenshot of it.
/**
* <EventAgendaGrid> — a conference day across parallel tracks.
*
* The reason this is not <CalendarMonth> with different data: a conference
* agenda has a second axis that a calendar does not. The question a
* delegate is answering is never "what is on at 14:00" — it is "at 14:00,
* which of these three rooms do I walk to", and that is a comparison across
* a row. A month grid puts time on both axes and has nowhere to put the
* track, so the moment a slot has three sessions it degrades into "+2 more"
* and hides the exact choice the reader came to make.
*
* SO: time down the start edge, tracks across, one cell per session, and
* everything that is not a choice — registration, lunch, the keynote —
* spans the full width. The span is load-bearing rather than decorative: a
* break rendered as three identical cells reads as three simultaneous
* break sessions you have to choose between.
*
* A GRID IS THE WRONG SHAPE ON A PHONE, so there are two renders and only
* one of them is in the accessibility tree at a time. Narrow screens get
* the same data as a linear list grouped by time, which is what a delegate
* standing in a corridor actually reads; the grid is `hidden` below `lg`.
* Both are built from the same array, so they cannot drift.
*
* TIMES ARE PRE-FORMATTED STRINGS AND THE ZONE IS NAMED. Calling
* `toLocaleTimeString` during render is a hydration mismatch — the server's
* zone is not the browser's — and an agenda whose times silently shift to
* the reader's own zone is worse than useless to someone booking a train.
* A conference happens in one city, in that city's time, and the strip says
* which.
*
* CSS GRID, NOT A <table>. The layout is genuinely tabular, but a table
* cannot express "this cell spans all columns and that one is empty"
* without `colspan` bookkeeping the author has to keep in step by hand, and
* an empty `<td>` is announced as a blank cell in every row a track has
* nothing on. A list of time groups, each with its sessions, is the honest
* structure — the grid is presentation laid over it.
*/
import * as React from 'react'
import { MapPin, Clock } from 'lucide-react'
/*
Hashed from the heading rather than fixed, because two agendas — a
Thursday and a Friday — legitimately appear on one page, and a literal
id would make the second one's `aria-labelledby` resolve to the first
one's heading. Server component, so no `useId`.
*/
function instanceId(...parts: (string | undefined)[]): string {
const text = parts.filter(Boolean).join('|')
let hash = 0
for (let i = 0; i < text.length; i++) hash = (Math.imul(hash, 31) + text.charCodeAt(i)) | 0
return (hash >>> 0).toString(36).slice(0, 6)
}
export interface AgendaSession {
title: string
/** Track name. Must match one of `tracks`, or the session renders full width. */
track?: string
speaker?: string
room?: string
/** Anything that is not a talk: a break, a keynote, registration. */
kind?: 'talk' | 'keynote' | 'break' | 'workshop'
href?: string
}
export interface AgendaSlot {
/** Pre-formatted in the event's own zone. See the header. */
start: string
end?: string
sessions: AgendaSession[]
}
export interface EventAgendaGridProps {
heading?: string
/** The day this agenda is for — "Thursday 14 May". */
dayLabel?: string
/** The zone the times are quoted in, spelled out. */
timeZoneLabel?: string
/** Column order. A session whose `track` is not here spans the full width. */
tracks?: string[]
slots?: AgendaSlot[]
className?: string
}
const DEFAULT_TRACKS = ['Platform', 'Interface', 'Practice']
const DEFAULT_SLOTS: AgendaSlot[] = [
{
start: '08:30',
end: '09:30',
sessions: [{ title: 'Registration and coffee', kind: 'break', room: 'Foyer' }],
},
{
start: '09:30',
end: '10:15',
sessions: [
{
title: 'What we got wrong about build tooling, twice',
speaker: 'Aisling Moreau',
room: 'Great Hall',
kind: 'keynote',
href: '#',
},
],
},
{
start: '10:30',
end: '11:10',
sessions: [
{
title: 'Deleting the cache layer and getting faster',
track: 'Platform',
speaker: 'Tobias Renner',
room: 'Room A',
href: '#',
},
{
title: 'Type-safe forms without a runtime',
track: 'Interface',
speaker: 'Priya Ramanathan',
room: 'Room B',
href: '#',
},
{
title: 'How we run a design review that people want to attend',
track: 'Practice',
speaker: 'Marcus Oyelaran',
room: 'Room C',
href: '#',
},
],
},
{
start: '11:20',
end: '12:00',
sessions: [
{
title: 'Postgres is still the answer, and here is the bill',
track: 'Platform',
speaker: 'Hana Lindqvist',
room: 'Room A',
href: '#',
},
{
title: 'Animation that survives prefers-reduced-motion',
track: 'Interface',
speaker: 'Dev Kaur',
room: 'Room B',
href: '#',
},
],
},
{
start: '12:00',
end: '13:30',
sessions: [{ title: 'Lunch, and the unconference board opens', kind: 'break', room: 'Foyer' }],
},
{
start: '13:30',
end: '15:00',
sessions: [
{
title: 'Workshop: profiling a slow page down to the frame',
track: 'Platform',
speaker: 'Tobias Renner',
room: 'Lab 1',
kind: 'workshop',
href: '#',
},
{
title: 'Workshop: accessible components from first principles',
track: 'Interface',
speaker: 'Priya Ramanathan',
room: 'Lab 2',
kind: 'workshop',
href: '#',
},
{
title: 'Open space: hiring without take-home tests',
track: 'Practice',
room: 'Room C',
href: '#',
},
],
},
{
start: '15:20',
end: '16:00',
sessions: [
{
title: 'Five years of one monorepo, honestly assessed',
track: 'Platform',
speaker: 'Hana Lindqvist',
room: 'Room A',
href: '#',
},
{
title: 'Design tokens after the second rename',
track: 'Interface',
speaker: 'Marcus Oyelaran',
room: 'Room B',
href: '#',
},
{
title: 'What on-call does to a team, and what to do about it',
track: 'Practice',
speaker: 'Aisling Moreau',
room: 'Room C',
href: '#',
},
],
},
{
start: '16:15',
end: '17:00',
sessions: [
{
title: 'Closing panel: the parts we are all still doing by hand',
room: 'Great Hall',
kind: 'keynote',
href: '#',
},
],
},
]
/** Per-kind chrome. Kept in one place so the two renders cannot diverge. */
function kindClasses(kind: AgendaSession['kind']): string {
switch (kind) {
case 'break':
return 'border-dashed border-border bg-muted/40 text-muted-foreground'
case 'keynote':
return 'border-primary/40 bg-primary/5'
case 'workshop':
return 'border-border bg-card ring-1 ring-inset ring-border/60'
default:
return 'border-border bg-card'
}
}
function kindLabel(kind: AgendaSession['kind']): string | null {
if (kind === 'keynote') return 'Keynote'
if (kind === 'workshop') return 'Workshop · 90 min'
return null
}
/** One session, used by both the grid and the narrow list. */
function SessionCard({ session }: { session: AgendaSession }) {
const label = kindLabel(session.kind)
const Tag = session.href ? 'a' : 'div'
return (
<Tag
{...(session.href ? { href: session.href } : {})}
className={`flex h-full flex-col rounded-xl border p-3 text-start transition-colors ${kindClasses(
session.kind,
)} ${session.href ? 'hover:border-primary/50 hover:bg-muted/40' : ''}`}
>
{label ? (
<span className="text-[10px] font-semibold uppercase tracking-wider text-primary">
{label}
</span>
) : null}
<p
className={`text-sm font-semibold leading-snug ${label ? 'mt-1' : ''} ${
session.kind === 'break' ? 'text-muted-foreground' : 'text-card-foreground'
}`}
>
{session.title}
</p>
{session.speaker ? (
<p className="mt-1.5 text-xs font-medium text-muted-foreground">{session.speaker}</p>
) : null}
{session.room ? (
<p className="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground">
<MapPin aria-hidden className="h-3 w-3" />
{session.room}
</p>
) : null}
</Tag>
)
}
export function EventAgendaGrid({
heading = 'The programme',
dayLabel = 'Thursday 14 May',
timeZoneLabel = 'All times CEST (Berlin)',
tracks = DEFAULT_TRACKS,
slots = DEFAULT_SLOTS,
className = '',
}: EventAgendaGridProps) {
const headingId = `event-agenda-heading-${instanceId(heading, dayLabel)}`
/* `6rem` for the time gutter, then one equal column per track. Inline
because the track count is data — a Tailwind class cannot be built
from a runtime number without the JIT having seen it. */
const gridTemplate = { gridTemplateColumns: `6rem repeat(${tracks.length}, minmax(0, 1fr))` }
return (
<section
aria-labelledby={headingId}
className={`mx-auto w-full max-w-6xl px-4 py-16 sm:px-6 lg:px-8 ${className}`}
>
<header className="flex flex-wrap items-end justify-between gap-x-6 gap-y-2">
<div>
<h2 id={headingId} className="text-3xl font-bold tracking-tight sm:text-4xl">
{heading}
</h2>
<p className="mt-2 text-sm font-medium text-muted-foreground">{dayLabel}</p>
</div>
<p className="inline-flex items-center gap-1.5 text-xs text-muted-foreground">
<Clock aria-hidden className="h-3.5 w-3.5" />
{timeZoneLabel}
</p>
</header>
{/* ── Wide: the grid ───────────────────────────────────────────── */}
<div className="mt-8 hidden lg:block">
<div className="grid gap-2 pb-2" style={gridTemplate} aria-hidden>
<span />
{tracks.map((track) => (
<span
key={track}
className="min-w-0 truncate px-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground"
>
{track}
</span>
))}
</div>
<ol className="space-y-2">
{slots.map((slot) => {
/* A slot is "shared" when nothing in it names a known track —
registration, lunch, a keynote. It spans every column. */
const shared = slot.sessions.filter((s) => !s.track || !tracks.includes(s.track))
return (
<li key={slot.start} className="grid gap-2" style={gridTemplate}>
<div className="pt-3 text-xs font-medium tabular-nums text-muted-foreground">
{slot.start}
{slot.end ? (
<span className="block text-muted-foreground/85">{slot.end}</span>
) : null}
</div>
{shared.length ? (
<div style={{ gridColumn: `span ${tracks.length}` }}>
{shared.map((session) => (
<SessionCard key={session.title} session={session} />
))}
</div>
) : (
tracks.map((track) => {
const session = slot.sessions.find((s) => s.track === track)
return (
<div key={track}>
{session ? <SessionCard session={session} /> : null}
</div>
)
})
)}
</li>
)
})}
</ol>
</div>
{/* ── Narrow: the same data, grouped by time ───────────────────── */}
<ol className="mt-8 space-y-8 lg:hidden">
{slots.map((slot) => (
<li key={slot.start}>
<p className="text-xs font-semibold uppercase tracking-wider tabular-nums text-muted-foreground">
{slot.start}
{slot.end ? ` – ${slot.end}` : null}
</p>
<ul className="mt-3 space-y-2">
{slot.sessions.map((session) => (
<li key={session.title}>
{session.track ? (
<p className="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">
{session.track}
</p>
) : null}
<SessionCard session={session} />
</li>
))}
</ul>
</li>
))}
</ol>
</section>
)
}
bg-card, text-muted-foreground) — it inherits your theme instead of overriding it.Drop it at components/event-agenda-grid.tsx and import it where you need the section:
import { EventAgendaGrid } from '@/components/event-agenda-grid'3 of this block’s props are simple enough to drive from here. Change them and the block below re-renders — it is the same component whose source is above, not a mock of it. Everything else it accepts is in the table underneath.
Read out of the component’s own type and signature, so this cannot drift from the source below. Every prop has a default — the component renders standalone before you pass it anything.
| Prop | Type | Default |
|---|---|---|
heading | string | 'The programme' |
dayLabelThe day this agenda is for — "Thursday 14 May". | string | 'Thursday 14 May' |
timeZoneLabelThe zone the times are quoted in, spelled out. | string | 'All times CEST (Berlin)' |
tracksColumn order. A session whose `track` is not here spans the full width. | string[] | DEFAULT_TRACKS |
slots | AgendaSlot[] | DEFAULT_SLOTS |
className | string | '' |
The same block rendered once to markup, wrapped as a file your framework compiles. Tailwind classes are framework-agnostic, so the design transfers intact — the behaviour does not.
<!--
Conference Agenda Grid — markup from the Hoverlab catalog.
This is the block rendered once to HTML and wrapped as a component
file. It is not a port of the React source: the Tailwind classes carry
the design, which is the part that took the work, and they are the same
in every framework.
This block has no interactive behaviour, so nothing is missing.
-->
<section aria-labelledby="event-agenda-heading-1108m9" class="mx-auto w-full max-w-6xl px-4 py-16 sm:px-6 lg:px-8 ">
<header class="flex flex-wrap items-end justify-between gap-x-6 gap-y-2">
<div>
<h2 id="event-agenda-heading-1108m9" class="text-3xl font-bold tracking-tight sm:text-4xl">The programme</h2>
<p class="mt-2 text-sm font-medium text-muted-foreground">Thursday 14 May</p>
</div>
<p class="inline-flex items-center gap-1.5 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clock h-3.5 w-3.5" aria-hidden="true"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></svg>All times CEST (Berlin)</p>
</header>
<div class="mt-8 hidden lg:block">
<div class="grid gap-2 pb-2" style="grid-template-columns:6rem repeat(3, minmax(0, 1fr))" aria-hidden="true">
<span></span>
<span class="min-w-0 truncate px-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground">Platform</span>
<span class="min-w-0 truncate px-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground">Interface</span>
<span class="min-w-0 truncate px-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground">Practice</span>
</div>
<ol class="space-y-2">
<li class="grid gap-2" style="grid-template-columns:6rem repeat(3, minmax(0, 1fr))">
<div class="pt-3 text-xs font-medium tabular-nums text-muted-foreground">08:30<span class="block text-muted-foreground/85">09:30</span></div>
<div style="grid-column:span 3">
<div class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-dashed border-border bg-muted/40 text-muted-foreground ">
<p class="text-sm font-semibold leading-snug text-muted-foreground">Registration and coffee</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Foyer</p>
</div>
</div>
</li>
<li class="grid gap-2" style="grid-template-columns:6rem repeat(3, minmax(0, 1fr))">
<div class="pt-3 text-xs font-medium tabular-nums text-muted-foreground">09:30<span class="block text-muted-foreground/85">10:15</span></div>
<div style="grid-column:span 3">
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-primary/40 bg-primary/5 hover:border-primary/50 hover:bg-muted/40">
<span class="text-[10px] font-semibold uppercase tracking-wider text-primary">Keynote</span>
<p class="text-sm font-semibold leading-snug mt-1 text-card-foreground">What we got wrong about build tooling, twice</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Aisling Moreau</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Great Hall</p>
</a>
</div>
</li>
<li class="grid gap-2" style="grid-template-columns:6rem repeat(3, minmax(0, 1fr))">
<div class="pt-3 text-xs font-medium tabular-nums text-muted-foreground">10:30<span class="block text-muted-foreground/85">11:10</span></div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Deleting the cache layer and getting faster</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Tobias Renner</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room A</p>
</a>
</div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Type-safe forms without a runtime</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Priya Ramanathan</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room B</p>
</a>
</div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">How we run a design review that people want to attend</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Marcus Oyelaran</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room C</p>
</a>
</div>
</li>
<li class="grid gap-2" style="grid-template-columns:6rem repeat(3, minmax(0, 1fr))">
<div class="pt-3 text-xs font-medium tabular-nums text-muted-foreground">11:20<span class="block text-muted-foreground/85">12:00</span></div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Postgres is still the answer, and here is the bill</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Hana Lindqvist</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room A</p>
</a>
</div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Animation that survives prefers-reduced-motion</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Dev Kaur</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room B</p>
</a>
</div>
<div></div>
</li>
<li class="grid gap-2" style="grid-template-columns:6rem repeat(3, minmax(0, 1fr))">
<div class="pt-3 text-xs font-medium tabular-nums text-muted-foreground">12:00<span class="block text-muted-foreground/85">13:30</span></div>
<div style="grid-column:span 3">
<div class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-dashed border-border bg-muted/40 text-muted-foreground ">
<p class="text-sm font-semibold leading-snug text-muted-foreground">Lunch, and the unconference board opens</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Foyer</p>
</div>
</div>
</li>
<li class="grid gap-2" style="grid-template-columns:6rem repeat(3, minmax(0, 1fr))">
<div class="pt-3 text-xs font-medium tabular-nums text-muted-foreground">13:30<span class="block text-muted-foreground/85">15:00</span></div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card ring-1 ring-inset ring-border/60 hover:border-primary/50 hover:bg-muted/40">
<span class="text-[10px] font-semibold uppercase tracking-wider text-primary">Workshop · 90 min</span>
<p class="text-sm font-semibold leading-snug mt-1 text-card-foreground">Workshop: profiling a slow page down to the frame</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Tobias Renner</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Lab 1</p>
</a>
</div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card ring-1 ring-inset ring-border/60 hover:border-primary/50 hover:bg-muted/40">
<span class="text-[10px] font-semibold uppercase tracking-wider text-primary">Workshop · 90 min</span>
<p class="text-sm font-semibold leading-snug mt-1 text-card-foreground">Workshop: accessible components from first principles</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Priya Ramanathan</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Lab 2</p>
</a>
</div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Open space: hiring without take-home tests</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room C</p>
</a>
</div>
</li>
<li class="grid gap-2" style="grid-template-columns:6rem repeat(3, minmax(0, 1fr))">
<div class="pt-3 text-xs font-medium tabular-nums text-muted-foreground">15:20<span class="block text-muted-foreground/85">16:00</span></div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Five years of one monorepo, honestly assessed</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Hana Lindqvist</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room A</p>
</a>
</div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Design tokens after the second rename</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Marcus Oyelaran</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room B</p>
</a>
</div>
<div>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">What on-call does to a team, and what to do about it</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Aisling Moreau</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room C</p>
</a>
</div>
</li>
<li class="grid gap-2" style="grid-template-columns:6rem repeat(3, minmax(0, 1fr))">
<div class="pt-3 text-xs font-medium tabular-nums text-muted-foreground">16:15<span class="block text-muted-foreground/85">17:00</span></div>
<div style="grid-column:span 3">
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-primary/40 bg-primary/5 hover:border-primary/50 hover:bg-muted/40">
<span class="text-[10px] font-semibold uppercase tracking-wider text-primary">Keynote</span>
<p class="text-sm font-semibold leading-snug mt-1 text-card-foreground">Closing panel: the parts we are all still doing by hand</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Great Hall</p>
</a>
</div>
</li>
</ol>
</div>
<ol class="mt-8 space-y-8 lg:hidden">
<li>
<p class="text-xs font-semibold uppercase tracking-wider tabular-nums text-muted-foreground">08:30 – 09:30</p>
<ul class="mt-3 space-y-2">
<li>
<div class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-dashed border-border bg-muted/40 text-muted-foreground ">
<p class="text-sm font-semibold leading-snug text-muted-foreground">Registration and coffee</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Foyer</p>
</div>
</li>
</ul>
</li>
<li>
<p class="text-xs font-semibold uppercase tracking-wider tabular-nums text-muted-foreground">09:30 – 10:15</p>
<ul class="mt-3 space-y-2">
<li>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-primary/40 bg-primary/5 hover:border-primary/50 hover:bg-muted/40">
<span class="text-[10px] font-semibold uppercase tracking-wider text-primary">Keynote</span>
<p class="text-sm font-semibold leading-snug mt-1 text-card-foreground">What we got wrong about build tooling, twice</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Aisling Moreau</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Great Hall</p>
</a>
</li>
</ul>
</li>
<li>
<p class="text-xs font-semibold uppercase tracking-wider tabular-nums text-muted-foreground">10:30 – 11:10</p>
<ul class="mt-3 space-y-2">
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Platform</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Deleting the cache layer and getting faster</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Tobias Renner</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room A</p>
</a>
</li>
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Interface</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Type-safe forms without a runtime</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Priya Ramanathan</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room B</p>
</a>
</li>
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Practice</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">How we run a design review that people want to attend</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Marcus Oyelaran</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room C</p>
</a>
</li>
</ul>
</li>
<li>
<p class="text-xs font-semibold uppercase tracking-wider tabular-nums text-muted-foreground">11:20 – 12:00</p>
<ul class="mt-3 space-y-2">
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Platform</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Postgres is still the answer, and here is the bill</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Hana Lindqvist</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room A</p>
</a>
</li>
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Interface</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Animation that survives prefers-reduced-motion</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Dev Kaur</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room B</p>
</a>
</li>
</ul>
</li>
<li>
<p class="text-xs font-semibold uppercase tracking-wider tabular-nums text-muted-foreground">12:00 – 13:30</p>
<ul class="mt-3 space-y-2">
<li>
<div class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-dashed border-border bg-muted/40 text-muted-foreground ">
<p class="text-sm font-semibold leading-snug text-muted-foreground">Lunch, and the unconference board opens</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Foyer</p>
</div>
</li>
</ul>
</li>
<li>
<p class="text-xs font-semibold uppercase tracking-wider tabular-nums text-muted-foreground">13:30 – 15:00</p>
<ul class="mt-3 space-y-2">
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Platform</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card ring-1 ring-inset ring-border/60 hover:border-primary/50 hover:bg-muted/40">
<span class="text-[10px] font-semibold uppercase tracking-wider text-primary">Workshop · 90 min</span>
<p class="text-sm font-semibold leading-snug mt-1 text-card-foreground">Workshop: profiling a slow page down to the frame</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Tobias Renner</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Lab 1</p>
</a>
</li>
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Interface</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card ring-1 ring-inset ring-border/60 hover:border-primary/50 hover:bg-muted/40">
<span class="text-[10px] font-semibold uppercase tracking-wider text-primary">Workshop · 90 min</span>
<p class="text-sm font-semibold leading-snug mt-1 text-card-foreground">Workshop: accessible components from first principles</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Priya Ramanathan</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Lab 2</p>
</a>
</li>
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Practice</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Open space: hiring without take-home tests</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room C</p>
</a>
</li>
</ul>
</li>
<li>
<p class="text-xs font-semibold uppercase tracking-wider tabular-nums text-muted-foreground">15:20 – 16:00</p>
<ul class="mt-3 space-y-2">
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Platform</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Five years of one monorepo, honestly assessed</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Hana Lindqvist</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room A</p>
</a>
</li>
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Interface</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">Design tokens after the second rename</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Marcus Oyelaran</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room B</p>
</a>
</li>
<li>
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wider text-primary/80">Practice</p>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-border bg-card hover:border-primary/50 hover:bg-muted/40">
<p class="text-sm font-semibold leading-snug text-card-foreground">What on-call does to a team, and what to do about it</p>
<p class="mt-1.5 text-xs font-medium text-muted-foreground">Aisling Moreau</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Room C</p>
</a>
</li>
</ul>
</li>
<li>
<p class="text-xs font-semibold uppercase tracking-wider tabular-nums text-muted-foreground">16:15 – 17:00</p>
<ul class="mt-3 space-y-2">
<li>
<a href="#" class="flex h-full flex-col rounded-xl border p-3 text-start transition-colors border-primary/40 bg-primary/5 hover:border-primary/50 hover:bg-muted/40">
<span class="text-[10px] font-semibold uppercase tracking-wider text-primary">Keynote</span>
<p class="text-sm font-semibold leading-snug mt-1 text-card-foreground">Closing panel: the parts we are all still doing by hand</p>
<p class="mt-auto pt-2 inline-flex items-center gap-1 text-xs text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin h-3 w-3" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>Great Hall</p>
</a>
</li>
</ul>
</li>
</ol>
</section>
What each framework gets across the whole catalog — effects convert properly; this rung is markup.
The component, its props, the design tokens it expects and the command that installs it — as one prompt. Paste it into Claude, Cursor, v0 or ChatGPT and what they build around it will match the rest of the catalog instead of inventing its own system.
Want the whole screen instead of this one section? Open a page and copy it entire.
All of them free to read, copy and install — no account, no locked tiles, no watermarked preview. The whole catalog is open, and so are the API and the CLI.
Browse Scheduling & CalendarCopying the code is free. Putting it in client work or a paid product is what Pro is for — the licence, not the access.
Day strip, slot grid and a time zone the visitor can change — because a slot list with no zone on it is how this pattern wastes an hour of everyone involved.
A month view where event chips truncate and overflow becomes "+n more", so a busy Tuesday never changes the height of the row it sits in.