{
  "$comment": "Generated from components/*.yaml by go generate ./.... Do not edit.",
  "aliases": {
    "{color.accentInk}": "var(--accent-ink)",
    "{color.accent}": "var(--accent)",
    "{color.alwaysWhite}": "var(--always-white)",
    "{color.card2}": "var(--card-2)",
    "{color.card}": "var(--card)",
    "{color.favorite}": "var(--favorite)",
    "{color.good}": "var(--good)",
    "{color.inactive}": "var(--inactive)",
    "{color.ink}": "var(--ink)",
    "{color.line}": "var(--line)",
    "{color.muted}": "var(--muted)",
    "{color.paper}": "var(--paper)",
    "{color.warn}": "var(--warn)",
    "{fontSize.body}": "var(--fs-body)",
    "{fontSize.cap}": "var(--fs-cap)",
    "{fontSize.sub}": "var(--fs-sub)",
    "{fontWeight.bold}": "var(--weight-bold)",
    "{fontWeight.heavy}": "var(--weight-heavy)",
    "{fontWeight.medium}": "var(--weight-medium)",
    "{fontWeight.semibold}": "var(--weight-semibold)",
    "{line.*}": "var(--route)",
    "{motion.liveValueDuration}": "var(--motion-live-value-duration)",
    "{motion.upcomingFadeDuration}": "var(--motion-upcoming-fade-duration)",
    "{motion.upcomingResizeDuration}": "var(--motion-upcoming-resize-duration)",
    "{radius.card}": "var(--r-card)",
    "{radius.lineBar}": "var(--radius-line-bar)",
    "{radius.pill}": "var(--radius-pill)",
    "{size.lineBarHeight}": "var(--size-line-bar-height)",
    "{size.lineBarSegmentSpacing}": "var(--size-line-bar-segment-spacing)",
    "{size.lineBarSegmentWidth}": "var(--size-line-bar-segment-width)",
    "{size.mapStationMarkerCompact}": "var(--size-map-station-marker-compact)",
    "{spacing.arrivalHorizontalPadding}": "var(--space-arrival-horizontal-padding)",
    "{spacing.arrivalVerticalPadding}": "var(--space-arrival-vertical-padding)",
    "{spacing.cardPadding}": "var(--space-card-padding)",
    "{spacing.routeToggleGap}": "var(--space-route-toggle-gap)"
  },
  "components": [
    {
      "accessibility": {
        "label": "\"\u003croute\u003e, to \u003cdestination\u003e, \u003cN minutes | Due | Delayed\u003e[, Live tracked | Scheduled]\"",
        "minTarget": 44,
        "notes": [
          "Expanded state: iOS accessibilityValue Expanded/Collapsed; web aria-expanded."
        ],
        "role": "button"
      },
      "anatomy": [
        {
          "description": "Card surface; the whole row is the tap target.",
          "part": "container",
          "tokens": {
            "fill": "{color.card}",
            "paddingX": "{spacing.arrivalHorizontalPadding}",
            "paddingY": "{spacing.arrivalVerticalPadding}",
            "radius": "{radius.card}"
          }
        },
        {
          "description": "Standard RouteIcon centred in a 40 pt slot.",
          "part": "routeIcon",
          "tokens": {
            "size": {
              "note": "Icon slot width; no token.",
              "unit": "pt",
              "value": 40
            }
          }
        },
        {
          "description": "Destination, one line, truncated (two lines at accessibility sizes on iOS).",
          "part": "destination",
          "tokens": {
            "color": "{color.ink}",
            "fontSize": "{fontSize.body}"
          }
        },
        {
          "description": "\"Due\", \"Delayed\" or \"N min\"; tabular digits.",
          "part": "time",
          "tokens": {
            "color": "{color.ink}",
            "fontSize": "{fontSize.body}",
            "fontWeight": "{fontWeight.medium}",
            "valueChange": "{motion.liveValueDuration}"
          }
        },
        {
          "description": "Predicted clock time under the time word.",
          "part": "clock",
          "tokens": {
            "color": "{color.muted}",
            "fontSize": "{fontSize.cap}"
          }
        },
        {
          "description": "Expand indicator; rotates 180° when expanded.",
          "part": "chevron",
          "tokens": {
            "color": "{color.muted}"
          }
        },
        {
          "description": "Upcoming stops for this vehicle, revealed when expanded.",
          "part": "timeline",
          "tokens": {
            "fade": "{motion.upcomingFadeDuration}",
            "resize": "{motion.upcomingResizeDuration}"
          }
        }
      ],
      "behavior": {
        "expansion": "One row's timeline toggles open per tap; others keep their state.",
        "sort": "Ascending minutes.",
        "timeWords": "Due at ≤ 1 min or approaching; Delayed overrides minutes."
      },
      "description": "One predicted arrival in a stop sheet: route icon, destination, the time as a word or minutes with the clock time under it, and a chevron that expands the upcoming-stops timeline.",
      "differences": [
        {
          "ios": "spacing.arrivalVerticalPadding (5 pt) around a taller row.",
          "status": "open",
          "topic": "Vertical padding",
          "web": "12 px top and bottom (hard-coded)."
        },
        {
          "ios": "Spoken minutes (\"6 minutes\"), bus routes include the route name, plus Live tracked / Scheduled / Schedule data issue.",
          "status": "open",
          "topic": "Accessibility label",
          "web": "\"\u003cLine name | Route N\u003e to \u003cdestination\u003e, \u003clabel\u003e\" with the minutes abbreviated (\"6 min\")."
        },
        {
          "ios": "\"--\" in Muted; \"Departed\" in Warn.",
          "status": "open",
          "topic": "Unknown and departed times",
          "web": "Unknown shows an em dash in Ink; no departed state."
        },
        {
          "ios": "Warn exclamation icon after the time.",
          "status": "open",
          "topic": "Schedule fault indicator",
          "web": "None."
        },
        {
          "ios": "No dimming; freshness lives in the header.",
          "status": "open",
          "topic": "Stale list",
          "web": "The list dims to 50% while refreshing after an error."
        },
        {
          "ios": "Toggles the timeline and pans the map to the vehicle.",
          "status": "deliberate",
          "topic": "Tap action",
          "web": "Toggles the timeline only."
        }
      ],
      "name": "ArrivalRow",
      "platforms": {
        "android": {
          "status": "planned"
        },
        "ios": {
          "implementations": [
            {
              "file": "chitransit-app/views/StopView.swift",
              "kind": "type",
              "symbol": "ArrivalCardView"
            },
            {
              "file": "chitransit-app/views/StopView.swift",
              "kind": "type",
              "symbol": "TimeTextView"
            },
            {
              "file": "chitransit-app/views/StopCardView.swift",
              "kind": "function",
              "symbol": "arrivalAccessibilityLabel"
            }
          ],
          "status": "implemented"
        },
        "web": {
          "implementations": [
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "arrivalCardEl"
            },
            {
              "file": "platforms/web/transit.css",
              "kind": "css",
              "symbol": ".arrival-card"
            }
          ],
          "status": "implemented"
        }
      },
      "sizes": {
        "standard": {
          "chevron": 12,
          "iconSlot": 40,
          "timeColumn": 92
        }
      },
      "states": [
        {
          "description": "A live prediction several minutes out.",
          "example": {
            "clock": "5:46 PM",
            "destination": "O'Hare",
            "minutes": 6,
            "route": "blue",
            "routeType": "train"
          },
          "name": "default",
          "platforms": [
            "web",
            "ios"
          ],
          "screenshots": {
            "ios": {
              "dark": "shots/stop-map/stop-detail-dark.png",
              "light": "shots/stop-map/stop-detail-light.png",
              "note": "First arrival row under the direction control."
            }
          },
          "tokens": {
            "time.color": "{color.ink}",
            "time.fontWeight": "{fontWeight.medium}"
          }
        },
        {
          "description": "One minute or less, or approaching.",
          "example": {
            "clock": "5:41 PM",
            "destination": "Howard",
            "minutes": 1,
            "route": "red",
            "routeType": "train",
            "state": "due"
          },
          "name": "due",
          "platforms": [
            "web",
            "ios"
          ],
          "tokens": {
            "time.color": "{color.good}",
            "time.fontWeight": "{fontWeight.bold}"
          }
        },
        {
          "description": "CTA flags the run as delayed.",
          "example": {
            "clock": "5:52 PM",
            "destination": "95th Street",
            "minutes": 12,
            "route": "9",
            "routeType": "bus",
            "state": "delayed"
          },
          "name": "delayed",
          "platforms": [
            "web",
            "ios"
          ],
          "tokens": {
            "time.color": "{color.warn}",
            "time.fontWeight": "{fontWeight.bold}"
          }
        },
        {
          "description": "A timetable time, not a tracked vehicle.",
          "example": {
            "clock": "5:58 PM",
            "destination": "Berwyn",
            "minutes": 18,
            "route": "146",
            "routeType": "bus",
            "scheduled": true
          },
          "name": "scheduled",
          "note": "Web adds a Muted \"Scheduled\" caption under the destination; iOS reads \"Scheduled\" in the label only.",
          "platforms": [
            "web",
            "ios"
          ]
        },
        {
          "description": "Timeline open; the chevron points up.",
          "example": {
            "clock": "5:44 PM",
            "destination": "Kimball",
            "expanded": true,
            "minutes": 4,
            "route": "brown",
            "routeType": "train"
          },
          "name": "expanded",
          "platforms": [
            "web",
            "ios"
          ],
          "tokens": {
            "timeline.resize": "{motion.upcomingResizeDuration}"
          }
        },
        {
          "description": "No usable time; shows \"--\" in Muted.",
          "example": {
            "destination": "Harlem/Lake",
            "route": "green",
            "routeType": "train",
            "unknown": true
          },
          "name": "unknown",
          "platforms": [
            "ios"
          ],
          "tokens": {
            "time.color": "{color.muted}"
          }
        },
        {
          "description": "The predicted time has passed; shows \"Departed\" in Warn.",
          "example": {
            "departed": true,
            "destination": "Midway",
            "route": "orange",
            "routeType": "train"
          },
          "name": "departed",
          "platforms": [
            "ios"
          ],
          "tokens": {
            "time.color": "{color.warn}"
          }
        },
        {
          "description": "A refresh failed; the list keeps the last arrivals dimmed.",
          "example": {
            "clock": "5:49 PM",
            "destination": "54th/Cermak",
            "minutes": 9,
            "route": "pink",
            "routeType": "train",
            "stale": true
          },
          "name": "stale",
          "platforms": [
            "web"
          ],
          "tokens": {
            "container.opacity": {
              "note": "Web .stale opacity; no token.",
              "unit": "ratio",
              "value": 0.5
            }
          }
        }
      ]
    },
    {
      "accessibility": {
        "label": "\"Route \u003cnumber\u003e bus to \u003cdestination\u003e\" (iOS adds next stop, update age and staleness)",
        "minTarget": 44,
        "role": "button",
        "selectedTrait": "selected"
      },
      "anatomy": [
        {
          "description": "Brand body with a 2 pt white outline.",
          "part": "body",
          "tokens": {
            "fill": "{color.accent}",
            "outline": "{color.alwaysWhite}"
          }
        },
        {
          "description": "Indicates the direction of travel from the bearing.",
          "part": "heading",
          "tokens": {
            "fill": "{color.accent}",
            "outline": "{color.alwaysWhite}"
          }
        },
        {
          "description": "Bus icon (icons/bus.svg), upright whatever the bearing.",
          "part": "glyph",
          "tokens": {
            "color": "{color.accentInk}"
          }
        },
        {
          "description": "Route number on Paper with a Brand outline; below the body when heading north, above otherwise.",
          "part": "routeTag",
          "tokens": {
            "color": "{color.accent}",
            "fill": "{color.paper}",
            "fontSize": {
              "note": "Fixed tag size; not on the type ramp.",
              "unit": "pt",
              "value": 11
            },
            "fontWeight": "{fontWeight.heavy}",
            "radius": {
              "note": "No token.",
              "unit": "pt",
              "value": 6
            }
          }
        },
        {
          "description": "Shown behind the selected vehicle.",
          "part": "selectionHalo",
          "tokens": {
            "fill": "{color.paper}",
            "ring": "{color.accent}"
          }
        },
        {
          "description": "+N badge when several vehicles share a spot (web only).",
          "part": "stackCount",
          "tokens": {
            "color": "{color.paper}",
            "fill": "{color.ink}"
          }
        }
      ],
      "behavior": {
        "glyphUpright": true,
        "selectedScale": "iOS 1.25; web 1",
        "selection": "single",
        "tagBelowWhenHeading": "North: iOS 315°–45°, web within ±60°."
      },
      "description": "A live bus (vehicle) on the map: a Brand body with a bus glyph, a heading indicator and the route number in a tag above or below. Trains share the structure with a line-colour disc.",
      "differences": [
        {
          "ios": "An upright rounded square (7 pt radius) with a separate arrow orbiting at 20.5 pt.",
          "status": "open",
          "topic": "Body shape and heading",
          "web": "A rotated square teardrop; one corner points along the bearing (no separate arrow)."
        },
        {
          "ios": "Paper disc 14 pt wider than the body with a 2 pt Brand ring, scale 1.25, spring animation.",
          "status": "open",
          "topic": "Selected treatment",
          "web": "Drop shadow in Ink around the SVG; no scale."
        },
        {
          "ios": "Muted body, white glyph, Muted tag.",
          "status": "open",
          "topic": "Stale state",
          "web": "Not implemented; stale vehicles look live."
        },
        {
          "ios": "Not implemented.",
          "status": "open",
          "topic": "Stacked vehicles",
          "web": "Offset copies and a +N badge."
        },
        {
          "ios": "allowsHitTesting(false); selection comes from the arrival row.",
          "status": "open",
          "topic": "Interactivity",
          "web": "role=button, tabindex 0, 44 px hit area."
        },
        {
          "ios": "Below when the bearing is 315°–45°.",
          "status": "open",
          "topic": "Route tag position threshold",
          "web": "Below when cos(bearing) \u003e 0.5 (±60° of north)."
        },
        {
          "ios": "Adds delay, next stop with minutes, update age and \"Location may be out of date\".",
          "status": "open",
          "topic": "Accessibility label",
          "web": "\"Route \u003cnumber\u003e bus to \u003cdestination\u003e\" plus \", and N more\" when stacked."
        }
      ],
      "name": "BusMarker",
      "platforms": {
        "android": {
          "status": "planned"
        },
        "ios": {
          "implementations": [
            {
              "file": "chitransit-app/views/VehicleMarkerView.swift",
              "kind": "type",
              "symbol": "VehicleMapMarkerView"
            },
            {
              "file": "chitransit-app/views/VehicleMarkerView.swift",
              "kind": "function",
              "symbol": "vehicleMarkerAccessibilityLabel"
            }
          ],
          "status": "implemented"
        },
        "web": {
          "implementations": [
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "vehicleEl"
            },
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "vehicleSVG"
            },
            {
              "file": "platforms/web/transit.css",
              "kind": "css",
              "symbol": ".veh"
            }
          ],
          "status": "implemented"
        }
      },
      "sizes": {
        "standard": {
          "body": 25,
          "glyph": 13.5,
          "tagHeight": 17,
          "tagMinWidth": 22
        }
      },
      "states": [
        {
          "description": "A tracked bus heading east.",
          "example": {
            "bearing": 90,
            "route": "22",
            "routeType": "bus"
          },
          "name": "default",
          "platforms": [
            "web",
            "ios"
          ],
          "screenshots": {
            "ios": {
              "note": "No screenshot yet; needs an iOS map snapshot with live vehicles."
            }
          },
          "tokens": {
            "body.fill": "{color.accent}",
            "glyph.color": "{color.accentInk}"
          }
        },
        {
          "description": "Heading north; the route tag moves below the body.",
          "example": {
            "bearing": 0,
            "route": "22",
            "routeType": "bus"
          },
          "name": "headingNorth",
          "note": "Default tokens; only the tag position changes.",
          "platforms": [
            "web",
            "ios"
          ]
        },
        {
          "description": "The vehicle whose arrival row was tapped.",
          "example": {
            "bearing": 90,
            "route": "22",
            "routeType": "bus",
            "selected": true
          },
          "name": "selected",
          "platforms": [
            "web",
            "ios"
          ],
          "tokens": {
            "selectionHalo.fill": "{color.paper}",
            "selectionHalo.ring": "{color.accent}"
          }
        },
        {
          "description": "Position older than the freshness cutoff; drawn solid in Muted, never faded.",
          "example": {
            "bearing": 90,
            "route": "22",
            "routeType": "bus",
            "stale": true
          },
          "name": "stale",
          "platforms": [
            "ios"
          ],
          "tokens": {
            "body.fill": "{color.muted}",
            "glyph.color": "{color.alwaysWhite}",
            "routeTag.color": "{color.muted}"
          }
        },
        {
          "description": "Several vehicles at one spot, drawn as a stack with a +N count.",
          "example": {
            "bearing": 90,
            "more": 2,
            "route": "22",
            "routeType": "bus"
          },
          "name": "stacked",
          "platforms": [
            "web"
          ],
          "tokens": {
            "stackCount.color": "{color.paper}",
            "stackCount.fill": "{color.ink}"
          }
        }
      ]
    },
    {
      "accessibility": {
        "label": "Line names in bar order, comma-separated: \"Brown Line, Green Line\".",
        "minTarget": 44,
        "notes": [
          "Read as one element; segments are not separately focusable."
        ],
        "role": "image"
      },
      "anatomy": [
        {
          "description": "One rounded bar per line, filled with the line colour.",
          "part": "segment",
          "tokens": {
            "fill": "{line.*}",
            "height": "{size.lineBarHeight}",
            "radius": "{radius.lineBar}",
            "width": "{size.lineBarSegmentWidth}"
          }
        },
        {
          "description": "Horizontal stack of segments; its width is count × segmentWidth + (count − 1) × segmentSpacing.",
          "part": "container",
          "tokens": {
            "gap": "{size.lineBarSegmentSpacing}"
          }
        }
      ],
      "behavior": {
        "duplicates": "One segment per line, however many platforms or route ids map to it.",
        "interactive": false,
        "order": "Contrast order: of every permutation, keep the one whose smallest adjacent CIELAB distance (line light hex) is largest, then the largest sum of adjacent distances; ties follow CTA order red, blue, brown, green, orange, purple, pink, yellow. Distances round to 3 places."
      },
      "description": "The lines serving a station, as one rounded vertical bar per CTA line in contrast order. Used in stop rows, stop cards and search results.",
      "differences": [
        {
          "ios": "Reads size.lineBarSegmentWidth, size.lineBarHeight, size.lineBarSegmentSpacing and radius.lineBar.",
          "status": "open",
          "topic": "Segment dimensions source",
          "web": "Hard-coded 10 × 24 px, 3 px gap and radius in .line-bar (same values as the tokens)."
        },
        {
          "ios": "CTALineStyle.routeColor(routeHex:colorScheme:) picks the same token pair.",
          "note": "Contrast order always uses the light hex on both platforms, so the order never flips with the theme.",
          "status": "deliberate",
          "topic": "Line colour in dark mode",
          "web": "--\u003cline\u003e-line tokens switch light/dark through CSS."
        }
      ],
      "name": "LineBar",
      "platforms": {
        "android": {
          "status": "planned"
        },
        "ios": {
          "implementations": [
            {
              "file": "chitransit-app/views/LineBarView.swift",
              "kind": "type",
              "symbol": "LineBarView"
            },
            {
              "file": "chitransit-app/utils/LineOrder.swift",
              "kind": "type",
              "symbol": "LineOrder"
            }
          ],
          "status": "implemented"
        },
        "web": {
          "implementations": [
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "lineBar"
            },
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "contrastOrder"
            },
            {
              "file": "platforms/web/transit.css",
              "kind": "css",
              "symbol": ".line-bar"
            }
          ],
          "status": "implemented"
        }
      },
      "sizes": {
        "standard": {
          "height": 24,
          "radius": 3,
          "segmentSpacing": 3,
          "segmentWidth": 10
        }
      },
      "states": [
        {
          "description": "A one-line station.",
          "example": {
            "lines": [
              "purple"
            ]
          },
          "name": "single",
          "note": "Segment tokens only; one segment.",
          "platforms": [
            "web",
            "ios"
          ]
        },
        {
          "description": "A transfer station; segments in contrast order.",
          "example": {
            "lines": [
              "brown",
              "green",
              "orange",
              "pink",
              "purple"
            ]
          },
          "name": "transfer",
          "platforms": [
            "web",
            "ios"
          ],
          "screenshots": {
            "ios": {
              "dark": "shots/lists/stops-dark.png",
              "light": "shots/lists/stops-light.png",
              "note": "Belmont and Clark/Lake rows on the Red Line's stop list."
            }
          },
          "tokens": {
            "segment.fill": "{line.*}"
          }
        },
        {
          "description": "All eight lines (worst case for width).",
          "example": {
            "lines": [
              "red",
              "blue",
              "brown",
              "green",
              "orange",
              "purple",
              "pink",
              "yellow"
            ]
          },
          "name": "allLines",
          "note": "Segment tokens only; eight segments.",
          "platforms": [
            "web",
            "ios"
          ]
        }
      ]
    },
    {
      "accessibility": {
        "label": "Train: \u003cline name\u003e (\"Red Line\"). Bus: \"Route \u003cnumber\u003e\".",
        "minTarget": 44,
        "notes": [
          "Not interactive on its own; when it sits in a row, the row speaks for it and the icon is hidden."
        ],
        "role": "image"
      },
      "anatomy": [
        {
          "description": "Circle filled with the line colour (train routes).",
          "part": "trainDisc",
          "tokens": {
            "fill": "{line.*}"
          }
        },
        {
          "description": "Rounded rectangle in Brand behind the route number (bus routes).",
          "part": "busCapsule",
          "tokens": {
            "fill": "{color.accent}",
            "radius": {
              "note": "Per size role (5/7/9); no radius token. Both platforms hard-code it.",
              "unit": "pt",
              "value": 7
            }
          }
        },
        {
          "description": "The route number, bold, one line.",
          "part": "busNumber",
          "tokens": {
            "color": "{color.accentInk}",
            "fontSize": {
              "note": "Fixed per size role (9/12/14), not on the type ramp.",
              "unit": "pt",
              "value": 12
            },
            "fontWeight": "{fontWeight.bold}"
          }
        }
      ],
      "behavior": {
        "interactive": false,
        "numberFit": "Bus numbers stay on one line and may shrink to 70% before truncating.",
        "routeColor": "Train routes use the line token; every bus route uses Brand."
      },
      "description": "A route's identity mark. Train lines are solid circles in the line colour; bus routes are numbered Brand capsules. One family in three size roles.",
      "differences": [
        {
          "ios": "The route name when the API has one (\"Ashland Express\"), otherwise \"Route \u003cnumber\u003e\".",
          "status": "open",
          "topic": "Bus accessibility label",
          "web": "\"Route \u003cnumber\u003e\""
        },
        {
          "ios": "Optional line code on the disc, from the showLineLetters setting.",
          "status": "open",
          "topic": "Line letters",
          "web": "Not supported."
        },
        {
          "ios": "RouteIconSize.mini, .standard, .large.",
          "status": "open",
          "topic": "Size role names",
          "web": "Classes mini and md (no class for standard)."
        }
      ],
      "name": "RouteIcon",
      "platforms": {
        "android": {
          "status": "planned"
        },
        "ios": {
          "implementations": [
            {
              "file": "chitransit-app/views/RouteIconView.swift",
              "kind": "type",
              "symbol": "RouteIconView"
            },
            {
              "file": "chitransit-app/views/RouteIconView.swift",
              "kind": "type",
              "symbol": "TrainRouteIcon"
            },
            {
              "file": "chitransit-app/views/RouteIconView.swift",
              "kind": "type",
              "symbol": "RouteIconSize"
            }
          ],
          "status": "implemented"
        },
        "web": {
          "implementations": [
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "routeIcon"
            },
            {
              "file": "platforms/web/transit.css",
              "kind": "css",
              "symbol": ".route-icon"
            }
          ],
          "note": "Sizes are classes on the icon (mini, md); there is no large name.",
          "status": "implemented"
        }
      },
      "sizes": {
        "large": {
          "busFontSize": 14,
          "busHeight": 32,
          "busMinWidth": 44,
          "busPaddingX": 9,
          "busRadius": 9,
          "trainDiameter": 32
        },
        "mini": {
          "busFontSize": 9,
          "busHeight": 16,
          "busMinWidth": 24,
          "busPaddingX": 4,
          "busRadius": 5,
          "trainDiameter": 14
        },
        "standard": {
          "busFontSize": 12,
          "busHeight": 24,
          "busMinWidth": 36,
          "busPaddingX": 7,
          "busRadius": 7,
          "trainDiameter": 24
        }
      },
      "states": [
        {
          "description": "Standard train line icon.",
          "example": {
            "route": "red",
            "routeType": "train",
            "size": "standard"
          },
          "name": "train",
          "platforms": [
            "web",
            "ios"
          ],
          "screenshots": {
            "ios": {
              "dark": "shots/stop-map/stop-detail-dark.png",
              "light": "shots/stop-map/stop-detail-light.png",
              "note": "Standard train icons in the arrival rows."
            }
          },
          "tokens": {
            "trainDisc.fill": "{line.*}"
          }
        },
        {
          "description": "Standard bus route capsule.",
          "example": {
            "route": "146",
            "routeType": "bus",
            "size": "standard"
          },
          "name": "bus",
          "platforms": [
            "web",
            "ios"
          ],
          "tokens": {
            "busCapsule.fill": "{color.accent}",
            "busNumber.color": "{color.accentInk}"
          }
        },
        {
          "description": "Mini role, used inside dense rows (stop card arrivals).",
          "example": {
            "route": "blue",
            "routeType": "train",
            "size": "mini"
          },
          "name": "mini",
          "note": "Same tokens as standard; only the size role changes.",
          "platforms": [
            "web",
            "ios"
          ],
          "screenshots": {
            "ios": {
              "dark": "shots/star/home-dark.png",
              "light": "shots/star/home-light.png",
              "note": "Mini icons in the favourite stop cards' arrival column."
            }
          }
        },
        {
          "description": "Large role, used for route headers and vehicle sheets.",
          "example": {
            "route": "J14",
            "routeType": "bus",
            "size": "large"
          },
          "name": "large",
          "note": "Same tokens as standard; only the size role changes.",
          "platforms": [
            "web",
            "ios"
          ],
          "screenshots": {
            "ios": {
              "dark": "shots/lists/stops-dark.png",
              "light": "shots/lists/stops-light.png",
              "note": "Large Red Line icon in the route header."
            }
          }
        },
        {
          "description": "Optional line code drawn on the train disc (Settings → Show line letters).",
          "example": {
            "route": "brown",
            "routeType": "train",
            "size": "standard"
          },
          "name": "lineLetters",
          "platforms": [
            "ios"
          ],
          "tokens": {
            "trainDisc.letterColor": {
              "note": "Picked per line by luminance (CTALineStyle.stationMarkerGlyphColor).",
              "value": "alwaysWhite or alwaysInk"
            },
            "trainDisc.letterSize": {
              "note": "Per size role (6/8/10).",
              "unit": "pt",
              "value": 8
            }
          }
        }
      ]
    },
    {
      "accessibility": {
        "label": "\"\u003cstation name\u003e station, \u003cline names\u003e\"",
        "minTarget": 44,
        "notes": [
          "The visible marker can be smaller than 44 pt; the hit area is padded to 44."
        ],
        "role": "button",
        "selectedTrait": "selected"
      },
      "anatomy": [
        {
          "description": "White outline that separates the marker from the map.",
          "part": "keyline",
          "tokens": {
            "color": "{color.alwaysWhite}",
            "width": {
              "note": "1.5 pt in the compact and smallest web form; no token.",
              "unit": "pt",
              "value": 2
            }
          }
        },
        {
          "description": "Neutral capsule (station icon) behind the glyph and bars.",
          "part": "capsule",
          "tokens": {
            "fill": "{color.card2}",
            "radius": "{radius.pill}"
          }
        },
        {
          "description": "Train icon (icons/train.svg) at 75% of the capsule height.",
          "part": "trainGlyph",
          "tokens": {
            "color": "{color.ink}"
          }
        },
        {
          "description": "One bar per line in contrast order (see LineBar); bar 22.5% × 60% of the capsule height, 1.5 pt apart.",
          "part": "lineBars",
          "tokens": {
            "fill": "{line.*}"
          }
        },
        {
          "description": "Zoomed-out replacement for the capsule.",
          "part": "compactDot",
          "tokens": {
            "size": "{size.mapStationMarkerCompact}"
          }
        },
        {
          "description": "Paper ring then Ink ring outside the keyline when selected.",
          "part": "selectionHalo",
          "tokens": {
            "innerRing": "{color.paper}",
            "outerRing": "{color.ink}"
          }
        }
      ],
      "behavior": {
        "compactBelowZoom": "iOS: below zoom 15. Web: tuner stages hidden → dot → tick → capsule.",
        "layering": "Stations draw above bus stops; the selected station above other stations; vehicles above all stops.",
        "selectedScale": 1.12,
        "selection": "single"
      },
      "description": "A train station on the map: a neutral capsule with a train glyph and one bar per line, in a white keyline. Zoomed out it collapses to a compact form.",
      "differences": [
        {
          "ios": "Solid 10 pt circle in the first line's colour (card2 for multi-line stations) with a 1.5 pt white stroke; no tick form.",
          "status": "open",
          "topic": "Compact form",
          "web": "White dot with a black border (map-station-dot); a white tick across the lanes at middle zooms."
        },
        {
          "ios": "30 pt default side; size.mapStationMarker (26) is unused.",
          "status": "open",
          "topic": "Marker size",
          "web": "Zoom-driven --m (18 / 28 / 40 px, tuner capsule sizes); size.mapStationMarker (26) is unused."
        },
        {
          "ios": "Paper stroke 6 pt then Ink stroke 2.5 pt around the capsule.",
          "note": "Same rings; each platform draws them natively.",
          "status": "deliberate",
          "topic": "Selection halo construction",
          "web": "box-shadow --selection-ring (3 px paper, then ink to 5.5 px)."
        },
        {
          "ios": "A 44 pt minimum frame with a rectangular content shape.",
          "status": "deliberate",
          "topic": "Hit area",
          "web": "A 44 px ::before pseudo-element."
        }
      ],
      "name": "StationMarker",
      "platforms": {
        "android": {
          "status": "planned"
        },
        "ios": {
          "implementations": [
            {
              "file": "chitransit-app/views/StopView.swift",
              "kind": "type",
              "symbol": "StopMapMarkerView"
            },
            {
              "file": "chitransit-app/views/LineBarView.swift",
              "kind": "type",
              "symbol": "StationIconView"
            },
            {
              "file": "chitransit-app/views/MapView.swift",
              "kind": "type",
              "symbol": "MapStationMarkerStyle"
            }
          ],
          "status": "implemented"
        },
        "web": {
          "implementations": [
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "stationMarkerEl"
            },
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "stationIconEl"
            },
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "stationDotEl"
            },
            {
              "file": "platforms/web/transit.css",
              "kind": "css",
              "symbol": ".map-marker.station"
            },
            {
              "file": "platforms/web/transit.css",
              "kind": "css",
              "symbol": ".station-icon"
            }
          ],
          "status": "implemented"
        }
      },
      "sizes": {
        "compact": 10,
        "standard": 26,
        "web": {
          "maximum": 40,
          "medium": 28,
          "minimum": 18
        }
      },
      "states": [
        {
          "description": "Standard station capsule.",
          "example": {
            "lines": [
              "blue",
              "brown",
              "green",
              "orange",
              "pink",
              "purple"
            ],
            "name": "Clark/Lake",
            "size": 28
          },
          "name": "default",
          "platforms": [
            "web",
            "ios"
          ],
          "screenshots": {
            "ios": {
              "note": "No current screenshot: shots/stop-map predates the capsule marker."
            }
          },
          "tokens": {
            "capsule.fill": "{color.card2}",
            "keyline.color": "{color.alwaysWhite}"
          }
        },
        {
          "description": "The open station. Scales to 1.12 and gains the selection halo.",
          "example": {
            "lines": [
              "blue",
              "brown",
              "green",
              "orange",
              "pink",
              "purple"
            ],
            "name": "Clark/Lake",
            "selected": true,
            "size": 28
          },
          "name": "selected",
          "platforms": [
            "web",
            "ios"
          ],
          "tokens": {
            "selectionHalo.innerRing": "{color.paper}",
            "selectionHalo.outerRing": "{color.ink}"
          }
        },
        {
          "description": "One-line station.",
          "example": {
            "lines": [
              "red"
            ],
            "name": "Belmont",
            "size": 28
          },
          "name": "singleLine",
          "note": "Default tokens with a single bar.",
          "platforms": [
            "web",
            "ios"
          ]
        },
        {
          "description": "Zoomed-out form.",
          "example": {
            "dot": 10,
            "lines": [
              "blue",
              "brown"
            ],
            "name": "Clark/Lake"
          },
          "name": "compact",
          "platforms": [
            "web",
            "ios"
          ],
          "tokens": {
            "compactDot.fill": {
              "note": "Platforms disagree; see differences.",
              "value": "web: alwaysWhite; ios: line colour or card2"
            },
            "compactDot.size": "{size.mapStationMarkerCompact}"
          }
        }
      ]
    },
    {
      "accessibility": {
        "label": "Contains the name, then one element per arrival: \"\u003croute\u003e, to \u003cdestination\u003e, \u003ctime\u003e[, Live tracked | Scheduled]\".",
        "minTarget": 44,
        "role": "group"
      },
      "anatomy": [
        {
          "description": "Card surface.",
          "part": "container",
          "tokens": {
            "fill": "{color.card}",
            "padding": "{spacing.cardPadding}",
            "radius": "{radius.card}"
          }
        },
        {
          "description": "Stop name, one line, truncated.",
          "part": "name",
          "tokens": {
            "color": "{color.ink}",
            "fontSize": "{fontSize.body}",
            "fontWeight": "{fontWeight.semibold}"
          }
        },
        {
          "description": "Star after the name when the stop is a favourite (decorative).",
          "part": "favoriteMark",
          "tokens": {
            "color": "{color.favorite}"
          }
        },
        {
          "description": "Direction or stop id, one line.",
          "part": "subtitle",
          "tokens": {
            "color": "{color.muted}",
            "fontSize": "{fontSize.sub}"
          }
        },
        {
          "description": "LineBar for trains, then the bus route overflow row (standard RouteIcons and a +N chip).",
          "part": "routes",
          "tokens": {
            "gap": "{spacing.routeToggleGap}"
          }
        },
        {
          "description": "1 pt rule between the columns.",
          "part": "divider",
          "tokens": {
            "color": "{color.line}"
          }
        },
        {
          "description": "Mini RouteIcon, destination, time word.",
          "part": "arrivalRow",
          "tokens": {
            "destinationColor": "{color.muted}",
            "destinationSize": "{fontSize.cap}",
            "timeColor": "{color.ink}",
            "timeDelayed": "{color.warn}",
            "timeDue": "{color.good}",
            "timeSize": "{fontSize.sub}",
            "valueChange": "{motion.liveValueDuration}"
          }
        }
      ],
      "behavior": {
        "arrivalCount": 2,
        "columns": "The arrival column keeps 45% of the width at every Dynamic Type size.",
        "favoriteMark": "decorative"
      },
      "description": "A saved or nearby stop as a card: name, subtitle and routes on the left, the next two arrivals in a ruled column on the right.",
      "differences": [
        {
          "ios": "StopCardView on Home (favourites) and Nearby.",
          "status": "open",
          "topic": "Component exists",
          "web": "No StopCard; stops are list rows with the routes trailing and no arrivals."
        },
        {
          "ios": "Routes sit under the name.",
          "status": "open",
          "topic": "Route position",
          "web": "Routes trail the name on the right (list-trail)."
        },
        {
          "ios": "+N chip labelled \"\u003cN\u003e more routes: \u003clist\u003e\".",
          "status": "open",
          "topic": "Bus overflow label",
          "web": "+N chip labelled \"Also routes \u003clist\u003e\"."
        }
      ],
      "name": "StopCard",
      "platforms": {
        "android": {
          "status": "planned"
        },
        "ios": {
          "implementations": [
            {
              "file": "chitransit-app/views/StopCardView.swift",
              "kind": "type",
              "symbol": "StopCardView"
            },
            {
              "file": "chitransit-app/views/RouteIconView.swift",
              "kind": "type",
              "symbol": "BusRouteOverflowRow"
            }
          ],
          "status": "implemented"
        },
        "web": {
          "implementations": [
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "listRow"
            },
            {
              "file": "platforms/web/transit.js",
              "kind": "function",
              "symbol": "stopTrail"
            },
            {
              "file": "platforms/web/transit.css",
              "kind": "css",
              "symbol": ".list-row"
            }
          ],
          "note": "The web has no stop card. The nearest is the stop list row (name, subtitle, LineBar or bus routes, chevron), shown for namesOnly.",
          "status": "partial"
        }
      },
      "sizes": {
        "standard": {
          "arrivalRowGap": 8,
          "arrivalRows": 2,
          "columnGap": 12,
          "favoriteGlyph": 18,
          "padding": 14
        }
      },
      "states": [
        {
          "description": "Two arrivals loaded.",
          "example": {
            "arrivals": [
              {
                "destination": "Howard",
                "minutes": 1,
                "route": "red"
              },
              {
                "destination": "Loop",
                "minutes": 7,
                "route": "purple"
              }
            ],
            "favorite": true,
            "lines": [
              "red",
              "yellow"
            ],
            "name": "Clark/Lake",
            "subtitle": "Outer Loop"
          },
          "name": "default",
          "platforms": [
            "ios"
          ],
          "screenshots": {
            "ios": {
              "dark": "shots/star/home-dark.png",
              "light": "shots/star/home-light.png",
              "note": "Favourites on Home."
            }
          },
          "tokens": {
            "arrivalRow.timeColor": "{color.ink}",
            "container.fill": "{color.card}"
          }
        },
        {
          "description": "Arrivals pending; two redacted placeholder rows.",
          "example": {
            "lines": [
              "red",
              "purple"
            ],
            "loading": true,
            "name": "Belmont",
            "subtitle": "Northbound"
          },
          "name": "loading",
          "platforms": [
            "ios"
          ],
          "tokens": {
            "arrivalRow.placeholder": "{color.inactive}"
          }
        },
        {
          "description": "No arrivals; \"No arrivals\" in the column.",
          "example": {
            "arrivals": [],
            "lines": [
              "red"
            ],
            "name": "Belmont",
            "subtitle": "Northbound"
          },
          "name": "empty",
          "platforms": [
            "ios"
          ],
          "tokens": {
            "arrivalRow.destinationColor": "{color.muted}"
          }
        },
        {
          "description": "Update failed with nothing cached; \"Unable to update\".",
          "example": {
            "error": true,
            "lines": [
              "red"
            ],
            "name": "Belmont",
            "subtitle": "Northbound"
          },
          "name": "error",
          "platforms": [
            "ios"
          ],
          "tokens": {
            "arrivalRow.destinationColor": "{color.muted}"
          }
        },
        {
          "description": "Update failed with cached arrivals; a caption under them says how old they are.",
          "example": {
            "arrivals": [
              {
                "destination": "Howard",
                "minutes": 3,
                "route": "red"
              }
            ],
            "error": true,
            "lines": [
              "red",
              "yellow"
            ],
            "name": "Clark/Lake",
            "subtitle": "Outer Loop"
          },
          "name": "stale",
          "note": "Caption is Cap in Muted.",
          "platforms": [
            "ios"
          ],
          "tokens": {
            "arrivalRow.destinationColor": "{color.muted}"
          }
        },
        {
          "description": "Without the arrival column (lists).",
          "example": {
            "lines": [
              "blue",
              "brown",
              "green",
              "orange",
              "pink",
              "purple"
            ],
            "name": "Clark/Lake",
            "namesOnly": true,
            "subtitle": "Station 40380"
          },
          "name": "namesOnly",
          "note": "Container and name tokens only.",
          "platforms": [
            "web",
            "ios"
          ]
        }
      ]
    }
  ]
}
