{
  "Background.gradient.width": {
    "rule": "Quarter-percent units: 400 = 100% of container. Soft glows use width 100–200, not 500.",
    "components": [
      "Background"
    ]
  },
  "Background.gradient.x": {
    "rule": "x/y are percent positions of the container (50/50 = center).",
    "components": [
      "Background"
    ]
  },
  "Background.absolute": {
    "rule": "Every absolute decorative layer needs top=\"0\" left=\"0\" or it offsets by parent padding.",
    "components": [
      "Background",
      "MatrixFx",
      "BlobFx"
    ]
  },
  "Card.interactive": {
    "rule": "Card is for interactive surfaces only (href or onClick). Static panels use Column + background=\"surface\" border radius=\"l\".",
    "components": [
      "Card"
    ]
  },
  "Fade.edge": {
    "rule": "Fade is an edge overlay strip on scroll containers, not a content wrapper.",
    "components": [
      "Fade"
    ]
  },
  "RevealFx.delay": {
    "rule": "delay is in milliseconds — stagger with delay={index * 100}, never index * 0.1 (that was 1.8.x; 2.0 reads 0.1 as a tenth of a millisecond, i.e. no stagger).",
    "components": [
      "RevealFx",
      "ShineFx"
    ]
  },
  "RevealFx.translateY": {
    "rule": "translateY as a number is in rem. Use translateY={1} or token \"16\" (1rem); 16 means 16rem.",
    "components": [
      "RevealFx"
    ]
  },
  "LogoCloud.columns": {
    "rule": "LogoCloud extends Grid — always set columns and responsive overrides or logos stack in one column.",
    "components": [
      "LogoCloud"
    ]
  },
  "StatusIndicator.color": {
    "rule": "StatusIndicator uses color=\"green\" | \"red\" | \"orange\" | \"gray\" etc. — not variant or semantic scheme names.",
    "components": [
      "StatusIndicator"
    ]
  },
  "Icon.names": {
    "rule": "Icon names must come from iconNames in spec.json, which is a real union in 2.0 (an unregistered name is a type error, not a blank space). Do not guess a plausible name, and do not cast one with `as IconName` to silence the check. Brand marks are not in the set — an app registers those itself by augmenting IconLibraryOverrides.",
    "components": [
      "Icon",
      "Button",
      "IconButton"
    ]
  },
  "Badge.duplicate": {
    "rule": "Do not pass the same text as both title prop and children — it renders twice.",
    "components": [
      "Badge"
    ]
  },
  "Tag.duplicate": {
    "rule": "Do not pass the same text as both label prop and children — it renders twice.",
    "components": [
      "Tag"
    ]
  },
  "Timeline.notScrubber": {
    "rule": "Timeline lays a sequence of steps down the page. For a video or audio editor's timeline — a playhead over stacked tracks of movable, trimmable blocks — use Scrubber. Do not build one out of Flex and absolute positioning.",
    "components": [
      "Timeline",
      "Scrubber"
    ]
  },
  "color.tokens": {
    "rule": "Colors are semantic tokens ({scheme}-{weight}), never hex/rgb. background also accepts surface | overlay | page | transparent.",
    "components": [
      "Column",
      "Row",
      "Text",
      "Button"
    ]
  },
  "color.background-weak": {
    "rule": "*-background-weak is nearly the page color — invisible as accent. Use alpha tokens (brand-alpha-medium) for glows.",
    "components": [
      "Background"
    ]
  },
  "layout.shorthands": {
    "rule": "Use fill not fillWidth fillHeight; center not horizontal=\"center\" vertical=\"center\"; Row/Column not Flex direction=.",
    "components": [
      "Row",
      "Column",
      "Flex",
      "Grid"
    ]
  },
  "layout.defaults": {
    "rule": "Never write props equal to defaults or implied by another prop on the element: position=\"relative\", direction on Row/Column, variant=\"primary\" on Button, fillWidth beside maxWidth (maxWidth already fills the width), minWidth={0} beside fillWidth (fillWidth already sets min-width: 0).",
    "components": [
      "Row",
      "Column",
      "Grid",
      "Button"
    ]
  },
  "border.default": {
    "rule": "Bare border (and borderTop, borderBottom, borderX, …) draws the default border. Do not write border=\"neutral-alpha-weak\" to get it; name a colour only to deviate, e.g. border=\"brand-alpha-medium\".",
    "components": [
      "Row",
      "Column",
      "Card"
    ]
  },
  "zIndex.scope": {
    "rule": "zIndex is for an isolated stacking group: an absolute decorative layer and its content sibling (zIndex={1}), or a sticky header over its pane. Never on a lone element, never a page-wide ladder of 3 / 5 / 9 — restructure the tree instead.",
    "components": [
      "Row",
      "Column",
      "Background"
    ]
  },
  "section.gaps": {
    "rule": "Gap between page sections: 80–160 (typical 104). Inside sections: 24–40.",
    "components": [
      "Column"
    ]
  },
  "heading.scale": {
    "rule": "Hero = display-strong-l/xl; section titles = display-strong-xs/s — never heading-* for section titles.",
    "components": [
      "Heading"
    ]
  },
  "animation.budget": {
    "rule": "Entry animation budget: hero + at most 1–2 key moments. Animate coherent blocks all-or-none within a sibling set.",
    "components": [
      "RevealFx",
      "CountFx"
    ]
  },
  "CountFx.trigger": {
    "rule": "CountFx has no trigger prop — drive value from useInViewport latch instead.",
    "components": [
      "CountFx"
    ]
  },
  "overflow.hero": {
    "rule": "overflow=\"hidden\" on full-bleed sections clips glows into hard bands — only use when parent has radius.",
    "components": [
      "Column",
      "Background"
    ]
  }
}
