// Shared sketchy primitives + helpers used across all screens const { useState, useEffect, useMemo, useRef } = React; // Variation card frame function Variation({ num, title, desc, children, w }) { return (
{num} {title} — {desc}
khs-tracker.app/{title.toLowerCase().replace(/\s+/g,'-')}
{children}
); } // Sketchy nav rail (sidebar) function Sidebar({ active = 'home', compact = false }) { const items = [ ['home','Dashboard','◐'], ['part','Participants','◧'], ['auth','Authorizations','▤'], ['log','Service log','◔'], ['staff','Staff','◑'], ['rep','Reports','◬'], ['set','Settings','✦'], ]; return (
{!compact &&
menu
}
{items.map(([k,label,glyph]) => (
{glyph} {!compact && {label}}
))}
); } // Top bar inside a sketch function SketchTopBar({ title, search = true, right }) { return (
{title}
{search &&
⌕ search participants…
}
{right || <> ⎘ export + new }
); } // Status dot function StatusDot({ s='ok', label }) { return {label} ; } // Mini horizontal usage bar with auth/used/remain split function UsageBar({ used = 60, remain = 40, warn = false, height = 12 }) { const total = used + remain || 100; const pUsed = (used/total)*100; const fill = warn ? 'var(--r-orange)' : 'var(--sage)'; return (
); } // SVG sketchy gauge (semicircle) function Gauge({ pct = 70, status = 'ok', label }) { const r = 44; const cx = 60, cy = 56; const angle = Math.PI * (1 - pct/100); const x = cx + r * Math.cos(angle); const y = cy - r * Math.sin(angle); const color = `var(--r-${status})`; const dash = pct/100 * Math.PI * r; const arcLen = Math.PI * r; return ( {label || `${pct}%`} ); } // 12-month grid function MonthGrid({ data, current = 4 /* index of current month, 0-11 */ }) { const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; return (
{months.map((m,i)=>{ const v = data[i] ?? null; const past = i < current; const cur = i === current; const bg = v == null ? 'var(--paper-2)' : v > 90 ? 'var(--r-orange)' : v > 60 ? 'var(--sage)' : v > 30 ? 'var(--sage-tint)' : 'var(--paper-2)'; return (
{m}
{v ?? '—'}
); })}
); } // Vertical sparkline bars function Spark({ data, height = 32, color = 'sage' }) { const max = Math.max(...data, 1); return (
{data.map((v,i)=>( .85?'#f0c0b3':v/max>.6?'var(--sage)':'var(--sage-tint)', borderRadius:'2px 2px 0 0' }}/> ))}
); } // Circle initials avatar function Avatar({ name = 'AP', tone = 'sage' }) { const bg = tone==='sage'?'var(--sage-tint)': tone==='warn'?'#f4d8b6':'#fff'; return ( {name} ); } // Sketchy text strokes (for placeholder copy) function Strokes({ lines = 3, widths = ['100%','85%','60%'] }) { return (
{Array.from({length:lines}).map((_,i)=>(
))}
); } // Annotation note function Note({ children, style }) { return
{children}
; } // Section header inside a sketch function SkH({ children, kicker, right }) { return (
{kicker && {kicker}}

{children}

{right && {right}}
); } Object.assign(window, { Variation, Sidebar, SketchTopBar, StatusDot, UsageBar, Gauge, MonthGrid, Spark, Avatar, Strokes, Note, SkH });