{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "records-table",
  "type": "registry:component",
  "title": "Records table",
  "description": "CRM-style grid with tags, sorting, and relationship status.",
  "categories": [
    "ai-interface"
  ],
  "registryDependencies": [
    "https://irisui.dev/r/foundation.json",
    "https://irisui.dev/r/utils.json",
    "https://irisui.dev/r/glide-menu.json"
  ],
  "files": [
    {
      "path": "src/registry/components/records-table/records-table.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport GlideMenu from \"../glide-menu/glide-menu\";\n\n/* ─────────────────────────────────────────────────────────\n * RECORDS TABLE — an AI spreadsheet grid. Columns are\n * *properties*: click a header to open its configuration\n * popover (type, tool, grounding, inputs, prompt, run), add\n * a new AI property from the + header, and watch cells\n * resolve row-by-row while it calculates.\n * ───────────────────────────────────────────────────────── */\n\ntype Strength = \"strong\" | \"weak\" | \"veryweak\" | \"none\";\ntype SortKey = \"name\" | \"last\" | \"strength\";\ntype ColumnKey = \"company\" | \"categories\" | \"last\" | \"strength\" | \"links\" | \"ai\";\n\nconst DEFAULT_COLUMN_WIDTHS: Record<ColumnKey, number> = {\n  company: 270,\n  categories: 275,\n  last: 190,\n  strength: 210,\n  links: 175,\n  ai: 240,\n};\n\nconst STRENGTH: Record<Strength, { label: string; color: string; rank: number }> = {\n  strong: { label: \"Very strong\", color: \"var(--green)\", rank: 3 },\n  weak: { label: \"Weak\", color: \"var(--orange)\", rank: 2 },\n  veryweak: { label: \"Very weak\", color: \"var(--red)\", rank: 1 },\n  none: { label: \"No communication\", color: \"var(--ink-3)\", rank: 0 },\n};\n\n// A single mid-lightness base hue per tag. Background, text, and border are\n// derived from this via color-mix() against the theme tokens in Tag,\n// so the chips adapt to light and dark automatically (same pattern as FilterTable).\ntype TagColor = { base: string };\n\nconst TAG_PALETTE: Record<string, TagColor> = {\n  amber: { base: \"oklch(0.76 0.13 70)\" },\n  lime: { base: \"oklch(0.77 0.16 122)\" },\n  yellow: { base: \"oklch(0.80 0.15 101)\" },\n  purple: { base: \"oklch(0.62 0.18 293)\" },\n  orange: { base: \"oklch(0.71 0.16 48)\" },\n  cyan: { base: \"oklch(0.72 0.10 221)\" },\n  red: { base: \"oklch(0.64 0.19 27)\" },\n  magenta: { base: \"oklch(0.66 0.21 323)\" },\n  green: { base: \"oklch(0.70 0.13 162)\" },\n  pink: { base: \"oklch(0.67 0.19 3)\" },\n};\n\nconst TAG_COLORS: Record<string, TagColor> = {\n  \"Pre-seed\": TAG_PALETTE.amber,\n  Seed: TAG_PALETTE.lime,\n  \"Series A\": TAG_PALETTE.purple,\n  \"Series B+\": TAG_PALETTE.magenta,\n  Growth: TAG_PALETTE.orange,\n  SaaS: TAG_PALETTE.cyan,\n  B2B: TAG_PALETTE.yellow,\n  Marketplaces: TAG_PALETTE.pink,\n  \"Deep tech\": TAG_PALETTE.red,\n  Climate: TAG_PALETTE.green,\n  Energy: TAG_PALETTE.yellow,\n  Fintech: TAG_PALETTE.cyan,\n  \"AI/ML\": TAG_PALETTE.purple,\n  DevTools: TAG_PALETTE.red,\n  Mobility: TAG_PALETTE.orange,\n  Generalist: TAG_PALETTE.green,\n};\n\nexport type RecordRow = {\n  id: string;\n  name: string;\n  tags: string[];\n  last: string;\n  strength: Strength;\n  website?: string;\n};\n\n/* a FundDaddy register: fictional investor firms with their stage and focus */\nconst INITIAL_ROWS: RecordRow[] = [\n  {\n    id: \"halcyon-ridge\",\n    name: \"Halcyon Ridge Ventures — Berlin\",\n    tags: [\"Seed\", \"SaaS\", \"B2B\"],\n    last: \"2 days ago\",\n    strength: \"strong\",\n    website: \"halcyonridge.example\",\n  },\n  {\n    id: \"aegean-seed\",\n    name: \"Aegean Seed Partners — Athens\",\n    tags: [\"Seed\", \"B2B\", \"Deep tech\"],\n    last: \"9 days ago\",\n    strength: \"strong\",\n    website: \"aegeanseed.example\",\n  },\n  {\n    id: \"northlight\",\n    name: \"Northlight Angels — Copenhagen\",\n    tags: [\"Pre-seed\", \"Climate\", \"Energy\"],\n    last: \"3 weeks ago\",\n    strength: \"weak\",\n    website: \"northlight.example\",\n  },\n  {\n    id: \"ferrovia\",\n    name: \"Ferrovia Growth — Milan\",\n    tags: [\"Growth\", \"Fintech\"],\n    last: \"2 months ago\",\n    strength: \"veryweak\",\n    website: \"ferrovia.example\",\n  },\n  {\n    id: \"kiln-street\",\n    name: \"Kiln Street Labs — Amsterdam\",\n    tags: [\"Pre-seed\", \"Generalist\"],\n    last: \"15 days ago\",\n    strength: \"weak\",\n    website: \"kilnstreet.example\",\n  },\n  {\n    id: \"morrow\",\n    name: \"Morrow Corporate Ventures — Paris\",\n    tags: [\"Series A\", \"Mobility\"],\n    last: \"No contact\",\n    strength: \"none\",\n  },\n  {\n    id: \"quayside\",\n    name: \"Quayside Fund — Dublin\",\n    tags: [\"Series A\", \"AI/ML\", \"DevTools\"],\n    last: \"4 days ago\",\n    strength: \"strong\",\n    website: \"quayside.example\",\n  },\n  {\n    id: \"tidewater-row\",\n    name: \"Tidewater Row Capital — Rotterdam\",\n    tags: [\"Seed\", \"Marketplaces\"],\n    last: \"5 months ago\",\n    strength: \"veryweak\",\n    website: \"tidewaterrow.example\",\n  },\n  {\n    id: \"saltmarsh\",\n    name: \"Saltmarsh Seed — Lisbon\",\n    tags: [\"Pre-seed\", \"Marketplaces\"],\n    last: \"about 1 month ago\",\n    strength: \"weak\",\n    website: \"saltmarsh.example\",\n  },\n  {\n    id: \"orchard-lane\",\n    name: \"Orchard Lane Ventures — Vienna\",\n    tags: [\"Seed\", \"Fintech\", \"B2B\"],\n    last: \"6 days ago\",\n    strength: \"strong\",\n    website: \"orchardlane.example\",\n  },\n  {\n    id: \"juniper-quay\",\n    name: \"Juniper Quay Partners — Oslo\",\n    tags: [\"Series A\", \"Climate\"],\n    last: \"8 months ago\",\n    strength: \"veryweak\",\n  },\n  {\n    id: \"granite-hollow\",\n    name: \"Granite Hollow Fund — Zürich\",\n    tags: [\"Series B+\", \"Deep tech\"],\n    last: \"over 1 year ago\",\n    strength: \"veryweak\",\n    website: \"granitehollow.example\",\n  },\n  {\n    id: \"lanternfield\",\n    name: \"Lanternfield Angels — Kraków\",\n    tags: [\"Pre-seed\", \"DevTools\"],\n    last: \"18 days ago\",\n    strength: \"weak\",\n    website: \"lanternfield.example\",\n  },\n  {\n    id: \"olive-terrace\",\n    name: \"Olive Terrace Seed — Thessaloniki\",\n    tags: [\"Seed\", \"B2B\", \"SaaS\"],\n    last: \"3 days ago\",\n    strength: \"strong\",\n    website: \"oliveterrace.example\",\n  },\n  {\n    id: \"harbourline\",\n    name: \"Harbourline Capital — Gothenburg\",\n    tags: [\"Growth\", \"Energy\"],\n    last: \"No contact\",\n    strength: \"none\",\n    website: \"harbourline.example\",\n  },\n  {\n    id: \"cobalt-meadow\",\n    name: \"Cobalt Meadow Ventures — Madrid\",\n    tags: [\"Seed\", \"SaaS\"],\n    last: \"2 months ago\",\n    strength: \"weak\",\n    website: \"cobaltmeadow.example\",\n  },\n  {\n    id: \"ashgrove\",\n    name: \"Ashgrove Labs — Brussels\",\n    tags: [\"Pre-seed\", \"Generalist\"],\n    last: \"almost 2 years ago\",\n    strength: \"none\",\n  },\n  {\n    id: \"northpine\",\n    name: \"Northpine Seed — Helsinki\",\n    tags: [\"Seed\", \"AI/ML\"],\n    last: \"11 days ago\",\n    strength: \"strong\",\n    website: \"northpine.example\",\n  },\n  {\n    id: \"sundial\",\n    name: \"Sundial Ventures — Barcelona\",\n    tags: [\"Series A\", \"Marketplaces\", \"Fintech\"],\n    last: \"5 weeks ago\",\n    strength: \"weak\",\n    website: \"sundial.example\",\n  },\n  {\n    id: \"pebble-coast\",\n    name: \"Pebble Coast Capital — Porto\",\n    tags: [\"Seed\", \"Fintech\"],\n    last: \"10 months ago\",\n    strength: \"veryweak\",\n    website: \"pebblecoast.example\",\n  },\n  {\n    id: \"ironleaf\",\n    name: \"Ironleaf Partners — Munich\",\n    tags: [\"Series A\", \"Mobility\", \"B2B\"],\n    last: \"7 days ago\",\n    strength: \"strong\",\n    website: \"ironleaf.example\",\n  },\n  {\n    id: \"tallow-street\",\n    name: \"Tallow Street Angels — Warsaw\",\n    tags: [\"Pre-seed\", \"B2B\"],\n    last: \"about 1 month ago\",\n    strength: \"weak\",\n  },\n  {\n    id: \"kastro\",\n    name: \"Kastro Seed — Heraklion\",\n    tags: [\"Pre-seed\", \"Deep tech\"],\n    last: \"5 days ago\",\n    strength: \"strong\",\n    website: \"kastroseed.example\",\n  },\n  {\n    id: \"brickfield\",\n    name: \"Brickfield Growth — Prague\",\n    tags: [\"Growth\", \"SaaS\", \"B2B\"],\n    last: \"over 1 year ago\",\n    strength: \"veryweak\",\n    website: \"brickfield.example\",\n  },\n];\n\n/* the AI column resolves to similar firms from the same register */\nconst AI_LABEL = \"Similar firms\";\nconst SIMILAR_POOL = [\n  \"Harbourline Capital\",\n  \"Ironleaf Partners\",\n  \"Olive Terrace Seed\",\n  \"Cobalt Meadow Ventures\",\n  \"Ashgrove Labs\",\n  \"Pebble Coast Capital\",\n  \"Sundial Ventures\",\n  \"Northpine Seed\",\n];\nconst similarFirmsFor = (index: number) => `${SIMILAR_POOL[index % 8]}, ${SIMILAR_POOL[(index + 3) % 8]}`;\n\n/* ── grid styling ─────────────────────────────────────────\n * One quiet surface, precise rules, colour reserved for meaning. The first column stays an anchor (sticky left) while\n * both axes scroll inside the table viewport; the header sticks to the top and the calculation row to the bottom.\n * Row hover and selection tint every cell of the row; the column whose property is being configured (\"colsel\")\n * carries its own accent tint, one step stronger on hover and selection. */\nconst CELL = \"h-[35px] overflow-hidden px-2 text-ellipsis whitespace-nowrap\";\nconst STICKY_CELL = \"sticky left-0 z-2 bg-surface shadow-[5px_0_8px_-10px_oklch(0_0_0/0.4)]\";\nconst HEADER_CELL = \"sticky top-0 z-5 h-[35px] bg-surface p-0 text-[12.5px] font-medium text-ink-2\";\nconst HEADER_COLSEL = \"bg-[color-mix(in_srgb,var(--accent)_8%,var(--surface))] shadow-[inset_0_2px_0_var(--accent)]\";\nconst FOOTER_CELL = \"sticky bottom-0 z-4 bg-inset text-[14px] leading-none font-medium text-ink-2\";\nconst FOOTER_VALUE = \"inline-flex h-[35px] items-center align-middle\";\nconst STRENGTH_DOT = \"inline-block size-2 flex-[0_0_8px] rounded-full\";\nconst RESIZE_HANDLE =\n  \"absolute top-0 -right-1.5 z-12 h-full w-3 cursor-col-resize touch-none after:absolute after:top-[7px] after:bottom-[7px] after:left-[5px] after:w-0.5 after:scale-y-[0.65] after:rounded-full after:bg-accent after:opacity-0 after:content-[''] after:[transition:opacity_120ms_ease-out,scale_150ms_var(--ease-out-strong)] hover:after:scale-y-100 hover:after:opacity-100\";\n\n/** Background tiers for a body cell: plain, row hovered, row selected, each tinted when its column is selected. */\nfunction rowCellClass(selectedRow: boolean, colsel: boolean, extra?: string) {\n  return cn(\n    CELL,\n    extra,\n    \"[transition:background-color_120ms_ease-out,color_120ms_ease-out]\",\n    colsel && \"bg-[color-mix(in_srgb,var(--accent)_4%,var(--surface))]\",\n    selectedRow\n      ? colsel\n        ? \"bg-[color-mix(in_srgb,var(--accent)_10%,var(--surface))]\"\n        : \"bg-[color-mix(in_srgb,var(--accent)_7%,var(--surface))]\"\n      : colsel\n        ? \"group-hover/row:bg-[color-mix(in_srgb,var(--accent)_8%,var(--surface))]\"\n        : \"group-hover/row:bg-hover\",\n  );\n}\n\nfunction Icon({\n  children,\n  size = 14,\n  strokeWidth = 1.8,\n}: {\n  children: React.ReactNode;\n  size?: number;\n  strokeWidth?: number;\n}) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={strokeWidth}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      aria-hidden=\"true\"\n    >\n      {children}\n    </svg>\n  );\n}\n\n/* glyph library for property types & tools */\nconst TYPE_GLYPHS: Record<string, React.ReactNode> = {\n  Text: <path d=\"M4 6h16M4 12h10M4 18h7\" />,\n  File: (\n    <g>\n      <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n      <path d=\"M14 2v6h6\" />\n    </g>\n  ),\n  Collection: (\n    <g>\n      <ellipse cx=\"12\" cy=\"5\" rx=\"8\" ry=\"3\" />\n      <path d=\"M4 5v14c0 1.66 3.58 3 8 3s8-1.34 8-3V5M4 12c0 1.66 3.58 3 8 3s8-1.34 8-3\" />\n    </g>\n  ),\n  \"Single select\": (\n    <g>\n      <circle cx=\"12\" cy=\"12\" r=\"9\" />\n      <path d=\"m8.5 12 2.4 2.4 4.6-4.9\" />\n    </g>\n  ),\n  \"Multi select\": (\n    <g>\n      <path d=\"M11 6h9M11 12h9M11 18h9\" />\n      <path d=\"M4 6l1.5 1.5L8 5M4 12l1.5 1.5L8 11M4 18l1.5 1.5L8 17\" />\n    </g>\n  ),\n  URL: (\n    <g>\n      <path d=\"M10 13a5 5 0 0 0 7.1.1l2-2a5 5 0 0 0-7.1-7.1l-1.1 1.1\" />\n      <path d=\"M14 11a5 5 0 0 0-7.1-.1l-2 2A5 5 0 0 0 12 20l1.1-1.1\" />\n    </g>\n  ),\n  Reference: <path d=\"M7 17 17 7M9 7h8v8\" />,\n  JSON: (\n    <g>\n      <path d=\"M8 4c-2 0-2 2-2 3s.5 3-2 3c2.5 0 2 2 2 3s0 3 2 3\" />\n      <path d=\"M16 4c2 0 2 2 2 3s-.5 3 2 3c-2.5 0-2 2-2 3s0 3-2 3\" />\n    </g>\n  ),\n  \"File splitter\": (\n    <g>\n      <rect x=\"8\" y=\"8\" width=\"12\" height=\"12\" rx=\"2\" />\n      <path d=\"M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2\" />\n    </g>\n  ),\n  Date: (\n    <g>\n      <rect x=\"3\" y=\"5\" width=\"18\" height=\"16\" rx=\"2.5\" />\n      <path d=\"M8 3v4M16 3v4M3 10h18\" />\n    </g>\n  ),\n};\n\nconst TOOL_GLYPHS: Record<string, React.ReactNode> = {\n  model: (\n    <path d=\"M12 3l1.7 5.1a2 2 0 0 0 1.2 1.2L20 11l-5.1 1.7a2 2 0 0 0-1.2 1.2L12 19l-1.7-5.1a2 2 0 0 0-1.2-1.2L4 11l5.1-1.7a2 2 0 0 0 1.2-1.2z\" />\n  ),\n  web: (\n    <g>\n      <circle cx=\"12\" cy=\"12\" r=\"9\" />\n      <path d=\"M3 12h18M12 3a13.5 13.5 0 0 1 3.5 9 13.5 13.5 0 0 1-3.5 9 13.5 13.5 0 0 1-3.5-9A13.5 13.5 0 0 1 12 3z\" />\n    </g>\n  ),\n  user: (\n    <g>\n      <circle cx=\"12\" cy=\"8\" r=\"4\" />\n      <path d=\"M4 21v-1a6 6 0 0 1 6-6h4a6 6 0 0 1 6 6v1\" />\n    </g>\n  ),\n};\n\n/* per-property configuration shown in the popover */\ntype Prompt = { before: string; chip?: string; after?: string };\ntype ToolKind = \"model\" | \"web\" | \"user\";\ntype ColumnMeta = { type: string; tool: string; toolKind: ToolKind; inputs?: string; prompt?: Prompt };\n\nconst COLUMN_META: Record<string, ColumnMeta> = {\n  Firm: { type: \"Text\", tool: \"User input\", toolKind: \"user\" },\n  Focus: {\n    type: \"Multi select\",\n    tool: \"Large\",\n    toolKind: \"model\",\n    inputs: \"Firm\",\n    prompt: { before: \"Tag each \", chip: \"Firm\", after: \" with its stages and focus.\" },\n  },\n  \"Last interaction\": { type: \"Date\", tool: \"User input\", toolKind: \"user\" },\n  \"Connection strength\": {\n    type: \"Single select\",\n    tool: \"Large\",\n    toolKind: \"model\",\n    inputs: \"Last interaction\",\n    prompt: { before: \"Score the relationship from \", chip: \"Last interaction\", after: \".\" },\n  },\n  Links: {\n    type: \"URL\",\n    tool: \"Web search\",\n    toolKind: \"web\",\n    inputs: \"Firm\",\n    prompt: { before: \"Find the website for \", chip: \"Firm\", after: \".\" },\n  },\n  [AI_LABEL]: {\n    type: \"Text\",\n    tool: \"Web search\",\n    toolKind: \"web\",\n    inputs: \"Firm\",\n    prompt: { before: \"Find firms similar to \", chip: \"Firm\" },\n  },\n};\n\nconst NEW_PROPERTY_TYPES = [\n  \"Text\",\n  \"File\",\n  \"Collection\",\n  \"Single select\",\n  \"Multi select\",\n  \"URL\",\n  \"Reference\",\n  \"JSON\",\n  \"File splitter\",\n];\nconst MODEL_OPTIONS = [\"Large\", \"Standard\", \"Small\"];\nconst INPUT_OPTIONS = [\"Firm\", \"Focus\", \"Last interaction\", \"Connection strength\", \"Links\"];\n\nfunction Checkbox({\n  checked,\n  mixed = false,\n  onChange,\n  label,\n  className,\n}: {\n  checked: boolean;\n  mixed?: boolean;\n  onChange: () => void;\n  label: string;\n  className?: string;\n}) {\n  const active = checked || mixed;\n  return (\n    <label\n      className={cn(\n        \"group/check relative inline-flex size-6 flex-[0_0_24px] items-center justify-center rounded-chip\",\n        className,\n      )}\n      title={label}\n    >\n      <input\n        type=\"checkbox\"\n        checked={checked}\n        onChange={onChange}\n        aria-label={label}\n        className=\"peer absolute size-px opacity-0\"\n      />\n      <span\n        className={cn(\n          \"inline-flex size-[18px] items-center justify-center rounded-[6px] border [transition:border-color_140ms_ease-out,background-color_140ms_ease-out,box-shadow_140ms_ease-out,scale_140ms_var(--ease-out-strong)] group-active/check:scale-[0.96] peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-accent\",\n          active\n            ? \"border-accent bg-accent text-white shadow-none\"\n            : \"border-[oklch(0.845_0.011_247.953)] bg-[oklch(1_0_0)] text-[oklch(0.446_0.018_251.343)] group-hover/check:border-[oklch(0.773_0.016_251.194)] group-hover/check:bg-[oklch(0.966_0.003_228.784)] dark:border-[oklch(0.45_0.017_254.711)] dark:bg-[oklch(0.346_0.015_252.294)] dark:text-[oklch(0.917_0.007_247.901)] dark:group-hover/check:border-[oklch(0.521_0.019_254)] dark:group-hover/check:bg-[oklch(0.391_0.016_251.761)]\",\n        )}\n      >\n        {mixed ? (\n          <span className=\"h-[1.5px] w-2 rounded-full bg-white\" />\n        ) : checked ? (\n          <Icon size={12}>\n            <path d=\"m5 12 4 4L19 6\" />\n          </Icon>\n        ) : null}\n      </span>\n    </label>\n  );\n}\n\nfunction Tag({ name }: { name: string }) {\n  const color = TAG_COLORS[name] ?? { base: \"var(--ink-3)\" };\n  return (\n    <span\n      className=\"inline-flex h-[23px] max-w-[115px] shrink-0 cursor-pointer items-center overflow-hidden rounded-control border border-[color-mix(in_srgb,var(--tag-base)_32%,var(--surface))] bg-[color-mix(in_srgb,var(--tag-base)_18%,var(--surface))] px-[7px] text-[13px] font-medium text-ellipsis whitespace-nowrap text-[color-mix(in_srgb,var(--tag-base)_92%,var(--ink))] dark:border-[color-mix(in_srgb,var(--tag-base)_34%,var(--surface))] dark:bg-[color-mix(in_srgb,var(--tag-base)_34%,var(--surface))] dark:text-[color-mix(in_srgb,var(--tag-base)_86%,white)]\"\n      style={{ \"--tag-base\": color.base } as React.CSSProperties}\n    >\n      {name}\n    </span>\n  );\n}\n\nconst MORE_TAG =\n  \"inline-flex h-[23px] shrink-0 items-center rounded-chip border border-line-strong bg-inset px-[7px] text-[11px] font-medium text-ink-3\";\n\nfunction TagList({ tags }: { tags: string[] }) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const measureRef = useRef<HTMLDivElement>(null);\n  const [visibleCount, setVisibleCount] = useState(tags.length);\n\n  useLayoutEffect(() => {\n    const container = containerRef.current;\n    const measure = measureRef.current;\n    if (!container || !measure) return;\n\n    const update = () => {\n      const available = container.clientWidth;\n      const tagWidths = Array.from(\n        measure.querySelectorAll<HTMLElement>(\"[data-tag-measure]\"),\n        (tag) => tag.offsetWidth,\n      );\n      const moreWidth = measure.querySelector<HTMLElement>(\"[data-more-measure]\")?.offsetWidth ?? 0;\n      let used = 0;\n      let count = 0;\n\n      for (let index = 0; index < tagWidths.length; index += 1) {\n        const nextUsed = used + (count > 0 ? 4 : 0) + tagWidths[index];\n        const hiddenAfter = tags.length - (index + 1);\n        const totalWithOverflow = nextUsed + (hiddenAfter > 0 ? 4 + moreWidth : 0);\n        if (totalWithOverflow > available) break;\n        used = nextUsed;\n        count += 1;\n      }\n\n      setVisibleCount(count);\n    };\n\n    update();\n    const observer = new ResizeObserver(update);\n    observer.observe(container);\n    return () => observer.disconnect();\n  }, [tags]);\n\n  const hiddenCount = tags.length - visibleCount;\n\n  return (\n    <div\n      ref={containerRef}\n      className=\"relative flex w-full min-w-0 items-center gap-1 overflow-hidden\"\n      title={tags.join(\", \")}\n      aria-label={`Focus: ${tags.join(\", \")}`}\n    >\n      <div\n        ref={measureRef}\n        className=\"pointer-events-none invisible absolute top-0 left-0 flex w-max gap-1 [&>span]:inline-flex\"\n        aria-hidden\n      >\n        {tags.map((tag) => (\n          <span key={tag} data-tag-measure>\n            <Tag name={tag} />\n          </span>\n        ))}\n        <span data-more-measure className={MORE_TAG}>\n          +{tags.length}\n        </span>\n      </div>\n      {tags.slice(0, visibleCount).map((tag) => (\n        <Tag key={tag} name={tag} />\n      ))}\n      {hiddenCount > 0 && <span className={MORE_TAG}>+{hiddenCount}</span>}\n    </div>\n  );\n}\n\nfunction CalcCell() {\n  return (\n    <span className=\"flex items-center justify-between gap-2\">\n      <span className=\"text-ink-3\">Calculating…</span>\n      <span className=\"size-2 flex-[0_0_8px] animate-records-table-pulse rounded-full bg-accent\" />\n    </span>\n  );\n}\n\nfunction MiniSwitch({ on, onToggle, label }: { on: boolean; onToggle: () => void; label: string }) {\n  return (\n    <button\n      type=\"button\"\n      role=\"switch\"\n      aria-checked={on}\n      aria-label={label}\n      onClick={onToggle}\n      className=\"relative h-4.5 w-7.5 shrink-0 rounded-full transition-colors duration-150\"\n      style={{ background: on ? \"var(--accent)\" : \"var(--line-strong)\" }}\n    >\n      <span\n        className=\"absolute top-0.5 left-0.5 size-3.5 rounded-full bg-white shadow-btn transition-transform duration-150\"\n        style={{\n          transform: on ? \"translateX(12px)\" : \"translateX(0)\",\n          transitionTimingFunction: \"cubic-bezier(0.23,1,0.32,1)\",\n        }}\n      />\n    </button>\n  );\n}\n\n/** A property header: it opens the property's configuration; the selected column reads in the accent. */\nfunction headerButtonClass(selected: boolean) {\n  return cn(\n    \"group/header flex h-[35px] w-full items-center gap-2 px-3 text-left [transition:background-color_120ms_ease-out,color_120ms_ease-out] hover:bg-hover\",\n    selected ? \"text-accent-ink\" : \"text-ink-2 hover:text-ink\",\n  );\n}\n\n/** The drag handle on a header's right edge; its accent bar shows on hover and while dragging. */\nfunction ResizeHandle({\n  label,\n  resizing,\n  onPointerDown,\n}: {\n  label: string;\n  resizing: boolean;\n  onPointerDown: (event: React.PointerEvent<HTMLElement>) => void;\n}) {\n  return (\n    <hr\n      aria-orientation=\"vertical\"\n      aria-label={`Resize ${label} column`}\n      className={cn(\"m-0 border-0\", RESIZE_HANDLE, resizing && \"after:scale-y-100 after:opacity-100\")}\n      onPointerDown={onPointerDown}\n    />\n  );\n}\n\nfunction HeaderCell({\n  label,\n  icon,\n  sortKey,\n  sort,\n  onSort,\n  onResizeStart,\n  resizing = false,\n  className = \"\",\n  selected = false,\n  onPick,\n}: {\n  label: string;\n  icon: React.ReactNode;\n  sortKey?: SortKey;\n  sort: { key: SortKey; dir: 1 | -1 };\n  onSort: (key: SortKey) => void;\n  onResizeStart: (event: React.PointerEvent<HTMLElement>) => void;\n  resizing?: boolean;\n  className?: string;\n  selected?: boolean;\n  onPick?: (event: React.MouseEvent) => void;\n}) {\n  return (\n    <th className={cn(HEADER_CELL, selected && HEADER_COLSEL, className)}>\n      {/* the label opens the property config; the arrow beside it sorts */}\n      <div className={headerButtonClass(selected)}>\n        <button\n          type=\"button\"\n          className=\"flex min-w-0 flex-1 items-center gap-2 self-stretch text-left\"\n          onClick={onPick}\n        >\n          <span className={cn(\"inline-flex shrink-0\", selected ? \"text-accent-ink\" : \"text-ink-3\")}>{icon}</span>\n          <span className=\"truncate\">{label}</span>\n        </button>\n        {sortKey && (\n          <button\n            type=\"button\"\n            aria-label={`Sort by ${label}`}\n            onClick={() => onSort(sortKey)}\n            className={cn(\n              \"ml-auto inline-flex shrink-0 opacity-0 [transition:opacity_120ms_ease-out,transform_160ms_var(--ease-out-strong)] group-hover/header:opacity-100 focus-visible:opacity-100\",\n              sort.key === sortKey && \"opacity-100\",\n            )}\n            style={{ transform: sort.key === sortKey && sort.dir === -1 ? \"rotate(180deg)\" : undefined }}\n          >\n            <Icon size={12}>\n              <path d=\"M12 5v14M5 12l7 7 7-7\" />\n            </Icon>\n          </button>\n        )}\n      </div>\n      <ResizeHandle label={label} resizing={resizing} onPointerDown={onResizeStart} />\n    </th>\n  );\n}\n\n/* config row inside the property popover */\nfunction ConfigRow({ label, children }: { label: string; children: React.ReactNode }) {\n  return (\n    <div className=\"relative flex h-8 items-center justify-between\">\n      <span className=\"text-[13px] text-ink-3\">{label}</span>\n      {children}\n    </div>\n  );\n}\n\nfunction ConfigPicker({\n  label,\n  options,\n  selected,\n  onSelect,\n}: {\n  label: string;\n  options: { label: string; icon: React.ReactNode }[];\n  selected: string;\n  onSelect: (value: string) => void;\n}) {\n  return (\n    <div\n      role=\"menu\"\n      aria-label={label}\n      className=\"origin-top-left animate-records-table-picker-in absolute left-full top-0 z-30 ml-5 w-[210px] rounded-[12px] bg-surface p-1.5 shadow-overlay\"\n    >\n      <div className=\"px-2 pb-1 pt-0.5 text-[11.5px] font-medium text-ink-3\">{label}</div>\n      <GlideMenu className=\"flex flex-col gap-px\">\n        {options.map((option) => (\n          <button\n            key={option.label}\n            data-menu-row\n            type=\"button\"\n            role=\"menuitemradio\"\n            aria-checked={selected === option.label}\n            onClick={() => onSelect(option.label)}\n            className=\"relative z-10 flex h-8 w-full items-center gap-1.5 rounded-[8px] px-1.5 text-left text-[13px] font-medium text-ink\"\n          >\n            <span className=\"flex size-4 shrink-0 items-center justify-center text-ink-2\">{option.icon}</span>\n            <span className=\"min-w-0 flex-1 truncate\">{option.label}</span>\n            <span className={selected === option.label ? \"text-ink\" : \"invisible\"}>\n              <Icon size={14} strokeWidth={2.2}>\n                <path d=\"m5 12 4 4L19 6\" />\n              </Icon>\n            </span>\n          </button>\n        ))}\n      </GlideMenu>\n    </div>\n  );\n}\n\nfunction InputPicker({\n  options,\n  selected,\n  onToggle,\n}: {\n  options: string[];\n  selected: string[];\n  onToggle: (value: string) => void;\n}) {\n  return (\n    <div\n      role=\"menu\"\n      aria-label=\"Calculation inputs\"\n      className=\"origin-top-left animate-records-table-picker-in absolute left-full top-0 z-30 ml-5 w-[220px] rounded-[12px] bg-surface p-1.5 shadow-overlay\"\n    >\n      <div className=\"px-2 pb-1 pt-0.5 text-[11.5px] font-medium text-ink-3\">Use values from</div>\n      <GlideMenu className=\"flex flex-col gap-px\">\n        {options.map((option) => {\n          const checked = selected.includes(option);\n          return (\n            <button\n              key={option}\n              data-menu-row\n              type=\"button\"\n              role=\"menuitemcheckbox\"\n              aria-checked={checked}\n              onClick={() => onToggle(option)}\n              className=\"relative z-10 flex h-8 w-full items-center gap-1.5 rounded-[8px] px-1.5 text-left text-[13px] font-medium text-ink\"\n            >\n              <span\n                className={`flex size-4 shrink-0 items-center justify-center rounded-[5px] border ${checked ? \"border-accent bg-accent text-white\" : \"border-line-strong text-transparent\"}`}\n              >\n                <Icon size={11} strokeWidth={2.4}>\n                  <path d=\"m5 12 4 4L19 6\" />\n                </Icon>\n              </span>\n              <span className=\"min-w-0 flex-1 truncate\">{option}</span>\n            </button>\n          );\n        })}\n      </GlideMenu>\n    </div>\n  );\n}\n\nexport default function RecordsTable({\n  rows = INITIAL_ROWS,\n  fill = false,\n}: {\n  rows?: RecordRow[];\n  fill?: boolean;\n  variant?: string;\n}) {\n  const [selected, setSelected] = useState<Set<string>>(new Set());\n  const [sort, setSort] = useState<{ key: SortKey; dir: 1 | -1 }>({ key: \"name\", dir: 1 });\n  const [columnWidths, setColumnWidths] = useState(DEFAULT_COLUMN_WIDTHS);\n  const [actionColumnWidth, setActionColumnWidth] = useState(100);\n  const [columnWidthsLocked, setColumnWidthsLocked] = useState(false);\n  const [resizingColumn, setResizingColumn] = useState<ColumnKey | null>(null);\n  const initialColumnWidthsRef = useRef<Record<ColumnKey, number> | null>(null);\n  const tableRef = useRef<HTMLTableElement>(null);\n\n  /* property popover, anchored to the clicked header */\n  const [prop, setProp] = useState<{ col: string; x: number; y: number } | null>(null);\n  const [grounding, setGrounding] = useState(false);\n  const [groundingHelpOpen, setGroundingHelpOpen] = useState(false);\n  const [configMenu, setConfigMenu] = useState<\"type\" | \"tool\" | \"inputs\" | null>(null);\n  const [columnOverrides, setColumnOverrides] = useState<Record<string, Partial<ColumnMeta>>>({});\n  const [inputSelections, setInputSelections] = useState<Record<string, string[]>>({});\n  const [pinnedColumns, setPinnedColumns] = useState<Set<string>>(new Set());\n  const [moreSettingsOpen, setMoreSettingsOpen] = useState(false);\n  const [advancedSettings, setAdvancedSettings] = useState({ required: false, allowEmpty: true, confidence: false });\n  /* + new-property menu */\n  const [addOpen, setAddOpen] = useState<{ x: number; y: number } | null>(null);\n  const [tableMenuOpen, setTableMenuOpen] = useState<{ x: number; y: number } | null>(null);\n  /* the added AI column and its lifecycle */\n  const [aiAdded, setAiAdded] = useState(false);\n  const [aiDone, setAiDone] = useState(false);\n  const [pendingOpenAi, setPendingOpenAi] = useState(false);\n  const aiThRef = useRef<HTMLTableCellElement>(null);\n  /* programmatic scrolls (revealing the new column) shouldn't close popovers */\n  const ignoreScrollRef = useRef(false);\n  /* a running calculation resolves rows one by one */\n  const [calc, setCalc] = useState<{ col: string; resolved: number } | null>(null);\n\n  /* Let the table fill its available space once, then capture those rendered\n   * widths before paint. From that point on every column is explicit, so a\n   * resize changes only the dragged column and the table's total width. */\n  useLayoutEffect(() => {\n    if (columnWidthsLocked || !tableRef.current) return;\n    const headers = Array.from(tableRef.current.querySelectorAll<HTMLTableCellElement>(\"thead th\"));\n    if (headers.length < 6) return;\n\n    const measured: Record<ColumnKey, number> = {\n      company: headers[0].getBoundingClientRect().width,\n      categories: headers[1].getBoundingClientRect().width,\n      last: headers[2].getBoundingClientRect().width,\n      strength: headers[3].getBoundingClientRect().width,\n      links: headers[4].getBoundingClientRect().width,\n      ai: DEFAULT_COLUMN_WIDTHS.ai,\n    };\n    initialColumnWidthsRef.current = measured;\n    setColumnWidths(measured);\n    setActionColumnWidth(headers[headers.length - 1].getBoundingClientRect().width);\n    setColumnWidthsLocked(true);\n  }, [columnWidthsLocked]);\n\n  const visibleRows = useMemo(() => {\n    return rows.toSorted((a, b) => {\n      const value =\n        sort.key === \"name\"\n          ? a.name.localeCompare(b.name)\n          : sort.key === \"last\"\n            ? a.last.localeCompare(b.last)\n            : STRENGTH[a.strength].rank - STRENGTH[b.strength].rank;\n      return value * sort.dir;\n    });\n  }, [rows, sort]);\n\n  /* stagger: one row resolves every beat */\n  useEffect(() => {\n    if (!calc) return;\n    const t = setTimeout(() => {\n      if (calc.resolved < visibleRows.length) {\n        setCalc({ ...calc, resolved: calc.resolved + 1 });\n        return;\n      }\n      if (calc.col === AI_LABEL) setAiDone(true);\n      setCalc(null);\n    }, 110);\n    return () => clearTimeout(t);\n  }, [calc, visibleRows.length]);\n\n  /* after adding the AI column, scroll it into view and open its config\n   * anchored to the new header */\n  useEffect(() => {\n    if (!pendingOpenAi || !aiThRef.current) return;\n    const scroller = aiThRef.current.closest(\"[data-records-scroll]\");\n    if (scroller) {\n      ignoreScrollRef.current = true;\n      scroller.scrollLeft = scroller.scrollWidth;\n    }\n    const rect = aiThRef.current.getBoundingClientRect();\n    setProp({ col: AI_LABEL, x: Math.min(rect.left, window.innerWidth - 336), y: rect.bottom + 6 });\n    setPendingOpenAi(false);\n  }, [pendingOpenAi, aiAdded]);\n\n  /* click anywhere else closes popovers */\n  useEffect(() => {\n    if (!prop && !addOpen && !tableMenuOpen) return;\n    const close = (event: PointerEvent) => {\n      if (!(event.target as Element).closest(\"[data-recpop]\")) {\n        setProp(null);\n        setConfigMenu(null);\n        setGroundingHelpOpen(false);\n        setMoreSettingsOpen(false);\n        setAddOpen(null);\n        setTableMenuOpen(null);\n      }\n    };\n    document.addEventListener(\"pointerdown\", close);\n    return () => document.removeEventListener(\"pointerdown\", close);\n  }, [prop, addOpen, tableMenuOpen]);\n\n  const openProp = (col: string) => (event: React.MouseEvent) => {\n    const th = (event.currentTarget as Element).closest(\"th\");\n    if (!th) return;\n    setAddOpen(null);\n    setTableMenuOpen(null);\n    setConfigMenu(null);\n    setGroundingHelpOpen(false);\n    setMoreSettingsOpen(false);\n    setProp((current) => {\n      if (current?.col === col) return null;\n      const rect = th.getBoundingClientRect();\n      return { col, x: Math.min(rect.left, window.innerWidth - 336), y: rect.bottom + 6 };\n    });\n  };\n\n  const isCalc = (col: string, index: number) => !!calc && calc.col === col && index >= calc.resolved;\n\n  const allSelected = visibleRows.length > 0 && visibleRows.every((row) => selected.has(row.id));\n  const partiallySelected = !allSelected && visibleRows.some((row) => selected.has(row.id));\n\n  const toggleSort = (key: SortKey) =>\n    setSort((current) => (current.key === key ? { key, dir: (current.dir * -1) as 1 | -1 } : { key, dir: 1 }));\n  const startColumnResize =\n    (key: ColumnKey, minWidth = 120) =>\n    (event: React.PointerEvent<HTMLElement>) => {\n      event.preventDefault();\n      event.stopPropagation();\n      setProp(null);\n      setConfigMenu(null);\n      setGroundingHelpOpen(false);\n      setMoreSettingsOpen(false);\n      setAddOpen(null);\n      setTableMenuOpen(null);\n\n      const startX = event.clientX;\n      const startWidth = columnWidths[key];\n      const previousCursor = document.body.style.cursor;\n      const previousSelection = document.body.style.userSelect;\n      document.body.style.cursor = \"col-resize\";\n      document.body.style.userSelect = \"none\";\n      setResizingColumn(key);\n\n      const move = (moveEvent: PointerEvent) => {\n        const width = Math.max(minWidth, startWidth + moveEvent.clientX - startX);\n        setColumnWidths((current) => ({ ...current, [key]: width }));\n      };\n      const finish = () => {\n        window.removeEventListener(\"pointermove\", move);\n        window.removeEventListener(\"pointerup\", finish);\n        window.removeEventListener(\"pointercancel\", finish);\n        document.body.style.cursor = previousCursor;\n        document.body.style.userSelect = previousSelection;\n        setResizingColumn(null);\n      };\n\n      window.addEventListener(\"pointermove\", move);\n      window.addEventListener(\"pointerup\", finish);\n      window.addEventListener(\"pointercancel\", finish);\n    };\n  const toggleRow = (id: string) =>\n    setSelected((current) => {\n      const next = new Set(current);\n      if (next.has(id)) next.delete(id);\n      else next.add(id);\n      return next;\n    });\n  const toggleAll = () =>\n    setSelected((current) => {\n      const next = new Set(current);\n      if (allSelected) visibleRows.forEach((row) => next.delete(row.id));\n      else visibleRows.forEach((row) => next.add(row.id));\n      return next;\n    });\n\n  const meta = prop ? { ...COLUMN_META[prop.col], ...columnOverrides[prop.col] } : null;\n  const selectedInputs = prop && meta ? (inputSelections[prop.col] ?? (meta.inputs ? [meta.inputs] : [])) : [];\n  const tableWidth =\n    columnWidths.company +\n    columnWidths.categories +\n    columnWidths.last +\n    columnWidths.strength +\n    columnWidths.links +\n    (aiAdded ? columnWidths.ai : 0) +\n    actionColumnWidth;\n\n  return (\n    <div\n      className={cn(\n        \"w-full min-w-0 overflow-hidden rounded-[10px] border border-line bg-surface shadow-[0_1px_2px_oklch(0_0_0/0.06)]\",\n        fill && \"flex h-full min-h-0 flex-1 flex-col rounded-none border-0 shadow-none\",\n      )}\n    >\n      <section\n        data-records-scroll\n        className={cn(\n          \"max-h-[438px] overflow-auto overscroll-none [scrollbar-color:var(--line-strong)_transparent] [scrollbar-gutter:stable] [--focus-outline-offset:-2px]\",\n          fill && \"max-h-none flex-1\",\n        )}\n        // oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- the scroll viewport must take keyboard focus so arrow keys can scroll it (WCAG 2.1.1)\n        tabIndex={0}\n        aria-label=\"Firms table. Scroll horizontally and vertically to view all columns and records.\"\n        onScroll={() => {\n          if (ignoreScrollRef.current) {\n            ignoreScrollRef.current = false;\n            return;\n          }\n          setProp(null);\n          setConfigMenu(null);\n          setGroundingHelpOpen(false);\n          setMoreSettingsOpen(false);\n          setAddOpen(null);\n          setTableMenuOpen(null);\n        }}\n      >\n        <table\n          ref={tableRef}\n          className=\"w-full min-w-[990px] table-fixed border-separate border-spacing-0 text-[13px] font-medium text-ink [&_:is(th,td)]:border-r [&_:is(th,td)]:border-b [&_:is(th,td)]:border-[color-mix(in_srgb,var(--line)_78%,transparent)] [&_:is(th,td)]:text-left [&_:is(th,td)]:align-middle [&_tr>:last-child]:border-r-0\"\n          style={{ width: columnWidthsLocked ? tableWidth : \"100%\", minWidth: tableWidth }}\n        >\n          <colgroup>\n            <col style={{ width: columnWidths.company }} />\n            <col style={{ width: columnWidths.categories }} />\n            <col style={{ width: columnWidths.last }} />\n            <col style={{ width: columnWidths.strength }} />\n            <col style={{ width: columnWidths.links }} />\n            {aiAdded && <col style={{ width: columnWidths.ai }} />}\n            <col style={{ width: 100 }} />\n          </colgroup>\n          <thead>\n            <tr>\n              <th className={cn(HEADER_CELL, STICKY_CELL, \"z-7\", prop?.col === \"Firm\" && HEADER_COLSEL)}>\n                <div\n                  className={cn(\n                    \"flex h-[35px] w-full items-center gap-2 pr-3 pl-1.5\",\n                    prop?.col === \"Firm\" && \"text-accent-ink\",\n                  )}\n                >\n                  <Checkbox\n                    checked={allSelected}\n                    mixed={partiallySelected}\n                    onChange={toggleAll}\n                    label=\"Select all firms\"\n                  />\n                  <button\n                    type=\"button\"\n                    className=\"flex-1 self-stretch text-left whitespace-nowrap\"\n                    onClick={openProp(\"Firm\")}\n                  >\n                    Firm\n                  </button>\n                </div>\n                <ResizeHandle\n                  label=\"Firm\"\n                  resizing={resizingColumn === \"company\"}\n                  onPointerDown={startColumnResize(\"company\", 180)}\n                />\n              </th>\n              <HeaderCell\n                label=\"Focus\"\n                selected={prop?.col === \"Focus\"}\n                onPick={openProp(\"Focus\")}\n                sort={sort}\n                onSort={toggleSort}\n                onResizeStart={startColumnResize(\"categories\")}\n                resizing={resizingColumn === \"categories\"}\n                icon={<Icon size={15}>{TYPE_GLYPHS[\"Multi select\"]}</Icon>}\n              />\n              <HeaderCell\n                label=\"Last interaction\"\n                selected={prop?.col === \"Last interaction\"}\n                onPick={openProp(\"Last interaction\")}\n                sortKey=\"last\"\n                sort={sort}\n                onSort={toggleSort}\n                onResizeStart={startColumnResize(\"last\")}\n                resizing={resizingColumn === \"last\"}\n                icon={<Icon size={15}>{TYPE_GLYPHS.Date}</Icon>}\n              />\n              <HeaderCell\n                label=\"Connection strength\"\n                selected={prop?.col === \"Connection strength\"}\n                onPick={openProp(\"Connection strength\")}\n                sortKey=\"strength\"\n                sort={sort}\n                onSort={toggleSort}\n                onResizeStart={startColumnResize(\"strength\")}\n                resizing={resizingColumn === \"strength\"}\n                icon={<Icon size={15}>{TYPE_GLYPHS[\"Single select\"]}</Icon>}\n              />\n              <HeaderCell\n                label=\"Links\"\n                selected={prop?.col === \"Links\"}\n                onPick={openProp(\"Links\")}\n                sort={sort}\n                onSort={toggleSort}\n                onResizeStart={startColumnResize(\"links\")}\n                resizing={resizingColumn === \"links\"}\n                icon={<Icon size={15}>{TYPE_GLYPHS.URL}</Icon>}\n              />\n              {aiAdded && (\n                <th ref={aiThRef} className={cn(HEADER_CELL, prop?.col === AI_LABEL && HEADER_COLSEL)}>\n                  <button\n                    type=\"button\"\n                    className={headerButtonClass(prop?.col === AI_LABEL)}\n                    onClick={openProp(AI_LABEL)}\n                  >\n                    <span\n                      className={cn(\"inline-flex shrink-0\", prop?.col === AI_LABEL ? \"text-accent-ink\" : \"text-ink-3\")}\n                    >\n                      <Icon size={15}>{TYPE_GLYPHS.Text}</Icon>\n                    </span>\n                    <span className=\"truncate\">{AI_LABEL}</span>\n                  </button>\n                  <ResizeHandle\n                    label={AI_LABEL}\n                    resizing={resizingColumn === \"ai\"}\n                    onPointerDown={startColumnResize(\"ai\")}\n                  />\n                </th>\n              )}\n              <th className={HEADER_CELL}>\n                <div className=\"flex h-[35px] items-center gap-1 px-2\">\n                  <button\n                    type=\"button\"\n                    aria-label=\"New property\"\n                    data-recpop\n                    onClick={(event) => {\n                      setProp(null);\n                      setTableMenuOpen(null);\n                      const rect = (event.currentTarget as Element).getBoundingClientRect();\n                      setAddOpen((current) =>\n                        current ? null : { x: Math.min(rect.left, window.innerWidth - 276), y: rect.bottom + 6 },\n                      );\n                    }}\n                    className=\"flex size-7 items-center justify-center rounded-button text-ink-2 transition-colors duration-100 hover:bg-hover hover:text-ink\"\n                  >\n                    <Icon size={15} strokeWidth={2}>\n                      <path d=\"M12 5v14M5 12h14\" />\n                    </Icon>\n                  </button>\n                  <button\n                    type=\"button\"\n                    aria-label=\"Table options\"\n                    aria-expanded={!!tableMenuOpen}\n                    data-recpop\n                    onClick={(event) => {\n                      setProp(null);\n                      setAddOpen(null);\n                      const rect = event.currentTarget.getBoundingClientRect();\n                      setTableMenuOpen((current) =>\n                        current\n                          ? null\n                          : {\n                              x: Math.max(8, Math.min(rect.right - 220, window.innerWidth - 228)),\n                              y: rect.bottom + 6,\n                            },\n                      );\n                    }}\n                    className=\"flex size-7 items-center justify-center rounded-button text-ink-3 transition-colors duration-100 hover:bg-hover hover:text-ink\"\n                  >\n                    <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden>\n                      <circle cx=\"5\" cy=\"12\" r=\"1.6\" />\n                      <circle cx=\"12\" cy=\"12\" r=\"1.6\" />\n                      <circle cx=\"19\" cy=\"12\" r=\"1.6\" />\n                    </svg>\n                  </button>\n                </div>\n              </th>\n            </tr>\n          </thead>\n          {/* data cells stay silent — the papery link/flick sound is too much when scanning rows */}\n          <tbody data-sound-silent>\n            {visibleRows.map((row, index) => {\n              const selectedRow = selected.has(row.id);\n              const strength = STRENGTH[row.strength];\n              const colsel = (col: string) => prop?.col === col;\n              return (\n                <tr key={row.id} className=\"group/row\">\n                  <td\n                    className={rowCellClass(\n                      selectedRow,\n                      colsel(\"Firm\"),\n                      cn(STICKY_CELL, \"flex items-center gap-1 overflow-visible pl-1.5\"),\n                    )}\n                  >\n                    <span\n                      className={cn(\n                        \"inline-flex size-6 flex-[0_0_24px] items-center justify-center text-[11.5px] text-ink-3 tabular-nums group-hover/row:hidden\",\n                        selectedRow && \"hidden\",\n                      )}\n                    >\n                      {index + 1}\n                    </span>\n                    <Checkbox\n                      checked={selectedRow}\n                      onChange={() => toggleRow(row.id)}\n                      label={`Select ${row.name}`}\n                      className={selectedRow ? \"inline-flex\" : \"hidden group-hover/row:inline-flex\"}\n                    />\n                    <span className=\"inline-flex size-5 flex-[0_0_20px] items-center justify-center rounded-[6px] border-0 bg-field text-[10px] font-medium text-ink-2\">\n                      {row.name.slice(0, 1).toUpperCase()}\n                    </span>\n                    <a\n                      href={row.website ? `https://${row.website}` : \"#\"}\n                      onClick={(event) => !row.website && event.preventDefault()}\n                      title={row.name}\n                      className={cn(\n                        \"min-w-0 truncate text-[13px] font-medium text-ink\",\n                        row.website &&\n                          \"hover:text-accent-ink hover:underline hover:underline-offset-[3px] focus-visible:text-accent-ink focus-visible:underline focus-visible:underline-offset-[3px]\",\n                      )}\n                    >\n                      {row.name}\n                    </a>\n                  </td>\n                  <td className={rowCellClass(selectedRow, colsel(\"Focus\"))}>\n                    {isCalc(\"Focus\", index) ? <CalcCell /> : <TagList tags={row.tags} />}\n                  </td>\n                  <td\n                    className={rowCellClass(\n                      selectedRow,\n                      colsel(\"Last interaction\"),\n                      row.last === \"No contact\" ? \"text-ink-3\" : undefined,\n                    )}\n                  >\n                    {isCalc(\"Last interaction\", index) ? <CalcCell /> : row.last}\n                  </td>\n                  <td className={rowCellClass(selectedRow, colsel(\"Connection strength\"))}>\n                    {isCalc(\"Connection strength\", index) ? (\n                      <CalcCell />\n                    ) : (\n                      <span className=\"inline-flex items-center gap-2 text-ink-2\">\n                        <span className={STRENGTH_DOT} style={{ background: strength.color }} />\n                        {strength.label}\n                      </span>\n                    )}\n                  </td>\n                  <td className={rowCellClass(selectedRow, colsel(\"Links\"))}>\n                    {isCalc(\"Links\", index) ? (\n                      <CalcCell />\n                    ) : row.website ? (\n                      <a\n                        className=\"inline-flex max-w-full items-center gap-[5px] overflow-hidden text-ellipsis text-accent-ink underline decoration-[color-mix(in_srgb,currentColor_35%,transparent)] underline-offset-[3px] [transition:color_120ms_ease-out,text-decoration-color_120ms_ease-out] hover:text-ink hover:decoration-current focus-visible:text-ink focus-visible:decoration-current [&_svg]:shrink-0\"\n                        href={`https://${row.website}`}\n                        title={row.website}\n                        target=\"_blank\"\n                        rel=\"noreferrer\"\n                      >\n                        <span className=\"min-w-0 truncate\">{row.website}</span>\n                        <Icon size={12}>\n                          <path d=\"M14 5h5v5M19 5l-8 8\" />\n                        </Icon>\n                      </a>\n                    ) : (\n                      <span className=\"text-ink-3\">—</span>\n                    )}\n                  </td>\n                  {aiAdded && (\n                    <td className={rowCellClass(selectedRow, colsel(AI_LABEL))}>\n                      {calc?.col === AI_LABEL ? (\n                        index < calc.resolved ? (\n                          similarFirmsFor(index)\n                        ) : (\n                          <CalcCell />\n                        )\n                      ) : aiDone ? (\n                        similarFirmsFor(index)\n                      ) : (\n                        <span className=\"text-ink-3\">—</span>\n                      )}\n                    </td>\n                  )}\n                  <td aria-hidden=\"true\" className={rowCellClass(selectedRow, false)} />\n                </tr>\n              );\n            })}\n          </tbody>\n          <tfoot>\n            <tr>\n              <td className={cn(CELL, FOOTER_CELL, STICKY_CELL, \"z-6 bg-inset\")}>\n                <span className={cn(FOOTER_VALUE, \"font-medium text-ink-2\")}>\n                  <span className=\"mr-[3px] text-ink tabular-nums\">{rows.length}</span> count\n                </span>\n              </td>\n              <td className={cn(CELL, FOOTER_CELL)}>\n                <button\n                  type=\"button\"\n                  className=\"inline-flex h-[35px] items-center gap-1.5 text-[14px] leading-none font-medium text-ink-3 [transition:color_120ms_ease-out,scale_140ms_var(--ease-out-strong)] hover:text-ink active:scale-[0.96]\"\n                >\n                  <Icon size={15}>\n                    <path d=\"M12 5v14M5 12h14\" />\n                  </Icon>\n                  Add calculation\n                </button>\n              </td>\n              <td className={cn(CELL, FOOTER_CELL)}>\n                <span className={FOOTER_VALUE}>—</span>\n              </td>\n              <td className={cn(CELL, FOOTER_CELL)}>\n                <span className={cn(FOOTER_VALUE, \"gap-[7px] text-ink-2\")}>\n                  <span className={STRENGTH_DOT} style={{ background: \"var(--orange)\" }} />\n                  {Math.round(\n                    (rows.reduce((sum, row) => sum + STRENGTH[row.strength].rank, 0) / rows.length / 3) * 100,\n                  )}\n                  % average\n                </span>\n              </td>\n              <td className={cn(CELL, FOOTER_CELL)}>\n                <span className={cn(FOOTER_VALUE, \"text-ink-3\")}>{rows.filter((row) => row.website).length} links</span>\n              </td>\n              {aiAdded && (\n                <td className={cn(CELL, FOOTER_CELL)}>\n                  <span className={FOOTER_VALUE}>{aiDone ? `${rows.length} filled` : \"—\"}</span>\n                </td>\n              )}\n              <td aria-hidden=\"true\" className={cn(CELL, FOOTER_CELL)} />\n            </tr>\n          </tfoot>\n        </table>\n      </section>\n\n      {/* ── property configuration popover ─────────────────── */}\n      {prop && meta && (\n        <div\n          data-recpop\n          className=\"origin-top-left animate-records-table-popover-in fixed z-50 w-[320px] rounded-[14px] bg-surface px-3 pt-3 pb-1.5 shadow-overlay\"\n          style={{ top: prop.y, left: prop.x }}\n        >\n          <div className=\"pb-2 text-[13.5px] font-medium text-ink\">{prop.col}</div>\n\n          <ConfigRow label=\"Type\">\n            <button\n              type=\"button\"\n              aria-haspopup=\"menu\"\n              aria-expanded={configMenu === \"type\"}\n              onClick={() => setConfigMenu((current) => (current === \"type\" ? null : \"type\"))}\n              className=\"flex items-center gap-1.5 rounded-button px-2 py-1 text-[13px] font-medium text-ink transition-colors duration-100 hover:bg-hover\"\n            >\n              <span className=\"text-ink-2\">\n                <Icon size={14}>{TYPE_GLYPHS[meta.type] ?? TYPE_GLYPHS.Text}</Icon>\n              </span>\n              {meta.type}\n              <span className=\"text-ink-3\">\n                <Icon size={12} strokeWidth={2.2}>\n                  <path d=\"M9 6l6 6-6 6\" />\n                </Icon>\n              </span>\n            </button>\n            {configMenu === \"type\" && (\n              <ConfigPicker\n                label=\"Property type\"\n                selected={meta.type}\n                options={NEW_PROPERTY_TYPES.map((type) => ({\n                  label: type,\n                  icon: <Icon size={15}>{TYPE_GLYPHS[type]}</Icon>,\n                }))}\n                onSelect={(type) => {\n                  setColumnOverrides((current) => ({ ...current, [prop.col]: { ...current[prop.col], type } }));\n                  setConfigMenu(null);\n                }}\n              />\n            )}\n          </ConfigRow>\n          <ConfigRow label=\"Tool\">\n            <button\n              type=\"button\"\n              aria-haspopup=\"menu\"\n              aria-expanded={configMenu === \"tool\"}\n              onClick={() => setConfigMenu((current) => (current === \"tool\" ? null : \"tool\"))}\n              className=\"flex items-center gap-1.5 rounded-button px-2 py-1 text-[13px] font-medium text-ink transition-colors duration-100 hover:bg-hover\"\n            >\n              <span className={meta.toolKind === \"model\" ? \"text-accent\" : \"text-ink-2\"}>\n                {meta.toolKind === \"model\" ? (\n                  <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden>\n                    {TOOL_GLYPHS.model}\n                  </svg>\n                ) : (\n                  <Icon size={14}>{TOOL_GLYPHS[meta.toolKind]}</Icon>\n                )}\n              </span>\n              {meta.tool}\n              <span className=\"text-ink-3\">\n                <Icon size={12} strokeWidth={2.2}>\n                  <path d=\"M9 6l6 6-6 6\" />\n                </Icon>\n              </span>\n            </button>\n            {configMenu === \"tool\" && (\n              <ConfigPicker\n                label=\"Model\"\n                selected={meta.tool}\n                options={MODEL_OPTIONS.map((model) => ({\n                  label: model,\n                  icon: (\n                    <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden>\n                      {TOOL_GLYPHS.model}\n                    </svg>\n                  ),\n                }))}\n                onSelect={(tool) => {\n                  setColumnOverrides((current) => ({\n                    ...current,\n                    [prop.col]: { ...current[prop.col], tool, toolKind: \"model\" },\n                  }));\n                  setConfigMenu(null);\n                }}\n              />\n            )}\n          </ConfigRow>\n          <ConfigRow label=\"Grounding\">\n            <span className=\"flex items-center gap-2\">\n              <MiniSwitch label=\"Grounding\" on={grounding} onToggle={() => setGrounding((current) => !current)} />\n              <button\n                type=\"button\"\n                aria-label=\"About grounding\"\n                aria-expanded={groundingHelpOpen}\n                onClick={() => setGroundingHelpOpen((open) => !open)}\n                className=\"flex size-6 items-center justify-center rounded-button text-ink-3 transition-colors duration-100 hover:bg-hover hover:text-ink\"\n              >\n                <Icon size={13}>\n                  <g>\n                    <circle cx=\"12\" cy=\"12\" r=\"9\" />\n                    <path d=\"M12 8h.01M11 12h1v4h1\" />\n                  </g>\n                </Icon>\n              </button>\n            </span>\n            {groundingHelpOpen && (\n              <output className=\"absolute top-[30px] right-0 z-30 w-[230px] rounded-[10px] bg-tooltip-bg px-3 py-2.5 text-[12px] leading-relaxed text-tooltip-fg shadow-overlay\">\n                Grounding lets the model verify generated values against connected sources.\n              </output>\n            )}\n          </ConfigRow>\n          <ConfigRow label=\"Inputs\">\n            <button\n              type=\"button\"\n              aria-haspopup=\"menu\"\n              aria-expanded={configMenu === \"inputs\"}\n              onClick={() => setConfigMenu((current) => (current === \"inputs\" ? null : \"inputs\"))}\n              className=\"flex max-w-[220px] items-center gap-1.5 rounded-button px-2 py-1 text-[13px] text-ink-2 transition-colors duration-100 hover:bg-hover hover:text-ink\"\n            >\n              {selectedInputs.length ? (\n                <span className=\"flex min-w-0 items-center gap-1\">\n                  {selectedInputs.slice(0, 2).map((input) => (\n                    <span\n                      key={input}\n                      className=\"max-w-[92px] truncate rounded-[5px] bg-accent-tint px-1.5 py-0.5 text-[12px] font-medium text-accent-ink\"\n                    >\n                      {input}\n                    </span>\n                  ))}\n                  {selectedInputs.length > 2 && (\n                    <span className=\"text-[11px] font-medium text-ink-3\">+{selectedInputs.length - 2}</span>\n                  )}\n                </span>\n              ) : (\n                <span>Select inputs</span>\n              )}\n              <span className=\"shrink-0 text-ink-3\">\n                <Icon size={12} strokeWidth={2.2}>\n                  <path d=\"M9 6l6 6-6 6\" />\n                </Icon>\n              </span>\n            </button>\n            {configMenu === \"inputs\" && (\n              <InputPicker\n                selected={selectedInputs}\n                options={INPUT_OPTIONS.filter((input) => input !== prop.col)}\n                onToggle={(input) => {\n                  setInputSelections((current) => {\n                    const existing = current[prop.col] ?? (meta.inputs ? [meta.inputs] : []);\n                    const next = existing.includes(input)\n                      ? existing.filter((item) => item !== input)\n                      : [...existing, input];\n                    return { ...current, [prop.col]: next };\n                  });\n                }}\n              />\n            )}\n          </ConfigRow>\n\n          {/* prompt — @-mention chips inline */}\n          <div\n            contentEditable\n            suppressContentEditableWarning\n            role=\"textbox\"\n            aria-label={`${prop.col} calculation prompt`}\n            aria-multiline=\"true\"\n            spellCheck\n            className=\"mt-2 min-h-[88px] cursor-text rounded-[10px] bg-inset p-3 text-[13px] leading-relaxed shadow-hairline outline-none transition-[box-shadow] duration-150 focus:shadow-[0_0_0_2px_var(--accent)]\"\n          >\n            {meta.prompt ? (\n              <span className=\"text-ink\">\n                {meta.prompt.before}\n                {meta.prompt.chip && (\n                  <span\n                    contentEditable={false}\n                    className=\"rounded-[5px] bg-accent-tint px-1.5 py-0.5 text-[12px] font-medium text-accent-ink\"\n                  >\n                    {meta.prompt.chip}\n                  </span>\n                )}\n                {meta.prompt.after}\n              </span>\n            ) : (\n              <span className=\"text-ink-3\">Set a prompt (press @ to mention an input)</span>\n            )}\n          </div>\n\n          <button\n            type=\"button\"\n            disabled={!!calc}\n            onClick={() => {\n              setCalc({ col: prop.col, resolved: 0 });\n              setProp(null);\n            }}\n            className=\"mt-2.5 flex h-9 w-full items-center justify-center gap-2 rounded-button text-[12.5px] font-medium text-ink shadow-btn transition-[background-color,transform] duration-150 hover:bg-hover active:scale-[0.98] disabled:opacity-60\"\n          >\n            <Icon size={14} strokeWidth={1.9}>\n              <path d=\"M21 12a9 9 0 1 1-2.64-6.36M21 3v6h-6\" />\n            </Icon>\n            Go calculate\n          </button>\n\n          <GlideMenu\n            className=\"mt-3 flex flex-col gap-0.5 border-t border-line pt-2\"\n            highlightClassName=\"-inset-x-1.5 rounded-[8px] bg-hover\"\n          >\n            <button\n              data-menu-row\n              type=\"button\"\n              aria-pressed={pinnedColumns.has(prop.col)}\n              onClick={() =>\n                setPinnedColumns((current) => {\n                  const next = new Set(current);\n                  if (next.has(prop.col)) next.delete(prop.col);\n                  else next.add(prop.col);\n                  return next;\n                })\n              }\n              className=\"relative z-10 -mx-1.5 flex h-8 items-center gap-2.5 rounded-[8px] px-1.5 text-left text-[13px] leading-none text-ink transition-transform duration-150 active:scale-[0.96]\"\n            >\n              <span className={pinnedColumns.has(prop.col) ? \"text-accent\" : \"text-ink-2\"}>\n                <Icon size={15}>\n                  <path d=\"M12 17v5M8 3h8l-1 7 3 3H6l3-3-1-7z\" />\n                </Icon>\n              </span>\n              {pinnedColumns.has(prop.col) ? \"Unpin\" : \"Pin\"}\n            </button>\n            <button\n              data-menu-row\n              type=\"button\"\n              aria-expanded={moreSettingsOpen}\n              onClick={() => setMoreSettingsOpen((open) => !open)}\n              className=\"relative z-10 -mx-1.5 flex h-8 items-center gap-2.5 rounded-[8px] px-1.5 text-left text-[13px] leading-none text-ink transition-transform duration-150 active:scale-[0.96]\"\n            >\n              <span className={moreSettingsOpen ? \"text-ink\" : \"text-ink-2\"}>\n                <Icon size={15}>\n                  <g>\n                    <circle cx=\"12\" cy=\"12\" r=\"3\" />\n                    <path d=\"M12 2v3M12 19v3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M2 12h3M19 12h3M4.9 19.1 7 17M17 7l2.1-2.1\" />\n                  </g>\n                </Icon>\n              </span>\n              <span className=\"flex-1\">More settings</span>\n              <span className={`text-ink-3 transition-transform duration-150 ${moreSettingsOpen ? \"rotate-90\" : \"\"}`}>\n                <Icon size={12} strokeWidth={2.2}>\n                  <path d=\"M9 6l6 6-6 6\" />\n                </Icon>\n              </span>\n            </button>\n            {prop.col === AI_LABEL && (\n              <button\n                data-menu-row\n                type=\"button\"\n                onClick={() => {\n                  setAiAdded(false);\n                  setAiDone(false);\n                  setProp(null);\n                }}\n                className=\"relative z-10 -mx-1.5 flex h-8 items-center gap-2.5 rounded-[8px] px-1.5 text-left text-[13px] leading-none text-ink transition-transform duration-150 active:scale-[0.96]\"\n              >\n                <span className=\"text-ink-2\">\n                  <Icon size={15}>\n                    <g>\n                      <path d=\"M10.6 5.1A9.8 9.8 0 0 1 12 5c7 0 10 7 10 7a16.3 16.3 0 0 1-2.1 3M6.6 6.6A16 16 0 0 0 2 12s3 7 10 7a9.7 9.7 0 0 0 5.4-1.6M3 3l18 18\" />\n                      <path d=\"M9.9 9.9a3 3 0 0 0 4.2 4.2\" />\n                    </g>\n                  </Icon>\n                </span>\n                Hide from view\n              </button>\n            )}\n          </GlideMenu>\n\n          {moreSettingsOpen && (\n            <div className=\"mt-2 animate-records-table-settings-in border-t border-line pt-2\">\n              <div className=\"pb-1 text-[11.5px] font-medium text-ink-3\">Behavior</div>\n              <ConfigRow label=\"Required value\">\n                <MiniSwitch\n                  label=\"Required value\"\n                  on={advancedSettings.required}\n                  onToggle={() => setAdvancedSettings((current) => ({ ...current, required: !current.required }))}\n                />\n              </ConfigRow>\n              <ConfigRow label=\"Allow empty results\">\n                <MiniSwitch\n                  label=\"Allow empty results\"\n                  on={advancedSettings.allowEmpty}\n                  onToggle={() => setAdvancedSettings((current) => ({ ...current, allowEmpty: !current.allowEmpty }))}\n                />\n              </ConfigRow>\n              <ConfigRow label=\"Show confidence\">\n                <MiniSwitch\n                  label=\"Show confidence\"\n                  on={advancedSettings.confidence}\n                  onToggle={() => setAdvancedSettings((current) => ({ ...current, confidence: !current.confidence }))}\n                />\n              </ConfigRow>\n            </div>\n          )}\n        </div>\n      )}\n\n      {/* ── new property type menu ─────────────────────────── */}\n      {addOpen && (\n        <div\n          data-recpop\n          className=\"origin-top-left animate-records-table-popover-in fixed z-50 w-[260px] rounded-[14px] bg-surface p-1.5 shadow-overlay\"\n          style={{ top: addOpen.y, left: addOpen.x }}\n        >\n          <div className=\"px-2 pb-1 pt-1 text-[12px] font-medium text-ink-3\">New property</div>\n          <GlideMenu className=\"flex flex-col gap-px\">\n            {NEW_PROPERTY_TYPES.map((type) => (\n              <button\n                key={type}\n                data-menu-row\n                type=\"button\"\n                onClick={() => {\n                  setAddOpen(null);\n                  setAiDone(false);\n                  setAiAdded(true);\n                  setPendingOpenAi(true);\n                }}\n                className=\"relative z-10 flex h-9 w-full items-center gap-2.5 rounded-[8px] px-2 text-left text-[13px] text-ink\"\n              >\n                <span className=\"text-ink-2\">\n                  <Icon size={15}>{TYPE_GLYPHS[type]}</Icon>\n                </span>\n                {type}\n              </button>\n            ))}\n          </GlideMenu>\n        </div>\n      )}\n\n      {/* ── table options menu ─────────────────────────────── */}\n      {tableMenuOpen && (\n        <div\n          data-recpop\n          className=\"origin-top-right animate-records-table-popover-in fixed z-50 w-[220px] rounded-[14px] bg-surface p-1.5 shadow-overlay\"\n          style={{ top: tableMenuOpen.y, left: tableMenuOpen.x }}\n        >\n          <div className=\"px-2 pb-1 pt-1 text-[12px] font-medium text-ink-3\">Table options</div>\n          <GlideMenu className=\"flex flex-col gap-px\">\n            <button\n              data-menu-row\n              type=\"button\"\n              onClick={() => {\n                const position = tableMenuOpen;\n                setTableMenuOpen(null);\n                setAddOpen({ x: Math.min(position.x, window.innerWidth - 276), y: position.y });\n              }}\n              className=\"relative z-10 flex h-9 w-full items-center gap-2.5 rounded-[8px] px-2 text-left text-[13px] text-ink\"\n            >\n              <span className=\"text-ink-2\">\n                <Icon size={15} strokeWidth={2}>\n                  <path d=\"M12 5v14M5 12h14\" />\n                </Icon>\n              </span>\n              Add property\n            </button>\n            <button\n              data-menu-row\n              type=\"button\"\n              onClick={() => {\n                setColumnWidths({ company: 220, categories: 220, last: 155, strength: 180, links: 160, ai: 200 });\n                setTableMenuOpen(null);\n              }}\n              className=\"relative z-10 flex h-9 w-full items-center gap-2.5 rounded-[8px] px-2 text-left text-[13px] text-ink\"\n            >\n              <span className=\"text-ink-2\">\n                <Icon size={15}>\n                  <path d=\"M4 8h16M7 4 3 8l4 4M17 4l4 4-4 4M4 16h16\" />\n                </Icon>\n              </span>\n              Compact columns\n            </button>\n            <button\n              data-menu-row\n              type=\"button\"\n              onClick={() => {\n                setColumnWidths({ ...(initialColumnWidthsRef.current ?? DEFAULT_COLUMN_WIDTHS) });\n                setTableMenuOpen(null);\n              }}\n              className=\"relative z-10 flex h-9 w-full items-center gap-2.5 rounded-[8px] px-2 text-left text-[13px] text-ink\"\n            >\n              <span className=\"text-ink-2\">\n                <Icon size={15}>\n                  <path d=\"M3 12a9 9 0 1 0 3-6.7M3 4v6h6\" />\n                </Icon>\n              </span>\n              Reset column widths\n            </button>\n            <div className=\"my-1 h-px bg-line\" />\n            <button\n              data-menu-row\n              type=\"button\"\n              onClick={() => {\n                setSelected(new Set());\n                setTableMenuOpen(null);\n              }}\n              className=\"relative z-10 flex h-9 w-full items-center gap-2.5 rounded-[8px] px-2 text-left text-[13px] text-ink\"\n            >\n              <span className=\"text-ink-2\">\n                <Icon size={15}>\n                  <path d=\"M5 5l14 14M19 5 5 19\" />\n                </Icon>\n              </span>\n              Clear selection\n            </button>\n          </GlideMenu>\n        </div>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/iris/records-table/records-table.tsx"
    }
  ],
  "docs": "Docs and live preview: https://irisui.dev/components/records-table",
  "meta": {
    "kind": "component",
    "group": "AI interface",
    "export": "RecordsTable",
    "docs": "https://irisui.dev/components/records-table"
  }
}
