spb/company-atlas
Public
Python 66.3%
TypeScript 22.7%
JavaScript 8.6%
HTML 1.4%
CSS 0.7%
1import Link from 'next/link';2import type { ReactNode } from 'react';3import { cn } from '@/lib/cn';45/** Page-width wrapper. Pages render inside <main> without padding: wrap content in Container (1280) or wide (1600). */6export function Container({ children, className, wide = false }: { children: ReactNode; className?: string; wide?: boolean }) {7 return <div className={cn('container-x mx-auto w-full', wide ? 'max-w-[1600px]' : 'max-w-[1280px]', className)}>{children}</div>;8}910/** Section with eyebrow + title + optional action link. Spatial composition with a hairline — not a card. */11export function Section({12 eyebrow,13 title,14 lede,15 action,16 children,17 className,18 id,19 hairline = true,20 aside,21}: {22 eyebrow?: string;23 title?: ReactNode;24 lede?: ReactNode;25 action?: { href: string; label: string };26 children: ReactNode;27 className?: string;28 id?: string;29 hairline?: boolean;30 aside?: ReactNode;31}) {32 return (33 <section id={id} className={cn('section-y scroll-mt-20', hairline && 'hairline', className)}>34 {(eyebrow || title) && (35 <div className="mb-4 flex items-end justify-between gap-4 md:mb-5">36 <div className="min-w-0">37 {eyebrow && <p className="eyebrow">{eyebrow}</p>}38 {title && <h2 className="mt-1 text-lg font-semibold tracking-tight md:text-xl">{title}</h2>}39 {lede && <p className="mt-1 max-w-2xl text-sm text-ink-2">{lede}</p>}40 </div>41 {aside}42 {action && (43 <Link href={action.href} className="link shrink-0 py-1 text-sm">44 {action.label} →45 </Link>46 )}47 </div>48 )}49 {children}50 </section>51 );52}5354export function PageHeader({ eyebrow, title, lede, children, className, aside }: { eyebrow?: ReactNode; title: ReactNode; lede?: ReactNode; children?: ReactNode; className?: string; aside?: ReactNode }) {55 return (56 <div className={cn('pb-5 pt-6 md:pb-7 md:pt-10', className)}>57 <div className="flex flex-col gap-4 md:flex-row md:items-end md:justify-between">58 <div className="min-w-0">59 {eyebrow && <div className="eyebrow flex flex-wrap items-center gap-2">{eyebrow}</div>}60 <h1 className="display mt-2 text-[26px] md:text-[38px]">{title}</h1>61 {lede && <p className="mt-3 max-w-2xl text-[15px] leading-relaxed text-ink-2 md:text-base">{lede}</p>}62 </div>63 {aside && <div className="shrink-0">{aside}</div>}64 </div>65 {children}66 </div>67 );68}6970/** Stat tile: big tabular number + label + optional delta/hint. Composes in a hairline grid, never in a card. */71export function Stat({ label, value, hint, delta, className, accent = false, href, size = 'md' }: { label: ReactNode; value: ReactNode; hint?: ReactNode; delta?: { value: string; tone?: 'positive' | 'negative' | 'neutral' }; className?: string; accent?: boolean; href?: string; size?: 'sm' | 'md' | 'lg' }) {72 const body = (73 <>74 <p className="eyebrow">{label}</p>75 <p className={cn('tnum mt-1 font-semibold leading-none tracking-tight', size === 'lg' && 'text-[30px] md:text-[38px]', size === 'md' && 'text-[24px] md:text-[30px]', size === 'sm' && 'text-[20px] md:text-[22px]', accent && 'text-accent')}>{value}</p>76 {(hint || delta) && (77 <p className="mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-ink-3">78 {delta && <span className={cn('tnum font-medium', delta.tone === 'positive' && 'text-positive', delta.tone === 'negative' && 'text-danger', (!delta.tone || delta.tone === 'neutral') && 'text-ink-2')}>{delta.value}</span>}79 {hint}80 </p>81 )}82 </>83 );84 if (href)85 return (86 <Link href={href} className={cn('block min-w-0 py-3 hover:text-accent md:py-4', className)}>87 {body}88 </Link>89 );90 return <div className={cn('min-w-0 py-3 md:py-4', className)}>{body}</div>;91}9293export function StatGrid({ children, cols = 4, className }: { children: ReactNode; cols?: 2 | 3 | 4 | 5 | 6; className?: string }) {94 const desktop = { 2: 'md:grid-cols-2', 3: 'md:grid-cols-3', 4: 'md:grid-cols-4', 5: 'md:grid-cols-5', 6: 'md:grid-cols-3 lg:grid-cols-6' }[cols];95 return <div className={cn('grid grid-cols-2 gap-x-6 border-y border-rule [&>*]:border-b [&>*]:border-rule md:[&>*]:border-b-0', desktop, className)}>{children}</div>;96}9798/** Small honest note. */99export function Note({ children, className }: { children: ReactNode; className?: string }) {100 return <p className={cn('text-xs leading-relaxed text-ink-3', className)}>{children}</p>;101}102103/** Honest empty state — spec §165 wording. */104export function Empty({ title = 'No monitored evidence available yet.', children, className, compact = false }: { title?: string; children?: ReactNode; className?: string; compact?: boolean }) {105 return (106 <div className={cn('border border-dashed border-rule-strong text-center text-ink-3', compact ? 'px-3 py-4 text-xs' : 'px-5 py-10 text-sm', className)} role="status">107 <p className="text-ink-2">{title}</p>108 {children && <div className="mt-1 text-xs">{children}</div>}109 </div>110 );111}112113export function Unavailable({ what = 'This panel', className, compact = false }: { what?: string; className?: string; compact?: boolean }) {114 return <Empty title={`${what} is temporarily unavailable.`} className={className} compact={compact}>The dataset itself is intact — the API did not answer in time.</Empty>;115}116