{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dashboard",
  "title": "Dashboard",
  "author": "AlgorizeTech",
  "description": "Ask the user to pick one of 3 dashboard variants: https://avaraui.com/view/dashboard. This item holds 3 of them as looks of one component (classic, glass, liquid-glass). Skip the question if the user already named one. An admin dashboard home: greeting with all-time totals, a period switch, an overview chart with a tab per metric, activity feed, traffic sources, visitors by country and top authors. Three looks: classic, glass, and liquid-glass (using LiquidGlassWrapper, LiquidGlassBackground, and LiquidGlassSvgFilter).",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://avaraui.com/r/chart.json",
    "https://avaraui.com/r/popover.json",
    "https://avaraui.com/r/toggle-group.json",
    "https://avaraui.com/r/ui-liquid-glass.json",
    "utils"
  ],
  "files": [
    {
      "path": "src/components/custom/blocks/DashboardBlock.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n  ArrowDownRight,\n  ArrowUpRight,\n  ChartColumnBig,\n  CheckCheck,\n  ChartSpline,\n  CloudOff,\n  Download,\n  Globe,\n  Inbox,\n  Info,\n  Mail,\n  Minus,\n  PenLine,\n  UserPlus,\n  Wallet,\n} from \"lucide-react\";\nimport React, {\n  useEffect,\n  useId,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useState,\n  useSyncExternalStore,\n} from \"react\";\nimport { ChartBlock } from \"./ChartBlock\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./PopoverBlock\";\nimport { ToggleGroup, ToggleGroupItem } from \"./ToggleGroupBlock\";\nimport {\n  LiquidGlassWrapper,\n  LiquidGlassBackground,\n  LiquidGlassSvgFilter,\n} from \"@/components/custom/ui/liquid-glass\";\n\nexport { LiquidGlassWrapper, LiquidGlassBackground, LiquidGlassSvgFilter };\n\n// ==========================================\n// TYPES\n// ==========================================\nexport type DashboardPeriod = \"day\" | \"week\" | \"month\";\n\n/** `classic` is the light canvas with solid panels; `glass` is milky glass panes on a soft colour canvas; `liquid-glass` is fluid SVG refractive glass. */\nexport type DashboardVariant = \"classic\" | \"glass\" | \"liquid-glass\";\n\nexport interface DashboardPoint {\n  /** Axis label, e.g. \"Mon\" or \"Sep\". */\n  label: string;\n  /** Narrow-screen label, e.g. \"M\". Defaults to `label`. */\n  shortLabel?: string;\n  /** Spoken and tooltip label, e.g. \"Monday, Sep 28\". Defaults to `label`. */\n  fullLabel?: string;\n  value: number;\n}\n\nexport interface DashboardMetric {\n  id: string;\n  /** Tab label, e.g. \"New users\". */\n  label: string;\n  /** Shown beside the label in the tab from `sm` up. */\n  icon?: React.ReactNode;\n  /** Singular and plural for counts and the summary line, e.g. [\"new user\", \"new users\"]. */\n  noun?: [string, string];\n  /** Set for money: an ISO currency code such as \"USD\". */\n  currency?: string;\n  /** One series per period, oldest first; the last point is the current one. */\n  series: Record<DashboardPeriod, DashboardPoint[]>;\n  /** Percent change of the current point against the one before (12.5 = +12.5%). */\n  change?: Partial<Record<DashboardPeriod, number | null>>;\n}\n\nexport interface DashboardTotal {\n  label: string;\n  value: number;\n  icon?: React.ReactNode;\n  /** Glass: the id of a metric whose current point the card reports, e.g. \"+1,094 new users this month\". */\n  metric?: string;\n  /** Glass: gives the card a round open button. */\n  href?: string;\n}\n\nexport type DashboardActivityType = \"user\" | \"post\" | \"payment\" | \"message\";\n\nexport interface DashboardActivity {\n  id: string | number;\n  type: DashboardActivityType;\n  title: string;\n  subtitle?: string;\n  /** e.g. \"pending\" or \"failed\"; shown as a small pill. */\n  status?: string;\n  /** When it happened: a Date or an ISO string. */\n  date: Date | string;\n  href?: string;\n  /** Glass: who it was, shown as an avatar. Without it the row shows the type's icon. */\n  person?: string;\n  /** Glass: a photo URL for the avatar; initials stand in until it loads or if it fails. */\n  avatar?: string;\n}\n\nexport interface DashboardShare {\n  id: string;\n  label: string;\n  value: number;\n  /** Countries: an ISO code shown as a small badge, e.g. \"US\". */\n  code?: string;\n}\n\nexport interface DashboardAuthor {\n  name: string;\n  posts: number;\n  /** Glass: a photo URL for the author's avatar in the tray. */\n  avatar?: string;\n}\n\n/** Glass: one row of the recent payments table. */\nexport interface DashboardPayment {\n  id: string | number;\n  amount: number;\n  /** An ISO currency code such as \"USD\". */\n  currency: string;\n  /** How they paid, e.g. \"Stripe\" or \"PayPal\". */\n  method: string;\n  /** e.g. \"completed\", \"pending\" or \"failed\". Defaults to \"completed\". */\n  status?: string;\n  /** Who paid. Without it the row shows the method instead. */\n  person?: string;\n  avatar?: string;\n  date: Date | string;\n}\n\n/** Glass: a tile in the activity pane's shortcut grid. The first one takes the brand fill. */\nexport interface DashboardShortcut {\n  label: string;\n  href: string;\n  icon?: React.ReactNode;\n}\n\nexport interface DashboardLinks {\n  activity?: string;\n  traffic?: string;\n  countries?: string;\n  authors?: string;\n  /** Glass: the round open button on the payments pane. */\n  payments?: string;\n}\n\nexport interface DashboardProps\n  extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n  /** The look: `classic` (default) | `glass` | `liquid-glass`. */\n  variant?: DashboardVariant;\n  /** Optional custom background image URL for liquid-glass and glass variants. */\n  bgImage?: string;\n  /** The greeting uses the first name. */\n  userName?: string;\n  /** The line under the greeting. Defaults to a sentence built from today's numbers. */\n  summary?: React.ReactNode;\n  /** All-time counts beside the greeting; the period switch never changes them. */\n  totals?: DashboardTotal[];\n  /** The overview chart's tabs. */\n  metrics?: DashboardMetric[];\n  activity?: DashboardActivity[];\n  /** Visits by source, per period. */\n  sources?: Partial<Record<DashboardPeriod, DashboardShare[]>>;\n  /** Sign-ups per period, for the visit-to-sign-up rate under the sources. */\n  signUps?: Partial<Record<DashboardPeriod, number>>;\n  /** Visits by country, per period. */\n  countries?: Partial<Record<DashboardPeriod, DashboardShare[]>>;\n  /** Posts per author, per period. */\n  authors?: Partial<Record<DashboardPeriod, DashboardAuthor[]>>;\n  /** Glass: the recent payments table. Leave it out and the pane is not shown. */\n  payments?: DashboardPayment[];\n  /** Glass: shortcut tiles beside the activity stages. */\n  shortcuts?: DashboardShortcut[];\n  defaultPeriod?: DashboardPeriod;\n  /** Replaces the built-in CSV download of the overview series. */\n  onExport?: (period: DashboardPeriod) => void;\n  /** Where each panel's round \"open\" button goes. Panels without a link show none. */\n  links?: DashboardLinks;\n  /** Renders links, e.g. `next/link`. Defaults to a plain `<a>`. */\n  linkComponent?: React.ElementType;\n  /** \"Now\" for the greeting and the activity times. Defaults to the visitor's clock. */\n  now?: Date;\n}\n\n// ==========================================\n// COPY & FORMATTING\n// ==========================================\nconst PERIODS: { value: DashboardPeriod; label: string }[] = [\n  { value: \"day\", label: \"Daily\" },\n  { value: \"week\", label: \"Weekly\" },\n  { value: \"month\", label: \"Monthly\" },\n];\n\nconst PERIOD_COPY: Record<\n  DashboardPeriod,\n  {\n    current: string;\n    previous: string;\n    window: string;\n    average: string;\n    traffic: string;\n    trafficPhrase: string;\n    authors: string;\n    authorsPhrase: string;\n  }\n> = {\n  day: {\n    current: \"today\",\n    previous: \"yesterday\",\n    window: \"Last 7 days\",\n    average: \"a day\",\n    traffic: \"Today\",\n    trafficPhrase: \"today\",\n    authors: \"Today\",\n    authorsPhrase: \"today\",\n  },\n  week: {\n    current: \"this week\",\n    previous: \"last week\",\n    window: \"Weeks of this month\",\n    average: \"a week\",\n    traffic: \"This week\",\n    trafficPhrase: \"this week\",\n    authors: \"Last 7 days\",\n    authorsPhrase: \"in the last 7 days\",\n  },\n  month: {\n    current: \"this month\",\n    previous: \"last month\",\n    window: \"This year by month\",\n    average: \"a month\",\n    traffic: \"This month\",\n    trafficPhrase: \"this month\",\n    authors: \"Last 30 days\",\n    authorsPhrase: \"in the last 30 days\",\n  },\n};\n\n// A fixed locale, so the server and the browser print the same digits.\nconst integerFormat = new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 0 });\nconst compactFormat = new Intl.NumberFormat(\"en-US\", {\n  notation: \"compact\",\n  maximumFractionDigits: 1,\n});\n\nconst formatNumber = (value: number) => integerFormat.format(value);\nconst formatCompact = (value: number) =>\n  Math.abs(value) < 10000 ? formatNumber(value) : compactFormat.format(value);\n\nconst formatMoney = (value: number, currency: string, compact = false) =>\n  new Intl.NumberFormat(\"en-US\", {\n    style: \"currency\",\n    currency,\n    notation: compact && Math.abs(value) >= 10000 ? \"compact\" : \"standard\",\n    maximumFractionDigits: compact || Math.abs(value) >= 1000 ? 0 : 2,\n  }).format(value);\n\nconst formatChange = (change: number) => {\n  const size = Math.abs(change);\n  const digits = size < 10 ? size.toFixed(1) : String(Math.round(size));\n  return `${change > 0 ? \"+\" : change < 0 ? \"−\" : \"\"}${digits}%`;\n};\n\nconst formatShare = (share: number) =>\n  share <= 0 ? \"0%\" : share < 0.01 ? \"<1%\" : `${Math.round(share * 100)}%`;\n\nconst plural = (count: number, [one, many]: [string, string]) =>\n  count === 1 ? one : many;\n\nconst sum = (values: number[]) => values.reduce((total, value) => total + value, 0);\n\n/** 1, 2, 2.5 or 5 times a power of ten: a ceiling the axis can halve cleanly. */\nfunction niceCeiling(value: number) {\n  if (value <= 0) return 1;\n  const power = 10 ** Math.floor(Math.log10(value));\n  const fraction = value / power;\n  const nice = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 2.5 ? 2.5 : fraction <= 5 ? 5 : 10;\n  return nice * power;\n}\n\nconst initialsOf = (name: string) =>\n  name\n    .trim()\n    .split(/\\s+/)\n    .filter(Boolean)\n    .slice(0, 2)\n    .map((part) => part.charAt(0).toUpperCase())\n    .join(\"\") || \"?\";\n\nconst toDate = (value: Date | string) => (value instanceof Date ? value : new Date(value));\n\nconst MONTHS_SHORT = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\nconst WEEKDAYS = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nconst MONTHS = [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"];\n\nconst dayKey = (date: Date) => `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;\n\nfunction greetingFor(date: Date) {\n  const hour = date.getHours();\n  if (hour >= 5 && hour < 12) return \"Good morning\";\n  if (hour >= 12 && hour < 18) return \"Good afternoon\";\n  return \"Good evening\";\n}\n\nconst longDate = (date: Date) =>\n  `${WEEKDAYS[date.getDay()]}, ${MONTHS[date.getMonth()]} ${date.getDate()}`;\n\nfunction activityTime(date: Date, now: Date) {\n  const minutes = Math.floor((now.getTime() - date.getTime()) / 60000);\n  if (minutes >= 0 && minutes < 1) return \"Just now\";\n  if (minutes >= 0 && minutes < 60) return `${minutes}m ago`;\n  if (minutes >= 0 && minutes < 360 && dayKey(date) === dayKey(now)) {\n    return `${Math.floor(minutes / 60)}h ago`;\n  }\n  const hours = date.getHours();\n  return `${hours % 12 || 12}:${String(date.getMinutes()).padStart(2, \"0\")} ${hours < 12 ? \"AM\" : \"PM\"}`;\n}\n\nfunction dayLabel(date: Date, now: Date) {\n  if (dayKey(date) === dayKey(now)) return \"Today\";\n  const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);\n  if (dayKey(date) === dayKey(yesterday)) return \"Yesterday\";\n  return `${WEEKDAYS[date.getDay()]?.slice(0, 3)}, ${MONTHS_SHORT[date.getMonth()]} ${date.getDate()}`;\n}\n\n// The visitor's clock, read once per page load. The server snapshot is\n// `null`, so hydration renders the same markup the server sent and the\n// clock-dependent text appears on the pass after it.\nlet clientNow: Date | null = null;\nconst readClientNow = () => (clientNow ??= new Date());\nconst readServerNow = () => null;\nconst subscribeToNothing = () => () => {};\n\nfunction useNow(fixed?: Date) {\n  const now = useSyncExternalStore(subscribeToNothing, readClientNow, readServerNow);\n  return fixed ?? now;\n}\n\n// ==========================================\n// TOKENS & SHARED PIECES\n// ==========================================\n// The section paints its own canvas: a quiet tint of the theme colour with\n// glows at three corners, two of them in neighbouring hues where the browser\n// can derive them.\nconst ROOT_TOKENS =\n  \"[--dash-accent:var(--primary)] [--dash-well:color-mix(in_oklab,var(--primary)_5%,color-mix(in_oklab,var(--foreground)_5%,var(--card)))] [--dash-track:color-mix(in_oklab,var(--primary)_9%,var(--card))] [--dash-mark-muted:color-mix(in_oklab,var(--primary)_26%,var(--card))] [--dash-cat-1:#6356d1] [--dash-cat-2:#e87ba4] [--dash-cat-3:#eda100] [--dash-cat-4:#1baf7a] [--dash-cat-other:#c2c7d0] [--dash-up:#137a39] [--dash-down:#b91c1c] dark:[--dash-accent:color-mix(in_oklab,var(--primary)_72%,white)] dark:[--dash-cat-1:#8f84ec] dark:[--dash-cat-2:#d55181] dark:[--dash-cat-3:#c98500] dark:[--dash-cat-4:#199e70] dark:[--dash-cat-other:#566072] dark:[--dash-up:#4ade80] dark:[--dash-down:#fa8585]\";\n\nconst CANVAS_STYLE: React.CSSProperties = {\n  backgroundColor: \"color-mix(in oklab, var(--primary) 4%, var(--background))\",\n  backgroundImage: [\n    \"radial-gradient(56rem 44rem at 0% 0%, color-mix(in oklab, var(--primary) 18%, transparent), transparent 70%)\",\n    \"radial-gradient(52rem 38rem at 100% 0%, color-mix(in oklab, var(--primary) 9%, transparent), transparent 70%)\",\n    \"radial-gradient(46rem 40rem at 0% 100%, color-mix(in oklab, var(--primary) 11%, transparent), transparent 72%)\",\n  ].join(\", \"),\n};\n\nconst PANEL =\n  \"rounded-[24px] bg-card text-card-foreground ring-1 ring-foreground/[0.08] shadow-[0_1px_2px_rgb(16_24_40/0.04),0_16px_32px_-20px_rgb(16_24_40/0.14)] dark:shadow-none\";\nconst FOCUS =\n  \"outline-none focus-visible:ring-2 focus-visible:ring-[color-mix(in_oklab,var(--primary)_60%,transparent)]\";\nconst SLICE_COLORS = [\"var(--dash-cat-1)\", \"var(--dash-cat-2)\", \"var(--dash-cat-3)\"];\nconst OTHER_COLOR = \"var(--dash-cat-other)\";\n\nfunction PanelHeader({\n  id,\n  title,\n  caption,\n  href,\n  linkLabel,\n  linkComponent: Link,\n}: {\n  id: string;\n  title: string;\n  caption?: string;\n  href?: string;\n  linkLabel: string;\n  linkComponent: React.ElementType;\n}) {\n  return (\n    <div className=\"flex items-start justify-between gap-4\">\n      <div className=\"min-w-0\">\n        <h2 id={id} className=\"text-base font-medium leading-6 text-foreground\">\n          {title}\n        </h2>\n        {caption && <p className=\"text-[13px] leading-5 text-muted-foreground\">{caption}</p>}\n      </div>\n      {href && (\n        <Link\n          href={href}\n          aria-label={linkLabel}\n          title={linkLabel}\n          className={cn(\n            \"grid size-9 shrink-0 place-items-center rounded-full text-muted-foreground ring-1 ring-foreground/[0.08] transition-colors hover:bg-[var(--dash-well)] hover:text-foreground\",\n            FOCUS,\n          )}\n        >\n          <ArrowUpRight className=\"size-4\" aria-hidden=\"true\" />\n        </Link>\n      )}\n    </div>\n  );\n}\n\nfunction EmptyState({\n  icon,\n  title,\n  children,\n  className,\n}: {\n  icon?: React.ReactNode;\n  title: string;\n  children?: React.ReactNode;\n  className?: string;\n}) {\n  return (\n    <div className={cn(\"flex flex-col items-center justify-center gap-2 px-6 text-center\", className)}>\n      {icon && (\n        <span className=\"mb-1 grid size-10 place-items-center rounded-full bg-[var(--dash-well)] text-muted-foreground [&_svg]:size-5\">\n          {icon}\n        </span>\n      )}\n      <p className=\"text-sm font-medium text-foreground\">{title}</p>\n      {children && <p className=\"max-w-[36ch] text-[13px] leading-5 text-muted-foreground\">{children}</p>}\n    </div>\n  );\n}\n\n/**\n * Arrow and percent change, coloured by direction (more is better for every\n * metric here). `onFill` is for brand-filled tiles, where the green and red\n * tints would be unreadable: it goes to the fill's text colour on a light chip.\n */\nfunction DeltaBadge({\n  change,\n  onFill = false,\n  className,\n}: {\n  change: number;\n  onFill?: boolean;\n  className?: string;\n}) {\n  const rounded = Math.abs(change) < 0.05 ? 0 : change;\n  const direction = rounded > 0 ? \"up\" : rounded < 0 ? \"down\" : \"flat\";\n  const Icon = direction === \"up\" ? ArrowUpRight : direction === \"down\" ? ArrowDownRight : Minus;\n  return (\n    <span\n      className={cn(\n        \"inline-flex items-center gap-0.5 rounded-full py-0.5 pl-1 pr-2 text-xs font-medium tabular-nums\",\n        onFill\n          ? \"bg-[color-mix(in_oklab,var(--primary-foreground)_18%,transparent)] text-[var(--primary-foreground)]\"\n          : direction === \"up\"\n            ? \"bg-[color-mix(in_oklab,var(--dash-up)_10%,transparent)] text-[var(--dash-up)]\"\n            : direction === \"down\"\n              ? \"bg-[color-mix(in_oklab,var(--dash-down)_10%,transparent)] text-[var(--dash-down)]\"\n              : \"bg-[var(--dash-well)] text-muted-foreground\",\n        className,\n      )}\n    >\n      <Icon className=\"size-3.5\" aria-hidden=\"true\" />\n      <span className=\"sr-only\">\n        {direction === \"up\" ? \"Up \" : direction === \"down\" ? \"Down \" : \"No change, \"}\n      </span>\n      {formatChange(rounded)}\n    </span>\n  );\n}\n\n// ==========================================\n// BREAKDOWNS (gauge, barcode, legend)\n// ==========================================\ninterface Slice {\n  key: string;\n  name: string;\n  value: number;\n  share: number;\n  color: string;\n  code?: string;\n}\n\n/** The top three shares in their own colours, the rest folded into \"Other\". */\nfunction compose(shares: DashboardShare[]): { slices: Slice[]; total: number } {\n  const rows = [...shares].filter((share) => share.value > 0).sort((a, b) => b.value - a.value);\n  const total = sum(rows.map((row) => row.value));\n  if (!total) return { slices: [], total: 0 };\n  const top = rows.slice(0, rows.length > 4 ? 3 : 4);\n  const rest = rows.slice(top.length);\n  const slices: Slice[] = top.map((row, index) => ({\n    key: row.id,\n    name: row.label,\n    value: row.value,\n    share: row.value / total,\n    color: SLICE_COLORS[index] ?? OTHER_COLOR,\n    code: row.code,\n  }));\n  if (rest.length) {\n    const value = sum(rest.map((row) => row.value));\n    slices.push({\n      key: \"__other\",\n      name: `Other (${rest.length})`,\n      value,\n      share: value / total,\n      color: OTHER_COLOR,\n    });\n  }\n  return { slices, total };\n}\n\n/** Hands `units` marks to slices in proportion to their share (largest remainder). */\nfunction allocateUnits(slices: Slice[], units: number): (Slice | null)[] {\n  const total = sum(slices.map((slice) => slice.value));\n  if (!total) return Array.from({ length: units }, () => null);\n  const exact = slices.map((slice) => (slice.value / total) * units);\n  const counts = exact.map(Math.floor);\n  let left = units - sum(counts);\n  exact\n    .map((value, index) => ({ index, rest: value - Math.floor(value) }))\n    .sort((a, b) => b.rest - a.rest)\n    .forEach(({ index }) => {\n      if (left > 0) {\n        counts[index] = (counts[index] ?? 0) + 1;\n        left -= 1;\n      }\n    });\n  return slices.flatMap((slice, index) => Array.from({ length: counts[index] ?? 0 }, () => slice));\n}\n\nconst GAUGE_TICKS = 44;\n\n/** A semicircle of ticks, handed to slices by share and read left to right. */\nfunction TickGauge({\n  slices,\n  focus,\n  onFocus,\n  label,\n  children,\n}: {\n  slices: Slice[];\n  focus: string | null;\n  onFocus: (key: string | null) => void;\n  label: string;\n  children: React.ReactNode;\n}) {\n  const units = allocateUnits(slices, GAUGE_TICKS);\n  return (\n    <div className=\"relative mx-auto w-full max-w-[20rem]\">\n      <svg viewBox=\"0 0 300 156\" className=\"block w-full\" role=\"img\" aria-label={label}>\n        {units.map((unit, index) => {\n          const angle = Math.PI - (index / (GAUGE_TICKS - 1)) * Math.PI;\n          const cos = Math.cos(angle);\n          const sin = Math.sin(angle);\n          return (\n            <line\n              key={index}\n              x1={150 + 104 * cos}\n              y1={150 - 104 * sin}\n              x2={150 + 136 * cos}\n              y2={150 - 136 * sin}\n              stroke={unit ? unit.color : \"var(--dash-track)\"}\n              strokeWidth={5}\n              strokeLinecap=\"round\"\n              opacity={focus && unit && unit.key !== focus ? 0.22 : 1}\n              style={{ transition: \"opacity 150ms ease-out\" }}\n              onPointerEnter={unit ? () => onFocus(unit.key) : undefined}\n              onPointerLeave={unit ? () => onFocus(null) : undefined}\n            />\n          );\n        })}\n      </svg>\n      <div className=\"pointer-events-none absolute inset-x-0 bottom-0 flex flex-col items-center\">\n        {children}\n      </div>\n    </div>\n  );\n}\n\n/** A row of thin bars split by share, with a wider gap between slices. */\nfunction Barcode({\n  slices,\n  focus,\n  onFocus,\n}: {\n  slices: Slice[];\n  focus: string | null;\n  onFocus: (key: string | null) => void;\n}) {\n  const units = allocateUnits(slices, 48);\n  return (\n    <div\n      aria-hidden=\"true\"\n      className=\"mt-5 flex h-12 items-stretch gap-[3px]\"\n      onPointerLeave={() => onFocus(null)}\n    >\n      {units.map((unit, index) => (\n        <span\n          key={index}\n          className={cn(\n            \"min-w-0 flex-1 rounded-full transition-opacity duration-150\",\n            index > 0 && units[index - 1]?.key !== unit?.key && \"ml-1.5\",\n            focus && focus !== unit?.key && \"opacity-25\",\n          )}\n          style={{ backgroundColor: unit?.color ?? \"var(--dash-track)\" }}\n          onPointerEnter={() => onFocus(unit?.key ?? null)}\n        />\n      ))}\n    </div>\n  );\n}\n\n/** Legend rows that carry every value as text, so nothing hides behind a hover. */\nfunction BreakdownList({\n  slices,\n  focus,\n  onFocus,\n  showCodes = false,\n  className,\n}: {\n  slices: Slice[];\n  focus: string | null;\n  onFocus: (key: string | null) => void;\n  showCodes?: boolean;\n  className?: string;\n}) {\n  return (\n    <ul className={cn(\"space-y-0.5\", className)} onPointerLeave={() => onFocus(null)}>\n      {slices.map((slice) => (\n        <li\n          key={slice.key}\n          onPointerEnter={() => onFocus(slice.key)}\n          className={cn(\n            \"flex items-center gap-3 rounded-xl px-2 py-2 transition-colors duration-150\",\n            focus === slice.key && \"bg-[var(--dash-well)]\",\n          )}\n        >\n          <span aria-hidden=\"true\" className=\"h-4 w-1.5 shrink-0 rounded-full\" style={{ backgroundColor: slice.color }} />\n          {showCodes && (\n            <span\n              aria-hidden=\"true\"\n              className=\"grid h-4 w-6 shrink-0 place-items-center rounded-[4px] bg-[var(--dash-well)] text-[9px] font-semibold tracking-wide text-muted-foreground\"\n            >\n              {slice.code ?? \"··\"}\n            </span>\n          )}\n          <span className=\"min-w-0 flex-1 truncate text-sm text-foreground\">{slice.name}</span>\n          <span className=\"text-sm tabular-nums text-foreground\">{formatNumber(slice.value)}</span>\n          <span className=\"w-14 shrink-0 rounded-full bg-[var(--dash-well)] py-0.5 text-center text-xs tabular-nums text-muted-foreground\">\n            {formatShare(slice.share)}\n          </span>\n        </li>\n      ))}\n    </ul>\n  );\n}\n\n// ==========================================\n// HEADER\n// ==========================================\nfunction DashboardHeader({\n  userName,\n  summary,\n  totals,\n  now,\n}: {\n  userName?: string;\n  summary: React.ReactNode;\n  totals: DashboardTotal[];\n  now: Date | null;\n}) {\n  const firstName = userName?.trim().split(/\\s+/)[0];\n  const greeting = now ? greetingFor(now) : \"Welcome back\";\n\n  return (\n    <header className=\"relative isolate\">\n      <div\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute -left-24 -top-32 -z-10 h-80 w-[min(40rem,calc(100%+6rem))]\"\n        style={{\n          background:\n            \"radial-gradient(closest-side, color-mix(in oklab, var(--primary) 16%, transparent), transparent)\",\n        }}\n      />\n      <div className=\"flex flex-col gap-6 xl:flex-row xl:items-end xl:justify-between\">\n        <div className=\"min-w-0\">\n          {/* The date only exists once the visitor's clock is read. */}\n          <p className=\"min-h-5 text-sm text-muted-foreground\">{now ? longDate(now) : null}</p>\n          <h1 className=\"mt-2 text-2xl font-light leading-[1.05] tracking-[-0.03em] text-foreground sm:text-[2.25rem]\">\n            {firstName ? `${greeting}, ${firstName}` : greeting}\n          </h1>\n          {summary && (\n            <p className=\"mt-3 max-w-[56ch] text-pretty text-[15px] leading-6 text-muted-foreground\">\n              {summary}\n            </p>\n          )}\n        </div>\n        {totals.length > 0 && (\n          <dl className=\"grid shrink-0 grid-cols-2 gap-x-4 gap-y-6 sm:grid-cols-4 2xl:gap-x-10\">\n            {totals.map((total) => (\n              <div key={total.label} className=\"min-w-0\">\n                <dt className=\"flex items-center gap-1.5 text-[13px] text-muted-foreground [&_svg]:size-3.5 [&_svg]:shrink-0\">\n                  {total.icon}\n                  <span className=\"truncate\" title={total.label}>\n                    {total.label}\n                  </span>\n                </dt>\n                <dd className=\"mt-2 text-[2rem] font-light leading-none tracking-[-0.03em] text-foreground xl:text-4xl\">\n                  {formatCompact(total.value)}\n                </dd>\n              </div>\n            ))}\n          </dl>\n        )}\n      </div>\n    </header>\n  );\n}\n\n// ==========================================\n// OVERVIEW (metric tabs + chart)\n// ==========================================\ntype ChartForm = \"columns\" | \"line\";\n\nconst CHART_HEIGHT = 304;\n\nfunction axisTicks(ceiling: number, money: boolean, empty: boolean) {\n  if (empty) return [0];\n  const half = ceiling / 2;\n  return !money && !Number.isInteger(half) ? [0, ceiling] : [0, half, ceiling];\n}\n\ninterface ResolvedMetric {\n  metric: DashboardMetric;\n  points: DashboardPoint[];\n  current: number;\n  change: number | null;\n  total: number;\n  average: number;\n  format: (value: number) => string;\n  formatShort: (value: number) => string;\n}\n\n/** Each metric's series for the period, with its summary numbers and formatters. */\nfunction resolveMetrics(metrics: DashboardMetric[], period: DashboardPeriod): ResolvedMetric[] {\n  return metrics.map((metric) => {\n    const points = metric.series[period] ?? [];\n    const total = sum(points.map((point) => point.value));\n    const currency = metric.currency;\n    return {\n      metric,\n      points,\n      current: points[points.length - 1]?.value ?? 0,\n      change: metric.change?.[period] ?? null,\n      total,\n      average: points.length ? total / points.length : 0,\n      format: currency ? (value: number) => formatMoney(value, currency) : formatNumber,\n      formatShort: currency ? (value: number) => formatMoney(value, currency, true) : formatNumber,\n    };\n  });\n}\n\n/** The chart's numbers as a table for screen readers. */\nfunction SeriesTable({ entry, caption }: { entry: ResolvedMetric; caption: string }) {\n  return (\n    <table className=\"sr-only\">\n      <caption>\n        {entry.metric.label}, {caption}\n      </caption>\n      <thead>\n        <tr>\n          <th scope=\"col\">Period</th>\n          <th scope=\"col\">{entry.metric.label}</th>\n        </tr>\n      </thead>\n      <tbody>\n        {entry.points.map((point, index) => (\n          <tr key={`${point.label}-${index}`}>\n            <th scope=\"row\">{point.fullLabel ?? point.label}</th>\n            <td>{entry.format(point.value)}</td>\n          </tr>\n        ))}\n      </tbody>\n    </table>\n  );\n}\n\nfunction OverviewPanel({\n  metrics,\n  period,\n  className,\n}: {\n  metrics: DashboardMetric[];\n  period: DashboardPeriod;\n  className?: string;\n}) {\n  const baseId = useId();\n  const [activeId, setActiveId] = useState(metrics[0]?.id ?? \"\");\n  const [form, setForm] = useState<ChartForm>(\"columns\");\n  const copy = PERIOD_COPY[period];\n\n  const resolved = useMemo(() => resolveMetrics(metrics, period), [metrics, period]);\n\n  const active = resolved.find((entry) => entry.metric.id === activeId) ?? resolved[0];\n\n  const onTabKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n    const index = resolved.findIndex((entry) => entry.metric.id === active?.metric.id);\n    const last = resolved.length - 1;\n    const next =\n      event.key === \"ArrowRight\"\n        ? index >= last ? 0 : index + 1\n        : event.key === \"ArrowLeft\"\n          ? index <= 0 ? last : index - 1\n          : event.key === \"Home\"\n            ? 0\n            : event.key === \"End\"\n              ? last\n              : null;\n    if (next === null) return;\n    event.preventDefault();\n    const target = resolved[next];\n    if (!target) return;\n    setActiveId(target.metric.id);\n    document.getElementById(`${baseId}-tab-${target.metric.id}`)?.focus();\n  };\n\n  return (\n    <section aria-labelledby={`${baseId}-title`} className={cn(PANEL, \"overflow-hidden\", className)}>\n      <h2 id={`${baseId}-title`} className=\"sr-only\">\n        Overview\n      </h2>\n      <div\n        role=\"tablist\"\n        aria-label=\"Metrics\"\n        onKeyDown={onTabKeyDown}\n        className=\"flex gap-px border-b border-foreground/[0.08] bg-foreground/[0.08]\"\n      >\n        {resolved.map((entry) => {\n          const selected = entry.metric.id === active?.metric.id;\n          return (\n            <button\n              key={entry.metric.id}\n              id={`${baseId}-tab-${entry.metric.id}`}\n              type=\"button\"\n              role=\"tab\"\n              aria-selected={selected}\n              aria-controls={`${baseId}-panel`}\n              tabIndex={selected ? 0 : -1}\n              onClick={() => setActiveId(entry.metric.id)}\n              className={cn(\n                \"group relative flex min-w-0 flex-1 basis-0 flex-col items-start gap-1 bg-card px-2.5 py-3 text-left transition-colors hover:bg-[color-mix(in_oklab,var(--dash-accent)_4%,var(--card))] focus-visible:z-10 sm:flex-auto sm:gap-1.5 sm:px-4 sm:py-3.5\",\n                \"outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[color-mix(in_oklab,var(--primary)_60%,transparent)]\",\n              )}\n            >\n              <span\n                aria-hidden=\"true\"\n                className={cn(\n                  \"absolute inset-x-0 bottom-0 h-[3px] origin-center bg-[var(--dash-accent)] transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none\",\n                  selected ? \"scale-x-100\" : \"scale-x-0\",\n                )}\n              />\n              <span\n                className={cn(\n                  \"flex max-w-full items-center gap-2 text-xs leading-4 transition-colors sm:text-[13px] sm:leading-5\",\n                  selected ? \"text-foreground\" : \"text-muted-foreground\",\n                )}\n              >\n                {entry.metric.icon && (\n                  <span aria-hidden=\"true\" className=\"hidden shrink-0 sm:block [&_svg]:size-4\">\n                    {entry.metric.icon}\n                  </span>\n                )}\n                <span className=\"truncate\">{entry.metric.label}</span>\n              </span>\n              <span className=\"flex min-h-[1.75rem] max-w-full flex-col items-start gap-1 sm:flex-row sm:flex-wrap sm:items-center sm:gap-x-1.5\">\n                <span className=\"text-[1.0625rem] font-normal leading-6 tracking-[-0.02em] tabular-nums text-foreground sm:text-[1.3125rem] sm:leading-7\">\n                  {entry.formatShort(entry.current)}\n                </span>\n                {entry.change !== null && (\n                  <DeltaBadge change={entry.change} className=\"text-[11px] sm:text-xs\" />\n                )}\n              </span>\n            </button>\n          );\n        })}\n      </div>\n\n      {active && (\n        <div\n          id={`${baseId}-panel`}\n          role=\"tabpanel\"\n          aria-labelledby={`${baseId}-tab-${active.metric.id}`}\n          className=\"px-5 pb-5 pt-4 sm:px-6 sm:pb-6\"\n        >\n          <figure className=\"m-0\">\n            <div className=\"flex flex-wrap items-center justify-between gap-x-4 gap-y-3\">\n              <figcaption className=\"flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 text-[13px] leading-5\">\n                <span className=\"sr-only\">{active.metric.label}, </span>\n                <span className=\"font-medium text-foreground\">{copy.window}</span>\n                {active.change !== null && (\n                  <>\n                    <span aria-hidden=\"true\" className=\"h-3.5 w-px bg-muted-foreground/30\" />\n                    <span className=\"text-muted-foreground\">vs {copy.previous}</span>\n                  </>\n                )}\n              </figcaption>\n              <div className=\"flex flex-wrap items-center gap-x-4 gap-y-2\">\n                {active.points.length > 0 && (\n                  <dl className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-[13px] leading-5\">\n                    <div className=\"flex items-baseline gap-1.5\">\n                      <dt className=\"text-muted-foreground\">Total</dt>\n                      <dd className=\"font-medium tabular-nums text-foreground\">{active.formatShort(active.total)}</dd>\n                    </div>\n                    <div className=\"flex items-baseline gap-1.5\">\n                      <dt className=\"flex items-center gap-1.5 self-center text-muted-foreground\">\n                        <span aria-hidden=\"true\" className=\"w-3 border-t border-dashed border-current\" />\n                        <span aria-hidden=\"true\">Avg</span>\n                        <span className=\"sr-only\">Average</span>\n                      </dt>\n                      <dd className=\"font-medium tabular-nums text-foreground\">\n                        {active.formatShort(active.average)}{\" \"}\n                        <span className=\"font-normal text-muted-foreground\">{copy.average}</span>\n                      </dd>\n                    </div>\n                  </dl>\n                )}\n                <ToggleGroup\n                  type=\"single\"\n                  variant=\"segmented\"\n                  size=\"sm\"\n                  value={form}\n                  onValueChange={(next) => {\n                    if (next === \"columns\" || next === \"line\") setForm(next);\n                  }}\n                  aria-label=\"Chart style\"\n                >\n                  <ToggleGroupItem value=\"columns\" aria-label=\"Columns\" title=\"Columns\" className=\"w-8 px-0\">\n                    <ChartColumnBig className=\"size-4\" aria-hidden=\"true\" />\n                  </ToggleGroupItem>\n                  <ToggleGroupItem value=\"line\" aria-label=\"Trend line\" title=\"Trend line\" className=\"w-8 px-0\">\n                    <ChartSpline className=\"size-4\" aria-hidden=\"true\" />\n                  </ToggleGroupItem>\n                </ToggleGroup>\n              </div>\n            </div>\n            <div className=\"mt-2 min-h-[304px]\">\n              {active.points.length === 0 ? (\n                <EmptyState title=\"No data for this period yet\" className=\"h-[304px]\" />\n              ) : form === \"line\" ? (\n                <ChartBlock\n                  type=\"line\"\n                  data={active.points.map((point) => ({ label: point.label, value: point.value }))}\n                  color=\"var(--primary)\"\n                  height={CHART_HEIGHT}\n                  valuePrefix={active.metric.currency ? currencySymbol(active.metric.currency) : \"\"}\n                />\n              ) : (\n                <ColumnsChart entry={active} />\n              )}\n            </div>\n            <SeriesTable entry={active} caption={copy.window} />\n          </figure>\n        </div>\n      )}\n    </section>\n  );\n}\n\nconst currencySymbol = (currency: string) =>\n  new Intl.NumberFormat(\"en-US\", { style: \"currency\", currency })\n    .formatToParts(0)\n    .find((part) => part.type === \"currency\")?.value ?? \"\";\n\n/**\n * Pill columns on full-height tracks: the current point in the accent with its\n * value on the cap, earlier points muted. Hover, tap or the arrow keys read any\n * column; the dashed rule is the average.\n */\nfunction ColumnsChart({ entry }: { entry: ResolvedMetric }) {\n  const { points } = entry;\n  const [inspected, setInspected] = useState<number | null>(null);\n  const lastIndex = points.length - 1;\n  const ceiling = niceCeiling(Math.max(0, ...points.map((point) => point.value)));\n  const empty = entry.total === 0;\n  const ticks = axisTicks(ceiling, Boolean(entry.metric.currency), empty);\n  const shown = empty ? inspected : (inspected ?? lastIndex);\n  const inspectedPoint = inspected === null ? null : points[inspected];\n\n  useEffect(() => {\n    setInspected(null);\n  }, [entry.points]);\n\n  const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n    const from = inspected ?? lastIndex;\n    const moves: Record<string, number> = {\n      ArrowLeft: Math.max(0, from - 1),\n      ArrowRight: Math.min(lastIndex, from + 1),\n      Home: 0,\n      End: lastIndex,\n    };\n    const move = moves[event.key];\n    if (move !== undefined) {\n      event.preventDefault();\n      setInspected(move);\n    } else if (event.key === \"Escape\") {\n      setInspected(null);\n    }\n  };\n\n  return (\n    <div>\n      <div\n        role=\"group\"\n        aria-roledescription=\"chart\"\n        aria-label={`${entry.metric.label}. Use the arrow keys to read each value.`}\n        tabIndex={0}\n        onKeyDown={onKeyDown}\n        onBlur={() => setInspected(null)}\n        onPointerLeave={(event) => {\n          if (event.pointerType !== \"touch\") setInspected(null);\n        }}\n        className={cn(\"relative rounded-2xl\", FOCUS)}\n      >\n        <div className=\"h-[16.5rem] pt-10\">\n          <div className=\"relative h-full\">\n            <div aria-hidden=\"true\" className=\"absolute inset-y-0 left-0 w-9\">\n              {ticks.map((tick) => (\n                <span\n                  key={tick}\n                  className=\"absolute right-0 translate-y-1/2 text-[11px] leading-none tabular-nums text-muted-foreground\"\n                  style={{ bottom: `${(tick / ceiling) * 100}%` }}\n                >\n                  {entry.formatShort(tick)}\n                </span>\n              ))}\n            </div>\n            <div className=\"absolute inset-y-0 left-12 right-0\">\n              {entry.average > 0 && !empty && (\n                <div\n                  aria-hidden=\"true\"\n                  className=\"absolute inset-x-0 border-t border-dashed border-muted-foreground/40\"\n                  style={{ bottom: `${(entry.average / ceiling) * 100}%` }}\n                />\n              )}\n              <div className=\"absolute inset-0 flex items-end gap-1 sm:gap-2 md:gap-3\">\n                {points.map((point, index) => {\n                  const percent = (point.value / ceiling) * 100;\n                  const isCurrent = index === lastIndex;\n                  const isInspected = index === inspected;\n                  return (\n                    <div\n                      key={`${point.label}-${index}`}\n                      className=\"relative flex h-full min-w-0 flex-1 justify-center\"\n                      onPointerEnter={() => setInspected(index)}\n                      onPointerDown={() => setInspected(index)}\n                    >\n                      <div className=\"relative h-full w-full max-w-7 rounded-full bg-[var(--dash-track)]\">\n                        {point.value > 0 && (\n                          <div\n                            className={cn(\n                              \"absolute inset-x-0 bottom-0 rounded-full transition-all duration-300 ease-out\",\n                              isCurrent\n                                ? \"bg-[var(--dash-accent)]\"\n                                : isInspected\n                                  ? \"bg-[color-mix(in_oklab,var(--dash-accent)_60%,var(--card))]\"\n                                  : \"bg-[var(--dash-mark-muted)]\",\n                            )}\n                            style={{ height: `max(${percent}%, 0.625rem)` }}\n                          />\n                        )}\n                        {index === shown && (\n                          <div\n                            className={cn(\n                              \"pointer-events-none absolute z-20 whitespace-nowrap\",\n                              index === 0 ? \"left-0\" : index === lastIndex ? \"right-0\" : \"left-1/2 -translate-x-1/2\",\n                            )}\n                            style={{ bottom: `calc(max(${percent}%, 0.625rem) + 0.5rem)` }}\n                          >\n                            {isInspected ? (\n                              <div className=\"rounded-xl bg-card px-3 py-1.5 shadow-lg ring-1 ring-foreground/[0.08]\">\n                                <p className=\"text-sm font-semibold leading-5 text-foreground\">{entry.format(point.value)}</p>\n                                <p className=\"text-[11px] leading-4 text-muted-foreground\">{point.fullLabel ?? point.label}</p>\n                              </div>\n                            ) : (\n                              <span className=\"block rounded-full bg-[var(--dash-accent)] px-2.5 py-1 text-xs font-semibold leading-none text-[var(--card)]\">\n                                {entry.formatShort(point.value)}\n                              </span>\n                            )}\n                          </div>\n                        )}\n                      </div>\n                    </div>\n                  );\n                })}\n              </div>\n            </div>\n          </div>\n        </div>\n        <div aria-hidden=\"true\" className=\"ml-12 mt-3 flex h-4 items-center gap-1 sm:gap-2 md:gap-3\">\n          {points.map((point, index) => (\n            <span\n              key={`${point.label}-${index}`}\n              className={cn(\n                \"min-w-0 flex-1 truncate text-center text-xs\",\n                index === lastIndex ? \"font-medium text-foreground\" : \"text-muted-foreground\",\n              )}\n            >\n              <span className=\"sm:hidden\">{point.shortLabel ?? point.label}</span>\n              <span className=\"hidden sm:inline\">{point.label}</span>\n            </span>\n          ))}\n        </div>\n        {empty && (\n          <div className=\"pointer-events-none absolute inset-0 grid place-items-center\">\n            <p className=\"rounded-full bg-card px-4 py-2 text-[13px] text-muted-foreground shadow-sm ring-1 ring-foreground/[0.08]\">\n              Nothing recorded in this window yet.\n            </p>\n          </div>\n        )}\n      </div>\n      <p className=\"sr-only\" aria-live=\"polite\">\n        {inspectedPoint ? `${inspectedPoint.fullLabel ?? inspectedPoint.label}: ${entry.format(inspectedPoint.value)}` : \"\"}\n      </p>\n    </div>\n  );\n}\n\n// ==========================================\n// ACTIVITY\n// ==========================================\nconst ACTIVITY_TABS: { value: \"all\" | DashboardActivityType; label: string; empty: string }[] = [\n  { value: \"all\", label: \"All\", empty: \"No activity yet\" },\n  { value: \"user\", label: \"Users\", empty: \"No sign-ups yet\" },\n  { value: \"post\", label: \"Posts\", empty: \"No published posts yet\" },\n  { value: \"payment\", label: \"Payments\", empty: \"No payments yet\" },\n  { value: \"message\", label: \"Messages\", empty: \"No messages yet\" },\n];\n\nconst ACTIVITY_TYPES: Record<\n  DashboardActivityType,\n  { label: string; icon: React.ComponentType<{ className?: string }>; color: string }\n> = {\n  user: { label: \"New user\", icon: UserPlus, color: \"var(--dash-cat-1)\" },\n  post: { label: \"Post\", icon: PenLine, color: \"var(--dash-cat-2)\" },\n  payment: { label: \"Payment\", icon: Wallet, color: \"var(--dash-cat-3)\" },\n  message: { label: \"Message\", icon: Mail, color: \"var(--dash-cat-4)\" },\n};\n\nconst STATUS_STYLES: Record<string, string> = {\n  pending: \"bg-amber-500/15 text-amber-700 dark:text-amber-300\",\n  failed: \"bg-[color-mix(in_oklab,var(--dash-down)_10%,transparent)] text-[var(--dash-down)]\",\n};\n\nconst FEED_LENGTH = 6;\n\nfunction ActivityPanel({\n  activity,\n  now,\n  href,\n  linkComponent: Link,\n  className,\n}: {\n  activity: DashboardActivity[];\n  now: Date | null;\n  href?: string;\n  linkComponent: React.ElementType;\n  className?: string;\n}) {\n  const id = useId();\n  const [tab, setTab] = useState<string>(\"all\");\n  const tabPaneRef = useRef<HTMLDivElement>(null);\n\n  // Each tab's list rises in as it replaces the last one.\n  useEffect(() => {\n    if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n    tabPaneRef.current?.animate(\n      [\n        { opacity: 0, transform: \"translateY(3px)\" },\n        { opacity: 1, transform: \"none\" },\n      ],\n      { duration: 220, easing: \"cubic-bezier(0.16, 1, 0.3, 1)\" },\n    );\n  }, [tab]);\n  const current = ACTIVITY_TABS.find((option) => option.value === tab) ?? ACTIVITY_TABS[0];\n  const feed = useMemo(\n    () =>\n      [...activity]\n        .map((item) => ({ ...item, at: toDate(item.date) }))\n        .sort((a, b) => b.at.getTime() - a.at.getTime()),\n    [activity],\n  );\n  const items = (tab === \"all\" ? feed : feed.filter((item) => item.type === tab)).slice(0, FEED_LENGTH);\n\n  // Grouped by day once the clock is known; before that, one plain list.\n  const groups = now\n    ? items.reduce<{ key: string; label: string; items: typeof items }[]>((list, item) => {\n        const key = dayKey(item.at);\n        const last = list[list.length - 1];\n        if (last && last.key === key) last.items.push(item);\n        else list.push({ key, label: dayLabel(item.at, now), items: [item] });\n        return list;\n      }, [])\n    : [{ key: \"all\", label: \"\", items }];\n\n  return (\n    <section aria-labelledby={`${id}-title`} className={cn(PANEL, \"flex flex-col p-5 sm:p-6 h-[580px] min-h-[580px]\", className)}>\n      <PanelHeader\n        id={`${id}-title`}\n        title=\"Recent activity\"\n        caption=\"The latest across the platform\"\n        href={href}\n        linkLabel=\"Open all activity\"\n        linkComponent={Link}\n      />\n      <div className=\"shrink-0 w-full mt-4 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden touch-pan-x\">\n        <div className=\"inline-flex w-max items-center gap-1 rounded-lg bg-[var(--tbl-well,color-mix(in_oklab,var(--primary)_5%,color-mix(in_oklab,var(--foreground)_5%,var(--card))))] p-1\">\n          {ACTIVITY_TABS.map((option) => {\n            const selected = tab === option.value;\n            return (\n              <button\n                key={option.value}\n                type=\"button\"\n                onClick={() => setTab(option.value)}\n                className={cn(\n                  \"shrink-0 whitespace-nowrap rounded-md px-3 py-1.5 text-xs font-medium transition-colors duration-150\",\n                  selected\n                    ? \"bg-card text-foreground shadow-sm ring-1 ring-foreground/[0.08]\"\n                    : \"text-muted-foreground hover:text-foreground\",\n                )}\n              >\n                {option.label}\n              </button>\n            );\n          })}\n        </div>\n      </div>\n      {/* Permanently fixed height matching all 6 items: zero shrinkage, zero expansion */}\n      <div className=\"mt-5 h-[420px] min-h-[420px] flex-1 overflow-hidden\">\n        <div ref={tabPaneRef} key={tab} className=\"h-full min-h-[435px]\">\n          {items.length ? (\n              <div className=\"space-y-5\">\n                {groups.map((group) => (\n                  <section key={group.key} aria-label={group.label || undefined}>\n                    {group.label && <h3 className=\"mb-3 text-xs font-medium text-muted-foreground\">{group.label}</h3>}\n                    {/* The rail runs chip centre to chip centre; chips sit on top of it. */}\n                    <ol className=\"relative space-y-4 before:absolute before:inset-y-4 before:left-4 before:w-px before:bg-foreground/[0.12]\">\n                      {group.items.map((item) => {\n                        const type = ACTIVITY_TYPES[item.type];\n                        const Icon = type.icon;\n                        return (\n                          <li key={item.id} className=\"relative flex gap-3\">\n                            <span\n                              className=\"relative grid size-8 shrink-0 place-items-center rounded-full\"\n                              style={{\n                                backgroundColor: `color-mix(in oklab, ${type.color} 16%, var(--card))`,\n                                color: `color-mix(in oklab, ${type.color} 72%, var(--foreground))`,\n                              }}\n                            >\n                              <Icon className=\"size-4\" aria-hidden=\"true\" />\n                            </span>\n                            <div className=\"min-w-0 flex-1\">\n                              <div className=\"flex items-baseline justify-between gap-3\">\n                                <p className=\"truncate text-sm font-medium leading-5 text-foreground\">\n                                  <span className=\"sr-only\">{type.label}: </span>\n                                  {item.href ? (\n                                    <Link href={item.href} className={cn(\"rounded-sm hover:underline\", FOCUS)}>\n                                      {item.title}\n                                    </Link>\n                                  ) : (\n                                    item.title\n                                  )}\n                                </p>\n                                {now && (\n                                  <time\n                                    dateTime={item.at.toISOString()}\n                                    className=\"shrink-0 text-xs tabular-nums text-muted-foreground\"\n                                  >\n                                    {activityTime(item.at, now)}\n                                  </time>\n                                )}\n                              </div>\n                              <div className=\"flex min-w-0 items-center gap-2\">\n                                {item.subtitle && (\n                                  <p className=\"truncate text-[13px] leading-5 text-muted-foreground\">{item.subtitle}</p>\n                                )}\n                                {item.status && (\n                                  <span\n                                    className={cn(\n                                      \"shrink-0 rounded-full px-2 py-px text-[11px] font-medium capitalize\",\n                                      STATUS_STYLES[item.status] ?? \"bg-[var(--dash-well)] text-muted-foreground\",\n                                    )}\n                                  >\n                                    {item.status}\n                                  </span>\n                                )}\n                              </div>\n                            </div>\n                          </li>\n                        );\n                      })}\n                    </ol>\n                  </section>\n                ))}\n              </div>\n            ) : (\n              <EmptyState icon={<Inbox />} title={current?.empty ?? \"No activity yet\"} className=\"h-full min-h-[380px] flex flex-col items-center justify-center\">\n                New events show up here as they happen.\n              </EmptyState>\n            )}\n          </div>\n      </div>\n    </section>\n  );\n}\n\n// ==========================================\n// TRAFFIC, COUNTRIES, AUTHORS\n// ==========================================\nconst formatRate = (rate: number) =>\n  rate === 0 ? \"0%\" : `${(rate * 100).toFixed(rate < 0.1 ? 1 : 0)}%`;\n\n// Tap-friendly explanation; a hover-only tooltip would hide it on touch.\nfunction RateExplainer() {\n  return (\n    <Popover>\n      <PopoverTrigger\n        aria-label=\"How this rate is calculated\"\n        className={cn(\n          \"grid size-6 cursor-pointer place-items-center rounded-full text-muted-foreground transition-colors hover:text-foreground\",\n          FOCUS,\n        )}\n      >\n        <Info className=\"size-3.5\" aria-hidden=\"true\" />\n      </PopoverTrigger>\n      <PopoverContent\n        side=\"top\"\n        className=\"w-64 rounded-lg border-0 bg-foreground p-3 text-xs leading-5 text-background lg:p-3 lg:text-xs\"\n      >\n        New sign-ups divided by the visits in the same period.\n      </PopoverContent>\n    </Popover>\n  );\n}\n\nfunction TrafficSourcesPanel({\n  shares,\n  signUps,\n  period,\n  href,\n  linkComponent,\n  className,\n}: {\n  shares?: DashboardShare[];\n  signUps?: number;\n  period: DashboardPeriod;\n  href?: string;\n  linkComponent: React.ElementType;\n  className?: string;\n}) {\n  const id = useId();\n  const [focus, setFocus] = useState<string | null>(null);\n  const copy = PERIOD_COPY[period];\n  const { slices, total } = useMemo(() => compose(shares ?? []), [shares]);\n  const gaugeLabel = total\n    ? `Visits by source ${copy.trafficPhrase}: ${slices.map((slice) => `${slice.name} ${formatShare(slice.share)}`).join(\", \")}`\n    : `No visits ${copy.trafficPhrase}`;\n\n  return (\n    <section aria-labelledby={`${id}-title`} className={cn(PANEL, \"flex flex-col p-5 sm:p-6\", className)}>\n      <PanelHeader\n        id={`${id}-title`}\n        title=\"Traffic sources\"\n        caption={copy.traffic}\n        href={href}\n        linkLabel=\"Open visitor analytics\"\n        linkComponent={linkComponent}\n      />\n      {!shares ? (\n        <EmptyState icon={<CloudOff />} title=\"Traffic data is unavailable\" className=\"flex-1 py-12\">\n          Connect visitor tracking to see where people come from.\n        </EmptyState>\n      ) : (\n        <div className=\"mt-5 flex flex-1 flex-col\">\n          <TickGauge slices={slices} focus={focus} onFocus={setFocus} label={gaugeLabel}>\n            <span className=\"text-[2rem] font-light leading-none tracking-[-0.02em] text-foreground\">\n              {formatCompact(total)}\n            </span>\n            <span className=\"mt-1.5 text-xs text-muted-foreground\">{plural(total, [\"visit\", \"visits\"])}</span>\n          </TickGauge>\n          {total ? (\n            <>\n              <BreakdownList slices={slices} focus={focus} onFocus={setFocus} className=\"mt-5\" />\n              {signUps !== undefined && <ConversionRate signUps={signUps} visits={total} />}\n            </>\n          ) : (\n            <p className=\"mt-5 text-center text-[13px] text-muted-foreground\">\n              No visits recorded {copy.trafficPhrase} yet.\n            </p>\n          )}\n        </div>\n      )}\n    </section>\n  );\n}\n\nfunction ConversionRate({ signUps, visits }: { signUps: number; visits: number }) {\n  // Sign-ups can outnumber tracked visits (blocked trackers, invited users);\n  // a rate above 100% would mislead, so that case is said plainly.\n  const rate = signUps / visits;\n  const valid = rate <= 1;\n  return (\n    <div className=\"mt-auto pt-5\">\n      <div className=\"border-t border-foreground/[0.08] pt-4\">\n        <div className=\"flex items-center justify-between gap-3\">\n          <div className=\"flex items-center gap-1 text-[13px] text-muted-foreground\">\n            Visit to sign-up rate\n            <RateExplainer />\n          </div>\n          <p className=\"text-sm font-medium tabular-nums text-foreground\">{valid ? formatRate(rate) : \"—\"}</p>\n        </div>\n        <div aria-hidden=\"true\" className=\"mt-2 h-1.5 overflow-hidden rounded-full bg-[var(--dash-track)]\">\n          <div className=\"h-full rounded-full bg-[var(--dash-accent)]\" style={{ width: `${valid ? rate * 100 : 0}%` }} />\n        </div>\n        <p className=\"mt-2 text-xs text-muted-foreground\">\n          {valid\n            ? `${formatNumber(signUps)} ${plural(signUps, [\"sign-up\", \"sign-ups\"])} from ${formatNumber(visits)} ${plural(visits, [\"visit\", \"visits\"])}`\n            : `${formatNumber(signUps)} sign-ups outnumber the ${formatNumber(visits)} tracked visits.`}\n        </p>\n      </div>\n    </div>\n  );\n}\n\nfunction CountriesPanel({\n  shares,\n  period,\n  href,\n  linkComponent,\n  className,\n}: {\n  shares?: DashboardShare[];\n  period: DashboardPeriod;\n  href?: string;\n  linkComponent: React.ElementType;\n  className?: string;\n}) {\n  const id = useId();\n  const [focus, setFocus] = useState<string | null>(null);\n  const copy = PERIOD_COPY[period];\n  const { slices, total } = useMemo(() => compose(shares ?? []), [shares]);\n  const countryCount = (shares ?? []).filter((share) => share.value > 0).length;\n  const leader = slices[0];\n\n  return (\n    <section aria-labelledby={`${id}-title`} className={cn(PANEL, \"flex flex-col p-5 sm:p-6\", className)}>\n      <PanelHeader\n        id={`${id}-title`}\n        title=\"Visitors by country\"\n        caption={copy.traffic}\n        href={href}\n        linkLabel=\"Open visitor analytics\"\n        linkComponent={linkComponent}\n      />\n      {!shares ? (\n        <EmptyState icon={<CloudOff />} title=\"Country data is unavailable\" className=\"flex-1 py-12\">\n          Connect visitor tracking to see where people are.\n        </EmptyState>\n      ) : !total || !leader ? (\n        <EmptyState icon={<Globe />} title={`No visits recorded ${copy.trafficPhrase} yet`} className=\"flex-1 py-12\">\n          Countries appear here once visitors arrive.\n        </EmptyState>\n      ) : (\n        <>\n          <div className=\"mt-5\">\n            <p className=\"flex items-baseline gap-2\">\n              <span className=\"text-[2rem] font-light leading-none tracking-[-0.02em] text-foreground\">\n                {formatNumber(countryCount)}\n              </span>\n              <span className=\"text-sm text-muted-foreground\">{plural(countryCount, [\"country\", \"countries\"])}</span>\n            </p>\n            <p className=\"mt-2 text-[13px] leading-5 text-muted-foreground\">\n              {leader.name} accounts for {formatShare(leader.share)} of visits.\n            </p>\n          </div>\n          <Barcode slices={slices} focus={focus} onFocus={setFocus} />\n          <BreakdownList slices={slices} focus={focus} onFocus={setFocus} showCodes className=\"mt-4\" />\n        </>\n      )}\n    </section>\n  );\n}\n\nfunction TopAuthorsPanel({\n  authors,\n  period,\n  href,\n  linkComponent,\n  className,\n}: {\n  authors: DashboardAuthor[];\n  period: DashboardPeriod;\n  href?: string;\n  linkComponent: React.ElementType;\n  className?: string;\n}) {\n  const id = useId();\n  const copy = PERIOD_COPY[period];\n  const ranked = [...authors].filter((author) => author.posts > 0).sort((a, b) => b.posts - a.posts);\n  const shown = ranked.slice(0, 5);\n  const totalPosts = sum(ranked.map((author) => author.posts));\n  const leaderPosts = shown[0]?.posts ?? 0;\n\n  return (\n    <section aria-labelledby={`${id}-title`} className={cn(PANEL, \"flex flex-col p-5 sm:p-6\", className)}>\n      <PanelHeader\n        id={`${id}-title`}\n        title=\"Top authors\"\n        caption={copy.authors}\n        href={href}\n        linkLabel=\"Open all posts\"\n        linkComponent={linkComponent}\n      />\n      {shown.length ? (\n        <>\n          <p className=\"mt-5 text-[13px] text-muted-foreground\">\n            <span className=\"font-medium text-foreground\">{formatNumber(totalPosts)}</span>{\" \"}\n            {plural(totalPosts, [\"post\", \"posts\"])} from{\" \"}\n            <span className=\"font-medium text-foreground\">{formatNumber(ranked.length)}</span>{\" \"}\n            {plural(ranked.length, [\"author\", \"authors\"])}\n          </p>\n          <ol className=\"mt-5 space-y-4\">\n            {shown.map((author, index) => {\n              const leads = index === 0;\n              return (\n                <li key={`${author.name}-${index}`} className=\"flex items-center gap-3\">\n                  <span\n                    aria-hidden=\"true\"\n                    className={cn(\n                      \"grid size-9 shrink-0 place-items-center rounded-full text-xs font-medium\",\n                      leads ? \"bg-[var(--dash-accent)] text-[var(--card)]\" : \"bg-[var(--dash-well)] text-foreground\",\n                    )}\n                  >\n                    {initialsOf(author.name)}\n                  </span>\n                  <div className=\"min-w-0 flex-1\">\n                    <div className=\"flex items-baseline justify-between gap-3\">\n                      <p className={cn(\"truncate text-sm text-foreground\", leads && \"font-medium\")}>{author.name}</p>\n                      <p className=\"shrink-0 text-sm tabular-nums text-foreground\">\n                        {formatNumber(author.posts)}{\" \"}\n                        <span className=\"text-muted-foreground\">{plural(author.posts, [\"post\", \"posts\"])}</span>\n                      </p>\n                    </div>\n                    <div aria-hidden=\"true\" className=\"mt-2 h-2 rounded-full bg-[var(--dash-track)]\">\n                      <div\n                        className={cn(\"h-full rounded-full\", leads ? \"bg-[var(--dash-accent)]\" : \"bg-[var(--dash-mark-muted)]\")}\n                        style={{ width: `${leaderPosts ? (author.posts / leaderPosts) * 100 : 0}%` }}\n                      />\n                    </div>\n                  </div>\n                </li>\n              );\n            })}\n          </ol>\n        </>\n      ) : (\n        <EmptyState icon={<PenLine />} title={`No posts published ${copy.authorsPhrase}`} className=\"flex-1 py-12\">\n          Authors are ranked here as posts go live.\n        </EmptyState>\n      )}\n    </section>\n  );\n}\n\n// ==========================================\n// EXPORT\n// ==========================================\n/** The selected period's overview series as a CSV download. */\nfunction downloadCsv(metrics: DashboardMetric[], period: DashboardPeriod) {\n  const longest = metrics.reduce<DashboardPoint[]>(\n    (best, metric) => ((metric.series[period]?.length ?? 0) > best.length ? metric.series[period] ?? [] : best),\n    [],\n  );\n  const escape = (value: string) => `\"${value.replace(/\"/g, '\"\"')}\"`;\n  const header = [\"Period\", ...metrics.map((metric) => metric.label)].map(escape).join(\",\");\n  const rows = longest.map((point, index) =>\n    [\n      escape(point.fullLabel ?? point.label),\n      ...metrics.map((metric) => String(metric.series[period]?.[index]?.value ?? \"\")),\n    ].join(\",\"),\n  );\n  const blob = new Blob([\"﻿\" + [header, ...rows].join(\"\\n\")], { type: \"text/csv;charset=utf-8;\" });\n  const url = URL.createObjectURL(blob);\n  const link = document.createElement(\"a\");\n  link.href = url;\n  link.download = `dashboard-${period}.csv`;\n  document.body.appendChild(link);\n  link.click();\n  link.remove();\n  URL.revokeObjectURL(url);\n}\n\n/** \"So far today: 12 new users, 4 messages and $1,240 in revenue.\" */\nfunction describeToday(metrics: DashboardMetric[]) {\n  const parts = metrics.flatMap((metric) => {\n    const points = metric.series.day ?? [];\n    const value = points[points.length - 1]?.value ?? 0;\n    if (!value) return [];\n    if (metric.currency) return [`${formatMoney(value, metric.currency)} in ${metric.label.toLowerCase()}`];\n    return [`${formatNumber(value)} ${metric.noun ? plural(value, metric.noun) : metric.label.toLowerCase()}`];\n  });\n  if (!parts.length) return \"Nothing new yet today.\";\n  const list =\n    parts.length < 2 ? parts[0] : `${parts.slice(0, -1).join(\", \")} and ${parts[parts.length - 1]}`;\n  return `So far today: ${list}.`;\n}\n\n// ==========================================\n// GLASS VARIANT\n// ==========================================\n// Milky glass on a cool grey canvas. The canvas carries soft colour blobs\n// (the theme colour and two neighbouring hues) and a brand glow rising from\n// the bottom, so the translucent panes have something to show through; on a\n// flat grey they would read as solid. Layers, lightest to densest: panes,\n// cards inside panes, then tiles and round buttons. Each one catches the\n// light with a 150° sheen, a bright top edge and a light rim.\n//\n// Nothing here uses a backdrop blur: the canvas behind is already soft, so a\n// blur would show nothing and would still cost a repaint on every scroll frame.\n//\n// The tokens are unlayered so they win over the theme. The surface helpers\n// sit in the components layer so utilities on the same element (size,\n// radius, display) still win. Dark mode re-sets every token the light block\n// sets, because both selectors have the same specificity.\nconst GLASS_CSS = `\n[data-dashboard-glass] {\n  --dash-accent: var(--primary);\n  --muted-foreground: oklch(0.49 0.012 265);\n  --dash-surface: #f4f5f7;\n  --dash-well: rgb(15 23 42 / 0.05);\n  --dash-hairline: rgb(15 23 42 / 0.07);\n  --dash-track: color-mix(in oklab, var(--primary) 12%, transparent);\n  --dash-mark-muted: color-mix(in oklab, var(--primary) 30%, #f4f5f7);\n  --dash-cat-1: #5b7fd6;\n  --dash-cat-2: #e0625f;\n  --dash-cat-3: #e3a43b;\n  --dash-cat-other: #c3c7cf;\n  --dash-up: #137a39;\n  --dash-down: #b91c1c;\n  --dash-warn: #a3510a;\n  --dash-ink: #464c5b;\n  --dash-icon: #6b7180;\n  --dash-canvas: #e7e7ee;\n  --dash-glow: color-mix(in oklab, var(--primary) 30%, #a9bbe2);\n  --dash-blob-1: color-mix(in oklab, var(--primary) 30%, transparent);\n  --dash-blob-2: color-mix(in oklab, #6ea8ff 30%, transparent);\n  --dash-blob-3: color-mix(in oklab, #ff8fc0 22%, transparent);\n  --dash-sheen-1: rgb(255 255 255 / 0.5);\n  --dash-sheen-2: rgb(255 255 255 / 0.06);\n  --dash-sheen-3: rgb(255 255 255 / 0.16);\n  --dash-edge: rgb(255 255 255 / 0.95);\n  --dash-rim: rgb(255 255 255 / 0.6);\n  --dash-border: rgb(15 23 42 / 0.045);\n  --dash-shadow: 0 1px 2px rgb(31 41 72 / 0.04), 0 22px 44px -26px rgb(31 41 72 / 0.32);\n  --dash-pane: rgb(255 255 255 / 0.3);\n  --dash-card: rgb(255 255 255 / 0.36);\n  --dash-card-strong: rgb(255 255 255 / 0.44);\n  --dash-card-shadow: 0 1px 2px rgb(31 41 72 / 0.04), 0 12px 26px -18px rgb(31 41 72 / 0.26);\n  --dash-round-shadow: 0 8px 18px -12px rgb(31 41 72 / 0.3);\n  --dash-notch-shadow: drop-shadow(0 18px 22px rgb(31 41 72 / 0.12));\n  --dash-hatch: color-mix(in oklab, var(--foreground) 14%, transparent);\n}\n\n:where(.dark) [data-dashboard-glass],\n[data-dashboard-glass]:where(.dark) {\n  --dash-accent: color-mix(in oklab, var(--primary) 72%, #fff);\n  --muted-foreground: oklch(0.72 0.01 265);\n  --dash-surface: #1b1d23;\n  --dash-well: rgb(255 255 255 / 0.05);\n  --dash-hairline: rgb(255 255 255 / 0.07);\n  --dash-track: color-mix(in oklab, var(--primary) 18%, transparent);\n  --dash-mark-muted: color-mix(in oklab, var(--primary) 38%, #1b1d23);\n  --dash-cat-1: #6286e3;\n  --dash-cat-2: #e05a6c;\n  --dash-cat-3: #b08c10;\n  --dash-cat-other: #4b505c;\n  --dash-up: #4ade80;\n  --dash-down: #fa8585;\n  --dash-warn: #fbbf24;\n  --dash-ink: #c5cbd6;\n  --dash-icon: #8f99ab;\n  --dash-canvas: #0c0d12;\n  --dash-glow: color-mix(in oklab, color-mix(in oklab, var(--primary) 45%, #1e2c52) 60%, transparent);\n  --dash-blob-1: color-mix(in oklab, var(--primary) 24%, transparent);\n  --dash-blob-2: color-mix(in oklab, #3f6fd8 20%, transparent);\n  --dash-blob-3: color-mix(in oklab, #c2457e 14%, transparent);\n  --dash-sheen-1: rgb(255 255 255 / 0.1);\n  --dash-sheen-2: rgb(255 255 255 / 0.01);\n  --dash-sheen-3: rgb(255 255 255 / 0.035);\n  --dash-edge: rgb(255 255 255 / 0.12);\n  --dash-rim: rgb(255 255 255 / 0.08);\n  --dash-border: rgb(0 0 0 / 0.25);\n  --dash-shadow: 0 22px 44px -24px rgb(0 0 0 / 0.75);\n  --dash-pane: rgb(255 255 255 / 0.04);\n  --dash-card: rgb(255 255 255 / 0.05);\n  --dash-card-strong: rgb(255 255 255 / 0.07);\n  --dash-card-shadow: 0 12px 26px -18px rgb(0 0 0 / 0.7);\n  --dash-round-shadow: 0 8px 18px -12px rgb(0 0 0 / 0.6);\n  --dash-notch-shadow: drop-shadow(0 18px 22px rgb(0 0 0 / 0.5));\n  --dash-hatch: rgb(255 255 255 / 0.13);\n}\n\n/* Two blobs follow the theme colour where the browser can derive\n   neighbouring hues (a violet theme gets blue and pink). */\n@supports (color: oklch(from red l c h)) {\n  [data-dashboard-glass] {\n    --dash-blob-2: oklch(from var(--primary) calc(l + 0.12) c calc(h + 55) / 0.3);\n    --dash-blob-3: oklch(from var(--primary) calc(l + 0.12) c calc(h - 50) / 0.2);\n  }\n\n  :where(.dark) [data-dashboard-glass],\n  [data-dashboard-glass]:where(.dark) {\n    --dash-blob-2: oklch(from var(--primary) l c calc(h + 55) / 0.2);\n    --dash-blob-3: oklch(from var(--primary) l c calc(h - 50) / 0.14);\n  }\n}\n\n[data-dashboard-glass] {\n  --dash-sheen: linear-gradient(150deg, var(--dash-sheen-1), var(--dash-sheen-2) 42%, var(--dash-sheen-3));\n  --dash-lit: inset 0 1px 0 0 var(--dash-edge), inset 0 0 0 1px var(--dash-rim), 0 0 0 1px var(--dash-border);\n  background-color: var(--dash-canvas);\n  background-image:\n    radial-gradient(40rem 30rem at 4% 6%, var(--dash-blob-1), transparent 70%),\n    radial-gradient(44rem 34rem at 98% 28%, var(--dash-blob-2), transparent 70%),\n    radial-gradient(38rem 30rem at 26% 80%, var(--dash-blob-3), transparent 70%),\n    radial-gradient(84rem 34rem at 62% 122%, var(--dash-glow), transparent 72%);\n}\n\n[data-dashboard-glass][data-liquid=\"true\"] {\n  --foreground: #ffffff;\n  --muted-foreground: rgba(255, 255, 255, 0.75);\n  --dash-ink: #ffffff;\n  --dash-icon: rgba(255, 255, 255, 0.9);\n  --dash-canvas: transparent;\n  --dash-surface: rgba(255, 255, 255, 0.1);\n  --dash-pane: rgba(255, 255, 255, 0.08);\n  --dash-card: rgba(255, 255, 255, 0.12);\n  --dash-card-strong: rgba(255, 255, 255, 0.18);\n  --dash-well: rgba(0, 0, 0, 0.25);\n  --dash-hairline: rgba(255, 255, 255, 0.18);\n  --dash-border: rgba(255, 255, 255, 0.2);\n  --dash-sheen-1: rgba(255, 255, 255, 0.25);\n  --dash-sheen-2: rgba(255, 255, 255, 0.05);\n  --dash-edge: rgba(255, 255, 255, 0.3);\n  --dash-rim: rgba(255, 255, 255, 0.25);\n  background-color: transparent !important;\n  background-image: none !important;\n  color: #ffffff !important;\n}\n\n[data-dashboard-glass][data-liquid=\"true\"] .dg-card,\n[data-dashboard-glass][data-liquid=\"true\"] .dg-pane {\n  background: rgba(255, 255, 255, 0.12) !important;\n  border-color: rgba(255, 255, 255, 0.2) !important;\n  backdrop-filter: blur(12px) !important;\n  color: #ffffff !important;\n}\n\n/* The period switch (the library's segmented toggle group): a glass track\n   with the brand pill and white text. */\n[data-dashboard-glass] .dg-seg {\n  background-color: var(--dash-card);\n  box-shadow: inset 0 1px 0 0 var(--dash-edge), 0 0 0 1px var(--dash-border);\n}\n\n[data-dashboard-glass] .dg-seg > span[aria-hidden=\"true\"],\n[data-dashboard-glass] .dg-seg [aria-checked=\"true\"].bg-card {\n  background-color: var(--primary);\n  background-image: linear-gradient(150deg, rgb(255 255 255 / 0.26), rgb(255 255 255 / 0) 55%);\n  box-shadow: 0 8px 18px -10px color-mix(in oklab, var(--primary) 80%, transparent);\n}\n\n[data-dashboard-glass] .dg-seg [aria-checked=\"true\"] {\n  color: var(--primary-foreground);\n}\n\n\n\n@layer components {\n  [data-dashboard-glass] .dg-pane {\n    background-color: var(--dash-pane);\n    background-image: var(--dash-sheen);\n    box-shadow: var(--dash-lit), var(--dash-shadow);\n  }\n\n  [data-dashboard-glass] .dg-card,\n  [data-dashboard-glass] .dg-tile {\n    background-color: var(--dash-card);\n    background-image: var(--dash-sheen);\n    box-shadow: var(--dash-lit), var(--dash-card-shadow);\n  }\n\n  [data-dashboard-glass] .dg-tile {\n    background-color: var(--dash-card-strong);\n  }\n\n  /* Round glass buttons: panel actions, the export, the KPI links. */\n  [data-dashboard-glass] .dg-round {\n    display: inline-grid;\n    place-items: center;\n    flex-shrink: 0;\n    border-radius: 999px;\n    color: var(--dash-ink);\n    background-color: var(--dash-card-strong);\n    background-image: var(--dash-sheen);\n    box-shadow: var(--dash-lit), var(--dash-round-shadow);\n    transition: background-color 150ms, color 150ms, box-shadow 150ms;\n  }\n\n  [data-dashboard-glass] .dg-round:hover {\n    color: var(--foreground);\n    background-color: color-mix(in oklab, var(--dash-card-strong), var(--foreground) 6%);\n  }\n\n  [data-dashboard-glass] .dg-round:focus-visible {\n    outline: 2px solid color-mix(in oklab, var(--dash-accent) 60%, transparent);\n    outline-offset: 2px;\n  }\n\n  /* The selected or lead item: the brand fill, catching the light too. */\n  [data-dashboard-glass] .dg-active {\n    color: var(--primary-foreground);\n    background-color: var(--primary);\n    background-image: linear-gradient(150deg, rgb(255 255 255 / 0.26), rgb(255 255 255 / 0) 55%);\n    box-shadow:\n      inset 0 1px 0 0 rgb(255 255 255 / 0.3),\n      inset 0 0 0 1px rgb(255 255 255 / 0.12),\n      0 10px 22px -12px color-mix(in oklab, var(--primary) 80%, transparent);\n  }\n\n  /* Diagonal hatching, the texture for empty tracks and \"Other\". */\n  [data-dashboard-glass] .dg-hatch {\n    background-image: repeating-linear-gradient(135deg, var(--dash-hatch) 0 1.5px, transparent 1.5px 6px);\n  }\n\n  /* The lead KPI card: the brand colour with a soft light from the top-left. */\n  [data-dashboard-glass] .dg-kpi-hero {\n    color: var(--primary-foreground);\n    background-color: var(--primary);\n    background-image:\n      radial-gradient(120% 90% at 0% 0%, rgb(255 255 255 / 0.22), transparent 60%),\n      linear-gradient(160deg, transparent 40%, rgb(0 0 0 / 0.18));\n    box-shadow:\n      inset 0 1px 0 0 rgb(255 255 255 / 0.25),\n      0 22px 40px -24px color-mix(in oklab, var(--primary) 85%, transparent);\n  }\n\n  /* The notched pane's shape is an SVG; drop-shadow follows its outline. Its\n     own layer, so the filter is drawn once rather than on every scroll frame. */\n  [data-dashboard-glass] .dg-notch-bg {\n    filter: var(--dash-notch-shadow);\n    will-change: transform;\n  }\n}\n\n/* People who ask for less transparency get solid surfaces. */\n@media (prefers-reduced-transparency: reduce) {\n  [data-dashboard-glass] .dg-pane,\n  [data-dashboard-glass] .dg-card,\n  [data-dashboard-glass] .dg-tile,\n  [data-dashboard-glass] .dg-round {\n    background-color: var(--dash-surface);\n    background-image: none;\n  }\n}\n`;\n\nconst GLASS_FOCUS =\n  \"outline-none focus-visible:ring-2 focus-visible:ring-[color-mix(in_oklab,var(--dash-accent)_60%,transparent)] focus-visible:ring-offset-2 focus-visible:ring-offset-transparent\";\nconst GLASS_PANE = \"dg-pane rounded-[28px] p-5 sm:p-6\";\nconst GLASS_CHART_HEIGHT = \"h-[15.5rem] @3xl:h-[20.5rem]\";\n\n// Server and client agree only on markup that never reads the clock or the\n// time zone, so layout effects run on the client alone.\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\nconst capitalize = (text: string) => text.charAt(0).toUpperCase() + text.slice(1);\n\n// Initials take a colour from the categorical slots by a hash of the name,\n// so a person keeps the same colour wherever they appear.\nconst AVATAR_HUES = [\"var(--dash-cat-1)\", \"var(--dash-cat-2)\", \"var(--dash-cat-3)\", \"var(--dash-accent)\"];\n\nfunction hueFor(name: string) {\n  let hash = 0;\n  for (let index = 0; index < name.length; index += 1) {\n    hash = (hash * 31 + name.charCodeAt(index)) >>> 0;\n  }\n  return AVATAR_HUES[hash % AVATAR_HUES.length];\n}\n\n/** A round avatar: the photo when it loads, initials otherwise. Decorative; a name always sits beside it. */\nfunction GlassAvatar({ name, src, size = \"md\" }: { name: string; src?: string; size?: \"md\" | \"lg\" }) {\n  const [failedSrc, setFailedSrc] = useState<string | null>(null);\n  const showImage = Boolean(src) && failedSrc !== src;\n  const hue = hueFor(name);\n  return (\n    <span\n      aria-hidden=\"true\"\n      className={cn(\n        \"relative grid shrink-0 select-none place-items-center overflow-hidden rounded-full font-medium\",\n        size === \"lg\" ? \"size-11 text-sm\" : \"size-9 text-xs\",\n      )}\n      style={\n        showImage\n          ? undefined\n          : {\n              backgroundColor: `color-mix(in oklab, ${hue} 16%, var(--dash-surface))`,\n              color: `color-mix(in oklab, ${hue} 72%, var(--foreground))`,\n            }\n      }\n    >\n      {showImage ? (\n        <img\n          src={src}\n          alt=\"\"\n          loading=\"lazy\"\n          className=\"size-full object-cover\"\n          onError={() => setFailedSrc(src ?? null)}\n        />\n      ) : (\n        initialsOf(name)\n      )}\n    </span>\n  );\n}\n\n/** Pane title row: a bold title, a muted caption, round actions on the right. */\nfunction GlassPaneHeader({\n  id,\n  title,\n  caption,\n  actions,\n}: {\n  id: string;\n  title: string;\n  caption?: string;\n  actions?: React.ReactNode;\n}) {\n  return (\n    <div className=\"flex items-start justify-between gap-4\">\n      <div className=\"min-w-0\">\n        <h2 id={id} className=\"text-[17px] font-semibold leading-6 tracking-[-0.01em] text-foreground\">\n          {title}\n        </h2>\n        {caption && <p className=\"mt-0.5 text-[13px] leading-5 text-muted-foreground\">{caption}</p>}\n      </div>\n      {actions && <div className=\"flex shrink-0 items-center gap-2\">{actions}</div>}\n    </div>\n  );\n}\n\nfunction GlassRoundLink({\n  href,\n  label,\n  linkComponent: Link,\n}: {\n  href: string;\n  label: string;\n  linkComponent: React.ElementType;\n}) {\n  return (\n    <Link href={href} aria-label={label} title={label} className=\"dg-round size-10\">\n      <ArrowUpRight className=\"size-[18px]\" aria-hidden=\"true\" />\n    </Link>\n  );\n}\n\n/** SVG hatch pattern; reference it as `url(#id)`. */\nfunction HatchPattern({ id, color = \"var(--dash-hatch)\", gap = 6 }: { id: string; color?: string; gap?: number }) {\n  return (\n    <pattern id={id} width={gap} height={gap} patternUnits=\"userSpaceOnUse\" patternTransform=\"rotate(45)\">\n      <rect width=\"1.6\" height={gap} style={{ fill: color }} />\n    </pattern>\n  );\n}\n\nconst svgId = (prefix: string, id: string) => `${prefix}-${id.replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n\n// ------------------------------------------\n// Header and KPI cards\n// ------------------------------------------\nfunction GlassHeader({\n  userName,\n  summary,\n  now,\n  period,\n  onPeriodChange,\n  onExport,\n}: {\n  userName?: string;\n  summary: React.ReactNode;\n  now: Date | null;\n  period: DashboardPeriod;\n  onPeriodChange: (period: DashboardPeriod) => void;\n  onExport: () => void;\n}) {\n  const firstName = userName?.trim().split(/\\s+/)[0];\n  const greeting = now ? greetingFor(now) : \"Welcome back\";\n\n  return (\n    <header className=\"flex flex-col gap-5 @5xl:flex-row @5xl:items-end @5xl:justify-between\">\n      <div className=\"min-w-0\">\n        <p className=\"min-h-5 text-[13px] text-muted-foreground\">{now ? longDate(now) : null}</p>\n        <h1 className=\"mt-1.5 text-[1.75rem] font-semibold leading-[1.1] tracking-[-0.025em] text-foreground @xl:text-[2rem]\">\n          {firstName ? `${greeting}, ${firstName}` : greeting}\n        </h1>\n        {summary && (\n          <p className=\"mt-2 max-w-[60ch] text-pretty text-[15px] leading-6 text-muted-foreground\">{summary}</p>\n        )}\n      </div>\n      <div className=\"flex flex-wrap items-center gap-2\">\n        <ToggleGroup\n          type=\"single\"\n          variant=\"segmented\"\n          value={period}\n          onValueChange={(next) => {\n            if (next === \"day\" || next === \"week\" || next === \"month\") onPeriodChange(next);\n          }}\n          aria-label=\"Reporting period\"\n          className=\"dg-seg h-11\"\n        >\n          {PERIODS.map((option) => (\n            <ToggleGroupItem key={option.value} value={option.value}>\n              {option.label}\n            </ToggleGroupItem>\n          ))}\n        </ToggleGroup>\n        <button\n          type=\"button\"\n          onClick={onExport}\n          className=\"dg-round inline-flex h-11 items-center gap-2 px-5 text-sm font-medium text-foreground\"\n        >\n          <Download className=\"size-4\" aria-hidden=\"true\" />\n          Export\n        </button>\n      </div>\n    </header>\n  );\n}\n\n/** What the current bucket of a metric added: \"+1,094 new users this month\". */\nfunction describeAdded(metric: DashboardMetric, period: DashboardPeriod) {\n  const points = metric.series[period] ?? [];\n  const value = points[points.length - 1]?.value ?? 0;\n  const { current } = PERIOD_COPY[period];\n  if (metric.currency) return `${formatMoney(value, metric.currency)} in ${metric.label.toLowerCase()} ${current}`;\n  return `+${formatNumber(value)} ${metric.noun ? plural(value, metric.noun) : metric.label.toLowerCase()} ${current}`;\n}\n\n/** All-time totals as cards; the first carries the brand fill. */\nfunction GlassKpiCards({\n  totals,\n  metrics,\n  period,\n  linkComponent: Link,\n}: {\n  totals: DashboardTotal[];\n  metrics: DashboardMetric[];\n  period: DashboardPeriod;\n  linkComponent: React.ElementType;\n}) {\n  return (\n    <section aria-label=\"Totals\" className=\"grid grid-cols-1 gap-4 @xl:grid-cols-2 @5xl:grid-cols-4 @5xl:gap-5\">\n      {totals.map((total, index) => {\n        const hero = index === 0;\n        const metric = total.metric ? metrics.find((entry) => entry.id === total.metric) : undefined;\n        const linkLabel = `Open ${total.label.toLowerCase()}`;\n        return (\n          <article\n            key={total.label}\n            className={cn(\n              \"relative flex min-w-0 flex-col overflow-hidden rounded-[24px] p-5\",\n              hero ? \"dg-kpi-hero\" : \"dg-pane text-foreground\",\n            )}\n          >\n            <div className=\"flex min-h-9 items-start justify-between gap-3\">\n              <h3 className=\"flex min-w-0 items-center gap-2 text-[15px] font-medium leading-6 [&_svg]:size-4 [&_svg]:shrink-0\">\n                {total.icon && (\n                  <span aria-hidden=\"true\" className={hero ? \"opacity-80\" : \"text-muted-foreground\"}>\n                    {total.icon}\n                  </span>\n                )}\n                <span className=\"truncate\">{total.label}</span>\n              </h3>\n              {total.href && (\n                <Link\n                  href={total.href}\n                  aria-label={linkLabel}\n                  title={linkLabel}\n                  className={cn(\n                    \"size-9\",\n                    hero\n                      ? \"grid shrink-0 place-items-center rounded-full bg-[var(--primary-foreground)] text-[var(--primary)] transition-opacity hover:opacity-90 outline-none focus-visible:ring-2 focus-visible:ring-[var(--primary-foreground)] focus-visible:ring-offset-2 focus-visible:ring-offset-transparent\"\n                      : \"dg-round\",\n                  )}\n                >\n                  <ArrowUpRight className=\"size-4\" aria-hidden=\"true\" />\n                </Link>\n              )}\n            </div>\n            <p\n              className=\"mt-4 text-[2.5rem] font-semibold leading-none tracking-[-0.035em] tabular-nums\"\n              title={formatNumber(total.value)}\n            >\n              {formatCompact(total.value)}\n            </p>\n            {metric && (\n              <p className={cn(\"mt-4 text-[13px] leading-5\", hero ? \"opacity-85\" : \"text-muted-foreground\")}>\n                {describeAdded(metric, period)}\n              </p>\n            )}\n          </article>\n        );\n      })}\n    </section>\n  );\n}\n\n// ------------------------------------------\n// Overview: metric tiles beside capsule columns\n// ------------------------------------------\nfunction GlassOverviewPanel({\n  metrics,\n  period,\n  className,\n}: {\n  metrics: DashboardMetric[];\n  period: DashboardPeriod;\n  className?: string;\n}) {\n  const baseId = useId();\n  const [activeId, setActiveId] = useState(metrics[0]?.id ?? \"\");\n  const copy = PERIOD_COPY[period];\n  const resolved = useMemo(() => resolveMetrics(metrics, period), [metrics, period]);\n  const active = resolved.find((entry) => entry.metric.id === activeId) ?? resolved[0];\n\n  // The tiles are a column from @3xl and a 2×2 grid below it, so every arrow moves.\n  const onTabKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n    const index = resolved.findIndex((entry) => entry.metric.id === active?.metric.id);\n    const last = resolved.length - 1;\n    const next =\n      event.key === \"ArrowDown\" || event.key === \"ArrowRight\"\n        ? index >= last ? 0 : index + 1\n        : event.key === \"ArrowUp\" || event.key === \"ArrowLeft\"\n          ? index <= 0 ? last : index - 1\n          : event.key === \"Home\"\n            ? 0\n            : event.key === \"End\"\n              ? last\n              : null;\n    if (next === null) return;\n    event.preventDefault();\n    const target = resolved[next];\n    if (!target) return;\n    setActiveId(target.metric.id);\n    document.getElementById(`${baseId}-tab-${target.metric.id}`)?.focus();\n  };\n\n  return (\n    <section aria-labelledby={`${baseId}-title`} className={cn(GLASS_PANE, \"flex flex-col\", className)}>\n      <GlassPaneHeader\n        id={`${baseId}-title`}\n        title=\"Overview\"\n        caption={`Pick a metric to chart it, ${copy.current} first`}\n      />\n      <div className=\"mt-5 grid flex-1 gap-5 @3xl:grid-cols-[minmax(0,14.5rem)_minmax(0,1fr)] @3xl:gap-6\">\n        <div\n          role=\"tablist\"\n          aria-label=\"Metrics\"\n          aria-orientation=\"vertical\"\n          onKeyDown={onTabKeyDown}\n          className=\"grid grid-cols-2 gap-2.5 @3xl:grid-cols-1 @3xl:content-start\"\n        >\n          {resolved.map((entry) => {\n            const selected = entry.metric.id === active?.metric.id;\n            return (\n              <button\n                key={entry.metric.id}\n                id={`${baseId}-tab-${entry.metric.id}`}\n                type=\"button\"\n                role=\"tab\"\n                aria-selected={selected}\n                aria-controls={`${baseId}-panel`}\n                tabIndex={selected ? 0 : -1}\n                onClick={() => setActiveId(entry.metric.id)}\n                className={cn(\n                  \"flex min-w-0 flex-col items-start gap-1.5 rounded-2xl px-4 py-3 text-left transition-[background-color,color,box-shadow] duration-200\",\n                  GLASS_FOCUS,\n                  selected ? \"dg-active\" : \"dg-tile hover:bg-[var(--dash-card)]\",\n                )}\n              >\n                <span\n                  className={cn(\n                    \"flex max-w-full items-center gap-2 text-[13px] leading-5 [&_svg]:size-4 [&_svg]:shrink-0\",\n                    selected ? \"opacity-85\" : \"text-muted-foreground\",\n                  )}\n                >\n                  {entry.metric.icon && <span aria-hidden=\"true\">{entry.metric.icon}</span>}\n                  <span className=\"truncate\">{entry.metric.label}</span>\n                </span>\n                <span className=\"flex max-w-full flex-wrap items-center gap-x-2 gap-y-1\">\n                  <span\n                    className={cn(\n                      \"text-[1.375rem] font-semibold leading-7 tracking-[-0.02em] tabular-nums\",\n                      !selected && \"text-foreground\",\n                    )}\n                  >\n                    {entry.formatShort(entry.current)}\n                  </span>\n                  {entry.change !== null && <DeltaBadge change={entry.change} onFill={selected} />}\n                </span>\n                <span className={cn(\"text-xs leading-4\", selected ? \"opacity-80\" : \"text-muted-foreground\")}>\n                  {capitalize(copy.current)}\n                  {entry.change !== null ? `, vs ${copy.previous}` : \"\"}\n                </span>\n              </button>\n            );\n          })}\n        </div>\n\n        {active && (\n          <div\n            id={`${baseId}-panel`}\n            role=\"tabpanel\"\n            aria-labelledby={`${baseId}-tab-${active.metric.id}`}\n            className=\"min-w-0\"\n          >\n            <figure className=\"m-0 flex h-full flex-col\">\n              <figcaption className=\"flex flex-wrap items-end justify-between gap-x-6 gap-y-2\">\n                <span className=\"min-w-0\">\n                  <span className=\"sr-only\">{active.metric.label}, </span>\n                  <span className=\"block text-[2rem] font-semibold leading-none tracking-[-0.03em] tabular-nums text-foreground\">\n                    {active.format(active.total)}\n                  </span>\n                  <span className=\"mt-2 block text-[13px] text-muted-foreground\">\n                    {active.metric.label} · {copy.window}\n                  </span>\n                </span>\n                {active.points.length > 0 && (\n                  <span className=\"flex items-center gap-2 text-[13px] text-muted-foreground\">\n                    <span aria-hidden=\"true\" className=\"w-4 border-t-2 border-dashed border-current opacity-60\" />\n                    <span>\n                      Average{\" \"}\n                      <span className=\"font-medium tabular-nums text-foreground\">\n                        {active.formatShort(active.average)}\n                      </span>{\" \"}\n                      {copy.average}\n                    </span>\n                  </span>\n                )}\n              </figcaption>\n              <div className=\"mt-auto pt-4\">\n                {active.points.length === 0 ? (\n                  <EmptyState title=\"No data for this period yet\" className={GLASS_CHART_HEIGHT} />\n                ) : (\n                  <GlassCapsuleColumns entry={active} />\n                )}\n              </div>\n              <SeriesTable entry={active} caption={copy.window} />\n            </figure>\n          </div>\n        )}\n      </div>\n    </section>\n  );\n}\n\n/**\n * Capsule columns on hatched full-height tracks. The current period wears the\n * brand fill with its value on a bubble; hover, tap or the arrow keys read any\n * other column. The dashed rule is the average.\n */\nfunction GlassCapsuleColumns({ entry }: { entry: ResolvedMetric }) {\n  const { points } = entry;\n  const [inspected, setInspected] = useState<number | null>(null);\n  const lastIndex = points.length - 1;\n  const ceiling = niceCeiling(Math.max(0, ...points.map((point) => point.value)));\n  const empty = entry.total === 0;\n  const ticks = axisTicks(ceiling, Boolean(entry.metric.currency), empty);\n  const shown = empty ? inspected : (inspected ?? lastIndex);\n  const inspectedPoint = inspected === null ? null : points[inspected];\n\n  useEffect(() => {\n    setInspected(null);\n  }, [entry.points]);\n\n  const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n    const from = inspected ?? lastIndex;\n    const moves: Record<string, number> = {\n      ArrowLeft: Math.max(0, from - 1),\n      ArrowRight: Math.min(lastIndex, from + 1),\n      Home: 0,\n      End: lastIndex,\n    };\n    const move = moves[event.key];\n    if (move !== undefined) {\n      event.preventDefault();\n      setInspected(move);\n    } else if (event.key === \"Escape\") {\n      setInspected(null);\n    }\n  };\n\n  return (\n    <div>\n      <div\n        role=\"group\"\n        aria-roledescription=\"chart\"\n        aria-label={`${entry.metric.label}. Use the arrow keys to read each value.`}\n        tabIndex={0}\n        onKeyDown={onKeyDown}\n        onBlur={() => setInspected(null)}\n        onPointerLeave={(event) => {\n          if (event.pointerType !== \"touch\") setInspected(null);\n        }}\n        className={cn(\"relative rounded-2xl\", GLASS_FOCUS, \"focus-visible:ring-offset-4\")}\n      >\n        <div className={cn(GLASS_CHART_HEIGHT, \"pt-10\")}>\n          <div className=\"relative h-full\">\n            <div aria-hidden=\"true\" className=\"absolute inset-y-0 left-0 w-9\">\n              {ticks.map((tick) => (\n                <span\n                  key={tick}\n                  className=\"absolute right-0 translate-y-1/2 text-[11px] leading-none tabular-nums text-muted-foreground\"\n                  style={{ bottom: `${(tick / ceiling) * 100}%` }}\n                >\n                  {entry.formatShort(tick)}\n                </span>\n              ))}\n            </div>\n            <div className=\"absolute inset-y-0 left-12 right-0\">\n              {entry.average > 0 && !empty && (\n                <div\n                  aria-hidden=\"true\"\n                  className=\"absolute inset-x-0 z-10 border-t-2 border-dashed border-muted-foreground/35\"\n                  style={{ bottom: `${(entry.average / ceiling) * 100}%` }}\n                />\n              )}\n              <div className=\"absolute inset-0 flex items-end gap-1.5 @xl:gap-2.5 @3xl:gap-3\">\n                {points.map((point, index) => {\n                  const percent = (point.value / ceiling) * 100;\n                  const height = `max(${percent}%, 0.75rem)`;\n                  const isCurrent = index === lastIndex;\n                  const isInspected = index === inspected;\n                  return (\n                    <div\n                      key={`${point.label}-${index}`}\n                      className=\"relative flex h-full min-w-0 flex-1 justify-center\"\n                      onPointerEnter={() => setInspected(index)}\n                      onPointerDown={() => setInspected(index)}\n                    >\n                      <div className=\"dg-hatch relative h-full w-full max-w-11 rounded-full bg-[color-mix(in_oklab,var(--dash-track)_70%,transparent)]\">\n                        {point.value > 0 && (\n                          <div\n                            className={cn(\n                              \"absolute inset-x-0 bottom-0 rounded-full transition-[height,background-color] duration-300 ease-out motion-reduce:transition-none\",\n                              isCurrent\n                                ? \"bg-[var(--dash-accent)] bg-[linear-gradient(180deg,rgb(255_255_255/0.28),transparent_45%)]\"\n                                : isInspected\n                                  ? \"bg-[color-mix(in_oklab,var(--dash-accent)_62%,var(--dash-surface))]\"\n                                  : \"bg-[var(--dash-mark-muted)]\",\n                            )}\n                            style={{ height }}\n                          />\n                        )}\n                        {index === shown && (\n                          <div\n                            className={cn(\n                              \"pointer-events-none absolute z-20 whitespace-nowrap\",\n                              index === 0 ? \"left-0\" : index === lastIndex ? \"right-0\" : \"left-1/2 -translate-x-1/2\",\n                            )}\n                            style={{ bottom: `calc(${height} + 0.375rem)` }}\n                          >\n                            {isInspected ? (\n                              <div className=\"dg-tile rounded-xl px-3 py-1.5\">\n                                <p className=\"text-sm font-semibold leading-5 text-foreground\">{entry.format(point.value)}</p>\n                                <p className=\"text-[11px] leading-4 text-muted-foreground\">{point.fullLabel ?? point.label}</p>\n                              </div>\n                            ) : (\n                              <span className=\"dg-active block rounded-full px-2.5 py-1 text-xs font-semibold leading-none\">\n                                {entry.formatShort(point.value)}\n                              </span>\n                            )}\n                          </div>\n                        )}\n                      </div>\n                    </div>\n                  );\n                })}\n              </div>\n            </div>\n          </div>\n        </div>\n        <div aria-hidden=\"true\" className=\"ml-12 mt-3 flex gap-1.5 @xl:gap-2.5 @3xl:gap-3\">\n          {points.map((point, index) => (\n            <span\n              key={`${point.label}-${index}`}\n              className={cn(\n                \"min-w-0 flex-1 truncate text-center text-xs\",\n                index === lastIndex ? \"font-semibold text-foreground\" : \"text-muted-foreground\",\n              )}\n            >\n              <span className=\"@xl:hidden\">{point.shortLabel ?? point.label}</span>\n              <span className=\"hidden @xl:inline\">{point.label}</span>\n            </span>\n          ))}\n        </div>\n        {empty && (\n          <div className=\"pointer-events-none absolute inset-0 grid place-items-center\">\n            <p className=\"dg-tile rounded-full px-4 py-2 text-[13px] text-muted-foreground\">\n              Nothing recorded in this window yet.\n            </p>\n          </div>\n        )}\n      </div>\n      <p className=\"sr-only\" aria-live=\"polite\">\n        {inspectedPoint ? `${inspectedPoint.fullLabel ?? inspectedPoint.label}: ${entry.format(inspectedPoint.value)}` : \"\"}\n      </p>\n    </div>\n  );\n}\n\n// ------------------------------------------\n// Traffic: a segmented half ring\n// ------------------------------------------\nconst RING_CX = 120;\nconst RING_CY = 118;\nconst RING_R = 94;\nconst RING_STROKE = 26;\nconst RING_GAP_DEG = 2.5;\nconst RING_MIN_DEG = 2;\n\nconst ringPoint = (deg: number) => {\n  const rad = (deg * Math.PI) / 180;\n  return [RING_CX + RING_R * Math.cos(rad), RING_CY - RING_R * Math.sin(rad)] as const;\n};\n\n// An arc from `from` to `to` degrees (180 = left, 0 = right), over the top.\nconst arcPath = (from: number, to: number) => {\n  const [x1, y1] = ringPoint(from);\n  const [x2, y2] = ringPoint(to);\n  return `M ${x1.toFixed(2)} ${y1.toFixed(2)} A ${RING_R} ${RING_R} 0 0 1 ${x2.toFixed(2)} ${y2.toFixed(2)}`;\n};\n\nfunction layoutSegments(slices: Slice[]) {\n  const total = sum(slices.map((slice) => slice.value));\n  if (!total) return [];\n  const gaps = RING_GAP_DEG * Math.max(0, slices.length - 1);\n  const spans = slices.map((slice) => Math.max(RING_MIN_DEG, (slice.value / total) * (180 - gaps)));\n  // Minimums can push past the half ring; scale back to fit.\n  const scale = (180 - gaps) / sum(spans);\n  let cursor = 180;\n  return slices.map((slice, index) => {\n    const span = (spans[index] ?? 0) * scale;\n    const segment = { slice, from: cursor, to: cursor - span };\n    cursor -= span + RING_GAP_DEG;\n    return segment;\n  });\n}\n\nconst isOther = (slice: Slice) => slice.key === \"__other\";\n\nfunction HalfRing({\n  slices,\n  focus,\n  onFocus,\n  label,\n  children,\n}: {\n  slices: Slice[];\n  focus: string | null;\n  onFocus: (key: string | null) => void;\n  label: string;\n  children: React.ReactNode;\n}) {\n  const hatchId = svgId(\"dg-ring-hatch\", useId());\n  const segments = layoutSegments(slices);\n\n  return (\n    <div className=\"relative mx-auto w-full max-w-[17rem]\">\n      <svg viewBox=\"0 0 240 132\" role=\"img\" aria-label={label} className=\"block h-auto w-full overflow-visible\">\n        <defs>\n          <HatchPattern id={hatchId} color=\"color-mix(in oklab, var(--foreground) 30%, transparent)\" gap={5} />\n        </defs>\n        {segments.length === 0 ? (\n          <>\n            <path d={arcPath(180, 0)} fill=\"none\" strokeWidth={RING_STROKE} style={{ stroke: \"var(--dash-track)\" }} />\n            <path d={arcPath(180, 0)} fill=\"none\" strokeWidth={RING_STROKE} stroke={`url(#${hatchId})`} />\n          </>\n        ) : (\n          segments.map(({ slice, from, to }) => (\n            <g\n              key={slice.key}\n              style={{ opacity: focus !== null && focus !== slice.key ? 0.3 : 1, transition: \"opacity 150ms ease-out\" }}\n              onPointerEnter={() => onFocus(slice.key)}\n              onPointerLeave={() => onFocus(null)}\n            >\n              <path d={arcPath(from, to)} fill=\"none\" strokeWidth={RING_STROKE} style={{ stroke: slice.color }} />\n              {isOther(slice) && (\n                <path d={arcPath(from, to)} fill=\"none\" strokeWidth={RING_STROKE} stroke={`url(#${hatchId})`} />\n              )}\n            </g>\n          ))\n        )}\n      </svg>\n      <div className=\"pointer-events-none absolute inset-x-0 bottom-0 flex flex-col items-center\">{children}</div>\n    </div>\n  );\n}\n\nfunction GlassTrafficPanel({\n  shares,\n  signUps,\n  period,\n  href,\n  linkComponent,\n  className,\n}: {\n  shares?: DashboardShare[];\n  signUps?: number;\n  period: DashboardPeriod;\n  href?: string;\n  linkComponent: React.ElementType;\n  className?: string;\n}) {\n  const id = useId();\n  const [focus, setFocus] = useState<string | null>(null);\n  const copy = PERIOD_COPY[period];\n  const { slices, total } = useMemo(() => compose(shares ?? []), [shares]);\n  const ringLabel = total\n    ? `Visits by source ${copy.trafficPhrase}: ${slices.map((slice) => `${slice.name} ${formatShare(slice.share)}`).join(\", \")}`\n    : `No visits ${copy.trafficPhrase}`;\n\n  return (\n    <section aria-labelledby={`${id}-title`} className={cn(GLASS_PANE, \"flex flex-col\", className)}>\n      <GlassPaneHeader\n        id={`${id}-title`}\n        title=\"Traffic sources\"\n        caption={copy.traffic}\n        actions={href && <GlassRoundLink href={href} label=\"Open visitor analytics\" linkComponent={linkComponent} />}\n      />\n      {!shares ? (\n        <EmptyState icon={<CloudOff />} title=\"Traffic data is unavailable\" className=\"flex-1 py-12\">\n          Connect visitor tracking to see where people come from.\n        </EmptyState>\n      ) : (\n        <div className=\"mt-6 flex flex-1 flex-col\">\n          <HalfRing slices={slices} focus={focus} onFocus={setFocus} label={ringLabel}>\n            <span className=\"text-[2.125rem] font-semibold leading-none tracking-[-0.03em] text-foreground\">\n              {formatCompact(total)}\n            </span>\n            <span className=\"mt-1.5 text-xs text-muted-foreground\">\n              {plural(total, [\"visit\", \"visits\"])} {copy.trafficPhrase}\n            </span>\n          </HalfRing>\n          {total ? (\n            <>\n              <BreakdownList slices={slices} focus={focus} onFocus={setFocus} className=\"mt-5\" />\n              {signUps !== undefined && <GlassConversion signUps={signUps} visits={total} />}\n            </>\n          ) : (\n            <p className=\"mt-5 text-center text-[13px] text-muted-foreground\">\n              No visits recorded {copy.trafficPhrase} yet.\n            </p>\n          )}\n        </div>\n      )}\n    </section>\n  );\n}\n\nfunction GlassConversion({ signUps, visits }: { signUps: number; visits: number }) {\n  // Sign-ups can outnumber tracked visits (blocked trackers, invited users);\n  // a rate above 100% would mislead, so that case is said plainly.\n  const rate = signUps / visits;\n  const valid = rate <= 1;\n  return (\n    <div className=\"mt-auto pt-5\">\n      <div className=\"dg-card rounded-2xl p-4\">\n        <div className=\"flex items-center justify-between gap-3\">\n          <div className=\"flex items-center gap-1 text-[13px] text-muted-foreground\">\n            Visit to sign-up rate\n            <RateExplainer />\n          </div>\n          <p className=\"text-[15px] font-semibold tabular-nums text-foreground\">{valid ? formatRate(rate) : \"—\"}</p>\n        </div>\n        <div aria-hidden=\"true\" className=\"dg-hatch mt-2.5 h-5 overflow-hidden rounded-full bg-[var(--dash-track)]\">\n          <div\n            className=\"h-full rounded-full bg-[var(--dash-accent)]\"\n            style={{ width: valid && rate > 0 ? `max(${rate * 100}%, 1.25rem)` : 0 }}\n          />\n        </div>\n        <p className=\"mt-2 text-xs text-muted-foreground\">\n          {valid\n            ? `${formatNumber(signUps)} ${plural(signUps, [\"sign-up\", \"sign-ups\"])} from ${formatNumber(visits)} ${plural(visits, [\"visit\", \"visits\"])}`\n            : `${formatNumber(signUps)} sign-ups outnumber the ${formatNumber(visits)} tracked visits.`}\n        </p>\n      </div>\n    </div>\n  );\n}\n\n// ------------------------------------------\n// Countries: a chunky segmented bar\n// ------------------------------------------\nfunction GlassCountriesPanel({\n  shares,\n  period,\n  href,\n  linkComponent,\n  className,\n}: {\n  shares?: DashboardShare[];\n  period: DashboardPeriod;\n  href?: string;\n  linkComponent: React.ElementType;\n  className?: string;\n}) {\n  const id = useId();\n  const [focus, setFocus] = useState<string | null>(null);\n  const copy = PERIOD_COPY[period];\n  const { slices, total } = useMemo(() => compose(shares ?? []), [shares]);\n  const countryCount = (shares ?? []).filter((share) => share.value > 0).length;\n  const leader = slices[0];\n\n  return (\n    <section aria-labelledby={`${id}-title`} className={cn(GLASS_PANE, \"flex flex-col\", className)}>\n      <GlassPaneHeader\n        id={`${id}-title`}\n        title=\"Visitors by country\"\n        caption={copy.traffic}\n        actions={href && <GlassRoundLink href={href} label=\"Open visitor analytics\" linkComponent={linkComponent} />}\n      />\n      {!shares ? (\n        <EmptyState icon={<CloudOff />} title=\"Country data is unavailable\" className=\"flex-1 py-12\">\n          Connect visitor tracking to see where people are.\n        </EmptyState>\n      ) : !total || !leader ? (\n        <EmptyState icon={<Globe />} title={`No visits recorded ${copy.trafficPhrase} yet`} className=\"flex-1 py-12\">\n          Countries appear here once visitors arrive.\n        </EmptyState>\n      ) : (\n        <>\n          <div className=\"mt-5 flex flex-wrap items-end justify-between gap-x-4 gap-y-1\">\n            <p className=\"flex items-baseline gap-2\">\n              <span className=\"text-[2rem] font-semibold leading-none tracking-[-0.03em] text-foreground\">\n                {formatNumber(countryCount)}\n              </span>\n              <span className=\"text-sm text-muted-foreground\">{plural(countryCount, [\"country\", \"countries\"])}</span>\n            </p>\n            <p className=\"text-[13px] text-muted-foreground\">\n              {formatNumber(total)} {plural(total, [\"visit\", \"visits\"])}\n            </p>\n          </div>\n          <p className=\"mt-2 text-[13px] leading-5 text-muted-foreground\">\n            {leader.name} accounts for {formatShare(leader.share)} of visits.\n          </p>\n          {/* The legend rows below carry every value, so the bar is decoration. */}\n          <div aria-hidden=\"true\" className=\"mt-5 flex h-9 gap-1\" onPointerLeave={() => setFocus(null)}>\n            {slices.map((slice) => (\n              <span\n                key={slice.key}\n                onPointerEnter={() => setFocus(slice.key)}\n                className={cn(\n                  \"min-w-2 rounded-[10px] transition-opacity duration-150\",\n                  isOther(slice) && \"dg-hatch\",\n                  focus !== null && focus !== slice.key && \"opacity-30\",\n                )}\n                style={{ flexGrow: slice.value / total, flexBasis: 0, backgroundColor: slice.color }}\n              />\n            ))}\n          </div>\n          <BreakdownList slices={slices} focus={focus} onFocus={setFocus} showCodes className=\"mt-4\" />\n        </>\n      )}\n    </section>\n  );\n}\n\n// ------------------------------------------\n// Payments: a quiet table\n// ------------------------------------------\nconst PAYMENT_HEAD = \"pb-3 text-left text-[11px] font-medium uppercase tracking-[0.06em] text-muted-foreground\";\nconst PAYMENT_CELL = \"border-t border-[var(--dash-hairline)] py-3\";\n\nconst STATUS_TONES: Record<string, \"good\" | \"warn\" | \"bad\"> = {\n  completed: \"good\",\n  complete: \"good\",\n  paid: \"good\",\n  succeeded: \"good\",\n  success: \"good\",\n  pending: \"warn\",\n  processing: \"warn\",\n  failed: \"bad\",\n  declined: \"bad\",\n  canceled: \"bad\",\n  cancelled: \"bad\",\n};\n\nconst TONE_CLASSES = {\n  good: \"bg-[color-mix(in_oklab,var(--dash-up)_10%,transparent)] text-[var(--dash-up)]\",\n  warn: \"bg-[color-mix(in_oklab,var(--dash-warn)_12%,transparent)] text-[var(--dash-warn)]\",\n  bad: \"bg-[color-mix(in_oklab,var(--dash-down)_10%,transparent)] text-[var(--dash-down)]\",\n  neutral: \"bg-[var(--dash-well)] text-muted-foreground\",\n};\n\nconst isSettled = (status?: string) => !status || STATUS_TONES[status.toLowerCase()] === \"good\";\n\n/** A status pill: a dot and the label on a tint of its tone, never colour alone. */\nfunction GlassStatus({ status, className }: { status: string; className?: string }) {\n  const tone = STATUS_TONES[status.toLowerCase()] ?? \"neutral\";\n  return (\n    <span\n      className={cn(\n        \"inline-flex h-6 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full px-2.5 text-xs font-medium capitalize leading-none\",\n        TONE_CLASSES[tone],\n        className,\n      )}\n    >\n      <span aria-hidden=\"true\" className=\"size-1.5 shrink-0 rounded-full bg-current\" />\n      {status}\n    </span>\n  );\n}\n\nconst paymentDate = new Intl.DateTimeFormat(\"en-US\", { month: \"short\", day: \"numeric\" });\nconst paymentTime = new Intl.DateTimeFormat(\"en-US\", { hour: \"numeric\", minute: \"2-digit\" });\n\nfunction GlassPaymentsPanel({\n  payments,\n  now,\n  href,\n  linkComponent,\n  className,\n}: {\n  payments: DashboardPayment[];\n  now: Date | null;\n  href?: string;\n  linkComponent: React.ElementType;\n  className?: string;\n}) {\n  const id = useId();\n  const rows = useMemo(\n    () =>\n      payments\n        .map((payment) => ({ ...payment, at: toDate(payment.date) }))\n        .sort((a, b) => b.at.getTime() - a.at.getTime())\n        .slice(0, 6),\n    [payments],\n  );\n\n  return (\n    <section aria-labelledby={`${id}-title`} className={cn(GLASS_PANE, \"flex flex-col\", className)}>\n      <GlassPaneHeader\n        id={`${id}-title`}\n        title=\"Recent payments\"\n        caption=\"The latest across every method\"\n        actions={href && <GlassRoundLink href={href} label=\"Open all payments\" linkComponent={linkComponent} />}\n      />\n      {rows.length ? (\n        <div className=\"-mx-2 mt-5 overflow-x-auto px-2\">\n          <table className=\"w-full min-w-[34rem] border-separate border-spacing-0 text-sm\">\n            <caption className=\"sr-only\">Recent payments</caption>\n            <thead>\n              <tr>\n                <th scope=\"col\" className={PAYMENT_HEAD}>\n                  Payment\n                </th>\n                <th scope=\"col\" className={PAYMENT_HEAD}>\n                  Status\n                </th>\n                <th scope=\"col\" className={cn(PAYMENT_HEAD, \"text-right\")}>\n                  Amount\n                </th>\n                <th scope=\"col\" className={cn(PAYMENT_HEAD, \"text-right\")}>\n                  Date\n                </th>\n              </tr>\n            </thead>\n            <tbody>\n              {rows.map((row) => (\n                <tr key={row.id}>\n                  <td className={cn(PAYMENT_CELL, \"pr-4\")}>\n                    <div className=\"flex items-center gap-3\">\n                      {row.person ? (\n                        <GlassAvatar name={row.person} src={row.avatar} />\n                      ) : (\n                        <span\n                          aria-hidden=\"true\"\n                          className=\"dg-tile grid size-9 shrink-0 place-items-center rounded-full text-[var(--dash-ink)]\"\n                        >\n                          <Wallet className=\"size-4\" />\n                        </span>\n                      )}\n                      <div className=\"min-w-0\">\n                        <p className=\"truncate font-medium text-foreground\">{row.person ?? row.method}</p>\n                        <p className=\"truncate text-xs text-muted-foreground\">{row.person ? row.method : \"Payment\"}</p>\n                      </div>\n                    </div>\n                  </td>\n                  <td className={cn(PAYMENT_CELL, \"pr-4\")}>\n                    <GlassStatus status={row.status ?? \"completed\"} />\n                  </td>\n                  <td className={cn(PAYMENT_CELL, \"pr-4 text-right font-semibold tabular-nums text-foreground\")}>\n                    {formatMoney(row.amount, row.currency)}\n                  </td>\n                  <td className={cn(PAYMENT_CELL, \"text-right text-[13px] tabular-nums text-muted-foreground\")}>\n                    {/* Dates print in the visitor's time zone, so only after mount. */}\n                    {now && (\n                      <time dateTime={row.at.toISOString()}>\n                        {paymentDate.format(row.at)}\n                        <span className=\"block text-[11px]\">{paymentTime.format(row.at)}</span>\n                      </time>\n                    )}\n                  </td>\n                </tr>\n              ))}\n            </tbody>\n          </table>\n        </div>\n      ) : (\n        <EmptyState icon={<Wallet />} title=\"No payments yet\" className=\"flex-1 py-12\">\n          Payments show up here as customers check out.\n        </EmptyState>\n      )}\n    </section>\n  );\n}\n\n// ------------------------------------------\n// Activity: the notched pane\n// ------------------------------------------\nconst NOTCH_RADIUS = 28;\nconst NOTCH_SHOULDER = 18;\nconst NOTCH_INNER = 22;\nconst NOTCH_TRAY_PAD = 12;\n\nfunction notchPath(width: number, height: number, x1: number, x2: number, depth: number) {\n  const r = NOTCH_RADIUS;\n  const s = NOTCH_SHOULDER;\n  const rn = NOTCH_INNER;\n  return [\n    `M 0 ${r}`,\n    `A ${r} ${r} 0 0 1 ${r} 0`,\n    `L ${x1 - s} 0`,\n    `A ${s} ${s} 0 0 1 ${x1} ${s}`,\n    `L ${x1} ${depth - rn}`,\n    `A ${rn} ${rn} 0 0 0 ${x1 + rn} ${depth}`,\n    `L ${x2 - rn} ${depth}`,\n    `A ${rn} ${rn} 0 0 0 ${x2} ${depth - rn}`,\n    `L ${x2} ${s}`,\n    `A ${s} ${s} 0 0 1 ${x2 + s} 0`,\n    `L ${width - r} 0`,\n    `A ${r} ${r} 0 0 1 ${width} ${r}`,\n    `L ${width} ${height - r}`,\n    `A ${r} ${r} 0 0 1 ${width - r} ${height}`,\n    `L ${r} ${height}`,\n    `A ${r} ${r} 0 0 1 0 ${height - r}`,\n    \"Z\",\n  ].join(\" \");\n}\n\ninterface NotchShape {\n  width: number;\n  height: number;\n  x1: number;\n  x2: number;\n  depth: number;\n}\n\n/**\n * A pane with a tray cut into the middle of its top edge: the tray holds a\n * row of people, and the pane's shoulders curve down around it so the canvas\n * shows through. The shape is an SVG drawn from the measured size, so its\n * fill, light edge and hairline match the other panes. Below `minWidth`, or\n * without a tray, it is a plain pane with the tray as a normal row.\n */\nfunction GlassNotchPane({\n  title,\n  tray,\n  actions,\n  minWidth = 860,\n  className,\n  children,\n  ...props\n}: {\n  title: React.ReactNode;\n  tray?: React.ReactNode;\n  actions?: React.ReactNode;\n  minWidth?: number;\n  className?: string;\n  children: React.ReactNode;\n} & Omit<React.HTMLAttributes<HTMLElement>, \"title\">) {\n  const paneRef = useRef<HTMLElement>(null);\n  const trayRef = useRef<HTMLDivElement>(null);\n  const gradientId = useId();\n  // Two phases: the pane's width decides whether the header takes the notched\n  // layout; the notch is then measured from where the tray sits in that layout\n  // (its size doesn't change when it moves, so a ResizeObserver alone wouldn't notice).\n  const [wide, setWide] = useState(false);\n  const [shape, setShape] = useState<NotchShape | null>(null);\n  const measureRef = useRef<() => void>(() => {});\n  const hasTray = Boolean(tray);\n\n  measureRef.current = () => {\n    const pane = paneRef.current;\n    const trayElement = trayRef.current;\n    if (!pane || !trayElement) {\n      setWide(false);\n      setShape(null);\n      return;\n    }\n    const box = pane.getBoundingClientRect();\n    const nextWide = box.width >= minWidth;\n    setWide(nextWide);\n    if (!nextWide || !wide) {\n      // Not wide, or the layout hasn't switched yet: the effect on `wide` measures again.\n      if (!nextWide) setShape(null);\n      return;\n    }\n    const trayBox = trayElement.getBoundingClientRect();\n    const x1 = Math.round(trayBox.left - box.left - NOTCH_TRAY_PAD);\n    const x2 = Math.round(trayBox.right - box.left + NOTCH_TRAY_PAD);\n    const depth = Math.round(trayBox.bottom - box.top + NOTCH_TRAY_PAD);\n    // The shoulders need room on both sides; otherwise draw a plain pane.\n    if (x1 - NOTCH_SHOULDER < NOTCH_RADIUS + 8 || x2 + NOTCH_SHOULDER > box.width - NOTCH_RADIUS - 8) {\n      setShape(null);\n      return;\n    }\n    const next = { width: Math.round(box.width), height: Math.round(box.height), x1, x2, depth };\n    setShape((previous) =>\n      previous &&\n      previous.width === next.width &&\n      previous.height === next.height &&\n      previous.x1 === next.x1 &&\n      previous.x2 === next.x2 &&\n      previous.depth === next.depth\n        ? previous\n        : next,\n    );\n  };\n\n  useIsomorphicLayoutEffect(() => {\n    const pane = paneRef.current;\n    const trayElement = trayRef.current;\n    if (!pane || !trayElement) return undefined;\n    const observer = new ResizeObserver(() => measureRef.current());\n    observer.observe(pane);\n    observer.observe(trayElement);\n    return () => observer.disconnect();\n  }, [hasTray]);\n\n  useIsomorphicLayoutEffect(() => {\n    measureRef.current();\n  }, [wide, minWidth, hasTray]);\n\n  const notched = shape !== null;\n  // Drawn half a pixel in, so the 1px strokes land on whole pixels.\n  const path = shape ? notchPath(shape.width - 1, shape.height - 1, shape.x1, shape.x2, shape.depth) : null;\n  const edgeId = svgId(\"dg-notch-edge\", gradientId);\n  const sheenId = svgId(\"dg-notch-sheen\", gradientId);\n\n  return (\n    <section\n      ref={paneRef}\n      className={cn(\"relative isolate rounded-[28px] p-5 sm:p-6\", !notched && \"dg-pane\", className)}\n      {...props}\n    >\n      {shape && path && (\n        <svg\n          aria-hidden=\"true\"\n          className=\"dg-notch-bg pointer-events-none absolute inset-0 -z-10 overflow-visible\"\n          width={shape.width}\n          height={shape.height}\n        >\n          <defs>\n            <linearGradient id={edgeId} x1=\"0\" y1=\"0\" x2=\"0\" y2={shape.depth + 24} gradientUnits=\"userSpaceOnUse\">\n              <stop offset=\"0\" style={{ stopColor: \"var(--dash-edge)\" }} />\n              <stop offset=\"1\" style={{ stopColor: \"var(--dash-edge)\", stopOpacity: 0 }} />\n            </linearGradient>\n            {/* The same 150° sheen as the other panes. */}\n            <linearGradient id={sheenId} x1=\"0\" y1=\"0\" x2=\"0.5\" y2=\"0.87\">\n              <stop offset=\"0\" style={{ stopColor: \"var(--dash-sheen-1)\" }} />\n              <stop offset=\"0.42\" style={{ stopColor: \"var(--dash-sheen-2)\" }} />\n              <stop offset=\"1\" style={{ stopColor: \"var(--dash-sheen-3)\" }} />\n            </linearGradient>\n          </defs>\n          <g transform=\"translate(0.5 0.5)\">\n            <path d={path} strokeWidth=\"1\" style={{ fill: \"var(--dash-pane)\", stroke: \"var(--dash-border)\" }} />\n            <path d={path} strokeWidth=\"1\" fill={`url(#${sheenId})`} style={{ stroke: \"var(--dash-rim)\" }} />\n            <path d={path} strokeWidth=\"1\" fill=\"none\" stroke={`url(#${edgeId})`} />\n          </g>\n        </svg>\n      )}\n\n      <div\n        className={cn(\n          \"flex flex-wrap items-start gap-x-4 gap-y-4\",\n          wide && hasTray && \"grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)]\",\n        )}\n      >\n        <div className=\"min-w-0 flex-1\">{title}</div>\n        {hasTray && (\n          // In the notch the tray rides up to the pane's top edge.\n          <div\n            ref={trayRef}\n            className={cn(\n              \"order-last -mx-1 w-full min-w-0 overflow-x-auto px-1 pb-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\",\n              wide && \"order-none mx-0 -mt-3 w-auto overflow-visible px-0 pb-0 sm:-mt-4\",\n            )}\n          >\n            {tray}\n          </div>\n        )}\n        {actions ? <div className=\"flex shrink-0 items-center justify-end gap-2\">{actions}</div> : <div />}\n      </div>\n      {children}\n    </section>\n  );\n}\n\n/** Top authors as a row of avatars with their post counts, the leader's badge in the brand colour. */\nfunction GlassAuthorTray({ authors, period }: { authors: DashboardAuthor[]; period: DashboardPeriod }) {\n  const copy = PERIOD_COPY[period];\n  const ranked = [...authors]\n    .filter((author) => author.posts > 0)\n    .sort((a, b) => b.posts - a.posts)\n    .slice(0, 8);\n\n  if (!ranked.length) {\n    return (\n      <p className=\"dg-tile flex h-11 items-center whitespace-nowrap rounded-full px-4 text-[13px] text-muted-foreground\">\n        No posts published {copy.authorsPhrase}\n      </p>\n    );\n  }\n\n  return (\n    <div>\n      <h3 className=\"sr-only\">Top authors, {copy.authors}</h3>\n      <ol className=\"flex items-start gap-2.5\">\n        {ranked.map((author, index) => {\n          const label = `${author.name}, ${formatNumber(author.posts)} ${plural(author.posts, [\"post\", \"posts\"])} ${copy.authorsPhrase}`;\n          return (\n            <li key={`${author.name}-${index}`} className=\"flex flex-col items-center\">\n              <span tabIndex={0} role=\"img\" aria-label={label} title={label} className={cn(\"rounded-full p-1\", GLASS_FOCUS)}>\n                <span className=\"dg-tile block rounded-full p-[3px]\">\n                  <GlassAvatar name={author.name} src={author.avatar} size=\"lg\" />\n                </span>\n              </span>\n              <span\n                aria-hidden=\"true\"\n                className={cn(\n                  \"-mt-2.5 grid h-5 min-w-5 place-items-center rounded-full px-1.5 text-[11px] font-semibold tabular-nums ring-2 ring-[var(--dash-canvas)]\",\n                  index === 0\n                    ? \"bg-[var(--primary)] text-[var(--primary-foreground)]\"\n                    : \"bg-[var(--dash-card-strong)] text-foreground\",\n                )}\n              >\n                {author.posts}\n              </span>\n            </li>\n          );\n        })}\n      </ol>\n    </div>\n  );\n}\n\n// The steps a customer takes, in order: a post brings them in, they sign up,\n// they pay, they write in.\nconst STAGES: { type: DashboardActivityType; label: string; noun: [string, string]; empty: string }[] = [\n  { type: \"post\", label: \"Published\", noun: [\"post\", \"posts\"], empty: \"No posts yet\" },\n  { type: \"user\", label: \"Signed up\", noun: [\"sign-up\", \"sign-ups\"], empty: \"No sign-ups yet\" },\n  { type: \"payment\", label: \"Paid\", noun: [\"payment\", \"payments\"], empty: \"No payments yet\" },\n  { type: \"message\", label: \"Wrote in\", noun: [\"message\", \"messages\"], empty: \"No messages yet\" },\n];\n\nconst ROWS_PER_STAGE = 3;\n\ntype TimedActivity = DashboardActivity & { at: Date };\n\n/** \"4m ago\" today; \"Yesterday, 3:12 PM\" or \"Mon, Oct 5, 3:12 PM\" before that. */\nfunction stageTime(date: Date, now: Date) {\n  const time = activityTime(date, now);\n  return dayKey(date) === dayKey(now) ? time : `${dayLabel(date, now)}, ${time}`;\n}\n\nfunction GlassStageRow({\n  item,\n  now,\n  anchor,\n  linkComponent: Link,\n}: {\n  item: TimedActivity;\n  now: Date | null;\n  anchor: string;\n  linkComponent: React.ElementType;\n}) {\n  const type = ACTIVITY_TYPES[item.type];\n  const Icon = type.icon;\n  return (\n    <li data-anchor={anchor || undefined} className=\"flex items-start gap-3 px-3 py-3\">\n      {item.person || item.avatar ? (\n        <GlassAvatar name={item.person ?? item.title} src={item.avatar} size=\"lg\" />\n      ) : (\n        <span\n          aria-hidden=\"true\"\n          className=\"dg-tile grid size-11 shrink-0 place-items-center rounded-full text-[var(--dash-ink)]\"\n        >\n          <Icon className=\"size-[18px]\" />\n        </span>\n      )}\n      <div className=\"min-w-0 flex-1\">\n        <div className=\"flex items-start justify-between gap-2\">\n          <p className=\"line-clamp-2 text-[13px] font-medium leading-[1.35] text-foreground\">\n            <span className=\"sr-only\">{type.label}: </span>\n            {item.href ? (\n              <Link href={item.href} className={cn(\"rounded-sm hover:underline\", FOCUS)}>\n                {item.title}\n              </Link>\n            ) : (\n              item.title\n            )}\n          </p>\n          {item.type === \"payment\" &&\n            (isSettled(item.status) ? (\n              <CheckCheck className=\"mt-px size-4 shrink-0 text-[var(--dash-up)]\" role=\"img\" aria-label=\"Completed\" />\n            ) : (\n              <GlassStatus status={item.status ?? \"\"} className=\"h-5 px-2 text-[11px]\" />\n            ))}\n        </div>\n        {item.subtitle && <p className=\"mt-0.5 truncate text-xs text-muted-foreground\">{item.subtitle}</p>}\n        {now && (\n          <time dateTime={item.at.toISOString()} className=\"mt-0.5 block text-[11px] tabular-nums text-muted-foreground\">\n            {stageTime(item.at, now)}\n          </time>\n        )}\n      </div>\n    </li>\n  );\n}\n\ninterface ConnectorPath {\n  key: number;\n  d: string;\n  x1: number;\n  y1: number;\n  x2: number;\n  y2: number;\n  empty: boolean;\n}\n\n/**\n * Curved connectors from the last row of each stage to the first row of the\n * next, drawn only while the stages sit in one row. Solid in the accent when\n * the next stage has items, dashed when it is empty.\n */\nfunction useConnectors(gridRef: React.RefObject<HTMLDivElement | null>, deps: unknown[]) {\n  const [paths, setPaths] = useState<ConnectorPath[]>([]);\n\n  useIsomorphicLayoutEffect(() => {\n    const grid = gridRef.current;\n    if (!grid) return undefined;\n\n    const measure = () => {\n      const box = grid.getBoundingClientRect();\n      const stages = [...grid.querySelectorAll<HTMLElement>(\"[data-stage]\")];\n      const first = stages[0];\n      const inOneRow =\n        first !== undefined && stages.length > 1 && stages.every((stage) => Math.abs(stage.offsetTop - first.offsetTop) < 2);\n      if (!inOneRow) {\n        setPaths([]);\n        return;\n      }\n      const next: ConnectorPath[] = [];\n      for (let index = 0; index < stages.length - 1; index += 1) {\n        const fromStage = stages[index];\n        const toStage = stages[index + 1];\n        const from = fromStage?.querySelector('[data-anchor~=\"last\"]');\n        const to = toStage?.querySelector('[data-anchor~=\"first\"]');\n        const fromCard = fromStage?.firstElementChild;\n        const toCard = toStage?.firstElementChild;\n        if (!toStage || !from || !to || !fromCard || !toCard) continue;\n        const a = from.getBoundingClientRect();\n        const b = to.getBoundingClientRect();\n        const x1 = fromCard.getBoundingClientRect().right - box.left;\n        const x2 = toCard.getBoundingClientRect().left - box.left;\n        const y1 = a.top + a.height / 2 - box.top;\n        const y2 = b.top + b.height / 2 - box.top;\n        const mid = (x1 + x2) / 2;\n        next.push({\n          key: index,\n          d: `M ${x1} ${y1} C ${mid} ${y1}, ${mid} ${y2}, ${x2} ${y2}`,\n          x1,\n          y1,\n          x2,\n          y2,\n          empty: toStage.hasAttribute(\"data-empty\"),\n        });\n      }\n      setPaths(next);\n    };\n\n    measure();\n    const observer = new ResizeObserver(measure);\n    observer.observe(grid);\n    return () => observer.disconnect();\n  }, deps);\n\n  return paths;\n}\n\n/**\n * Recent activity as a journey: the top authors sit in the notch with their\n * post counts; below, one card per step a customer takes, the newest few in\n * each, joined by connectors; beside them, the shortcut tiles.\n */\nfunction GlassActivityPanel({\n  activity,\n  authors,\n  period,\n  now,\n  href,\n  shortcuts,\n  linkComponent,\n  className,\n}: {\n  activity: DashboardActivity[];\n  authors?: DashboardAuthor[];\n  period: DashboardPeriod;\n  now: Date | null;\n  href?: string;\n  shortcuts: DashboardShortcut[];\n  linkComponent: React.ElementType;\n  className?: string;\n}) {\n  const id = useId();\n  const Link = linkComponent;\n  const stages = useMemo(() => {\n    const feed: TimedActivity[] = activity\n      .map((item) => ({ ...item, at: toDate(item.date) }))\n      .sort((a, b) => b.at.getTime() - a.at.getTime());\n    return STAGES.map((stage) => {\n      const all = feed.filter((item) => item.type === stage.type);\n      return { ...stage, items: all.slice(0, ROWS_PER_STAGE), count: all.length };\n    });\n  }, [activity]);\n  const gridRef = useRef<HTMLDivElement>(null);\n  const connectors = useConnectors(gridRef, [stages, now]);\n\n  return (\n    <GlassNotchPane\n      aria-labelledby={`${id}-title`}\n      className={className}\n      title={\n        <GlassPaneHeader\n          id={`${id}-title`}\n          title=\"Recent activity\"\n          caption=\"The newest at each step, from a first post to a message\"\n        />\n      }\n      tray={authors && <GlassAuthorTray authors={authors} period={period} />}\n      actions={href && <GlassRoundLink href={href} label=\"Open all activity\" linkComponent={linkComponent} />}\n    >\n      <div\n        className={cn(\n          \"mt-6 grid gap-6\",\n          shortcuts.length > 0 && \"@7xl:grid-cols-[minmax(0,1fr)_14.5rem] @7xl:gap-8\",\n        )}\n      >\n        <div ref={gridRef} className=\"relative grid gap-4 @2xl:grid-cols-2 @5xl:grid-cols-4 @5xl:gap-8\">\n          {connectors.length > 0 && (\n            <svg aria-hidden=\"true\" className=\"pointer-events-none absolute inset-0 z-10 size-full overflow-visible\">\n              {connectors.map((connector) => (\n                <g key={connector.key}>\n                  <path\n                    d={connector.d}\n                    fill=\"none\"\n                    strokeWidth=\"1.5\"\n                    strokeDasharray={connector.empty ? \"4 4\" : undefined}\n                    style={{\n                      stroke: connector.empty ? \"var(--muted-foreground)\" : \"var(--dash-accent)\",\n                      opacity: connector.empty ? 0.5 : 0.7,\n                    }}\n                  />\n                  {[\n                    [connector.x1, connector.y1],\n                    [connector.x2, connector.y2],\n                  ].map(([cx, cy]) => (\n                    <circle\n                      key={`${cx}-${cy}`}\n                      cx={cx}\n                      cy={cy}\n                      r=\"3.5\"\n                      strokeWidth=\"2\"\n                      style={{\n                        fill: connector.empty ? \"var(--muted-foreground)\" : \"var(--dash-accent)\",\n                        stroke: \"var(--dash-surface)\",\n                      }}\n                    />\n                  ))}\n                </g>\n              ))}\n            </svg>\n          )}\n          {stages.map((stage) => {\n            const Icon = ACTIVITY_TYPES[stage.type].icon;\n            return (\n              <div\n                key={stage.type}\n                className=\"flex min-w-0 flex-col\"\n                data-stage={stage.type}\n                data-empty={stage.items.length ? undefined : \"\"}\n              >\n                <div className=\"dg-card flex-1 rounded-[24px] p-1.5\">\n                  {stage.items.length ? (\n                    <ol className=\"divide-y divide-[var(--dash-hairline)]\">\n                      {stage.items.map((item, index) => (\n                        <GlassStageRow\n                          key={item.id}\n                          item={item}\n                          now={now}\n                          linkComponent={linkComponent}\n                          anchor={[index === 0 ? \"first\" : null, index === stage.items.length - 1 ? \"last\" : null]\n                            .filter(Boolean)\n                            .join(\" \")}\n                        />\n                      ))}\n                    </ol>\n                  ) : (\n                    <div\n                      data-anchor=\"first last\"\n                      className=\"flex h-full min-h-32 flex-col items-center justify-center gap-2 px-4 py-6 text-center\"\n                    >\n                      <span\n                        aria-hidden=\"true\"\n                        className=\"grid size-11 place-items-center rounded-full border border-dashed border-muted-foreground/40 text-muted-foreground\"\n                      >\n                        <Icon className=\"size-[18px]\" />\n                      </span>\n                      <p className=\"text-[13px] text-muted-foreground\">{stage.empty}</p>\n                    </div>\n                  )}\n                </div>\n                <h3 className=\"mt-3 flex items-center justify-center gap-2 text-[13px] font-medium text-foreground\">\n                  {stage.label}\n                  <span className=\"rounded-full bg-[var(--dash-card)] px-2 py-px text-[11px] tabular-nums text-muted-foreground\">\n                    {formatNumber(stage.count)}\n                    <span className=\"sr-only\"> recent {plural(stage.count, stage.noun)}</span>\n                  </span>\n                </h3>\n              </div>\n            );\n          })}\n        </div>\n\n        {shortcuts.length > 0 && (\n          <nav aria-label=\"Shortcuts\" className=\"min-w-0\">\n            <ul className=\"grid grid-cols-2 gap-2.5 @2xl:grid-cols-3 @5xl:grid-cols-6 @7xl:grid-cols-2\">\n              {shortcuts.map((shortcut, index) => (\n                <li key={`${shortcut.href}-${shortcut.label}`}>\n                  <Link\n                    href={shortcut.href}\n                    className={cn(\n                      \"flex h-[5.25rem] flex-col justify-between rounded-[20px] p-3.5 text-[13px] font-medium leading-tight transition-[background-color,box-shadow] [&_svg]:size-[18px]\",\n                      GLASS_FOCUS,\n                      index === 0 ? \"dg-active\" : \"dg-tile text-foreground hover:bg-[var(--dash-card)]\",\n                    )}\n                  >\n                    <span aria-hidden=\"true\" className={index === 0 ? undefined : \"text-[var(--dash-icon)]\"}>\n                      {shortcut.icon}\n                    </span>\n                    {shortcut.label}\n                  </Link>\n                </li>\n              ))}\n            </ul>\n          </nav>\n        )}\n      </div>\n    </GlassNotchPane>\n  );\n}\n\nfunction GlassDashboard({\n  isLiquid,\n  userName,\n  summary,\n  totals,\n  metrics,\n  activity,\n  sources,\n  signUps,\n  countries,\n  authors,\n  payments,\n  shortcuts,\n  links,\n  linkComponent,\n  period,\n  onPeriodChange,\n  onExport,\n  now,\n  className,\n  style,\n  ...props\n}: Omit<DashboardProps, \"variant\" | \"defaultPeriod\" | \"onExport\" | \"now\"> & {\n  isLiquid?: boolean;\n  summary: React.ReactNode;\n  totals: DashboardTotal[];\n  metrics: DashboardMetric[];\n  activity: DashboardActivity[];\n  shortcuts: DashboardShortcut[];\n  links: DashboardLinks;\n  linkComponent: React.ElementType;\n  period: DashboardPeriod;\n  onPeriodChange: (period: DashboardPeriod) => void;\n  onExport: () => void;\n  now: Date | null;\n}) {\n  return (\n    <section\n      aria-label=\"Dashboard\"\n      data-dashboard-glass=\"\"\n      data-liquid={isLiquid ? \"true\" : undefined}\n      className={cn(\n        \"@container relative isolate w-full overflow-hidden rounded-[28px] p-4 text-foreground sm:p-6 lg:p-8\",\n        className,\n      )}\n      style={style}\n      {...props}\n    >\n      <style>{GLASS_CSS}</style>\n      <div className=\"space-y-5 @5xl:space-y-6\">\n        <GlassHeader\n          userName={userName}\n          summary={summary}\n          now={now}\n          period={period}\n          onPeriodChange={onPeriodChange}\n          onExport={onExport}\n        />\n\n        {totals.length > 0 && (\n          <GlassKpiCards totals={totals} metrics={metrics} period={period} linkComponent={linkComponent} />\n        )}\n\n        {/* One grid, so that between @3xl and @5xl traffic and countries pair\n            up beside each other; from @5xl the desktop order returns. */}\n        <div className=\"grid grid-cols-1 gap-5 @3xl:grid-cols-12 @5xl:gap-6\">\n          <GlassOverviewPanel metrics={metrics} period={period} className=\"@3xl:col-span-12 @5xl:col-span-8\" />\n          <GlassTrafficPanel\n            shares={sources?.[period]}\n            signUps={signUps?.[period]}\n            period={period}\n            href={links.traffic}\n            linkComponent={linkComponent}\n            className=\"@3xl:col-span-6 @5xl:col-span-4\"\n          />\n          <GlassActivityPanel\n            activity={activity}\n            authors={authors ? (authors[period] ?? []) : undefined}\n            period={period}\n            now={now}\n            href={links.activity}\n            shortcuts={shortcuts}\n            linkComponent={linkComponent}\n            className=\"@3xl:order-4 @3xl:col-span-12 @5xl:order-none\"\n          />\n          {payments && (\n            <GlassPaymentsPanel\n              payments={payments}\n              now={now}\n              href={links.payments}\n              linkComponent={linkComponent}\n              className=\"@3xl:order-5 @3xl:col-span-12 @5xl:order-none @5xl:col-span-7\"\n            />\n          )}\n          <GlassCountriesPanel\n            shares={countries?.[period]}\n            period={period}\n            href={links.countries}\n            linkComponent={linkComponent}\n            className={cn(\n              \"@3xl:order-3 @3xl:col-span-6 @5xl:order-none\",\n              payments ? \"@5xl:col-span-5\" : \"@5xl:col-span-12\",\n            )}\n          />\n        </div>\n      </div>\n    </section>\n  );\n}\n\nfunction LiquidGlassDashboard({\n  userName,\n  summary,\n  totals,\n  metrics,\n  activity,\n  sources,\n  signUps,\n  countries,\n  authors,\n  payments,\n  shortcuts,\n  links,\n  linkComponent,\n  period,\n  onPeriodChange,\n  onExport,\n  now,\n  bgImage,\n  className,\n  style,\n  ...props\n}: Omit<DashboardProps, \"variant\" | \"defaultPeriod\" | \"onExport\" | \"now\"> & {\n  summary: React.ReactNode;\n  totals: DashboardTotal[];\n  metrics: DashboardMetric[];\n  activity: DashboardActivity[];\n  shortcuts: DashboardShortcut[];\n  links: DashboardLinks;\n  linkComponent: React.ElementType;\n  period: DashboardPeriod;\n  onPeriodChange: (period: DashboardPeriod) => void;\n  onExport: () => void;\n  now: Date | null;\n}) {\n  const liquidBg =\n    bgImage ||\n    \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_1600,c_limit/v1790146209/photo-1470071459604-3b5ec3a7fe05-1790146212205.jpg\";\n\n  const filterId = \"glass-blur-dashboard\";\n\n  return (\n    <div\n      className={cn(\n        \"@container relative w-full overflow-hidden rounded-3xl p-2 sm:p-6 md:p-8 isolate [contain:paint] [transform:translateZ(0)]\",\n        className,\n      )}\n      style={style}\n      {...props}\n    >\n      {/* Single Liquid Glass SVG Filter */}\n      <LiquidGlassSvgFilter id={filterId} bgImage={liquidBg} />\n\n      {/* Full-bleed background image */}\n      <img\n        src={liquidBg}\n        alt=\"Liquid glass backdrop\"\n        className=\"absolute inset-0 w-full h-full object-cover select-none pointer-events-none\"\n      />\n\n      <LiquidGlassWrapper\n        radius=\"1.5rem\"\n        bgImage={liquidBg}\n        filterId={filterId}\n        contrastOverlayClassName=\"bg-black/45 backdrop-blur-md\"\n        className=\"relative z-10 w-full text-white transition-all duration-300 flex flex-col p-4 sm:p-6 lg:p-8 shadow-[0_30px_70px_rgba(0,0,0,0.6)]\"\n      >\n        <GlassDashboard\n          isLiquid={true}\n          userName={userName}\n          summary={summary}\n          totals={totals}\n          metrics={metrics}\n          activity={activity}\n          sources={sources}\n          signUps={signUps}\n          countries={countries}\n          authors={authors}\n          payments={payments}\n          shortcuts={shortcuts}\n          links={links}\n          linkComponent={linkComponent}\n          period={period}\n          onPeriodChange={onPeriodChange}\n          onExport={onExport}\n          now={now}\n          className=\"!p-0 border-0 bg-transparent shadow-none\"\n        />\n      </LiquidGlassWrapper>\n    </div>\n  );\n}\n\n// ==========================================\n// SECTION\n// ==========================================\n/**\n * An admin dashboard home: a greeting with all-time totals, a period switch,\n * an overview chart with a tab per metric, the activity feed, traffic\n * sources, visitors by country and the top authors. Every number comes from\n * props, so wire it to your API and the layout takes care of itself.\n *\n * `variant=\"glass\"` re-presents the same props as milky glass panes: KPI\n * cards with a brand-filled lead, metric tiles beside capsule columns, a\n * segmented half ring, the activity as a journey in a notched pane with the\n * top authors in the notch, a payments table and a chunky country bar.\n *\n * `variant=\"liquid-glass\"` wraps the glass dashboard with fluid SVG displacement map filters,\n * specular inner highlights, and ambient depth gradients using LiquidGlassWrapper, LiquidGlassBackground,\n * and LiquidGlassSvgFilter.\n */\nexport function Dashboard({\n  variant = \"classic\",\n  bgImage,\n  userName,\n  summary,\n  totals = [],\n  metrics = [],\n  activity = [],\n  sources,\n  signUps,\n  countries,\n  authors,\n  payments,\n  shortcuts = [],\n  defaultPeriod = \"month\",\n  onExport,\n  links = {},\n  linkComponent = \"a\",\n  now: fixedNow,\n  className,\n  style,\n  ...props\n}: DashboardProps) {\n  const [period, setPeriod] = useState<DashboardPeriod>(defaultPeriod);\n  const now = useNow(fixedNow);\n  const exportReport = () => (onExport ? onExport(period) : downloadCsv(metrics, period));\n\n  if (variant === \"glass\") {\n    return (\n      <GlassDashboard\n        userName={userName}\n        summary={summary ?? describeToday(metrics)}\n        totals={totals}\n        metrics={metrics}\n        activity={activity}\n        sources={sources}\n        signUps={signUps}\n        countries={countries}\n        authors={authors}\n        payments={payments}\n        shortcuts={shortcuts}\n        links={links}\n        linkComponent={linkComponent}\n        period={period}\n        onPeriodChange={setPeriod}\n        onExport={exportReport}\n        now={now}\n        className={className}\n        style={style}\n        {...props}\n      />\n    );\n  }\n\n  if (variant === \"liquid-glass\") {\n    return (\n      <LiquidGlassDashboard\n        userName={userName}\n        summary={summary ?? describeToday(metrics)}\n        totals={totals}\n        metrics={metrics}\n        activity={activity}\n        sources={sources}\n        signUps={signUps}\n        countries={countries}\n        authors={authors}\n        payments={payments}\n        shortcuts={shortcuts}\n        links={links}\n        linkComponent={linkComponent}\n        period={period}\n        onPeriodChange={setPeriod}\n        onExport={exportReport}\n        now={now}\n        bgImage={bgImage}\n        className={className}\n        style={style}\n        {...props}\n      />\n    );\n  }\n\n  return (\n    <section\n      aria-label=\"Dashboard\"\n      className={cn(\n        \"relative isolate w-full overflow-hidden rounded-[28px] p-4 text-foreground sm:p-6 lg:p-8\",\n        ROOT_TOKENS,\n        className,\n      )}\n      style={{ ...CANVAS_STYLE, ...style }}\n      {...props}\n    >\n      <div className=\"space-y-6 lg:space-y-8\">\n        <DashboardHeader\n          userName={userName}\n          summary={summary ?? describeToday(metrics)}\n          totals={totals}\n          now={now}\n        />\n\n        <div className=\"flex flex-wrap items-center justify-between gap-3\">\n          <ToggleGroup\n            type=\"single\"\n            variant=\"segmented\"\n            value={period}\n            onValueChange={(next) => {\n              if (next === \"day\" || next === \"week\" || next === \"month\") setPeriod(next);\n            }}\n            aria-label=\"Reporting period\"\n          >\n            {PERIODS.map((option) => (\n              <ToggleGroupItem key={option.value} value={option.value}>\n                {option.label}\n              </ToggleGroupItem>\n            ))}\n          </ToggleGroup>\n          <button\n            type=\"button\"\n            onClick={exportReport}\n            className={cn(\n              \"inline-flex h-10 items-center gap-2 rounded-full bg-primary px-5 text-sm font-medium text-primary-foreground shadow-sm transition-[filter] hover:brightness-110\",\n              FOCUS,\n            )}\n          >\n            <Download className=\"size-4\" aria-hidden=\"true\" />\n            Export\n          </button>\n        </div>\n\n        <div className=\"grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-12 xl:gap-6\">\n          {/* Side by side, the overview sets the row height: `contain: size`\n              stops the activity feed from stretching the row, so it fills the\n              row and scrolls instead. */}\n          <OverviewPanel metrics={metrics} period={period} className=\"md:col-span-2 xl:col-span-8\" />\n          <ActivityPanel\n            activity={activity}\n            now={now}\n            href={links.activity}\n            linkComponent={linkComponent}\n            className=\"md:col-span-2 xl:col-span-4\"\n          />\n          <TrafficSourcesPanel\n            shares={sources?.[period]}\n            signUps={signUps?.[period]}\n            period={period}\n            href={links.traffic}\n            linkComponent={linkComponent}\n            className=\"xl:col-span-4\"\n          />\n          <CountriesPanel\n            shares={countries?.[period]}\n            period={period}\n            href={links.countries}\n            linkComponent={linkComponent}\n            className=\"xl:col-span-4\"\n          />\n          <TopAuthorsPanel\n            authors={authors?.[period] ?? []}\n            period={period}\n            href={links.authors}\n            linkComponent={linkComponent}\n            className=\"md:col-span-2 xl:col-span-4\"\n          />\n        </div>\n      </div>\n    </section>\n  );\n}\n\nexport default Dashboard;\n",
      "type": "registry:component",
      "target": "components/custom/blocks/DashboardBlock.tsx"
    }
  ],
  "docs": "Named exports: Dashboard (also the default) plus LiquidGlassWrapper, LiquidGlassBackground, LiquidGlassSvgFilter, and the DashboardMetric, DashboardPoint, DashboardTotal, DashboardActivity, DashboardShare, DashboardAuthor, DashboardPayment, DashboardShortcut, DashboardLinks, DashboardPeriod and DashboardVariant types. variant: 'classic' (default) | 'glass' | 'liquid-glass'; all read the same props, and glass/liquid-glass also read `payments`, `shortcuts`, `totals[].metric` / `href` and `activity[].person` / `avatar`. Every number comes from props: `metrics` carry a series per period (day | week | month, oldest first) and an optional `change` per period; `sources`, `countries` and `authors` are keyed by period; `activity` items have a type of user | post | payment | message. Pass `linkComponent` (e.g. next/link) for the panel links and `onExport` to replace the built-in CSV. Composes toggle-group (segmented), chart and popover. Clock-dependent text renders after mount, so server and client markup agree.",
  "categories": [
    "data-display",
    "layout"
  ],
  "type": "registry:component"
}
