> ## Oymyakon DS index
> Fetch the full index at: https://super-dollop-pzmo65r.pages.github.io/llms.txt
> HTML preview: https://super-dollop-pzmo65r.pages.github.io/tariff-cell.html
> Source: specs/components/tariff-cell/tariff-cell.md, specs/components/tariff-cell/tariff-cell-a11y.md, specs/components/tariff-cell/capabilities.json

---

# TariffCell · Oymyakon DS 3

> The cross-vertical tariff row: a CustomCell configuration with the tariff's 3D image, name with
> surge/info icons, persons · time line, price with an optional strikethrough, and one optional
> Description / Status line below.

**Version:** 3.1.0 · **Status:** Draft · **Figma:** `[TariffCell] 3.1` (a **branch** `EJbpHRrzXcwFCjQFSbRwlN` of the main components file, set `34319:112412`, key `c7b5507f…`; Specification frame `34317:26613`)

---

## 1. Description

TariffCell provides tariff information with text, image, icons and price. It is built on
`[CustomCell] 3.3` and inherits its slot structure (StartSlot, StartText, EndText, Description)
and spacing logic. It can be part of a tariff card — the organism that adds the border,
selection, numeric stepper and notch — or stand alone in a list of tariffs.

**Out of this component's scope by contract** (owner, 2026-09-25): the tariff card organism, the
Notch and the NumericStepper — a next-level composite. TariffCell carries **no background and no
border of its own** — both are provided by the tariff card; standalone use may re-point the fill
to any `background` group token (Figma names Primary, Alt. Primary, Secondary, Alt. Secondary,
Tertiary, Alt. Tertiary — the synced web palette carries only Primary and Secondary; the
Alt/Tertiary steps are a recorded colours-sync gap, flagged).

The Figma staging is the conditional-logic workaround made explicit: the cell's real variability
lives in satellite building-block sets (`Tariff name 3.1`, `Subtitle 3.1`, `⚪️EndText 3.1`,
`⚫️Status 3.1`, `🟡StartText 3.1`, `Price`) plus ~22 labelled CustomCell configurations on the
page — Figma cannot vary nested elements per assembly, so each block enumerates its own variants
and the cell swaps them. This file collapses that staging back into one contract; the
`capabilities.json` carries the cross-block conditions as `onlyWhen`/constraints.

### Platform implementations

| Platform | Entry point | Version | Notes |
|---|---|---|---|
| iOS | — | — | Not shipped (searched 2026-09-25) |
| Android | — | — | Not shipped |
| Flutter | — | — | Not shipped |
| Web | `src/shared/shared.css` (`.tariff-cell`) | 3.1.0 | Reference implementation, this repo; preview page `src/tariff-cell.njk` — the first build of this component on any platform |

---

## 2. Anatomy

```
TariffCell (.tariff-cell)                 row · fills the host's width · radius s28 · no own fill
├── StartSlot (.tariff-cell__start-slot)    the tariff's 3D image — s64 × s56 min, top aligned, clipped s20
├── content (.tariff-cell__content)         a column at s4, CENTRED in the s56 line: text row + Description/Status
│   ├── main (.tariff-cell__main)             Columns: name column + price column at s8
│   │   ├── name column (.tariff-cell__name-col)
│   │   │     ├── title row (.tariff-cell__title-row)   name + surge/info icons at s4
│   │   │     └── persons · time (.tariff-cell__persons)
│   │   └── price column (.tariff-cell__price-col)      price + strikethrough price at s4
│   └── Description / Status (.tariff-cell__status)     one optional bottom line
└── (Rows layout: the price sits in the title row; the price column is not used)
```

| Part | Required | Description |
|---|---|---|
| **StartSlot** | Required | The `X / tariff` picture, swapped via the instance property: the `Surface/OnWhite` squircle under the per-vehicle `tariff-full` window (see the recipe bullet below). `s64 × s56` minimum, the slot clipped at `s20` on all four corners, **top aligned** within the cell. Statuses: Default, Unavailable (grayscale) |
| **Tariff name** | Required | Heading 4, hugs its text in all four configs: `Default` · `+ info-ic` · `+ surge-ic` · `surge+info` — up to two 16×16 icons at `s4` |
| **persons · time** | Off · on by default | The Subtitle line («4 · 5 min»), Compact Body with the 16 `person-outline` icon; hidden by the boolean **Persons** (e.g. Intercity) |
| **Price** | Off · on by default | Heading 4, single line, top aligned; optional strikethrough price (Compact Body, struck) `s4` away. Hidden entirely by the boolean **Price** — e.g. when the price lives in the numeric stepper |
| **Description / Status** | Off (boolean) | One optional bottom line, four configs that never combine: `Description` (plain text, multiline, no line limit) · `Recommended` · `Good price` · `Low price` (icon + one line, ellipsized) |

- **Long names:** in Columns the name is cut at the edge of the name column — **no ellipsis**,
  trailing icons may be hidden. Use Rows for long names.
- **Rows price placement (owner, 2026-09-29):** on the name's line the price always hugs the
  **right edge** of the row and never carries a strikethrough — two prices next to the name read
  wrong. A long name switches to the **two-line configuration** (the Figma two-line StartText
  staging): the price pair moves to its own second line, left-aligned under the name, `s4`
  between the lines — the only place the Rows strikethrough (`Aside`) exists. The two
  arrangements are explicit configurations, not an automatic wrap.
- When the cell width changes (checked 280–420): the name column and the Description/Status
  stretch, the price and persons · time hug; descriptions wrap.
- The StartSlot always holds the **`X / tariff` picture**, which is two layers: the `base`
  **squircle under the illustration** (56×56, radius 20 at smoothing 0.6, fill `Surface/OnWhite` —
  theme-substituted, so the web paints it as `var(--surface-on-white)` and never bakes it into
  the asset) and the per-vehicle `tariff-full` window (economy 108×56, moto 98×56 …) whose right
  edge is flush with the slot's right edge (window x = 64 − width). Each vehicle's inner
  placement of its square render is its own recipe — artwork data resolved inside the exported
  window asset (`illu/3D/vehicle/tariff-full/`), per the ImageContainer opaque-boundary
  convention. The Skeleton style swaps the picture to `skeleton / tariff` (the ghost vehicle over
  the same squircle). The tariff illustration sets carry their own `RTL` and `Flat` variants.

### RTL layout

Inherited from the Cell family: the row mirrors, directional glyphs follow their own RTL
variants; the tariff illustrations carry their own `RTL` property (and a `Flat` variant).
In the picture's `RTL=true` the `base` squircle right-anchors in the 64×56 box (x 8) and the
illustration's overhang flips to the left — the mirrored composition ships as the picture's
own RTL variant, never as a CSS flip of the LTR asset.

---

## 3. Variants and sizes

| Axis | Values | Default | Web mechanism |
|---|---|---|---|
| **Style** | `default` · `skeleton` | `default` | `.tariff-cell--skeleton` |
| **State** | `standard` · `unavailable` | `standard` | `.tariff-cell--unavailable` — the image goes grayscale (set via the StartSlot's own Status in Figma) |
| **Layout** | `columns` · `rows` | `columns` | `.tariff-cell--rows`; Columns = the price in the right column (Ride Hailing), Rows = the price in the title row (Intercity, long names) |
| **Description / Status** | off · on, config `description` \| `recommended` \| `good-price` \| `low-price` | off; config `description` | Presence of `.tariff-cell__status` + its modifier |
| **Price** | off · on | on | Presence of the price column / the in-row price |
| **Persons** | off · on | on | Presence of `.tariff-cell__persons` |
| **Strikethrough price** | `off` · `below` (Columns) · `aside` (Rows, two-line only) | `off` | Presence of `.tariff-cell__old-price`; the position follows the layout. `aside` exists only in the two-line Rows configuration (`.tariff-cell__price-row`) — a one-line Rows title never carries a strikethrough |
| **Fill** | none · any `background` group token | none | `--tariff-cell-fill` re-pointed on a standalone instance |

- In **Columns** the price lives in EndText (`Strikethrough: Off | Below`); in **Rows** EndText
  is not used — the price moves to the title row, hugging the right edge (`Strikethrough: Off`
  there), or to its own second line in the two-line configuration (`Strikethrough: Off | Aside`).
  The `Price` boolean hides it in both.
- Only one Description / Status config shows at a time.

---

## 4. States

TariffCell has three states: **Standard, Unavailable, Skeleton** (Figma names the first `Default`).

| State | What shows |
|---|---|
| **Standard** (Figma: Default) | The full assembly; no own background or border |
| **Unavailable** | The tariff image is shown in grayscale — set via the StartSlot `3D-service` Status property; the texts keep their Default styles |
| **Skeleton** | The StartSlot swaps to the tariff image placeholder (`skeleton / tariff`); tariff name, subtitle and price become `[Skeleton] 3.1` text bars (title bars 16 at radius `s6` in the 20 line, subtitle 12 at `s4` in the 16 line); the Description / Status line is not shown. **Unlike Default, the Skeleton variant paints its own `Background/Primary` fill** |

**Selected / not selected are not TariffCell's states** — the border
(`Surface/ContrastOverlay`, 2 inside, radius s28) and the selection belong to the tariff card
organism, out of this component's scope.

---

## 5. Animation and behavior

TariffCell declares no motion of its own; press belongs to the host list/card (the Cell family
`pushHighlight` when the row itself is tappable). Reference:
[`motion-rules.md`](https://super-dollop-pzmo65r.pages.github.io/motion.md).

| Event | Token | Notes |
|---|---|---|
| Skeleton sweep | `var(--pattern-shimmer)` | The gradient wave over the placeholders (`Skeleton/OnWhite` + `Skeleton/Wave`) while tariff data loads |
| Press / selection | — | The host's: the tariff card organism or the list row |

Reduced motion zeroes the shimmer at the token; no override in the component.

---

## 6. Color tokens

| Element | Token | Notes |
|---|---|---|
| Cell fill | none (Skeleton: `var(--background-primary)`) | Provided by the tariff card; the Skeleton style paints its own Primary fill; standalone: any `background` group token via `--tariff-cell-fill` |
| Tariff name | `var(--text-and-icon-primary)` | Heading 4 |
| `surge-up` icon | `var(--accent-blue1)` | 16 × 16 |
| `info-outline` icon | `var(--text-and-icon-secondary)` | 16 × 16 |
| persons · time + `person-outline` | `var(--text-and-icon-primary)` | Compact Body |
| Price | `var(--text-and-icon-primary)` | Heading 4 |
| Strikethrough price | `var(--text-and-icon-secondary)` | Compact Body, struck |
| Description / Status text | `var(--text-and-icon-secondary)` | Compact Body |
| `play-filled` icon (Recommended) | `var(--text-and-icon-secondary)` | 16 × 16 |
| `chevron-fill-up` icon (Good price) | `var(--text-and-icon-success)` | 16 × 16 |
| `chevron-fill-down` icon (Low price) | `var(--text-and-icon-warning)` | 16 × 16 |
| Skeleton | `var(--skeleton-on-white)` + `var(--skeleton-wave)` | The placeholder + the wave |
| Image (StartSlot) | — | All styles inherit from the `3D-service` master; Unavailable = grayscale |

---

## 7. Typography

| Element | Style | Tokens |
|---|---|---|
| Tariff name · Price | Heading/Heading 4 | `var(--text-heading-heading4-*)` — 17/20 |
| persons · time · strikethrough price · Description / Status | Body/Compact Body | `var(--text-body-compact-body-*)` — 14/16 |

---

## 8. Spacing

All spacings are bound to `SP/100% ratio` tokens — use the tokens, not fixed values.

| Property | Token | Value @100% | Notes |
|---|---|---|---|
| Width | — | fills the host | Auto-layout fill: `Flexible` (iOS) / `FillMax` (Android); name column and status stretch, price and persons hug |
| Top / bottom padding | `var(--sp-s12)` | 12 | 3.0 had `s10` |
| Left padding | `var(--sp-s12)` | 12 | |
| Right padding | `var(--sp-s16)` | 16 | |
| Corner radius | `var(--sp-s28)` | 28 | The same radius as the tariff card |
| StartSlot ↔ content | `var(--sp-s12)` | 12 | |
| StartText ↔ EndText | `var(--sp-s8)` | 8 | The two columns of the Columns layout |
| Text row ↔ Description / Status | `var(--sp-s4)` | 4 | |
| Tariff name ↔ subtitle | `var(--sp-s4)` | 4 | |
| Name ↔ icons · icon ↔ icon | `var(--sp-s4)` | 4 | Icons 16 × 16 |
| Price ↔ strikethrough price | `var(--sp-s4)` | 4 | |
| Persons / Status icon ↔ text | `var(--sp-s4)` | 4 | Icons 16 × 16 — the same gap in the persons · time line and the status line |
| Title line ↔ price line (Rows two-line) | `var(--sp-s4)` | 4 | The two-line Rows configuration (`.tariff-cell__price-row` under the title row): the StartText Title block turns vertical with the same `s4` between the lines. In the one-line Rows title the price hugs the right edge — a flexible span, not a token |
| StartSlot | `var(--sp-s64)` × `var(--sp-s56)` | 64 × 56 | Minimum sizes; the slot clips **all four corners** at `var(--sp-s20)` (the Specification's "left corner radius" names the artwork's own left rounding — asset data) |

---

## 9. Usage context

### When to use TariffCell

- Inside the tariff card organism — with the border, numeric stepper and notch (Ride Hailing).
- Standalone in a list of tariffs (e.g. Intercity), where the row itself is the option.

### When not to use

- As a generic list row — that is Cell's job; TariffCell's slots are locked to tariff content.
- Rebuilding selection inside the cell — the border and the selected state are the tariff
  card's, by contract.
- Long tariff names in the Columns layout — the name clips without an ellipsis; switch to Rows.

### Related components

- [Cell](https://super-dollop-pzmo65r.pages.github.io/cell.md) — the base family; TariffCell is a `[CustomCell] 3.3` configuration.
- Tariff card organism, `[Notch] 1.0`, NumericStepper — the next-level composite (out of scope,
  owner 2026-09-25).
- `[TariffCard] 3.0` — the sibling card-shaped tariff component on the same branch (renamed from
  1.0 on 2026-09-28; its Specification frame is still titled 1.0 — flagged). Not in this cycle.

---

## 10. Accessibility

Brief summary. Full spec: [`tariff-cell-a11y.md`](https://super-dollop-pzmo65r.pages.github.io/tariff-cell.md).

- The cell is **one focus area** whose announcement composes the visible content in the
  layout's own DOM order — Columns: name, persons · time, price (the former price phrased as
  "was …", never as struck glyphs), status; Rows: name, price, persons · time, status.
- The tariff image and every 16×16 icon are decorative — the meaning lives in the text
  (surge/info icons must pair with the host's explanation surface, not carry it alone).
- Status colours (Success / Warning) never carry the meaning alone — the status text does.
- Unavailable is announced as a state (dimmed/disabled per the host's semantics), not by the
  grayscale image.
- Selection semantics belong to the tariff card organism (radio-group of tariffs); standalone
  rows take the host list's semantics.

---

## 11. Analytics and coverage contract

Contract per [`prototype-analytics-and-coverage.md`](https://github.com/inDriver/oymyakon-ds/blob/main/docs/prototype-analytics-and-coverage.md).

| Field | Value |
|---|---|
| `data-ds-component` | `tariff-cell` |
| Coverage unit | yes |
| Tap target model | The whole row when the host makes it tappable; no internal targets of its own |
| Actions | `tap` (select the tariff) — emitted by the host |
| Internal targets | none — surge/info icons are not tappable in this component |
| Emits value | The tariff id/name via `data-ds-value` is the host's call; never the price as a user-tracking value |

Attribute model: `data-ds-variant` = the layout (`columns` \| `rows`), `data-ds-state` =
`standard` \| `unavailable` \| `skeleton`; `data-ds-preset` = `custom` when the fill is
re-pointed.

```html
<div class="tariff-cell" data-ds-component="tariff-cell" data-ds-component-id="tariff-economy"
     data-ds-variant="columns" data-ds-state="standard">
  <span class="tariff-cell__start-slot" aria-hidden="true"><img src="…/economy-white.png" alt=""></span>
  <div class="tariff-cell__content">
    <div class="tariff-cell__main">
      <div class="tariff-cell__name-col">
        <div class="tariff-cell__title-row">
          <span class="tariff-cell__name">Economy</span>
          <span class="tariff-cell__name-icon tariff-cell__name-icon--surge" aria-hidden="true"><!-- surge-up --></span>
        </div>
        <span class="tariff-cell__persons"><!-- person-outline -->4 · 5 min</span>
      </div>
      <div class="tariff-cell__price-col">
        <span class="tariff-cell__price">$16</span>
        <span class="tariff-cell__old-price" aria-hidden="true">$20</span>
        <span class="visually-hidden">was $20</span>
      </div>
    </div>
    <span class="tariff-cell__status tariff-cell__status--good"><!-- chevron-fill-up -->Good price – more offers</span>
  </div>
</div>
```

---

## Changelog

| Version | Date | Change |
|---|---|---|
| 3.1.0 | 2026-09-25 | Initial spec, aligned to the Figma line `[TariffCell] 3.1` in the `[TariffCell] 3.0` working file (set `34319:112412`; the 11668px Specification frame read in full; the page also carries the 3.0 generation — the recorded 3.0→3.1 delta: paddings `s10`→`s12`, Description and Status merged into one boolean bottom line, hug tariff names, the EndText Strikethrough modes). The staging's conditional-logic workaround (six satellite block sets + ~22 labelled CustomCell configurations) collapsed into one contract: Style default/skeleton, State standard/unavailable (selected belongs to the tariff card organism — Notch/NumericStepper/organism out of scope, owner 2026-09-25), Layout columns/rows with the price migration rule, booleans Price/Persons/Description-Status, four never-combined status configs with their icon/colour pairs (`play-filled`/Secondary · `chevron-fill-up`/Success · `chevron-fill-down`/Warning), no own background/border. Neither mobile platform ships the component — the web build here is the first implementation. Flags: the set name carries the "Tarrif" typo; the `play-filled` icon is missing from the repo's synced icon set (icons 3.28.1 — exported from the library inline until the next icons sync); the file is a BRANCH of the main library, so no manifest binding until it merges (the node-ids then carry over). Re-checked 2026-09-28 after the owner's corrections: the 3.0 generation was removed from the page (the original link's set is gone), `[TariffCard]` renamed 1.0 → 3.0 (its Specification frame still titled 1.0 — flagged), the Specification frame reflowed (+721px, no text change) — the 3.1 contract itself is byte-identical: set, props, blocks, geometry, colours and spacings unchanged; this spec needed no contract edits. StartSlot corrected 2026-09-28 (owner's catch — the moto crop): the slot always consumes the `X / tariff` picture — the `base` squircle (56×56, radius 20, smoothing 0.6, `Surface/OnWhite`; theme-substituted, so it is a CSS layer) under the per-vehicle `tariff-full` window export, right-flush in the slot (per-vehicle crops are artwork data in `illu/3D/vehicle/tariff-full/`) — replacing the single economy-derived crop recipe; the Skeleton slot carries the `skeleton / tariff` ghost-vehicle picture, not a painted skeleton box. Flags: the library set `ntercity-carpool / tariff-full` (3D-Illustrations) lost its leading "i"; the repo's pre-existing `illu/3D/vehicle/tariff/` exports (May, 192×168 whole-box) are stale against the 2026-09-08 library update and carry no squircle. RTL detail recorded from the library `base` set (425:13681): `RTL=true` right-anchors the squircle (x 8), the overhang flips left — the picture's own variant, never a CSS flip. Spacing table: the icon ↔ text `s4` row extended to name the persons · time line alongside the status line (the same gap, one fact); added the Rows two-line row — the two-line StartText staging turns its Title block vertical with `s4` between the name line and the price line (verified in the `🟡StartText 3.1` set, the 64-tall Rows variant). Rows price rule recorded (owner, 2026-09-29): on the name's line the price always hugs the right edge (the one-line Figma staging right-anchors it) and never carries a strikethrough; `aside` exists only in the two-line configuration — the web contract gains the explicit `.tariff-cell__price-row` element (onlyWhen rows), the in-title price takes `margin-inline-start: auto`, and the automatic flex-wrap is removed: the two arrangements are explicit configurations, as in Figma. |

---

# TariffCell — Accessibility

One row, one focus area: the cell reads as a single element whose announcement composes the
visible content **in the layout's own DOM order** — Columns: tariff name, persons · time, price
(then the former price as "was …"), status; Rows: tariff name, price, persons · time, status.
Every image and 16×16 icon is decorative; the texts carry the whole meaning.

Label rule: announce the offer, not the decoration — ✅ Columns: "Economy, 4 seats, 5 minutes,
16 dollars, was 20, Good price – more offers", ❌ "Car image, lightning icon…". The order is the
reading order, never a hand-reshuffled string — iOS `children: .combine` and TalkBack's merge
both follow the on-screen order, and the two layouts differ.

## Android · TalkBack

| Element | Label | Value | Trait | Hint |
|---|---|---|---|---|
| Cell | The composed content in DOM order (see above; the "was …" phrase follows the price) | — | One focus area; the host adds the row semantics (selectable / button) | The host's ("double-tap to select") |
| Tariff image | — | — | Not a separate element — decorative | — |
| surge / info icons | — | — | Decorative; the surge and info meanings must reach the user through the host's explanation surface, never the icon alone | — |
| Status icon | — | — | Decorative — the status TEXT is the announcement | — |

- **Strikethrough price is announced as a former price** ("16 dollars, was 20") — struck glyphs
  read as plain digits, so the host supplies the phrasing; two bare numbers in a row are a
  defect.
- **Unavailable** is a state, not a look: the host marks the row disabled/dimmed; the grayscale
  image alone announces nothing.
- **Skeleton**: not announced; the loading container carries the busy semantics (the DS
  pattern). The status line is absent by contract.
- **Selection** (chosen tariff) belongs to the tariff card organism — a radio-group of tariffs
  where exactly one is checked; standalone lists take the host list's semantics.

### Edge states

- **Description config:** multiline, read in full with the cell — long Intercity descriptions
  are part of the one announcement.
- **Status colours:** Good price (Success green) and Low price (Warning orange) never carry the
  meaning alone — the text does; TalkBack users hear the same words sighted users read.
- **RTL:** the row mirrors; announcements unchanged.
- **Reduced motion:** only the skeleton wave moves; it zeroes at the token.

---

## iOS · VoiceOver

| Element | Label | Value | Trait | Hint |
|---|---|---|---|---|
| Cell | The composed content in the layout's DOM order | — | One element (`children: .combine` when the host wraps it); the host adds Button/Selected traits | The host's |
| Tariff image / icons | — | — | Hidden — never their own elements | — |
| Status | — | — | Part of the cell's label | — |

### Edge states

- **Unavailable / Skeleton / RTL / Reduced motion:** as on Android — host-owned state
  semantics, decorative visuals, token-zeroed motion.

---

## Web preview (reference)

- **The cell is content, not a control** — the reference marks the root's media and icons
  decorative and leaves the interactive semantics to the host:

  ```html
  <div class="tariff-cell" data-ds-component="tariff-cell">
    <span class="tariff-cell__start-slot" aria-hidden="true"><img src="…" alt=""></span>
    <div class="tariff-cell__content">…</div>
  </div>
  ```

  A tappable row wraps the cell in the host's `<button>`/option with the composed accessible
  name; a selectable tariff list is a radio group (the organism's duty).
- **The strikethrough price pairs visual and spoken forms**: the struck text is
  `aria-hidden="true"` and a visually-hidden "was $20" phrase sits beside it — glyph
  strikethrough has no accessible meaning of its own.
- **Icons**: every 16×16 (`surge-up`, `info-outline`, `person-outline`, `play-filled`,
  `chevron-fill-up/down`) is `aria-hidden="true"`; the persons · time and status TEXT carry the
  content.
- **Unavailable:** `.tariff-cell--unavailable` only desaturates the image; the host adds the
  real `disabled`/`aria-disabled` semantics on its interactive wrapper.
- **Skeleton:** `aria-hidden="true"` on the cell; the loading container carries
  `aria-busy="true"`.
- **Reduced motion:** the shimmer sits on `var(--pattern-shimmer)`, zeroed at the token.

### Colour and contrast

| Pairing | Tokens | Requirement |
|---|---|---|
| Name / price / persons on the host fill | `--text-and-icon-primary` | ≥ 4.5:1 — ≈ 15:1 Day on `--background-primary` |
| Strikethrough price / status text | `--text-and-icon-secondary` | ≥ 4.5:1 — measured ≈ 5.25:1 Day / ≈ 7.9:1 Night on `--background-primary` (the BaseTextLine measurement). `color-rules.md` §14.2 still scopes this pair to large text only — stale against the measured values, flagged to the owner |
| Good price icon | `--text-and-icon-success` on the host fill | ≥ 3:1 (non-text) — verify per theme; the text beside it is the real channel |
| Low price icon | `--text-and-icon-warning` | ≥ 3:1 — same duty |
| surge icon | `--accent-blue1` | Decorative reinforcement; the meaning never rides the icon alone |
| Tariff image | — | None — identification artwork; the name carries it |
| Skeleton | `--skeleton-on-white` | None — no content of its own |

The cell has no fill of its own — every pairing is against the **host's** fill (the tariff card
or the standalone re-point); the host verifies its combination.

---

## Machine contract — `specs/components/tariff-cell/capabilities.json`

Axes, allowed values, defaults, constraints and CSS/Figma bindings. Read this instead of guessing what the component can do.

```json
{
  "$schema": "../component-capabilities.schema.json",
  "id": "tariff-cell",
  "name": "TariffCell",
  "version": "3.1.0",
  "description": "The cross-vertical tariff row, a [CustomCell] 3.3 configuration: the 3D tariff image, the name with surge/info icons, persons · time, the price with an optional strikethrough, and one optional Description/Status line. No own background or border — the tariff card organism provides both; selection, Notch and NumericStepper are the organism's (out of scope, owner 2026-09-25).",
  "files": {
    "spec": "specs/components/tariff-cell/tariff-cell.md",
    "a11y": "specs/components/tariff-cell/tariff-cell-a11y.md",
    "preview": "src/tariff-cell.njk",
    "css": "src/shared/shared.css"
  },
  "figma": {
    "library": "Branch EJbpHRrzXcwFCjQFSbRwlN of 🕹️ Oymyakon (components) — merges into the main file",
    "fileKey": "EJbpHRrzXcwFCjQFSbRwlN",
    "componentSets": {
      "[TarrifCell] 3.1": {
        "key": "c7b5507feab3370e8ae7f144274af009f6f26df0"
      }
    },
    "capturedAt": "2026-09-28"
  },
  "root": {
    "class": "tariff-cell",
    "dataDsComponent": "tariff-cell"
  },
  "anatomy": {
    "startSlot": {
      "class": "tariff-cell__start-slot",
      "notes": "The X / tariff picture — s64 × s56 minimum, top aligned, swapped per tariff: a Surface/OnWhite squircle (56, radius s20 — a CSS layer, theme-substituted) under the per-vehicle tariff-full window export, right-flush in the slot (per-vehicle crops are artwork data, illu/3D/vehicle/tariff-full/). Unavailable renders it grayscale."
    },
    "nameCol": {
      "class": "tariff-cell__name-col",
      "notes": "Stretches; long names CLIP without an ellipsis in Columns (trailing icons may hide) — use Rows for long names."
    },
    "titleRow": {
      "class": "tariff-cell__title-row",
      "notes": "The name (Heading 4, hug) + up to two 16×16 icons at s4: surge-up in Accent/Blue 1, info-outline in Secondary. In one-line Rows the price joins this row and always hugs the RIGHT edge (margin-inline-start auto) — never with a strikethrough (owner, 2026-09-29)."
    },
    "persons": {
      "class": "tariff-cell__persons",
      "optional": true,
      "notes": "persons · time — person-outline 16 + Compact Body Primary; hidden by the Persons boolean (e.g. Intercity)."
    },
    "priceCol": {
      "class": "tariff-cell__price-col",
      "optional": true,
      "onlyWhen": { "layout": ["columns"] },
      "notes": "Columns only — the price (Heading 4, single line, top aligned) + the optional strikethrough price (Compact Body Secondary, struck) at s4; right-aligned, hugs."
    },
    "priceRow": {
      "class": "tariff-cell__price-row",
      "optional": true,
      "onlyWhen": { "layout": ["rows"] },
      "notes": "The two-line Rows configuration only (an explicit arrangement, not an automatic wrap): the price pair on its own line under the title row, left-aligned, s4 to the title line and s4 inside the pair. The ONLY place the Rows strikethrough (aside) exists (owner, 2026-09-29)."
    },
    "status": {
      "class": "tariff-cell__status",
      "optional": true,
      "notes": "One optional bottom line at s4 under the text row: Description (multiline, no limit) or a one-line ellipsized status with a 16 icon at s4."
    }
  },
  "axes": {
    "style": {
      "title": "Style",
      "type": "enum",
      "values": [
        "default",
        "skeleton"
      ],
      "default": "default",
      "css": {
        "modifier": ".tariff-cell--skeleton",
        "mechanism": "default is the bare root; skeleton swaps the picture to the skeleton / tariff ghost vehicle and the texts to .tariff-cell__bar shimmer bars; the status line is not shown"
      },
      "figma": {
        "kind": "variant-property",
        "property": "Style",
        "values": {
          "default": "Default",
          "skeleton": "Skeleton"
        }
      }
    },
    "state": {
      "title": "State",
      "type": "enum",
      "values": [
        "standard",
        "unavailable"
      ],
      "default": "standard",
      "css": {
        "modifier": ".tariff-cell--unavailable",
        "mechanism": "the image goes grayscale; the texts keep their styles — the host adds the real disabled semantics"
      },
      "figma": {
        "kind": "none",
        "notes": "Set via the StartSlot 3D-service Status property, not a cell variant. Selected/not selected are NOT this component's states — the tariff card organism owns them."
      }
    },
    "layout": {
      "title": "Layout",
      "type": "enum",
      "values": [
        "columns",
        "rows"
      ],
      "default": "columns",
      "css": {
        "modifier": ".tariff-cell--rows",
        "mechanism": "columns: the price in the right column; rows: the price joins the title row hugging the right edge, or moves to .tariff-cell__price-row in the two-line configuration — the price column is not used"
      },
      "figma": {
        "kind": "variant-property",
        "property": "Layout",
        "notes": "On the nested 🟡StartText 3.1 instance, exposed in the cell's panel. Columns = Ride Hailing default; Rows = Intercity/long names."
      }
    },
    "descriptionStatus": {
      "title": "Description / Status",
      "type": "enum",
      "values": [
        "off",
        "description",
        "recommended",
        "good-price",
        "low-price"
      ],
      "default": "off",
      "css": {
        "mechanism": "presence of .tariff-cell__status + its modifier (--recommended/--good/--low); description is the bare class, multiline"
      },
      "figma": {
        "kind": "boolean-property",
        "property": "⚫️Description / Status",
        "notes": "The boolean shows the line; the config is the nested ⚫️Status 3.1 set's Status variant. Only one config shows at a time."
      },
      "constraints": [
        "The three statuses are one line, ellipsized; Description is multiline with no line limit.",
        "Not shown in the Skeleton style."
      ]
    },
    "price": {
      "title": "Price",
      "type": "boolean",
      "default": true,
      "css": {
        "mechanism": "presence of the price (the column in Columns, the in-row price in Rows)"
      },
      "figma": {
        "kind": "boolean-property",
        "property": "Price",
        "notes": "On the nested 🟡StartText 3.1. Hide when the numeric stepper shows the fare."
      }
    },
    "persons": {
      "title": "Persons",
      "type": "boolean",
      "default": true,
      "css": {
        "mechanism": "presence of .tariff-cell__persons"
      },
      "figma": {
        "kind": "boolean-property",
        "property": "Persons"
      }
    },
    "strikethrough": {
      "title": "Strikethrough price",
      "type": "enum",
      "values": [
        "off",
        "below",
        "aside"
      ],
      "default": "off",
      "css": {
        "mechanism": "presence of .tariff-cell__old-price — below the price in Columns, beside it on the two-line .tariff-cell__price-row in Rows"
      },
      "figma": {
        "kind": "variant-property",
        "property": "Strikethrough price",
        "notes": "On the nested Price/⚪️EndText blocks: Off|Below in Columns, Off|Aside in Rows (Aside = the two-line staging only)."
      },
      "constraints": [
        "below is Columns-only; aside is Rows-only — the position follows the layout.",
        "aside exists ONLY in the two-line Rows configuration (.tariff-cell__price-row — the price pair on the second title line). A one-line Rows title, the price hugging the right edge on the name's line, never carries a strikethrough — two prices next to the name read wrong (owner, 2026-09-29)."
      ]
    },
    "fill": {
      "title": "Fill",
      "type": "token",
      "customizable": true,
      "tokens": [
        "--background-primary",
        "--background-secondary"
      ],
      "css": {
        "mechanism": "--tariff-cell-fill, transparent by default — the tariff card provides the surface; a standalone instance re-points it to a background-group token only"
      },
      "figma": {
        "kind": "none",
        "notes": "The Specification frame's 'bg color' note: any background group token."
      },
      "notes": "The Figma contract names the full background group (Primary, Alt. Primary, Secondary, Alt. Secondary, Tertiary, Alt. Tertiary); the synced web palette (Colors 3.13.0) carries only Primary and Secondary — the Alt/Tertiary steps are a recorded colours-sync gap, flagged to the owner."
    }
  },
  "analytics": {
    "dataDsComponent": "tariff-cell",
    "action": "tap",
    "valueAttr": "data-ds-state",
    "notes": "Coverage unit. The whole row is the target when the host makes it tappable; no internal targets (surge/info icons are not tappable here). data-ds-variant = columns|rows; data-ds-state = standard|unavailable|skeleton. The tariff id via data-ds-value is the host's call."
  },
  "rtl": {
    "supported": true,
    "notes": "Inherited from the Cell family — the row mirrors; the tariff illustrations carry their own RTL (and Flat) variants."
  },
  "constraints": [
    "Built on [CustomCell] 3.3 — slots and spacing inherited; no background and no border of its own (the tariff card provides both).",
    "Selected / not selected, the Notch and the NumericStepper belong to the tariff card organism (out of scope, owner 2026-09-25).",
    "Long names: Columns clips at the column edge without an ellipsis; Rows switches to the two-line configuration — the price pair on its own second line (an explicit arrangement, not an automatic wrap).",
    "Rows price placement: on the name's line the price always hugs the right edge, with no strikethrough; the strikethrough (aside) exists only on the two-line price row (owner, 2026-09-29).",
    "The price is single-line and top aligned; only one Description/Status config shows at a time.",
    "The Figma set lives in the [TariffCell] 3.0 working file, not the main components library — no manifest binding until it merges (the DestinationButton precedent)."
  ]
}
```
