{
  "name": "akta-benchmarks-01-default",
  "type": "registry:block",
  "tier": "free",
  "dependencies": [
    "@hugeicons/react",
    "@hugeicons/core-free-icons",
    "clsx",
    "tailwind-merge"
  ],
  "registryDependencies": [
    "https://www.craftui.space/r/craftui-base.json",
    "utils"
  ],
  "css": {
    "@property --akta-notch-arm": {
      "syntax": "\"<length>\"",
      "inherits": "true",
      "initial-value": "12px"
    },
    "@property --akta-notch-inset": {
      "syntax": "\"<length>\"",
      "inherits": "true",
      "initial-value": "6px"
    },
    "@layer components": {
      ".akta-notch": {
        "position": "relative",
        "--akta-notch-color": "oklch(0.488 0.243 264.376)",
        "--akta-notch-weight": "1.5px",
        "--akta-notch-rest": "6px"
      },
      ".akta-notch::before": {
        "content": "\"\"",
        "position": "absolute",
        "inset": "calc(var(--akta-notch-inset) * -1)",
        "border": "var(--akta-notch-weight, 1.5px) solid var(--akta-notch-color, currentColor)",
        "pointer-events": "none",
        "--akta-notch-window": "linear-gradient(#000 0 0) no-repeat",
        "-webkit-mask": "var(--akta-notch-window) top left / var(--akta-notch-arm) var(--akta-notch-arm), var(--akta-notch-window) top right / var(--akta-notch-arm) var(--akta-notch-arm), var(--akta-notch-window) bottom left / var(--akta-notch-arm) var(--akta-notch-arm), var(--akta-notch-window) bottom right / var(--akta-notch-arm) var(--akta-notch-arm)",
        "mask": "var(--akta-notch-window) top left / var(--akta-notch-arm) var(--akta-notch-arm), var(--akta-notch-window) top right / var(--akta-notch-arm) var(--akta-notch-arm), var(--akta-notch-window) bottom left / var(--akta-notch-arm) var(--akta-notch-arm), var(--akta-notch-window) bottom right / var(--akta-notch-arm) var(--akta-notch-arm)"
      },
      ".dark .akta-notch": {
        "--akta-notch-color": "oklch(0.546 0.245 262.881)"
      },
      "@supports (animation-timeline: view())": {
        "@media (prefers-reduced-motion: no-preference)": {
          "[data-akta-reveal]": {
            "animation": "akta-enter 1s linear both",
            "animation-timeline": "view()",
            "animation-range": "entry 15% entry 85%"
          }
        }
      }
    },
    "@keyframes akta-enter": {
      "from": {
        "opacity": "0",
        "transform": "translateY(12px)"
      },
      "to": {
        "opacity": "1",
        "transform": "translateY(0)"
      }
    }
  },
  "files": [
    {
      "path": "components/craftui/sections/akta-benchmarks-01/default.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport {\n  ChartAverageIcon,\n  CheckmarkCircle02Icon,\n  Coins01Icon,\n  Radar01Icon,\n} from \"@hugeicons/core-free-icons\";\n\nimport { cn } from \"@/lib/utils\";\n\n/* ---------------------------------------------------------------------------\n * akta-benchmarks-01 — four metrics, four graphic forms\n *\n * Same contract as akta-hero-01: every value lives in this file as a Tailwind\n * arbitrary value with its dark twin. Nothing is written into globals.css\n * except the notch device class and the scroll-reveal keyframe, which the hero\n * already ships. There is no `@theme` block and no `:root` palette.\n *\n * The SVG charts follow the same rule. Fills and strokes are `fill-[…]` /\n * `stroke-[…]` utilities on the SVG elements rather than inline `var()`s, so\n * the charts flip with `.dark` like everything else and never depend on a\n * token that was not installed.\n *\n * Every className is assembled with `cn()` in a fixed order:\n *\n *     cn( layout , type , colour , state , device )\n * ------------------------------------------------------------------------ */\n\n/* ── Type ───────────────────────────────────────────────────────────────── */\nconst FONT_SANS =\n  \"font-[family-name:var(--font-geist-sans),ui-sans-serif,system-ui,sans-serif]\";\nconst FONT_MONO =\n  \"font-[family-name:var(--font-geist-mono),ui-monospace,SFMono-Regular,Menlo,monospace]\";\n\n/* section headline — h2 */\nconst TYPE_HEADING_48 =\n  \"text-[32px] leading-[1.12] tracking-[-0.01em] font-normal sm:text-[40px] sm:leading-[1.1] sm:tracking-[-0.03em] lg:text-[48px] lg:leading-[1.08]\";\n/* cell title — h3 */\nconst TYPE_HEADING_20 =\n  \"text-[18px] leading-[1.33] tracking-[-0.02em] font-semibold sm:text-[20px] sm:leading-[1.3]\";\n/* the headline figure in every cell */\nconst TYPE_DISPLAY = `${FONT_MONO} text-[40px] leading-[1.1] tracking-[-0.06em] font-normal tabular-nums`;\n/* body copy */\nconst TYPE_COPY_16 =\n  \"text-[15px] leading-[1.6] tracking-[-0.01em] font-normal sm:text-[16px] sm:leading-[1.625]\";\n/* dense body, captions */\nconst TYPE_COPY_14 =\n  \"text-[13px] leading-[1.54] tracking-[-0.01em] font-normal sm:text-[14px] sm:leading-[1.57]\";\n/* the one mono label size — used in HTML cells and matched by the SVG charts */\nconst TYPE_LABEL = `${FONT_MONO} text-[12px] leading-[16px] font-normal`;\n\n/* ── Colour ─────────────────────────────────────────────────────────────── */\n\n/* surfaces */\nconst BG_CANVAS = \"bg-white dark:bg-black\";\nconst BG_PANEL =\n  \"bg-[oklch(0.985_0.002_247.839)] dark:bg-[oklch(0.13_0.028_261.692)]\";\nconst BG_BRAND =\n  \"bg-[oklch(0.488_0.243_264.376)] dark:bg-[oklch(0.546_0.245_262.881)]\";\nconst BG_BRAND_TILE =\n  \"bg-[oklch(0.932_0.032_255.585)] dark:bg-[oklch(0.379_0.146_265.522)]\";\nconst BG_TRACK =\n  \"bg-[oklch(0.967_0.003_264.542)] dark:bg-[oklch(0.21_0.034_264.665)]\";\nconst BG_TRACK_HOVER =\n  \"bg-[oklch(0.928_0.006_264.531)] dark:bg-[oklch(0.278_0.033_256.848)]\";\nconst BG_INERT =\n  \"bg-[oklch(0.872_0.01_258.338)] dark:bg-[oklch(0.373_0.034_259.733)]\";\n\n/* text */\nconst INK =\n  \"text-[oklch(0.13_0.028_261.692)] dark:text-[oklch(0.967_0.003_264.542)]\";\nconst INK_MUTED =\n  \"text-[oklch(0.21_0.034_264.665)] dark:text-[oklch(0.707_0.022_261.325)]\";\nconst INK_BRAND =\n  \"text-[oklch(0.379_0.146_265.522)] dark:text-[oklch(0.707_0.165_254.624)]\";\nconst BRAND =\n  \"text-[oklch(0.488_0.243_264.376)] dark:text-[oklch(0.546_0.245_262.881)]\";\n\n/* SVG ink — the same roles as the text constants, as fill utilities */\nconst FILL_INK =\n  \"fill-[oklch(0.13_0.028_261.692)] dark:fill-[oklch(0.967_0.003_264.542)]\";\nconst FILL_INK_MUTED =\n  \"fill-[oklch(0.21_0.034_264.665)] dark:fill-[oklch(0.707_0.022_261.325)]\";\nconst FILL_BRAND =\n  \"fill-[oklch(0.488_0.243_264.376)] dark:fill-[oklch(0.546_0.245_262.881)]\";\nconst FILL_ON_BRAND = \"fill-white\";\nconst FILL_INERT =\n  \"fill-[oklch(0.872_0.01_258.338)] dark:fill-[oklch(0.373_0.034_259.733)]\";\nconst FILL_BRAND_TILE =\n  \"fill-[oklch(0.932_0.032_255.585)] dark:fill-[oklch(0.379_0.146_265.522)]\";\nconst FILL_RULE =\n  \"fill-[oklch(0.707_0.022_261.325)] dark:fill-[oklch(0.373_0.034_259.733)]\";\n\nconst STROKE_RULE =\n  \"stroke-[oklch(0.707_0.022_261.325)] dark:stroke-[oklch(0.373_0.034_259.733)]\";\nconst STROKE_BRAND =\n  \"stroke-[oklch(0.488_0.243_264.376)] dark:stroke-[oklch(0.546_0.245_262.881)]\";\nconst STROKE_BRAND_BORDER =\n  \"stroke-[oklch(0.623_0.214_259.815)] dark:stroke-[oklch(0.546_0.245_262.881)]\";\nconst STROKE_CANVAS = \"stroke-white dark:stroke-black\";\n\n/* Isometric faces recede toward the canvas rather than toward a darker step,\n * because the brand ramp inverts between themes — the dark-mode hover step is\n * *lighter* than solid, which would flip the shading. Mixing with the canvas\n * keeps the top face the most saturated in both. */\nconst FACE_BRAND = {\n  top: FILL_BRAND,\n  left: \"fill-[color-mix(in_oklab,oklch(0.488_0.243_264.376)_74%,white)] dark:fill-[color-mix(in_oklab,oklch(0.546_0.245_262.881)_74%,black)]\",\n  right:\n    \"fill-[color-mix(in_oklab,oklch(0.488_0.243_264.376)_50%,white)] dark:fill-[color-mix(in_oklab,oklch(0.546_0.245_262.881)_50%,black)]\",\n};\nconst FACE_GRAY = {\n  top: FILL_INERT,\n  left: \"fill-[color-mix(in_oklab,oklch(0.872_0.01_258.338)_74%,white)] dark:fill-[color-mix(in_oklab,oklch(0.373_0.034_259.733)_74%,black)]\",\n  right:\n    \"fill-[color-mix(in_oklab,oklch(0.872_0.01_258.338)_50%,white)] dark:fill-[color-mix(in_oklab,oklch(0.373_0.034_259.733)_50%,black)]\",\n};\nconst faces = (brand?: boolean) => (brand ? FACE_BRAND : FACE_GRAY);\n\n/* notch colour, set per call site as an arbitrary property */\nconst NOTCH_GRAY =\n  \"[--akta-notch-color:oklch(0.707_0.022_261.325)] dark:[--akta-notch-color:oklch(0.373_0.034_259.733)]\";\n\n/* ── Depth and separators ─────────────────────────────────────────────────\n * Dark rules sit at 16% white rather than the hero's 10%: this section is a\n * grid of hairlines on pure black with no panel behind them, and at 10% the\n * cell dividers dropped below what a normal display resolves. */\nconst SHADOW_BORDER =\n  \"shadow-[0_0_0_1px_rgba(0,0,0,0.06),0_1px_2px_-1px_rgba(0,0,0,0.06),0_2px_4px_0_rgba(0,0,0,0.04)] dark:shadow-[0_0_0_1px_rgba(255,255,255,0.08)]\";\n/* color-mix(in oklab, C 45%, transparent) is just C at 45% alpha */\nconst RING_TILE =\n  \"shadow-[0_0_0_1px_oklch(0.623_0.214_259.815/0.45)] dark:shadow-[0_0_0_1px_oklch(0.546_0.245_262.881/0.6)]\";\n\nconst RULE_T =\n  \"shadow-[inset_0_1px_0_0_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.16)]\";\nconst RULE_Y =\n  \"shadow-[inset_0_1px_0_0_rgba(0,0,0,0.08),inset_0_-1px_0_0_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.16),inset_0_-1px_0_0_rgba(255,255,255,0.16)]\";\n/* right + bottom in one value, so a wrapping grid separates at any column count */\nconst RULE_CELL =\n  \"shadow-[inset_-1px_0_0_0_rgba(0,0,0,0.08),inset_0_-1px_0_0_rgba(0,0,0,0.08)] dark:shadow-[inset_-1px_0_0_0_rgba(255,255,255,0.16),inset_0_-1px_0_0_rgba(255,255,255,0.16)]\";\nconst RULE_X_LG =\n  \"lg:shadow-[inset_1px_0_0_0_rgba(0,0,0,0.08),inset_-1px_0_0_0_rgba(0,0,0,0.08)] dark:lg:shadow-[inset_1px_0_0_0_rgba(255,255,255,0.16),inset_-1px_0_0_0_rgba(255,255,255,0.16)]\";\nconst RULE_L_LG =\n  \"lg:shadow-[inset_1px_0_0_0_rgba(0,0,0,0.08)] dark:lg:shadow-[inset_1px_0_0_0_rgba(255,255,255,0.16)]\";\n\n/* ── Layout ─────────────────────────────────────────────────────────────── */\nconst GRID = \"mx-auto w-full max-w-[90rem]\";\n\n/* ── Content ────────────────────────────────────────────────────────────────\n *\n * Benchmark readouts. Every figure is drawn from its own value — `ratio` is the\n * value normalised against the largest in its column, so each field reads as\n * data rather than as decoration. `brand` marks the one row the section is\n * actually about; it is the only place blue is spent inside a cell.\n *\n * `chart` picks the form. The four metrics tell four different stories — a\n * blowout, a tight cluster, a near-miss, a ratio — and one shared bar list\n * flattened all of them into the same shape. `short` is the iso cell's rotated\n * axis label, which has a 66px lane to live in; `n` is the raw number for forms\n * that need to place a value on an absolute scale rather than a 0–1 ratio. */\nexport type BenchmarkRow = {\n  name: string;\n  short?: string;\n  value: string;\n  ratio: number;\n  n?: number;\n  brand?: boolean;\n};\n\nexport type BenchmarkMetric = {\n  title: string;\n  qualifier: string;\n  direction: string;\n  icon: typeof ChartAverageIcon;\n  /* Unit line beside the headline figure. The figure itself is always the\n   * brand row's own value, so there is one source for the number. */\n  headline: string;\n  blurb: string;\n  chart: \"iso\" | \"units\" | \"radar\" | \"slab\" | \"bars\";\n  rows: BenchmarkRow[];\n};\n\nconst METRICS: BenchmarkMetric[] = [\n  {\n    title: \"F1 Score\",\n    headline: \"top F1 score\",\n    qualifier: \"accuracy × recall\",\n    direction: \"Higher is better\",\n    icon: ChartAverageIcon,\n    chart: \"iso\",\n    blurb:\n      \"The F1 score is a measure of a model's accuracy that considers both precision.\",\n    rows: [\n      {\n        name: \"akta.pro\",\n        short: \"akta.pro\",\n        value: \"81.3\",\n        ratio: 1,\n        brand: true,\n      },\n      { name: \"GPT-5.5\", short: \"GPT-5.5\", value: \"62.5\", ratio: 0.769 },\n      { name: \"SerpAPI\", short: \"SerpAPI\", value: \"52.6\", ratio: 0.647 },\n      { name: \"Perigon\", short: \"Perigon\", value: \"48.6\", ratio: 0.598 },\n      {\n        name: \"Claude Sonnet 4.5\",\n        short: \"Sonnet 4.5\",\n        value: \"47.8\",\n        ratio: 0.588,\n      },\n      { name: \"Parallel\", short: \"Parallel\", value: \"46.4\", ratio: 0.571 },\n    ],\n  },\n  {\n    title: \"Accuracy\",\n    headline: \"of 100 verified\",\n    qualifier: \"precision\",\n    direction: \"Higher is better\",\n    icon: CheckmarkCircle02Icon,\n    chart: \"units\",\n    blurb:\n      \"How much of what we return is correct — the share of articles that survive verification.\",\n    rows: [\n      {\n        name: \"akta.pro\",\n        short: \"akta.pro\",\n        value: \"93%\",\n        ratio: 1,\n        n: 93,\n        brand: true,\n      },\n      {\n        name: \"Claude Opus 4.8\",\n        short: \"Opus 4.8\",\n        value: \"88%\",\n        ratio: 0.946,\n        n: 88,\n      },\n      {\n        name: \"GPT-5.4 mini\",\n        short: \"GPT-5.4\",\n        value: \"86%\",\n        ratio: 0.925,\n        n: 86,\n      },\n      {\n        name: \"Claude Sonnet 4.5\",\n        short: \"Sonnet 4.5\",\n        value: \"82%\",\n        ratio: 0.882,\n        n: 82,\n      },\n      { name: \"GPT-5.5\", short: \"GPT-5.5\", value: \"81%\", ratio: 0.871, n: 81 },\n      {\n        name: \"Parallel\",\n        short: \"Parallel\",\n        value: \"72%\",\n        ratio: 0.774,\n        n: 72,\n      },\n    ],\n  },\n  {\n    title: \"Coverage\",\n    headline: \"of all news reached\",\n    qualifier: \"recall\",\n    direction: \"Higher is better\",\n    icon: Radar01Icon,\n    chart: \"radar\",\n    blurb:\n      \"How much of the news that exists we actually reach, across every source in the index.\",\n    rows: [\n      { name: \"Perigon\", value: \"73.7%\", ratio: 1, n: 73.7 },\n      { name: \"akta.pro\", value: \"72.7%\", ratio: 0.986, n: 72.7, brand: true },\n      { name: \"Exa\", value: \"68.7%\", ratio: 0.932, n: 68.7 },\n      { name: \"NewsAPI\", value: \"59.4%\", ratio: 0.806, n: 59.4 },\n      { name: \"GPT-5.5\", value: \"55.9%\", ratio: 0.758, n: 55.9 },\n      { name: \"SerpAPI\", value: \"50.6%\", ratio: 0.686, n: 50.6 },\n    ],\n  },\n  {\n    title: \"Cost\",\n    headline: \"per 1,000 verified\",\n    qualifier: \"per 1K accurate articles\",\n    direction: \"Lower is better\",\n    icon: Coins01Icon,\n    chart: \"slab\",\n    blurb:\n      \"What a thousand verified articles costs you — priced on results, not on requests made.\",\n    rows: [\n      { name: \"akta.pro\", value: \"$0.50\", ratio: 0.175, brand: true },\n      { name: \"SerpAPI\", value: \"$0.67\", ratio: 0.235 },\n      { name: \"NewsAPI\", value: \"$0.72\", ratio: 0.253 },\n      { name: \"Perigon\", value: \"$0.73\", ratio: 0.256 },\n      { name: \"Parallel\", value: \"$1.52\", ratio: 0.533 },\n      { name: \"GPT-5.4 mini\", value: \"$2.85\", ratio: 1 },\n    ],\n  },\n];\n\n/* ── Header ─────────────────────────────────────────────────────────────── */\n\n/* Eyebrow — a notched chip carrying a solid brand square and one mono label.\n * The square is the section's single decorative use of blue; everything else\n * structural in this section is gray. */\nfunction SectionEyebrow({ children }: { children: React.ReactNode }) {\n  return (\n    <span\n      className={cn(\n        \"inline-flex items-center gap-2 px-3 py-1.5\",\n        INK,\n        BG_PANEL,\n        SHADOW_BORDER,\n        \"akta-notch [--akta-notch-arm:8px] [--akta-notch-inset:4px] [--akta-notch-weight:1px]\",\n        NOTCH_GRAY,\n      )}\n    >\n      <span className={cn(\"size-2 shrink-0\", BG_BRAND)} aria-hidden=\"true\" />\n      <span className={cn(TYPE_LABEL, \"uppercase\")}>{children}</span>\n    </span>\n  );\n}\n\n/* Section header — eyebrow, h2, one paragraph of body copy, centred on the\n * grid and capped at a readable measure. */\nfunction SectionHeader({\n  eyebrow,\n  heading,\n  description,\n}: {\n  eyebrow: string;\n  heading: string;\n  description: string;\n}) {\n  return (\n    <div\n      data-akta-reveal\n      className=\"flex flex-col items-center px-4 py-12 text-center sm:px-6 sm:py-16 md:py-20 lg:px-10 lg:py-24\"\n    >\n      <SectionEyebrow>{eyebrow}</SectionEyebrow>\n\n      <h2\n        className={cn(\n          \"mt-6 max-w-3xl text-balance sm:mt-8\",\n          TYPE_HEADING_48,\n          INK,\n        )}\n      >\n        {heading}\n      </h2>\n\n      <p\n        className={cn(\n          \"mt-4 max-w-[70ch] text-balance sm:mt-5\",\n          TYPE_COPY_16,\n          INK_MUTED,\n        )}\n      >\n        {description}\n      </p>\n    </div>\n  );\n}\n\n/* ── Chart chrome ───────────────────────────────────────────────────────────\n *\n * Type inside the charts. An SVG's text scales with its viewBox, so the same\n * `fontSize` renders at a different pixel size in every chart unless the\n * charts share a scale. Two rules keep all four cells on one size:\n *\n *   1. every chart SVG uses the same viewBox WIDTH of 330, so one user unit is\n *      one user unit everywhere — geometry is scaled to fit that box rather\n *      than the box being fitted to the geometry;\n *   2. every chart SVG is capped at that same 330px, so the scale factor stays\n *      between 0.97 (a 320px column at lg) and 1.00, never above.\n *\n * Together those hold every label at 11.6–12px — the same as the HTML cells'\n * TYPE_LABEL, which is the section's one mono label size. */\nconst LABEL = 12;\nconst CHART_SVG = \"w-full max-w-[330px]\";\n\n/* Every graphic form is drawn, so every graphic form needs a text equivalent.\n * This is the one the screen reader gets; the visual is hidden from it. */\nfunction MetricTable({ metric }: { metric: BenchmarkMetric }) {\n  return (\n    <ul className=\"sr-only\">\n      {metric.rows.map((row) => (\n        <li key={row.name}>\n          {row.name}: {row.value}\n        </li>\n      ))}\n    </ul>\n  );\n}\n\n/* Splits a headline value into its digits and whatever symbol sits against\n * them — \"$0.50\" -> \"$\" + \"0.50\", \"93%\" -> \"93\" + \"%\" — so the symbol can be\n * held off the number. The display step carries -0.06em tracking, which eats\n * into any gap set here, so 0.16em lands at roughly 4px of actual air at 40px.\n * Headline only: the chart labels are left alone. */\nconst FIGURE_PARTS = /^(\\D*)([\\d.,]+)(\\D*)$/;\n\nfunction Figure({ value }: { value: string }) {\n  const parts = FIGURE_PARTS.exec(value);\n  if (!parts) return <>{value}</>;\n\n  const [, prefix, digits, suffix] = parts;\n  return (\n    <>\n      {prefix && <span className=\"mr-[0.16em]\">{prefix}</span>}\n      {digits}\n      {suffix && <span className=\"ml-[0.16em]\">{suffix}</span>}\n    </>\n  );\n}\n\n/* The cell's claim, stated in words and figures before the graphic argues it.\n * Every cell carries one, so the four read as a set however different their\n * bodies are. */\nfunction MetricHeadline({ metric }: { metric: BenchmarkMetric }) {\n  const brand = metric.rows.find((row) => row.brand) ?? metric.rows[0];\n  return (\n    <p className=\"mt-4 flex flex-wrap items-baseline gap-x-2 gap-y-1\">\n      <span className={cn(TYPE_DISPLAY, BRAND)}>\n        <Figure value={brand.value} />\n      </span>\n      <span className={cn(TYPE_LABEL, \"uppercase\", INK_MUTED)}>\n        {metric.headline}\n      </span>\n    </p>\n  );\n}\n\n/* Shared chrome for the graphic half of a cell: the direction line — the rule\n * for reading the header above it — then the claim, then whichever form the\n * metric asked for. Keeping this out of the forms means the\n * four cells stay aligned to each other however different their bodies get. */\nfunction ChartFrame({\n  metric,\n  children,\n}: {\n  metric: BenchmarkMetric;\n  children: React.ReactNode;\n}) {\n  return (\n    <div className=\"flex flex-1 flex-col px-4 py-5 sm:px-5 sm:py-6\">\n      <div>\n        <p className={cn(TYPE_LABEL, \"uppercase\", INK_MUTED)}>\n          {metric.direction}\n        </p>\n        <MetricHeadline metric={metric} />\n      </div>\n\n      {/* The figure floats in the middle of the space the claim leaves, so a\n       * short chart falls between the claim and the graphic rather than\n       * pooling under it. py-10 is the floor on that gap when a chart is tall. */}\n      <div className=\"my-auto py-10\">{children}</div>\n\n      <MetricTable metric={metric} />\n    </div>\n  );\n}\n\n/* ── Form 1 · isometric bar city ────────────────────────────────────────────\n *\n * 2:1 dimetric rather than true 30° isometric: the shallower angle costs half\n * the vertical drop across six bars, which is the difference between fitting a\n * 1/4-width cell and not.\n *\n *   project(x, y, z) = [x - y, (x + y) / 2 - z]\n *\n * 3D is a poor instrument for comparing close values — foreshortening and the\n * top face both read as extra length — so it is spent on F1 and only F1, where\n * the winning margin is 30% and precision is not what the cell is for.\n *\n * Labels are the whole difficulty here: in isometric, the space below-right of\n * every bar is occupied by the next bar, so nothing can sit under a bar. They\n * drop instead on vertical leaders to a lane below the ground line, rotated\n * -90°. A leader at the front vertex of bar i has x = i * PITCH, and bar i+1\n * starts at i * PITCH + 12, so no leader ever crosses a solid.\n *\n * PITCH must clear BW + BD (48) or adjacent bars overlap in projection: a bar\n * spans ±BW from its own centre while centres sit PITCH apart. 50 leaves a\n * 2px gap. */\nconst ISO = { BW: 24, BD: 24, PITCH: 50, HMAX: 84, BASE: 162, LANE: 66 };\n\nconst project = (x: number, y: number, z: number) => [x - y, (x + y) / 2 - z];\nconst pt = (x: number, y: number, z: number) => project(x, y, z).join(\",\");\n\nfunction IsoBar({ row, i }: { row: BenchmarkRow; i: number }) {\n  const { BW, BD, PITCH, HMAX, BASE } = ISO;\n  const x0 = i * PITCH;\n  const x1 = x0 + BW;\n  const h = row.ratio * HMAX;\n\n  const face = faces(row.brand);\n\n  /* The front-most ground vertex: where the leader starts. */\n  const [lx, ly] = project(x1, BD, 0);\n  /* Centre of the top face: mean of its four projected corners. */\n  const [tx, ty] = project(x0 + BW / 2, BD / 2, h);\n\n  return (\n    <g>\n      <title>{`${row.name}: ${row.value}`}</title>\n\n      <polygon\n        points={`${pt(x0, BD, h)} ${pt(x1, BD, h)} ${pt(x1, BD, 0)} ${pt(x0, BD, 0)}`}\n        className={face.left}\n      />\n      <polygon\n        points={`${pt(x1, 0, h)} ${pt(x1, BD, h)} ${pt(x1, BD, 0)} ${pt(x1, 0, 0)}`}\n        className={face.right}\n      />\n      <polygon\n        points={`${pt(x0, 0, h)} ${pt(x1, 0, h)} ${pt(x1, BD, h)} ${pt(x0, BD, h)}`}\n        className={face.top}\n      />\n\n      <line\n        x1={lx}\n        y1={ly}\n        x2={lx}\n        y2={BASE}\n        strokeWidth={1}\n        strokeOpacity={0.5}\n        className={STROKE_RULE}\n      />\n\n      {/* Seated on the top face, which binds the figure to its own solid. The\n       * face is a constant 2*BW wide whatever the bar's height, so this never\n       * crowds. */}\n      <text\n        x={tx}\n        y={ty}\n        textAnchor=\"middle\"\n        dominantBaseline=\"middle\"\n        fontSize={LABEL}\n        className={cn(FONT_MONO, row.brand ? FILL_ON_BRAND : FILL_INK)}\n      >\n        {row.value}\n      </text>\n\n      <text\n        x={lx}\n        y={BASE + 7}\n        textAnchor=\"end\"\n        fontSize={LABEL}\n        transform={`rotate(-90 ${lx} ${BASE + 7})`}\n        className={cn(FONT_MONO, row.brand ? FILL_BRAND : FILL_INK_MUTED)}\n      >\n        {row.short ?? row.name}\n      </text>\n    </g>\n  );\n}\n\nfunction IsoBars({ metric }: { metric: BenchmarkMetric }) {\n  const { BD, PITCH } = ISO;\n  const span = (metric.rows.length - 1) * PITCH + ISO.BW;\n  const [gx1, gy1] = project(-12, BD, 0);\n  const [gx2, gy2] = project(span + 12, BD, 0);\n\n  return (\n    <svg\n      viewBox=\"-40 -94 330 352\"\n      className={CHART_SVG}\n      role=\"img\"\n      aria-label={`${metric.title} by provider, isometric bar chart`}\n    >\n      {/* Ground plane: one hairline the bars stand on and the leaders drop from. */}\n      <line\n        x1={gx1}\n        y1={gy1}\n        x2={gx2}\n        y2={gy2}\n        strokeWidth={1}\n        strokeOpacity={0.5}\n        className={STROKE_RULE}\n      />\n\n      {/* Back to front. Bars never overlap at these spacings, but painting in\n       * depth order keeps that true if PITCH is ever tightened. */}\n      {metric.rows.map((row, i) => (\n        <IsoBar key={row.name} row={row} i={i} />\n      ))}\n    </svg>\n  );\n}\n\n/* ── Form 2 · unit field ───────────────────────────────────────────────────\n *\n * One cell per article, a hundred per provider, filled by how many come back\n * correct. Counted in the direction the cell's own qualifier promises: the\n * column says HIGHER IS BETTER, so the longer comb has to be the better one.\n *\n * The trade this makes: accuracy only runs 72–93%, so the combs differ by 21\n * units out of 100 rather than dramatically. Read as errors the same data\n * spreads 4x and separates far harder — but it inverts the direction, and a\n * chart that contradicts its own label costs more than the contrast is worth.\n *\n * The track takes the full width with its label above rather than beside it;\n * at a hundred units a name column would squeeze each unit under a pixel.\n * Units flex, so the comb stays true at any column width. */\nconst UNITS = 100;\n\nfunction UnitField({ metric }: { metric: BenchmarkMetric }) {\n  const rows = metric.rows.filter((row) => row.n != null);\n\n  return (\n    <div>\n      <p className={cn(TYPE_LABEL, \"uppercase\", INK_MUTED)}>Correct per 100</p>\n\n      <ul className=\"mt-3 space-y-3\">\n        {rows.map((row) => {\n          const correct = Math.round(row.n as number);\n          const ink = row.brand ? BRAND : INK_MUTED;\n\n          return (\n            <li\n              key={row.name}\n              title={`${row.name}: ${correct} correct per 100`}\n            >\n              <div className=\"flex items-baseline justify-between gap-2\">\n                <span className={cn(\"truncate\", TYPE_LABEL, ink)}>\n                  {row.short ?? row.name}\n                </span>\n                <span className={cn(\"shrink-0 tabular-nums\", TYPE_LABEL, ink)}>\n                  {correct}\n                </span>\n              </div>\n\n              <span className=\"mt-1.5 flex gap-px\" aria-hidden=\"true\">\n                {Array.from({ length: UNITS }, (_, k) => (\n                  <span\n                    key={k}\n                    className={cn(\n                      \"h-2 flex-1\",\n                      k < correct\n                        ? row.brand\n                          ? BG_BRAND\n                          : BG_INERT\n                        : BG_TRACK,\n                    )}\n                  />\n                ))}\n              </span>\n            </li>\n          );\n        })}\n      </ul>\n    </div>\n  );\n}\n\n/* ── Form 3 · ring radar ────────────────────────────────────────────────────\n *\n * Coverage is the one column akta does not win, and a bar list said so twice —\n * once by rank, once by putting a visibly shorter bar under the brand name. A\n * polar plot states the same fact more fairly: two dots a hair apart on the\n * outer ring read as \"tied at the top of the field\", which is what a 1.0-point\n * gap on 73.7 actually is.\n *\n * The rings are the grid, not the data — providers sit at their own bearings,\n * and the dots are deliberately NOT joined into a spider polygon. Six rival\n * vendors are not one series, and connecting them would draw a shape whose\n * area means nothing.\n *\n * The radial scale starts at 40%, not 0. On a radial axis that is a real\n * distortion (area goes as r squared), so the grid rings are drawn and\n * labelled — the truncation is stated on the chart rather than hidden in it. */\nconst RADAR = {\n  R: 92,\n  MIN: 40,\n  MAX: 80,\n  RINGS: [50, 60, 70, 80],\n  SWEEP: 24,\n  LABEL_R: 116,\n  HUB: 14,\n};\n\n/* Rounded, because cos(-90°) lands on 6.1e-17 and SVG then carries an\n * exponent-notation coordinate through to the DOM. */\nconst round2 = (n: number) => Math.round(n * 100) / 100;\n\nconst polar = (r: number, deg: number): [number, number] => {\n  const a = (deg * Math.PI) / 180;\n  return [round2(r * Math.cos(a)), round2(r * Math.sin(a))];\n};\n\nconst radarR = (n: number) =>\n  ((n - RADAR.MIN) / (RADAR.MAX - RADAR.MIN)) * RADAR.R;\n\nfunction RingRadar({ metric }: { metric: BenchmarkMetric }) {\n  const { R, RINGS, SWEEP, LABEL_R, HUB } = RADAR;\n\n  /* Brand takes 12 o'clock; the rest follow in the order they were ranked. */\n  const brand = metric.rows.find((row) => row.brand) ?? metric.rows[0];\n  const ordered = [brand, ...metric.rows.filter((row) => !row.brand)];\n  const bearing = (i: number) => -90 + i * (360 / ordered.length);\n\n  const rBrand = radarR(brand.n ?? 0);\n  const [wx1, wy1] = polar(rBrand, -90 - SWEEP);\n  const [wx2, wy2] = polar(rBrand, -90 + SWEEP);\n\n  return (\n    <svg\n      viewBox=\"-165 -130 330 268\"\n      className={CHART_SVG}\n      role=\"img\"\n      aria-label={`${metric.title} by provider, polar plot`}\n    >\n      <defs>\n        <pattern\n          id=\"akta-radar-hatch\"\n          width=\"6\"\n          height=\"6\"\n          patternUnits=\"userSpaceOnUse\"\n        >\n          <circle\n            cx=\"3\"\n            cy=\"3\"\n            r=\"1.1\"\n            fillOpacity=\"0.45\"\n            className={FILL_BRAND}\n          />\n        </pattern>\n      </defs>\n\n      <g strokeOpacity=\"0.45\" fill=\"none\" className={STROKE_RULE}>\n        {RINGS.map((v) => (\n          <circle key={v} r={radarR(v)} strokeWidth={1} />\n        ))}\n        {/* Spokes stay well under the rings — they associate a marker with its\n         * label without competing with either. */}\n        <g strokeOpacity=\"0.3\">\n          {ordered.map((row, i) => {\n            const [x, y] = polar(R, bearing(i));\n            return (\n              <line\n                key={row.name}\n                x1={0}\n                y1={0}\n                x2={x}\n                y2={y}\n                strokeWidth={1}\n              />\n            );\n          })}\n        </g>\n      </g>\n\n      {/* Tick dots where each ring meets the crosshair — the reference's way of\n       * making a grid read as measured. */}\n      <g fillOpacity=\"0.7\" className={FILL_RULE}>\n        {[90, 270].map((deg) => (\n          <g key={deg}>\n            {RINGS.map((v) => {\n              const [x, y] = polar(radarR(v), deg);\n              return <circle key={v} cx={x} cy={y} r={1.8} />;\n            })}\n          </g>\n        ))}\n      </g>\n\n      {/* Brand reach as area, in a halftone the grid cannot be mistaken for. */}\n      <path\n        d={`M 0 0 L ${wx1} ${wy1} A ${rBrand} ${rBrand} 0 0 1 ${wx2} ${wy2} Z`}\n        fill=\"url(#akta-radar-hatch)\"\n      />\n\n      {ordered.map((row, i) => {\n        const deg = bearing(i);\n        const [x, y] = polar(radarR(row.n ?? 0), deg);\n        const [lx, ly] = polar(LABEL_R, deg);\n        const cos = Math.cos((deg * Math.PI) / 180);\n        const anchor =\n          Math.abs(cos) < 0.25 ? \"middle\" : cos > 0 ? \"start\" : \"end\";\n\n        return (\n          <g key={row.name}>\n            <title>{`${row.name}: ${row.value}`}</title>\n\n            {/* Markers carry the data, so they outweigh every grid mark: a halo\n             * on the brand, a 2.5px canvas ring on all of them so a dot stays\n             * legible where it lands on a ring or a spoke. */}\n            {row.brand && (\n              <circle\n                cx={x}\n                cy={y}\n                r={12}\n                fill=\"none\"\n                strokeWidth={1}\n                strokeOpacity={0.35}\n                className={STROKE_BRAND}\n              />\n            )}\n            <circle\n              cx={x}\n              cy={y}\n              r={row.brand ? 7 : 5.5}\n              strokeWidth={2.5}\n              className={cn(row.brand ? FILL_BRAND : FILL_INERT, STROKE_CANVAS)}\n            />\n\n            <text\n              x={lx}\n              y={ly}\n              textAnchor={anchor}\n              fontSize={LABEL}\n              className={cn(FONT_MONO, row.brand ? FILL_BRAND : FILL_INK)}\n            >\n              {row.name}\n            </text>\n            <text\n              x={lx}\n              y={ly + 14}\n              textAnchor={anchor}\n              fontSize={LABEL}\n              className={cn(FONT_MONO, row.brand ? FILL_BRAND : FILL_INK_MUTED)}\n            >\n              {row.value}\n            </text>\n          </g>\n        );\n      })}\n\n      {/* Hub, drawn last so the spokes tuck under it. Same treatment as the\n       * icon chip in the cell's title band. */}\n      <circle\n        r={HUB}\n        strokeWidth={1}\n        strokeOpacity={0.6}\n        className={cn(FILL_BRAND_TILE, STROKE_BRAND_BORDER)}\n      />\n      <g transform=\"translate(-8,-8)\" className={INK_BRAND}>\n        <HugeiconsIcon icon={metric.icon} size={16} strokeWidth={1.5} />\n      </g>\n    </svg>\n  );\n}\n\n/* ── Form 4 · isometric slab stack ──────────────────────────────────────────\n *\n * Cost is a ratio story, not a rank story: $0.50 against $2.85 is 5.7x, and\n * six bars sorted cheapest-first buried that under their own ordering. Stacked\n * slabs of identical footprint put thickness — the only thing that varies —\n * directly against thickness.\n *\n * Every slab projects to the same rhombus, so a gap in z is a clean vertical\n * gap on screen: nothing occludes anything, and each slab shows its top face\n * and both edges. Cheapest rides on top, so cost reads as weight piling up\n * underneath it.\n *\n * Scaled to the shared 330-unit box (x1.5 from the 220 this was drawn in) so\n * the stack keeps its physical size while its labels join the common scale. */\nconst SLAB = { W: 69, D: 69, TMAX: 69, GAP: 18 };\n\nfunction SlabStack({ metric }: { metric: BenchmarkMetric }) {\n  const { W, D, TMAX, GAP } = SLAB;\n\n  /* Rows arrive cheapest-first; the stack builds from the bottom, so the most\n   * expensive is laid down first and the brand wafer ends up on top. */\n  const stack = [...metric.rows].reverse();\n  const slabs = stack.reduce<{ row: BenchmarkRow; t: number; z0: number }[]>(\n    (acc, row) => {\n      const prev = acc[acc.length - 1];\n      const z0 = prev ? prev.z0 + prev.t + GAP : 0;\n      return [...acc, { row, t: row.ratio * TMAX, z0 }];\n    },\n    [],\n  );\n\n  return (\n    <svg\n      viewBox=\"-190 -270 330 345\"\n      className={CHART_SVG}\n      role=\"img\"\n      aria-label={`${metric.title} by provider, stacked isometric slabs`}\n    >\n      {slabs.map(({ row, t, z0 }) => {\n        const face = faces(row.brand);\n        const z1 = z0 + t;\n        /* The left and right vertical edges project to (D/2 - z) and (W/2 - z),\n         * not to the front edge's (W+D)/2 - z. Using the front edge put every\n         * label 23px low — one plate out of step with its own slab. */\n        const [, midY] = project(0, D, z0 + t / 2);\n        const ink = row.brand ? FILL_BRAND : FILL_INK_MUTED;\n\n        return (\n          <g key={row.name}>\n            <title>{`${row.name}: ${row.value}`}</title>\n\n            <polygon\n              points={`${pt(0, D, z1)} ${pt(W, D, z1)} ${pt(W, D, z0)} ${pt(0, D, z0)}`}\n              className={face.left}\n            />\n            <polygon\n              points={`${pt(W, 0, z1)} ${pt(W, D, z1)} ${pt(W, D, z0)} ${pt(W, 0, z0)}`}\n              className={face.right}\n            />\n            <polygon\n              points={`${pt(0, 0, z1)} ${pt(W, 0, z1)} ${pt(W, D, z1)} ${pt(0, D, z1)}`}\n              className={face.top}\n            />\n\n            <text\n              x={-(W + D) / 2 - 8}\n              y={midY + 3}\n              textAnchor=\"end\"\n              fontSize={LABEL}\n              className={cn(FONT_MONO, ink)}\n            >\n              {row.name}\n            </text>\n            <text\n              x={(W + D) / 2 + 8}\n              y={midY + 3}\n              textAnchor=\"start\"\n              fontSize={LABEL}\n              className={cn(FONT_MONO, ink)}\n            >\n              {row.value}\n            </text>\n          </g>\n        );\n      })}\n    </svg>\n  );\n}\n\n/* ── Form 5 · ranked bars ───────────────────────────────────────────────────\n * One ranked row: name and figure on a line, the bar beneath it. The bar is a\n * track plus a fill rather than a border, so it survives any surface. Not used\n * by the default data, but kept so a metric can opt back into the plain form. */\nfunction MetricBar({ row }: { row: BenchmarkRow }) {\n  return (\n    <li className=\"space-y-1.5\">\n      <div className=\"flex items-baseline justify-between gap-3\">\n        <span\n          className={cn(\n            \"truncate\",\n            TYPE_LABEL,\n            \"uppercase\",\n            row.brand ? INK : INK_MUTED,\n          )}\n        >\n          {row.name}\n        </span>\n        <span\n          className={cn(\n            \"shrink-0 tabular-nums\",\n            TYPE_LABEL,\n            row.brand ? BRAND : INK_MUTED,\n          )}\n        >\n          {row.value}\n        </span>\n      </div>\n\n      <div className={cn(\"h-1.5 w-full\", BG_TRACK_HOVER)}>\n        <div\n          className={cn(\"h-full\", row.brand ? BG_BRAND : BG_INERT)}\n          style={{ width: `${Math.round(row.ratio * 100)}%` }}\n        />\n      </div>\n    </li>\n  );\n}\n\nfunction RankedBars({ metric }: { metric: BenchmarkMetric }) {\n  return (\n    <ul className=\"mt-5 space-y-3\" aria-hidden=\"true\">\n      {metric.rows.map((row) => (\n        <MetricBar key={row.name} row={row} />\n      ))}\n    </ul>\n  );\n}\n\nconst CHARTS: Record<\n  BenchmarkMetric[\"chart\"],\n  React.ComponentType<{ metric: BenchmarkMetric }>\n> = {\n  iso: IsoBars,\n  units: UnitField,\n  radar: RingRadar,\n  slab: SlabStack,\n  bars: RankedBars,\n};\n\nfunction MetricChart({ metric }: { metric: BenchmarkMetric }) {\n  const Form = CHARTS[metric.chart];\n  return (\n    <ChartFrame metric={metric}>\n      <Form metric={metric} />\n    </ChartFrame>\n  );\n}\n\n/* ── Cell ───────────────────────────────────────────────────────────────────\n * One bento cell, read like a table column: the header band names the\n * metric and says what it measures, then below a rule comes the rule for\n * reading it, the claim, and the graphic that argues it. Two bands, one\n * rule between them, no boxes. Title first because the four headers then line up across the band —\n * under charts of different heights they never did — and because \"higher is\n * better\" only means something once you know what it is higher *of*. The\n * cell's own right and bottom rules come from RULE_CELL, so the grid keeps\n * separating correctly at one, two or four columns. */\nfunction MetricCell({ metric }: { metric: BenchmarkMetric }) {\n  return (\n    <article\n      className={cn(\n        \"flex flex-col\",\n        RULE_CELL,\n        \"akta-notch [--akta-notch-arm:0px] [--akta-notch-inset:0px] [--akta-notch-weight:1px] lg:[--akta-notch-arm:10px]\",\n        NOTCH_GRAY,\n      )}\n    >\n      {/* Header band: icon + name, then the blurb under it. The whole\n       * definition of the column sits above the rule; everything below it is\n       * evidence. */}\n      <div className=\"px-4 py-4 sm:px-5 sm:py-5\">\n        <div className=\"flex items-center gap-3\">\n          <span\n            className={cn(\n              \"flex size-7 shrink-0 items-center justify-center\",\n              INK_BRAND,\n              BG_BRAND_TILE,\n              RING_TILE,\n            )}\n            aria-hidden=\"true\"\n          >\n            <HugeiconsIcon icon={metric.icon} size={16} strokeWidth={1.5} />\n          </span>\n\n          <h3 className={cn(TYPE_HEADING_20, INK)}>\n            {metric.title}{\" \"}\n            <span className={cn(TYPE_LABEL, \"uppercase\", INK_MUTED)}>\n              ({metric.qualifier})\n            </span>\n          </h3>\n        </div>\n\n        <p className={cn(\"mt-3 max-w-[42ch]\", TYPE_COPY_14, INK_MUTED)}>\n          {metric.blurb}\n        </p>\n      </div>\n\n      <div className={cn(\"flex flex-1 flex-col\", RULE_T)}>\n        <MetricChart metric={metric} />\n      </div>\n    </article>\n  );\n}\n\n/* ── Section ──────────────────────────────────────────────────────────────\n * Header on the grid, then a full-bleed band whose top and bottom rules cross\n * the viewport while the cells stay on the 1440 grid. */\nexport interface AktaBenchmarks01Props {\n  eyebrow?: string;\n  heading?: string;\n  description?: string;\n  /* Replace the four default readouts. Each metric picks its own chart form. */\n  metrics?: BenchmarkMetric[];\n  className?: string;\n}\n\nfunction AktaBenchmarks01({\n  eyebrow = \"Benchmarks\",\n  heading = \"We Ranked Top as News Provider\",\n  description = \"Best news quality at the lowest cost across News APIs, Agentic Search APIs, LLMs, and Bulk Scrapers.\",\n  metrics = METRICS,\n  className = \"\",\n}: AktaBenchmarks01Props) {\n  return (\n    <section\n      className={cn(\n        \"relative isolate overflow-hidden\",\n        FONT_SANS,\n        BG_CANVAS,\n        INK,\n        className,\n      )}\n    >\n      <div className={cn(GRID, \"relative\", RULE_X_LG)}>\n        <SectionHeader\n          eyebrow={eyebrow}\n          heading={heading}\n          description={description}\n        />\n      </div>\n\n      <div className={cn(\"relative\", RULE_Y)}>\n        <div\n          data-akta-reveal\n          className={cn(\n            GRID,\n            \"relative grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4\",\n            RULE_L_LG,\n          )}\n        >\n          {metrics.map((metric) => (\n            <MetricCell key={metric.title} metric={metric} />\n          ))}\n        </div>\n      </div>\n    </section>\n  );\n}\n\nexport default AktaBenchmarks01;\n",
      "type": "registry:component",
      "target": "~/components/craftui/sections/akta-benchmarks-01/default.tsx"
    }
  ],
  "meta": {
    "displayName": "Akta Benchmarks 01 - Akta Benchmarks 01",
    "description": "Four benchmark readouts on the 1440 grid, separated by hairline rules and corner notches rather than cards. F1 is an isometric bar city, accuracy a hundred-unit comb, coverage a polar ring plot, and cost a stack of slabs whose thickness is price. Each cell leads with the brand figure in mono display type, and blue is spent only on the one row the section is about.",
    "category": "feature-section",
    "tags": [
      "benchmarks",
      "comparison",
      "data",
      "chart",
      "svg",
      "isometric",
      "radar",
      "bento",
      "technical",
      "monospace",
      "design-system"
    ],
    "animationType": "scroll"
  }
}