{
  "name": "Localintel design system",
  "description": "The live catalogue of the tokens, components and composition patterns the Localintel apps are built from. Generated from source; do not hand-edit.",
  "generatedFrom": [
    "projects/localintel3.lib.core/src/lib/styling/styles.scss",
    "projects/localintel3.lib.core/src/lib/styling/design-tokens.scss",
    "projects/localintel3.lib.fashion/src/lib/*/*.component.ts",
    "projects/localintel3.boutique/src/app/app.routes.ts"
  ],
  "contract": "/docs/design-contract.md",
  "breakpoint": {
    "name": "md",
    "width": "768px",
    "note": "The only breakpoint the app switches on."
  },
  "routes": [
    "/buttons",
    "/contract",
    "/forms",
    "/frame/card-grid",
    "/frame/navigation",
    "/frame/preview",
    "/frame/toolbar",
    "/navigation",
    "/overlays",
    "/patterns",
    "/table",
    "/tokens"
  ],
  "rule": "Across breakpoints, keep the same elements in the same containers. Change size, direction, order and visibility as freely as you like. Do not re-parent an element, and do not split one group into two.",
  "checklist": [
    "Are colours named as tokens rather than hexes, and does every colour have a dark value?",
    "Are type sizes, radii and spacing on our scales — or, where they are not, is that a deliberate ask?",
    "Does every breakpoint change amount to size, direction, order or visibility only? If something is re-parented or regrouped, is that called out?",
    "Is the mobile view drawn at 375px, and does every over-wide row say whether it scrolls, wraps, or truncates?",
    "Are grids expressed as *N across at width W*, and is your canvas width stated?",
    "Does every control show its disabled and loading states, and every list its empty and loading states?"
  ],
  "notes": [
    "A token marked \"utility\": true generates bg-/text-/border- classes. One marked false is a plain :root variable with no class behind it and must be read as text-(--color-name).",
    "Every scale sits in an @theme inline block, which substitutes values into utilities and emits no custom property, so a token cannot be read back at runtime with getPropertyValue.",
    "Topic, tag and notification colours are chosen by content rather than by a design, and the lookup has no fallback: an unrecognised name renders with no colour at all."
  ],
  "tokens": {
    "colour": [
      {
        "name": "action",
        "token": "--color-action",
        "utility": false,
        "declared": "light-dark(#111826, #ffffff)"
      },
      {
        "name": "app-background",
        "token": "--color-app-background",
        "utility": true,
        "declared": "light-dark(#ffffff, #1B2331)"
      },
      {
        "name": "block-outline",
        "token": "--color-block-outline",
        "utility": true,
        "declared": "light-dark(#E3E7ED, #333C4B)"
      },
      {
        "name": "block-outline-active",
        "token": "--color-block-outline-active",
        "utility": true,
        "declared": "light-dark(#E9ECF1, #3C4657)"
      },
      {
        "name": "button-secondary",
        "token": "--color-button-secondary",
        "utility": true,
        "declared": "light-dark(#F2F4F8, #1B2331)"
      },
      {
        "name": "button-secondary-active",
        "token": "--color-button-secondary-active",
        "utility": true,
        "declared": "light-dark(#DCE0E7, #3C4657)"
      },
      {
        "name": "button-secondary-hover",
        "token": "--color-button-secondary-hover",
        "utility": true,
        "declared": "light-dark(#E9ECF1, #2A3444)"
      },
      {
        "name": "button-secondary-text",
        "token": "--color-button-secondary-text",
        "utility": true,
        "declared": "light-dark(#69717E, #98A0AC)"
      },
      {
        "name": "contrast",
        "token": "--color-contrast",
        "utility": true,
        "declared": "light-dark(black, white)"
      },
      {
        "name": "contrast-fill",
        "token": "--color-contrast-fill",
        "utility": true,
        "declared": "light-dark(#0D1420, #ffffff)"
      },
      {
        "name": "contrast-fill-active",
        "token": "--color-contrast-fill-active",
        "utility": true,
        "declared": "light-dark(#2B3341, #C6CCD6)"
      },
      {
        "name": "contrast-fill-hover",
        "token": "--color-contrast-fill-hover",
        "utility": true,
        "declared": "light-dark(#111826, #E3E7ED)"
      },
      {
        "name": "danger",
        "token": "--color-danger",
        "utility": true,
        "declared": "light-dark(#ef4444, #f87171)"
      },
      {
        "name": "date",
        "token": "--color-date",
        "utility": false,
        "declared": "light-dark(#5A6474, #ffffff)"
      },
      {
        "name": "error",
        "token": "--color-error",
        "utility": true,
        "declared": "light-dark(#ef4444, #f87171)"
      },
      {
        "name": "error-active",
        "token": "--color-error-active",
        "utility": true,
        "declared": "light-dark(#b91c1c, #fecaca)"
      },
      {
        "name": "error-hover",
        "token": "--color-error-hover",
        "utility": true,
        "declared": "light-dark(#dc2626, #fca5a5)"
      },
      {
        "name": "error-outline",
        "token": "--color-error-outline",
        "utility": true,
        "declared": "light-dark(#fecaca, #b91c1c)"
      },
      {
        "name": "error-tint",
        "token": "--color-error-tint",
        "utility": true,
        "declared": "light-dark(#fef2f2, color-mix(in srgb, #f87171, transparent 96%))"
      },
      {
        "name": "error-tint-active",
        "token": "--color-error-tint-active",
        "utility": true,
        "declared": "light-dark(#fee2e2, color-mix(in srgb, #f87171, transparent 84%))"
      },
      {
        "name": "filter-option",
        "token": "--color-filter-option",
        "utility": false,
        "declared": "light-dark(#39424F, #ffffff)"
      },
      {
        "name": "focus-ring",
        "token": "--color-focus-ring",
        "utility": true,
        "declared": "light-dark(#1F5FE0, #9CC2FF)"
      },
      {
        "name": "focus-ring-wash",
        "token": "--color-focus-ring-wash",
        "utility": true,
        "declared": "color-mix(in srgb, #1F5FE0 13%, transparent)"
      },
      {
        "name": "heading-1",
        "token": "--color-heading-1",
        "utility": false,
        "declared": "light-dark(#111826, #ffffff)"
      },
      {
        "name": "heading-2",
        "token": "--color-heading-2",
        "utility": false,
        "declared": "light-dark(#111826, #ffffff)"
      },
      {
        "name": "heading-3",
        "token": "--color-heading-3",
        "utility": false,
        "declared": "light-dark(#111826, #ffffff)"
      },
      {
        "name": "heading-4",
        "token": "--color-heading-4",
        "utility": false,
        "declared": "light-dark(#5A6474, #ffffff)"
      },
      {
        "name": "heading-5",
        "token": "--color-heading-5",
        "utility": false,
        "declared": "light-dark(#98A0AC, #6B7482)"
      },
      {
        "name": "icon-chart",
        "token": "--color-icon-chart",
        "utility": false,
        "declared": "#4f46e5"
      },
      {
        "name": "icon-kpi",
        "token": "--color-icon-kpi",
        "utility": false,
        "declared": "#e8500f"
      },
      {
        "name": "icon-map",
        "token": "--color-icon-map",
        "utility": false,
        "declared": "#0f766e"
      },
      {
        "name": "icon-report",
        "token": "--color-icon-report",
        "utility": false,
        "declared": "#be185d"
      },
      {
        "name": "icon-story",
        "token": "--color-icon-story",
        "utility": false,
        "declared": "#2a78d6"
      },
      {
        "name": "icon-table",
        "token": "--color-icon-table",
        "utility": false,
        "declared": "#7c3aed"
      },
      {
        "name": "image-chip",
        "token": "--color-image-chip",
        "utility": true,
        "declared": "rgb(20 27 38 / 0.78)"
      },
      {
        "name": "image-chip-danger",
        "token": "--color-image-chip-danger",
        "utility": true,
        "declared": "rgb(177 22 60 / 0.9)"
      },
      {
        "name": "input-background",
        "token": "--color-input-background",
        "utility": true,
        "declared": "light-dark(#ffffff, #0B111C)"
      },
      {
        "name": "input-outline",
        "token": "--color-input-outline",
        "utility": true,
        "declared": "light-dark(#DCE0E7, #3C4657)"
      },
      {
        "name": "input-outline-strong",
        "token": "--color-input-outline-strong",
        "utility": true,
        "declared": "light-dark(#C6CCD6, #5A6474)"
      },
      {
        "name": "notification-data-update",
        "token": "--color-notification-data-update",
        "utility": true,
        "declared": "light-dark(#fdba74, #fb923c)"
      },
      {
        "name": "notification-new-feature",
        "token": "--color-notification-new-feature",
        "utility": true,
        "declared": "light-dark(#c4b5fd, #a78bfa)"
      },
      {
        "name": "notification-report",
        "token": "--color-notification-report",
        "utility": true,
        "declared": "light-dark(#fca5a5, #f87171)"
      },
      {
        "name": "notification-story",
        "token": "--color-notification-story",
        "utility": true,
        "declared": "light-dark(#fde047, #facc15)"
      },
      {
        "name": "notification-system-update",
        "token": "--color-notification-system-update",
        "utility": true,
        "declared": "light-dark(#93c5fd, #60a5fa)"
      },
      {
        "name": "on-contrast",
        "token": "--color-on-contrast",
        "utility": true,
        "declared": "light-dark(#ffffff, #0B111C)"
      },
      {
        "name": "page-ground",
        "token": "--color-page-ground",
        "utility": true,
        "declared": "light-dark(#F6F7F9, #111826)"
      },
      {
        "name": "placeholder",
        "token": "--color-placeholder",
        "utility": true,
        "declared": "light-dark(#98A0AC, #6B7482)"
      },
      {
        "name": "primary",
        "token": "--color-primary",
        "utility": true,
        "declared": "light-dark(#1F5FE0, #9CC2FF)"
      },
      {
        "name": "primary-active",
        "token": "--color-primary-active",
        "utility": true,
        "declared": "light-dark(#1743A8, #DCE7FA)"
      },
      {
        "name": "primary-contrast",
        "token": "--color-primary-contrast",
        "utility": true,
        "declared": "light-dark(#ffffff, #111826)"
      },
      {
        "name": "primary-hover",
        "token": "--color-primary-hover",
        "utility": true,
        "declared": "light-dark(#1749BC, #B9CDF6)"
      },
      {
        "name": "primary-outline",
        "token": "--color-primary-outline",
        "utility": true,
        "declared": "light-dark(#DCE7FA, #1743A8)"
      },
      {
        "name": "primary-selected",
        "token": "--color-primary-selected",
        "utility": true,
        "declared": "light-dark(#133A8F, #F2F6FE)"
      },
      {
        "name": "primary-tint",
        "token": "--color-primary-tint",
        "utility": true,
        "declared": "light-dark(#F2F6FE, color-mix(in srgb, #9CC2FF, transparent 88%))"
      },
      {
        "name": "primary-tint-active",
        "token": "--color-primary-tint-active",
        "utility": true,
        "declared": "light-dark(#F2F6FE, color-mix(in srgb, #9CC2FF, transparent 84%))"
      },
      {
        "name": "primary-tint-strong",
        "token": "--color-primary-tint-strong",
        "utility": true,
        "declared": "light-dark(#DCE7FA, #1743A8)"
      },
      {
        "name": "subtitle",
        "token": "--color-subtitle",
        "utility": false,
        "declared": "light-dark(#5A6474, #ffffff)"
      },
      {
        "name": "success",
        "token": "--color-success",
        "utility": true,
        "declared": "light-dark(#22c55e, #4ade80)"
      },
      {
        "name": "success-active",
        "token": "--color-success-active",
        "utility": true,
        "declared": "light-dark(#15803d, #bbf7d0)"
      },
      {
        "name": "success-hover",
        "token": "--color-success-hover",
        "utility": true,
        "declared": "light-dark(#16a34a, #86efac)"
      },
      {
        "name": "success-outline",
        "token": "--color-success-outline",
        "utility": true,
        "declared": "light-dark(#bbf7d0, #15803d)"
      },
      {
        "name": "success-tint",
        "token": "--color-success-tint",
        "utility": true,
        "declared": "light-dark(#f0fdf4, color-mix(in srgb, #4ade80, transparent 96%))"
      },
      {
        "name": "success-tint-active",
        "token": "--color-success-tint-active",
        "utility": true,
        "declared": "light-dark(#dcfce7, color-mix(in srgb, #4ade80, transparent 84%))"
      },
      {
        "name": "surface-100",
        "token": "--color-surface-100",
        "utility": false,
        "declared": "light-dark(#F2F4F8, #E3E7ED)"
      },
      {
        "name": "surface-25",
        "token": "--color-surface-25",
        "utility": true,
        "declared": "light-dark(#FAFBFC, #1B2331)"
      },
      {
        "name": "surface-300",
        "token": "--color-surface-300",
        "utility": false,
        "declared": "light-dark(#DCE0E7, #98A0AC)"
      },
      {
        "name": "surface-400",
        "token": "--color-surface-400",
        "utility": false,
        "declared": "light-dark(#C6CCD6, #6B7482)"
      },
      {
        "name": "surface-50",
        "token": "--color-surface-50",
        "utility": true,
        "declared": "light-dark(#F6F7F9, #1B2331)"
      },
      {
        "name": "surface-500",
        "token": "--color-surface-500",
        "utility": false,
        "declared": "light-dark(#98A0AC, #5A6474)"
      },
      {
        "name": "surface-600",
        "token": "--color-surface-600",
        "utility": false,
        "declared": "light-dark(#69717E, #3C4657)"
      },
      {
        "name": "surface-700",
        "token": "--color-surface-700",
        "utility": false,
        "declared": "light-dark(#5A6474, #2A3444)"
      },
      {
        "name": "surface-800",
        "token": "--color-surface-800",
        "utility": false,
        "declared": "light-dark(#2B3341, #1B2331)"
      },
      {
        "name": "surface-900",
        "token": "--color-surface-900",
        "utility": false,
        "declared": "light-dark(#111826, #111826)"
      },
      {
        "name": "surface-hover",
        "token": "--color-surface-hover",
        "utility": false,
        "declared": "light-dark(#F6F7F9, color-mix(in srgb, #ffffff, transparent 96%))"
      },
      {
        "name": "tag-newly-added-bg",
        "token": "--color-tag-newly-added-bg",
        "utility": false,
        "declared": "light-dark(#F6F7F9, color-mix(in srgb, #98A0AC, transparent 75%))"
      },
      {
        "name": "tag-newly-added-text",
        "token": "--color-tag-newly-added-text",
        "utility": false,
        "declared": "light-dark(#98A0AC, #DCE0E7)"
      },
      {
        "name": "tag-popular-bg",
        "token": "--color-tag-popular-bg",
        "utility": false,
        "declared": "light-dark(#FFF8E1, color-mix(in srgb, #FFC107, transparent 75%))"
      },
      {
        "name": "tag-popular-text",
        "token": "--color-tag-popular-text",
        "utility": false,
        "declared": "light-dark(#FFC107, #FFD54F)"
      },
      {
        "name": "tag-recently-updated-bg",
        "token": "--color-tag-recently-updated-bg",
        "utility": false,
        "declared": "light-dark(#E1F5FE, color-mix(in srgb, #03A9F4, transparent 75%))"
      },
      {
        "name": "tag-recently-updated-text",
        "token": "--color-tag-recently-updated-text",
        "utility": false,
        "declared": "light-dark(#03A9F4, #4FC3F7)"
      },
      {
        "name": "text-default",
        "token": "--color-text-default",
        "utility": false,
        "declared": "light-dark(#111826, #ffffff)"
      },
      {
        "name": "text-hover",
        "token": "--color-text-hover",
        "utility": false,
        "declared": "light-dark(#2B3341, #ffffff)"
      },
      {
        "name": "topic-affordability-bg",
        "token": "--color-topic-affordability-bg",
        "utility": false,
        "declared": "light-dark(#FFEBEE, color-mix(in srgb, #FF1744, transparent 75%))"
      },
      {
        "name": "topic-affordability-text",
        "token": "--color-topic-affordability-text",
        "utility": false,
        "declared": "light-dark(#D5190B, #FF8A80)"
      },
      {
        "name": "topic-diversity-bg",
        "token": "--color-topic-diversity-bg",
        "utility": false,
        "declared": "light-dark(#FCE4EC, color-mix(in srgb, #EF6091, transparent 75%))"
      },
      {
        "name": "topic-diversity-text",
        "token": "--color-topic-diversity-text",
        "utility": false,
        "declared": "light-dark(#E91E63, #F6A2BE)"
      },
      {
        "name": "topic-economy-bg",
        "token": "--color-topic-economy-bg",
        "utility": false,
        "declared": "light-dark(#F3E5F5, color-mix(in srgb, #B966C7, transparent 75%))"
      },
      {
        "name": "topic-economy-text",
        "token": "--color-topic-economy-text",
        "utility": false,
        "declared": "light-dark(#9C27B0, #D6A6DE)"
      },
      {
        "name": "topic-education-bg",
        "token": "--color-topic-education-bg",
        "utility": false,
        "declared": "light-dark(#E8F5E9, color-mix(in srgb, #80C683, transparent 75%))"
      },
      {
        "name": "topic-education-text",
        "token": "--color-topic-education-text",
        "utility": false,
        "declared": "light-dark(#4CAF50, #B4DEB6)"
      },
      {
        "name": "topic-households-bg",
        "token": "--color-topic-households-bg",
        "utility": false,
        "declared": "light-dark(#E3F2FD, color-mix(in srgb, #62B5F6, transparent 75%))"
      },
      {
        "name": "topic-households-text",
        "token": "--color-topic-households-text",
        "utility": false,
        "declared": "light-dark(#2196F3, #A2D3FA)"
      },
      {
        "name": "topic-housing-bg",
        "token": "--color-topic-housing-bg",
        "utility": false,
        "declared": "light-dark(#F1F8E9, color-mix(in srgb, #A8D17A, transparent 75%))"
      },
      {
        "name": "topic-housing-text",
        "token": "--color-topic-housing-text",
        "utility": false,
        "declared": "light-dark(#8BC34A, #D0E6B9)"
      },
      {
        "name": "topic-immigration-bg",
        "token": "--color-topic-immigration-bg",
        "utility": false,
        "declared": "light-dark(#ECEFF1, color-mix(in srgb, #8FA3AD, transparent 75%))"
      },
      {
        "name": "topic-immigration-text",
        "token": "--color-topic-immigration-text",
        "utility": false,
        "declared": "light-dark(#607D8B, #BDC9CF)"
      },
      {
        "name": "topic-income-bg",
        "token": "--color-topic-income-bg",
        "utility": false,
        "declared": "light-dark(#FBE9E7, color-mix(in srgb, #FE8864, transparent 75%))"
      },
      {
        "name": "topic-income-text",
        "token": "--color-topic-income-text",
        "utility": false,
        "declared": "light-dark(#FF5722, #FCB8A5)"
      },
      {
        "name": "topic-industry-bg",
        "token": "--color-topic-industry-bg",
        "utility": false,
        "declared": "light-dark(#E8EAF6, color-mix(in srgb, #7784CB, transparent 75%))"
      },
      {
        "name": "topic-industry-text",
        "token": "--color-topic-industry-text",
        "utility": false,
        "declared": "light-dark(#3F51B5, #B0B7E0)"
      },
      {
        "name": "topic-language-bg",
        "token": "--color-topic-language-bg",
        "utility": false,
        "declared": "light-dark(#FCE4EC, color-mix(in srgb, #EF6091, transparent 75%))"
      },
      {
        "name": "topic-language-text",
        "token": "--color-topic-language-text",
        "utility": false,
        "declared": "light-dark(#E91E63, #F6A2BE)"
      },
      {
        "name": "topic-location-bg",
        "token": "--color-topic-location-bg",
        "utility": false,
        "declared": "light-dark(#EFEBE9, color-mix(in srgb, #A0877E, transparent 75%))"
      },
      {
        "name": "topic-location-text",
        "token": "--color-topic-location-text",
        "utility": false,
        "declared": "light-dark(#795548, #C8B9B3)"
      },
      {
        "name": "topic-mobility-bg",
        "token": "--color-topic-mobility-bg",
        "utility": false,
        "declared": "light-dark(#EDE7F6, color-mix(in srgb, #9474CC, transparent 75%))"
      },
      {
        "name": "topic-mobility-text",
        "token": "--color-topic-mobility-text",
        "utility": false,
        "declared": "light-dark(#673AB7, #C0ADE1)"
      },
      {
        "name": "topic-population-bg",
        "token": "--color-topic-population-bg",
        "utility": false,
        "declared": "light-dark(#FFF3E0, color-mix(in srgb, #FFB64B, transparent 75%))"
      },
      {
        "name": "topic-population-text",
        "token": "--color-topic-population-text",
        "utility": false,
        "declared": "light-dark(#FF9800, #FFD595)"
      },
      {
        "name": "topic-workforce-bg",
        "token": "--color-topic-workforce-bg",
        "utility": false,
        "declared": "light-dark(#E0F2F1, color-mix(in srgb, #4BB5AB, transparent 75%))"
      },
      {
        "name": "topic-workforce-text",
        "token": "--color-topic-workforce-text",
        "utility": false,
        "declared": "light-dark(#009688, #95D3CE)"
      },
      {
        "name": "version-bg",
        "token": "--color-version-bg",
        "utility": false,
        "declared": "light-dark(#f5f3ff, color-mix(in srgb, #8b5cf6, transparent 75%))"
      },
      {
        "name": "version-text",
        "token": "--color-version-text",
        "utility": false,
        "declared": "light-dark(#6d28d9, #c4b5fd)"
      },
      {
        "name": "warning",
        "token": "--color-warning",
        "utility": false,
        "declared": "light-dark(#FFA000, #FFCA28)"
      }
    ],
    "type": [
      {
        "name": "action",
        "token": "--text-action",
        "utility": true,
        "declared": "14px",
        "fontStyle": "normal",
        "fontWeight": "400",
        "lineHeight": "normal"
      },
      {
        "name": "action-sm",
        "token": "--text-action-sm",
        "utility": true,
        "declared": "13.5px",
        "fontWeight": "600",
        "lineHeight": "1.2"
      },
      {
        "name": "action-strong",
        "token": "--text-action-strong",
        "utility": true,
        "declared": "14.5px",
        "fontWeight": "600",
        "lineHeight": "1.2"
      },
      {
        "name": "badge",
        "token": "--text-badge",
        "utility": true,
        "declared": "11px",
        "fontWeight": "600",
        "letterSpacing": "0.01em"
      },
      {
        "name": "body-sm",
        "token": "--text-body-sm",
        "utility": true,
        "declared": "13.5px",
        "fontWeight": "400",
        "lineHeight": "1.45"
      },
      {
        "name": "body-xs",
        "token": "--text-body-xs",
        "utility": true,
        "declared": "13px",
        "fontWeight": "400",
        "lineHeight": "1.5"
      },
      {
        "name": "caption",
        "token": "--text-caption",
        "utility": true,
        "declared": "11.5px",
        "fontWeight": "400",
        "lineHeight": "1.35"
      },
      {
        "name": "code",
        "token": "--text-code",
        "utility": true,
        "declared": "11px",
        "fontWeight": "400",
        "lineHeight": "1.6"
      },
      {
        "name": "count-badge",
        "token": "--text-count-badge",
        "utility": true,
        "declared": "11.5px",
        "fontWeight": "700"
      },
      {
        "name": "date",
        "token": "--text-date",
        "utility": true,
        "declared": "12.5px",
        "fontStyle": "normal",
        "fontWeight": "400",
        "lineHeight": "1.45"
      },
      {
        "name": "display",
        "token": "--text-display",
        "utility": true,
        "declared": "30px",
        "fontWeight": "700",
        "letterSpacing": "-0.022em",
        "lineHeight": "1.15"
      },
      {
        "name": "display-sm",
        "token": "--text-display-sm",
        "utility": true,
        "declared": "25px",
        "fontWeight": "700",
        "letterSpacing": "-0.022em",
        "lineHeight": "1.2"
      },
      {
        "name": "ext-chip",
        "token": "--text-ext-chip",
        "utility": true,
        "declared": "10.5px",
        "fontWeight": "700",
        "letterSpacing": "0.04em"
      },
      {
        "name": "eyebrow",
        "token": "--text-eyebrow",
        "utility": true,
        "declared": "11.5px",
        "fontWeight": "700",
        "letterSpacing": "0.07em"
      },
      {
        "name": "filter-option",
        "token": "--text-filter-option",
        "utility": true,
        "declared": "14px",
        "fontStyle": "normal",
        "fontWeight": "500",
        "lineHeight": "1.3"
      },
      {
        "name": "heading-1",
        "token": "--text-heading-1",
        "utility": true,
        "declared": "24.5px",
        "fontStyle": "normal",
        "fontWeight": "700",
        "lineHeight": "normal"
      },
      {
        "name": "heading-2",
        "token": "--text-heading-2",
        "utility": true,
        "declared": "20px",
        "fontStyle": "normal",
        "fontWeight": "700",
        "lineHeight": "normal"
      },
      {
        "name": "heading-3",
        "token": "--text-heading-3",
        "utility": true,
        "declared": "17.5px",
        "fontStyle": "normal",
        "fontWeight": "700",
        "lineHeight": "normal"
      },
      {
        "name": "heading-4",
        "token": "--text-heading-4",
        "utility": true,
        "declared": "14px",
        "fontStyle": "normal",
        "fontWeight": "600",
        "lineHeight": "normal"
      },
      {
        "name": "heading-5",
        "token": "--text-heading-5",
        "utility": true,
        "declared": "12.5px",
        "fontStyle": "normal",
        "fontWeight": "700",
        "lineHeight": "normal"
      },
      {
        "name": "subtitle",
        "token": "--text-subtitle",
        "utility": true,
        "declared": "14.5px",
        "fontStyle": "normal",
        "fontWeight": "400",
        "lineHeight": "1.5"
      },
      {
        "name": "table-header",
        "token": "--text-table-header",
        "utility": true,
        "declared": "12px",
        "fontWeight": "700",
        "letterSpacing": "0.05em"
      },
      {
        "name": "topic",
        "token": "--text-topic",
        "utility": true,
        "declared": "11.5px",
        "fontStyle": "normal",
        "fontWeight": "600",
        "lineHeight": "normal"
      }
    ],
    "radius": [
      {
        "name": "panel",
        "token": "--radius-panel",
        "utility": true,
        "declared": "10px"
      },
      {
        "name": "sheet",
        "token": "--radius-sheet",
        "utility": true,
        "declared": "22px"
      }
    ],
    "spacing": [
      {
        "name": "block-outline",
        "token": "--spacing-block-outline",
        "utility": true,
        "declared": "1px"
      },
      {
        "name": "horizontal-page-padding",
        "token": "--spacing-horizontal-page-padding",
        "utility": true,
        "declared": "36px"
      },
      {
        "name": "horizontal-page-padding-mobile",
        "token": "--spacing-horizontal-page-padding-mobile",
        "utility": true,
        "declared": "calc(var(--spacing-horizontal-page-padding) / 2)"
      },
      {
        "name": "page-width",
        "token": "--spacing-page-width",
        "utility": true,
        "declared": "1240px"
      },
      {
        "name": "topic-x",
        "token": "--spacing-topic-x",
        "utility": true,
        "declared": "14px"
      },
      {
        "name": "topic-y",
        "token": "--spacing-topic-y",
        "utility": true,
        "declared": "4px"
      },
      {
        "name": "topics",
        "token": "--spacing-topics",
        "utility": true,
        "declared": "12px"
      }
    ]
  },
  "components": [
    {
      "selector": "lib-anchored-panel",
      "inputs": [
        "align",
        "fullWidth",
        "label",
        "panelClass",
        "panelRole"
      ],
      "outputs": [
        "opened"
      ],
      "limits": [
        "What every filter, sort and item menu is built from. There is no bottom-sheet presentation yet — a mobile design that wants one is asking for new work."
      ]
    },
    {
      "selector": "lib-avatar",
      "inputs": [
        "icon",
        "label",
        "shape",
        "size"
      ],
      "outputs": [],
      "unions": {
        "LibAvatarShape": "'square' | 'circle'",
        "LibAvatarSize": "'normal' | 'xlarge'"
      }
    },
    {
      "selector": "lib-badge",
      "inputs": [
        "value"
      ],
      "outputs": [],
      "limits": [
        "A skeleton stands in for content that is loading. Every list needs one designed, or it flashes empty."
      ]
    },
    {
      "selector": "lib-breadcrumb",
      "inputs": [
        "itemTemplate",
        "items",
        "separator",
        "separatorTemplate"
      ],
      "outputs": [],
      "limits": [
        "Every crumb is your template — the component supplies the trail and the separators only. Which is why the last crumb looks different from the rest: that is a decision in the template, not a mode on the component.",
        "Left alone the separator is the character '/'. A glyph needs separatorTemplate, which is what the app passes.",
        "Nothing truncates on its own. The app caps the last crumb and lets it ellipsize; a design with deep trails should say what gives way, since the alternative is a row that pushes the layout sideways."
      ]
    },
    {
      "selector": "lib-button",
      "inputs": [
        "ariaLabel",
        "disabled",
        "extraClass",
        "icon",
        "iconPos",
        "label",
        "loading",
        "rounded",
        "severity",
        "size",
        "type",
        "variant"
      ],
      "outputs": [
        "onClick"
      ],
      "unions": {
        "LibButtonSeverity": "'primary' | 'secondary' | 'danger' | 'success' | 'contrast'",
        "LibButtonVariant": "'filled' | 'outlined' | 'text'",
        "LibButtonSize": "'small' | 'normal'",
        "LibButtonType": "'button' | 'submit' | 'reset'",
        "LibButtonIconPos": "'left' | 'right'"
      },
      "limits": [
        "Two only. A design's 38px button lands on one of these or costs a bespoke height.",
        "Every one of these needs a design. Disabled and loading are the two most often missing."
      ]
    },
    {
      "selector": "lib-card",
      "inputs": [],
      "outputs": []
    },
    {
      "selector": "lib-checkbox",
      "inputs": [
        "disabled",
        "emphasis",
        "indeterminate",
        "inputId",
        "size",
        "value"
      ],
      "outputs": [],
      "unions": {
        "LibCheckboxSize": "'small' | 'normal'",
        "LibCheckboxEmphasis": "'subtle' | 'strong'"
      },
      "limits": [
        "Indeterminate and the strong emphasis exist; strong is for a checkbox on white, where the subtle outline all but disappears."
      ]
    },
    {
      "selector": "lib-color-picker",
      "inputs": [
        "disabled",
        "extraClass",
        "inputId"
      ],
      "outputs": [],
      "limits": [
        "A native colour input, hex only. The swatch panel is the browser's and the operating system's, so it cannot be designed or restyled -- design the trigger, not the picker."
      ]
    },
    {
      "selector": "lib-confirm-dialog",
      "inputs": [],
      "outputs": []
    },
    {
      "selector": "lib-dialog",
      "inputs": [
        "closable",
        "contentExtraClass",
        "dismissible",
        "extraClass",
        "header",
        "label",
        "visible"
      ],
      "outputs": [],
      "limits": [
        "Confirm dialogs stack their buttons full-width below md, primary first. Escape and backdrop dismissal are ours, not the design's to specify."
      ]
    },
    {
      "selector": "lib-divider",
      "inputs": [
        "layout"
      ],
      "outputs": [],
      "unions": {
        "LibDividerLayout": "'horizontal' | 'vertical'"
      }
    },
    {
      "selector": "lib-drawer",
      "inputs": [
        "extraClass",
        "headerTemplate",
        "visible"
      ],
      "outputs": [
        "onShow"
      ],
      "limits": [
        "Right edge only — no side input, because every caller used the right. Always closable too: the close button, backdrop click and Escape are unconditional rather than configurable, so a drawer designed without a way out is not buildable as drawn. extraClass carries the width; both real callers use 36rem capped at the viewport."
      ]
    },
    {
      "selector": "lib-file-upload",
      "inputs": [
        "accept",
        "ariaLabel",
        "chooseIcon",
        "chooseLabel",
        "disabled",
        "extraClass",
        "maxFileSize",
        "multiple"
      ],
      "outputs": [
        "onSelect"
      ]
    },
    {
      "selector": "lib-icon-field",
      "inputs": [
        "iconPosition"
      ],
      "outputs": [],
      "unions": {
        "LibIconFieldPosition": "'left' | 'right'"
      }
    },
    {
      "selector": "lib-input-group",
      "inputs": [],
      "outputs": []
    },
    {
      "selector": "lib-input-group-addon",
      "inputs": [],
      "outputs": []
    },
    {
      "selector": "lib-input-icon",
      "inputs": [],
      "outputs": []
    },
    {
      "selector": "lib-input-number",
      "inputs": [
        "inputExtraClass",
        "inputId",
        "max",
        "min",
        "placeholder",
        "showButtons",
        "size",
        "step",
        "useGrouping"
      ],
      "outputs": [],
      "unions": {
        "LibInputNumberSize": "'small' | 'normal'"
      }
    },
    {
      "selector": "lib-menu",
      "inputs": [
        "extraClass",
        "itemTemplate",
        "model",
        "popup",
        "submenuHeaderTemplate"
      ],
      "outputs": []
    },
    {
      "selector": "lib-multi-select",
      "inputs": [
        "dataKey",
        "disabled",
        "display",
        "filter",
        "filterBy",
        "filterPlaceholder",
        "inputId",
        "maxSelectedLabels",
        "optionLabel",
        "optionValue",
        "options",
        "panelExtraClass",
        "placeholder",
        "scrollHeight",
        "selectedItemsLabel",
        "showClear",
        "showToggleAll",
        "size",
        "toggleAllAriaLabel"
      ],
      "outputs": [],
      "unions": {
        "LibMultiSelectDisplay": "'comma' | 'chip'",
        "LibMultiSelectSize": "'small' | 'normal'"
      },
      "limits": [
        "Search is on by default here, unlike the single select, and a 24px select-all checkbox sits beside it in the panel header — no visible label, so it needs one in a design that shows the panel. Chip mode shows at most two chips before collapsing to a count; comma mode lists three. That one-item difference is inherited behaviour we kept deliberately, so design for the count appearing at the third selection."
      ]
    },
    {
      "selector": "lib-panel",
      "inputs": [
        "collapsed",
        "header",
        "toggleable"
      ],
      "outputs": []
    },
    {
      "selector": "lib-panel-menu",
      "inputs": [
        "extraClass",
        "itemTemplate",
        "model"
      ],
      "outputs": []
    },
    {
      "selector": "lib-picklist",
      "inputs": [
        "emptyMessageSourceTemplate",
        "emptyMessageTargetTemplate",
        "filterBy",
        "itemTemplate",
        "scrollHeight",
        "source",
        "sourceFilterPlaceholder",
        "sourceHeader",
        "sourceSortBy",
        "target",
        "targetFilterPlaceholder",
        "targetHeader"
      ],
      "outputs": [],
      "limits": [
        "Click to select then move, or double-click. Deliberately no drag-and-drop and no within-list reordering: a design showing drag handles or up/down arrows is asking for new work. Each list scrolls at 14rem."
      ]
    },
    {
      "selector": "lib-popover",
      "inputs": [
        "contentExtraClass",
        "extraClass"
      ],
      "outputs": [
        "onHide",
        "onShow"
      ]
    },
    {
      "selector": "lib-progress-spinner",
      "inputs": [],
      "outputs": []
    },
    {
      "selector": "lib-radio-button",
      "inputs": [
        "disabled",
        "inputId",
        "name",
        "size",
        "value"
      ],
      "outputs": [],
      "unions": {
        "LibRadioButtonSize": "'small' | 'normal'"
      }
    },
    {
      "selector": "lib-select",
      "inputs": [
        "dataKey",
        "disabled",
        "dropdownIconTemplate",
        "emptyMessage",
        "extraClass",
        "filter",
        "filterBy",
        "filterPlaceholder",
        "headerTemplate",
        "inputId",
        "invalid",
        "itemTemplate",
        "loading",
        "name",
        "optionLabel",
        "optionValue",
        "options",
        "panelAnchor",
        "panelExtraClass",
        "placeholder",
        "scrollHeight",
        "selectedItemTemplate",
        "showClear",
        "size"
      ],
      "outputs": [],
      "unions": {
        "LibSelectSize": "'small' | 'normal'"
      },
      "limits": [
        "Single choice. Add a search inside the panel with one input rather than a different component.",
        "A pill trigger with a map-pin template, a panel header, and rows carrying a type badge. Nothing bespoke — the same lib-select as above.",
        "The same control as a labelled form field — small, full width, plain rows, no panel header. While the options load it is replaced by a skeleton rather than shown empty, which is the pattern for any select fed by a request."
      ]
    },
    {
      "selector": "lib-select-button",
      "inputs": [
        "allowEmpty",
        "inputId",
        "itemTemplate",
        "optionDisabled",
        "optionLabel",
        "optionValue",
        "options",
        "variant"
      ],
      "outputs": [],
      "unions": {
        "LibSelectButtonVariant": "'solid' | 'tinted' | 'track'"
      },
      "limits": [
        "Three variants: solid, tinted, track. The grid/list switch on an index page is track."
      ]
    },
    {
      "selector": "lib-skeleton",
      "inputs": [
        "shape",
        "width"
      ],
      "outputs": [],
      "unions": {
        "LibSkeletonShape": "'rectangle' | 'circle'"
      }
    },
    {
      "selector": "lib-skeleton-wrapper",
      "inputs": [],
      "outputs": []
    },
    {
      "selector": "lib-slider",
      "inputs": [
        "disabled",
        "extraClass",
        "max",
        "min",
        "step"
      ],
      "outputs": []
    },
    {
      "selector": "lib-table",
      "inputs": [
        "bodyTemplate",
        "captionTemplate",
        "dataKey",
        "emptyMessageTemplate",
        "extraClass",
        "footerTemplate",
        "headerTemplate",
        "loading",
        "paginator",
        "rows",
        "rowsPerPageOptions",
        "selection",
        "value"
      ],
      "outputs": [],
      "unions": {
        "LibTableSortOrder": "1 | -1"
      },
      "limits": [
        "Sorting is opt-in per column: the header calls sort() and reads back sortField/sortOrder to pick its own glyph, so a design has to say which columns sort. Row selection needs dataKey. A wide table gets a min-width and scrolls sideways rather than crushing its columns.",
        "The same component with none of the extras: no pagination, no sorting, no selection. Note the two unlabelled columns at each end, for the avatar and the row action — they still need a width in a design, and an accessible name from us.",
        "Both come from the component: [loading] and emptyMessageTemplate. Worth designing, because a table fed by a request shows one of them first."
      ]
    },
    {
      "selector": "lib-tabs",
      "inputs": [
        "lazy",
        "value"
      ],
      "outputs": []
    },
    {
      "selector": "lib-toast",
      "inputs": [],
      "outputs": [],
      "unions": {
        "LibToastSeverity": "'success' | 'error' | 'info' | 'warn'"
      }
    }
  ],
  "specimens": [
    {
      "page": "/table",
      "label": "Sortable, paginated, selectable — the admin client list",
      "demonstrates": "lib-table",
      "note": "Sorting is opt-in per column: the header calls sort() and reads back sortField/sortOrder to pick its own glyph, so a design has to say which columns sort. Row selection needs dataKey. A wide table gets a min-width and scrolls sideways rather than crushing its columns."
    },
    {
      "page": "/table",
      "label": "Plain, with a footer — the user management list",
      "demonstrates": "lib-table",
      "note": "The same component with none of the extras: no pagination, no sorting, no selection. Note the two unlabelled columns at each end, for the avatar and the row action — they still need a width in a design, and an accessible name from us."
    },
    {
      "page": "/table",
      "label": "Loading and empty",
      "demonstrates": "lib-table",
      "note": "Both come from the component: [loading] and emptyMessageTemplate. Worth designing, because a table fed by a request shows one of them first."
    },
    {
      "page": "/overlays",
      "label": "Anchored panel",
      "demonstrates": "lib-anchored-panel",
      "note": "What every filter, sort and item menu is built from. There is no bottom-sheet presentation yet — a mobile design that wants one is asking for new work."
    },
    {
      "page": "/overlays",
      "label": "Dialog",
      "demonstrates": "lib-dialog",
      "note": "Confirm dialogs stack their buttons full-width below md, primary first. Escape and backdrop dismissal are ours, not the design's to specify."
    },
    {
      "page": "/overlays",
      "label": "Drawer",
      "demonstrates": "lib-drawer",
      "note": "Right edge only — no side input, because every caller used the right. Always closable too: the close button, backdrop click and Escape are unconditional rather than configurable, so a drawer designed without a way out is not buildable as drawn. extraClass carries the width; both real callers use 36rem capped at the viewport."
    },
    {
      "page": "/navigation",
      "label": "Breadcrumb",
      "demonstrates": "lib-breadcrumb",
      "note": "Every crumb is your template — the component supplies the trail and the separators only. Which is why the last crumb looks different from the rest: that is a decision in the template, not a mode on the component."
    },
    {
      "page": "/navigation",
      "label": "Breadcrumb — default separator",
      "demonstrates": "lib-breadcrumb",
      "note": "Left alone the separator is the character '/'. A glyph needs separatorTemplate, which is what the app passes."
    },
    {
      "page": "/navigation",
      "label": "Breadcrumb — a long current crumb",
      "demonstrates": "lib-breadcrumb",
      "note": "Nothing truncates on its own. The app caps the last crumb and lets it ellipsize; a design with deep trails should say what gives way, since the alternative is a row that pushes the layout sideways."
    },
    {
      "page": "/forms",
      "label": "Checkbox",
      "demonstrates": "lib-checkbox",
      "note": "Indeterminate and the strong emphasis exist; strong is for a checkbox on white, where the subtle outline all but disappears."
    },
    {
      "page": "/forms",
      "label": "Select button",
      "demonstrates": "lib-select-button",
      "note": "Three variants: solid, tinted, track. The grid/list switch on an index page is track."
    },
    {
      "page": "/forms",
      "label": "Select",
      "demonstrates": "lib-select",
      "note": "Single choice. Add a search inside the panel with one input rather than a different component."
    },
    {
      "page": "/forms",
      "label": "Configured: the navbar location picker",
      "demonstrates": "lib-select",
      "note": "A pill trigger with a map-pin template, a panel header, and rows carrying a type badge. Nothing bespoke — the same lib-select as above."
    },
    {
      "page": "/forms",
      "label": "Multi-select",
      "demonstrates": "lib-multi-select",
      "note": "Search is on by default here, unlike the single select, and a 24px select-all checkbox sits beside it in the panel header — no visible label, so it needs one in a design that shows the panel. Chip mode shows at most two chips before collapsing to a count; comma mode lists three. That one-item difference is inherited behaviour we kept deliberately, so design for the count appearing at the third selection."
    },
    {
      "page": "/forms",
      "label": "Configured: the chart aspect picker",
      "demonstrates": "lib-select",
      "note": "The same control as a labelled form field — small, full width, plain rows, no panel header. While the options load it is replaced by a skeleton rather than shown empty, which is the pattern for any select fed by a request."
    },
    {
      "page": "/forms",
      "label": "Text input",
      "demonstrates": "lib-input-text"
    },
    {
      "page": "/forms",
      "label": "Colour picker",
      "demonstrates": "lib-color-picker",
      "note": "A native colour input, hex only. The swatch panel is the browser's and the operating system's, so it cannot be designed or restyled -- design the trigger, not the picker."
    },
    {
      "page": "/forms",
      "label": "Picklist",
      "demonstrates": "lib-picklist",
      "note": "Click to select then move, or double-click. Deliberately no drag-and-drop and no within-list reordering: a design showing drag handles or up/down arrows is asking for new work. Each list scrolls at 14rem."
    },
    {
      "page": "/forms",
      "label": "Badge and skeleton",
      "demonstrates": "lib-badge",
      "note": "A skeleton stands in for content that is loading. Every list needs one designed, or it flashes empty."
    },
    {
      "page": "/buttons",
      "label": "Sizes",
      "demonstrates": "lib-button",
      "note": "Two only. A design's 38px button lands on one of these or costs a bespoke height."
    },
    {
      "page": "/buttons",
      "label": "States",
      "demonstrates": "lib-button",
      "note": "Every one of these needs a design. Disabled and loading are the two most often missing."
    }
  ]
}
