{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stat",
  "title": "Stat",
  "description": "A named value: a giant thin figure whose feet sink under one hairline, the name small beneath; a changed figure turns over like a counter's wheels.",
  "registryDependencies": [
    "https://thedirectors.agency/ui/r/0nlytype.json",
    "https://thedirectors.agency/ui/r/fraction.json"
  ],
  "files": [
    {
      "path": "registry/0nlytype/ui/stat.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { Fraction } from \"@/components/ui/fraction\"\nimport { cn } from \"@/lib/utils\"\n\ntype StatsProps = React.ComponentProps<\"dl\"> & {\n  /**\n   * crop: SPECTRA's giant thin figures, their feet cut off by one hairline that runs under the whole row, the\n   * name small beneath. beside: WOVE's 03, a heavy figure with its name and a line about it set beside it.\n   * grid: \"Less is more.\", each stat a cell of a hairline grid, its name in the top corner and its figure in the far one.\n   */\n  variant?: \"crop\" | \"beside\" | \"grid\"\n}\n\n/**\n * A row of named values. Each Stat is a figure and what it counts. The figures in a row share one size, set by the\n * longest, so they stand on one line.\n */\nfunction Stats({ className, variant = \"crop\", style, children, ...props }: StatsProps) {\n  // ponytail: reads the Stats among the direct children; a Stat wrapped in another component keeps its own size.\n  const lens = React.Children.toArray(children).flatMap((c) =>\n    React.isValidElement<StatProps>(c) && c.type === Stat ? [figure(c.props.value, c.props.format).length] : [],\n  )\n  return (\n    <dl\n      data-slot=\"stats\"\n      data-variant={variant === \"crop\" ? undefined : variant}\n      className={cn(\"ot-stats\", className)}\n      style={lens.length ? ({ \"--row-len\": Math.max(...lens), ...style } as React.CSSProperties) : style}\n      {...props}\n    >\n      {children}\n    </dl>\n  )\n}\n\ntype StatProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  value: number | string\n  /** What the figure counts: \"visits in September\". */\n  label: React.ReactNode\n  /** A line about it, in pencil: \"up 12% on August\". */\n  note?: React.ReactNode\n  /** Formats a number. Defaults to en-GB, grouped by a thin space as SI sets figures, since crop cuts off a comma's tail. */\n  format?: (value: number) => string\n}\n\nconst en = new Intl.NumberFormat(\"en-GB\")\nconst grouped = (n: number) => en.format(n).replace(/,/g, \"\\u202f\")\nconst figure = (value: number | string, format = grouped) => (typeof value === \"number\" ? format(value) : value)\n\n/**\n * A figure and what it counts. When the value changes the figure turns over like a counter's wheels (a Fraction\n * with no total): only the figures that changed turn, the units first and each carry one arpeggio step behind.\n * A figure that gains or loses a place turns over whole. The figure is read aloud whole.\n */\nfunction Stat({ value, label, note, format = grouped, className, style, ...props }: StatProps) {\n  const text = figure(value, format)\n  return (\n    <div data-slot=\"stat\" className={cn(\"ot-stat\", className)} style={{ \"--len\": text.length, ...style } as React.CSSProperties} {...props}>\n      <dt className=\"ot-stat-label\">{label}</dt>\n      <dd className=\"ot-stat-figure\">\n        <Fraction count={text} />\n      </dd>\n      {note ? <dd className=\"ot-stat-note\">{note}</dd> : null}\n    </div>\n  )\n}\n\nexport { Stats, Stat, type StatsProps, type StatProps }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/0nlytype/styles/stat.css",
      "content": "@layer components {\n  /* ── ot-stat ── a named value: SPECTRA's giant thin figures, their feet cut off by the one hairline that runs under the row, the\n     name small beneath it. The figure is set to its cell (--len is its length; the row takes its longest, --row-len, so a row shares one size), as large as the cell allows. Its wheels turn inside\n     the crop, so a changed figure drops under the rule and the new one rises out of it, as in a counter's window. */\n  .ot-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); row-gap: var(--ot-space-7); min-inline-size: min(11rem, 100%); margin: 0; }\n  .ot-stat { --ot-stat-pad: var(--ot-space-5); display: grid; align-content: start; min-width: 0; padding-inline-end: var(--ot-stat-pad); container-type: inline-size; }\n  :where(.ot-stat > dd) { margin: 0; }\n  .ot-stat-label { grid-row: 2; padding-block-start: var(--ot-space-3); font-size: var(--ot-pp); line-height: var(--ot-pp-lh); letter-spacing: var(--ot-pp-tr); color: var(--ot-graphite); }\n  .ot-stat-figure {\n    grid-row: 1; white-space: nowrap; overflow: clip;\n    font-size: clamp(var(--ot-mf), calc(100cqi / (var(--row-len, var(--len)) * 0.6)), var(--ot-fff)); line-height: 0.9; font-weight: 200; letter-spacing: var(--ot-ff-tr);\n    font-variant-numeric: tabular-nums lining-nums; color: var(--ot-ink);\n    /* the rule runs on through the gap to the next stat, so a row stands on one line */\n    margin-inline-end: calc(-1 * var(--ot-stat-pad)); padding-inline-end: var(--ot-stat-pad);\n    border-block-end: var(--ot-hairline) solid var(--ot-rule-strong);\n  }\n  @supports (text-box: trim-both cap alphabetic) {\n    .ot-stat-figure { line-height: 1; text-box: trim-both cap alphabetic; block-size: calc(1cap - 0.05em); }\n  }\n  /* The figure is a Fraction with no total (its wheels), set in the stat's own face: roman, thin, the cell's size. */\n  .ot-stat-figure > .ot-fraction { display: inline; font-size: inherit; line-height: inherit; color: inherit; }\n  .ot-stat-figure .ot-fraction > .ot-yours { display: inline-block; font: inherit; letter-spacing: inherit; }\n  .ot-stat-note { grid-row: 3; font-size: var(--ot-pp); line-height: var(--ot-pp-lh); letter-spacing: var(--ot-pp-tr); color: var(--ot-pencil); }\n\n  /* ── beside ── WOVE's 03: the figure heavy and whole, its name and a line about it set beside it, their caps on its cap line.\n     The stats stand in a list, figures flush to one edge (tabular, so the units line up) and the words in one column. */\n  .ot-stats[data-variant=\"beside\"] { grid-template-columns: max-content minmax(0, 20rem); gap: var(--ot-space-6) var(--ot-space-4); }\n  .ot-stats[data-variant=\"beside\"] .ot-stat { grid-column: 1 / -1; grid-template-columns: subgrid; padding: 0; container-type: normal; }\n  .ot-stats[data-variant=\"beside\"] .ot-stat-figure {\n    grid-row: 1 / span 2; justify-self: end; margin: 0; padding: 0; border: 0; block-size: auto; overflow: visible;\n    font-size: var(--ot-ff); line-height: 0.9; font-weight: 700; letter-spacing: var(--ot-mf-tr); text-box: trim-both cap alphabetic;\n  }\n  .ot-stats[data-variant=\"beside\"] .ot-stat-label { grid-row: 1; grid-column: 2; padding: 0; font-size: var(--ot-p); line-height: 1.25; font-weight: 600; color: var(--ot-ink); text-box: trim-start cap; }\n  .ot-stats[data-variant=\"beside\"] .ot-stat-note { grid-row: 2; grid-column: 2; font-size: var(--ot-p); line-height: 1.25; color: var(--ot-pencil); }\n  @media (max-width: 40rem) { .ot-stats[data-variant=\"beside\"] .ot-stat-figure { font-size: var(--ot-mf); } }\n\n  /* ── grid ── \"Less is more.\": each stat a cell of a hairline grid; the name in the cell's first corner, the figure large in the\n     far one. The lines run between the cells only (the outer ones are cut off), so they stop where the stats do. */\n  .ot-stats[data-variant=\"grid\"] { overflow: clip; row-gap: 0; grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); }\n  .ot-stats[data-variant=\"grid\"] .ot-stat {\n    grid-template-rows: auto auto 1fr; min-block-size: 11rem; padding: var(--ot-space-4) var(--ot-space-4) var(--ot-space-4) var(--ot-space-4);\n    margin-block-start: calc(-1 * var(--ot-hairline)); margin-inline-start: calc(-1 * var(--ot-hairline));\n    border-block-start: var(--ot-hairline) solid var(--ot-rule-strong); border-inline-start: var(--ot-hairline) solid var(--ot-rule-strong);\n  }\n  .ot-stats[data-variant=\"grid\"] .ot-stat-label { grid-row: 1; padding: 0; }\n  .ot-stats[data-variant=\"grid\"] .ot-stat-note { grid-row: 2; }\n  .ot-stats[data-variant=\"grid\"] .ot-stat-figure {\n    grid-row: 3; align-self: end; justify-self: end; margin: 0; padding: var(--ot-space-4) 0 0; border: 0; block-size: auto; overflow: visible;\n    font-size: clamp(var(--ot-mf), calc(80cqi / (var(--row-len, var(--len)) * 0.6)), var(--ot-ff)); line-height: 0.9; font-weight: 500; text-box: trim-both cap alphabetic;\n  }\n}\n",
      "type": "registry:file",
      "target": "styles/0nlytype/stat.css"
    }
  ],
  "css": {
    "@import \"../styles/0nlytype/stat.css\"": {}
  },
  "meta": {
    "movement": "X",
    "underneath": "native",
    "contract": "ot-stat"
  },
  "type": "registry:ui"
}
