import clsx from "clsx"; /* * The surface every screen is built from. * * Before this there were four panel treatments in the app: `rounded border * border-rule bg-panel p-5` with an `

`, the same thing * with `text-[0.95rem] font-medium`, a bare `
` * with no border at all, and a table wrapper that was a panel in everything but * name. They were all trying to be the same object. * * The header is title-left, meta-right. Meta is the keyed idiom — mono, small, * tracked, dimmed — because it is always a count, a scope or an identifier, * never prose. */ export function Panel({ title, meta, actions, tone, children, bodyless, className, }: { title?: string; meta?: React.ReactNode; actions?: React.ReactNode; /** Draws the panel's own border in a state colour. For a panel that IS the warning. */ tone?: "warn" | "expired"; children: React.ReactNode; /** Skip the padded body — for a panel whose content is a full-bleed table. */ bodyless?: boolean; className?: string; }) { const head = title || meta || actions; return (
{head && (
{title &&

{title}

}
{meta && {meta}} {actions}
)} {bodyless ? children :
{children}
}
); } /* * An aside that is part of the argument rather than beside it: the consequence * of the action on screen, or the constraint the reader is about to hit. The * left rule carries the tone, so the note reads as annotation and never as a * second panel competing with the one it sits in. */ export function Note({ tone = "accent", children }: { tone?: "accent" | "warn" | "expired"; children: React.ReactNode }) { return (

{children}

); } /* * An empty screen is an invitation to act. Every one of these says what the * thing is before offering to make one — "No licences match those filters" on * its own tells someone the filter worked, not what to do about it. */ export function EmptyState({ title, body, action }: { title: string; body?: React.ReactNode; action?: React.ReactNode }) { return (

{title}

{body &&

{body}

} {action &&
{action}
}
); }