{
  "name": "ToolCall",
  "type": "pattern",
  "description": "One tool invocation: a pill naming the product that owns the tool, beside the tool's function name in monospace. One line on desktop, stacked on tablet.",
  "export": "@gtmi/strix-react/atoms/tool-call",
  "path": "@gtmi/strix-react/atoms/tool-call",
  "props": [
    {
      "name": "product",
      "type": "ReactNode",
      "required": true,
      "description": "The pill's content — the product that owns the tool. A ReactNode rather than a string so a leading glyph can sit beside the name; the pill's own 8px gap exists for that."
    },
    {
      "name": "name",
      "type": "string",
      "required": true,
      "description": "The tool's function name, rendered in a <code>. A string and not a ReactNode: it is an identifier, and marking up part of an identifier means nothing."
    },
    {
      "name": "device",
      "type": "desktop | tablet",
      "default": "desktop",
      "description": "Figma's only variant axis, and a layout axis rather than a style one: desktop is a row, tablet stacks. No colour and no spacing differs between them."
    },
    {
      "name": "className",
      "type": "string",
      "description": "Merged through cn(), so a caller's utility wins."
    }
  ],
  "parts": [
    {
      "name": "tool-call",
      "description": "Root, via data-slot. Also carries data-device."
    },
    {
      "name": "tool-call-product",
      "description": "The product pill"
    },
    {
      "name": "tool-call-name",
      "description": "The monospace function name, a <code>"
    }
  ],
  "ai_tags": ["tool-call", "function", "code", "pill", "badge", "cintel", "conversation-intelligence", "pattern"],
  "since": "0.1.2",
  "figma": {
    "file": "85sD2HI8wksP9Y7uDmJ9DK",
    "fileName": "Multi product demo",
    "node": "883:9493",
    "component": "Tool Call",
    "usedBy": {
      "Under the hood": "836:7151 — three of these inside an InfoSection at density=loose"
    }
  },
  "tokens_used": [
    "--color-text-accent-strong",
    "--color-text-primary",
    "--color-surface-neutral-semi-medium",
    "--spacing-gap-200",
    "--spacing-padding-horizontal-sm",
    "--spacing-padding-vertical-xs",
    "--radius-2x-large",
    "--text-size-figma-12",
    "--leading-height-figma-12",
    "--text-size-mono-large",
    "--leading-height-mono-large",
    "--tracking-mono-large",
    "--font-weight-mono-large-semibold"
  ],
  "notes": "DEVICE IS A LAYOUT AXIS. Figma's `Device=Desktop` is 26px tall — exactly the pill's own height, so pill and name share a line — and `Device=Tablet` is 54px, which is 26 + 8 + 20: the same two children, the same 8px gap, stacked. Nothing else differs between the variants, so `device` switches flex-direction and touches no colour and no spacing.\n\nMONO INK: Figma binds `colors/code/3` (#7688c2) and Decision 1 of docs/superpowers/plans/2026-07-31-0.1.2-gtm-990-plan.md rejects it. That token is theme-invariant and measures 5.16:1 against the dark surface but 3.46:1 against the light one — under WCAG 1.4.3's 4.5:1 for this 16px name. --color-text-accent-strong is 6.98:1 light / 12.12:1 dark. The token itself is left alone: the docs/AGENTS.md note blessing `colors/code/3` measured it only against dark backdrops, which is correct for a code block on a dark page and does not generalise to a light one. The test names the REJECTED token rather than the chosen one, so reinstating it fails; the chosen one is derived from the component's own cva output.\n\nPILL FILL: not Figma's. Figma paints the pill `color/brand/accesible-red-dark-theme` #ef223a at 20% (#ef223a33) with a `color/brand/white` label. A translucent fill's composite depends on its backdrop, and the backdrop is the one thing that differs between the themes, so it cannot carry a fixed ink across. Composited on the light surface the tint is #fcd3d8 and the white label measures 1.36:1; on the dark surface it is #3c192d and the label is 15.34:1, which is why the dark-only source never had to face it. Per docs/AGENTS.md the alpha is measured, not ported. --color-surface-neutral-semi-medium is opaque in both themes (#f1f5ff / #232e47) and carries --color-text-primary at 17.75:1 light / 13.52:1 dark. Keeping the red as the INK instead was measured and rejected: --color-text-error on this fill is a legible 6.50:1 / 5.69:1, but an error token on a product label announces a failure that is not happening — the same meaning-over-contrast argument as Decision 4. There is no opaque red surface token to substitute: every --color-surface-error* step resolves to an rgba() in dark, and a test asserts the chosen fill is an opaque hex in both themes precisely so that substitution cannot be made silently.\n\nThe pill's edge measures 1.09:1 light / 1.32:1 dark against the page and no border is drawn. WCAG 1.4.11 asks 3:1 of the boundary of a control; this pill is not interactive and its edge carries no information the label does not, so the boundary is decoration.\n\nTYPE ROLES. The pill label uses --text-size-figma-12 with --leading-height-figma-12 rather than the `body-xs` role, because 18 + 2x4 of vertical padding is exactly Figma's 26px height where body-xs's 16px leading gives 24. `Badge`'s `small` size uses the same pair for the same reason. The function name uses the `mono-l-semibold` role: 16px matches Figma exactly, and two divergences are knowing ones — the role is weight 600 where Figma reports 400, and its leading is 24 where Figma reports 20. Figma binds NO font variable on that layer (family, size, weight and leading are all unbound literals there), so 400 is provenance rather than a token, and the layer has no 16px regular-weight mono step. `MetricList` took the same role for the same reason. The role's five utilities are written out literally in the component's own cva rather than reached through `<Typography variant>`, which is a deliberate exception to the house pattern: the name belongs in a <code>, and Typography's AllowedTags has no `code`, so the alternative was nesting a span inside a bare code element to say one thing twice. Every utility is a literal, because Tailwind extracts class names by scanning source text and a name assembled from TYPOGRAPHY_VARIANTS would generate no CSS. Dropping to `mono-m-regular` to match the weight would shrink the identifier to 14px, the larger of the two divergences.\n\nBADGE WAS CONSIDERED AND REJECTED for the pill. Its `small` size is close — the same 8px horizontal padding, the same fully-round shape, the same 12/18 label — but it takes `label: string` where Figma's pill holds arbitrary content, its vertical padding is the 2px step so it renders 22px rather than 26px, and its `variant` axis is a status scale (error, warning, success). A product name is not a status, and choosing one of those variants to obtain a colour would announce a state that is not happening.\n\nTRACK A2 VERDICT: NO COMPONENT. The five Conversation Intelligence sections — Profile 836:6991, Live intelligence 836:7042 (GTM-990's \"Operators\"), Under the hood 836:7151, Observations 836:7614, Summary 836:7604 — are `<InfoSection density=… icon=… >` over different children and nothing accumulates, so they ship as the six `CIntel / *` stories in tool-call.stories.tsx rather than as a `CintelPanel` component. No src/patterns/cintel-panel/ directory was created. Three things came out of writing them, all recorded here rather than resolved silently. (1) The plan states that Under the hood's `Details` block has fill #ef223a33. It does not: per the draft binding, that node's root is `fill: none` with `border: color/gray/gray-700`, and the #ef223a33 fills belong to the `Product` pills of the three nested Tool Calls. The 20% tint is therefore the pill's, handled above, and there is no translucent Details fill to port or decline. (2) Under the hood is the only one of the five that is a bordered card — `borderWidth: 1`, `paddingVertical: 12`, `fill: none` — where the other four have neither border nor padding. One instance is not a variant, so the story adds it through `className` rather than growing `InfoSection` a prop, and uses --color-border-control (4.78:1 / 5.55:1) rather than Figma's #38425e, which measures 1.79:1 against its own surface. (3) CORRECTED. Figma keeps two gaps and InfoSection now keeps both: the header-to-body gap that `density` encodes (12/12/16/8/8, off each section's root itemSpacing) and the inner list gap, 12 in four of the five and 10 in Summary text. This note used to say InfoSection had ONE gap and that density=\"loose\" therefore spaced Under the hood's three calls 16 apart -- recorded as a knowing divergence scoped to one section. It was the inverted-axis bug: `density` drove the BODY's gap while the root sat pinned at 8px, so all five sections had the header-to-body gap wrong, and density=\"default\" was wrong ONLY there. The inner gap is now a constant 12 and the root varies. Summary's 10px has no token and never renders -- that block holds one paragraph.\n\nOBSERVATIONS' PILL is a second pill, not this one: 24px tall around a 14/20 label where Tool Call's is 26px around 12/18. The story reuses the exported `toolCallProductStyles()` class list rather than introducing a component. If a third pill appears, that is the rule of three and a `Pill` primitive earns its place.\n\nMUTATION-TESTED, three ways, all quoting real ratios. Name ink to `text-highlight`: \"--color-highlight (light): #3acefa on #ffffff = 1.84:1, wanted 4.5:1\". Pill ink to `text-highlight`: \"--color-highlight on --color-surface-neutral-semi-medium (light): #3acefa on #f1f5ff = 1.69:1, wanted 4.5:1\". Pill fill to `bg-surface-error`: two failures, both reporting it unmeasurable in dark — \"--color-surface-error=rgba(177, 15, 35, 0.2)\". Each role is derived from its own separate style function; they are never collected into one list and split by token-name prefix, which is the mistake that made InfoSection's first draft vacuous."
}
