@uiresponse/renderer-react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ATTRIBUTION.md +59 -0
- package/README.md +191 -0
- package/dist/uir-renderer-web.css +1 -0
- package/dist/uir-renderer-web.js +2213 -0
- package/package.json +63 -0
- package/src/UIResponsePage.jsx +576 -0
- package/src/blocks/containers.jsx +150 -0
- package/src/blocks/content.jsx +292 -0
- package/src/blocks/context.js +27 -0
- package/src/blocks/data.jsx +551 -0
- package/src/blocks/primitives.jsx +290 -0
- package/src/blocks/viz.jsx +616 -0
- package/src/core/actions.js +201 -0
- package/src/core/condition.js +90 -0
- package/src/core/empty.js +44 -0
- package/src/core/format.js +225 -0
- package/src/core/presentation.js +58 -0
- package/src/core/record.js +160 -0
- package/src/core/store.js +357 -0
- package/src/core/value.js +211 -0
- package/src/index.js +22 -0
- package/src/theme/tokens.js +364 -0
- package/src/theme/uir.css +1070 -0
package/ATTRIBUTION.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Third-party attribution
|
|
2
|
+
|
|
3
|
+
## `@openuidev/react-ui` — MIT
|
|
4
|
+
|
|
5
|
+
Copyright (c) Thesys Inc.
|
|
6
|
+
|
|
7
|
+
`@uiresponse/renderer-react` **depends on** `@openuidev/react-ui` from npm. It does not fork it and it does
|
|
8
|
+
not vendor it: no file in this package is copied from that project, and there is no `vendor/`
|
|
9
|
+
directory. Preferring the upstream dependency over a fork until divergence forces one is DECISIONS
|
|
10
|
+
X4's licence-hygiene constraint, and it is also the cheaper engineering position — their charts get
|
|
11
|
+
better without us doing anything.
|
|
12
|
+
|
|
13
|
+
**What we use, and why it is theirs rather than ours:**
|
|
14
|
+
|
|
15
|
+
| Component | Where | Why we did not rebuild it |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `AreaChart`, `LineChart`, `BarChart` | `src/blocks/viz.jsx` | Recharts wrappers carrying tooltips, legends, responsive sizing, axis ticks, scroll behaviour and export hooks. The teardown (§4) found them "fairly mature visually"; that assessment held. **We pass them no `customPalette` and override none of their CSS variables in the light theme** — their styling IS the element look we want. |
|
|
18
|
+
| `MiniLineChart` | `src/blocks/viz.jsx` (`chart.kind: "sparkline"`) | Same. |
|
|
19
|
+
|
|
20
|
+
**What we replaced:**
|
|
21
|
+
|
|
22
|
+
- **Their `PieChart`.** It grows its ring to fill whatever box it is given and stacks its legend
|
|
23
|
+
beneath. On a tile that produced a bloated hoop with one dominant slice and the small categories
|
|
24
|
+
bunched into unreadable slivers. A pie exists to compare parts to a whole, so the category, its
|
|
25
|
+
value and its share have to sit beside the arc. `src/blocks/viz.jsx` draws `Donut` — about sixty
|
|
26
|
+
lines of trigonometry, and the only chart we colour.
|
|
27
|
+
|
|
28
|
+
**What we deliberately did NOT take:**
|
|
29
|
+
|
|
30
|
+
- **Their `genui-lib` `Table`.** It is column-oriented (`Table([Col("Label", data)])`), paginates at a
|
|
31
|
+
fixed 10 rows on the client, and has no per-column format or empty policy. OBL's `table` is
|
|
32
|
+
row-oriented, has `columnSpec.format` and `columnSpec.empty`, and must never client-paginate a
|
|
33
|
+
result the resolver already truncated with a `limit` — that shows the top of the wrong set. We
|
|
34
|
+
build the `<table>` ourselves.
|
|
35
|
+
- **OpenUI Lang and its renderer.** The compile-to-Lang path (DECISIONS X3) is now an interop/export
|
|
36
|
+
target, not the product surface. Every fidelity loss it produced — dropped `format`, no null
|
|
37
|
+
Select option, metric-as-generic-Card — came from the Lang, not from these components (X4).
|
|
38
|
+
|
|
39
|
+
**Theming — and the decision to stop.** An earlier pass re-pointed their `--openui-*` custom
|
|
40
|
+
properties at OBL tokens so their charts would adopt a tenant's brand. That was a mistake, and Dan
|
|
41
|
+
caught it: their components already look right, and repainting them is how they stopped looking like
|
|
42
|
+
themselves. `themeToCssVars()` now emits **no `--openui-*` overrides in the light theme at all**.
|
|
43
|
+
|
|
44
|
+
It remaps them in **dark only**, because their stylesheet defines a single light `:root` and their
|
|
45
|
+
charts would otherwise render white on a near-black ground. Every value in that remap is a neutral.
|
|
46
|
+
That is a repair, not a redecoration — and it is still done through the seam their CSS exposes, with
|
|
47
|
+
no `!important` and no fork.
|
|
48
|
+
|
|
49
|
+
Their licence text travels with the package in `node_modules/@openuidev/react-ui/LICENSE`.
|
|
50
|
+
|
|
51
|
+
## Fonts
|
|
52
|
+
|
|
53
|
+
None. The default theme uses the platform's own UI font (`-apple-system` / `Segoe UI` / `system-ui`)
|
|
54
|
+
and the platform's monospace for the few places tabular figures need one. Nothing is downloaded, and
|
|
55
|
+
no `@fontsource` package is a dependency.
|
|
56
|
+
|
|
57
|
+
An earlier pass shipped Archivo Narrow, Libre Franklin and IBM Plex Mono. They were removed with the
|
|
58
|
+
rest of the editorial identity: a page generated inside someone else's product should be set in that
|
|
59
|
+
product's typeface, which on every platform we target is the system one.
|
package/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# `@uiresponse/renderer-react`
|
|
2
|
+
|
|
3
|
+
UIR's **first-party** React renderer. It consumes a UIR page document directly — no Lang step,
|
|
4
|
+
nothing lossy — and uses OpenUI's MIT component layer as its visual base where that layer is good
|
|
5
|
+
(charts), unrestyled, while owning everything UIR is semantically richer about (DECISIONS **X4**).
|
|
6
|
+
(`uir/demo/web` still imports it under the legacy alias `@uir/renderer-web` via a `file:` dep.)
|
|
7
|
+
|
|
8
|
+
```jsx
|
|
9
|
+
import { UIResponsePage, darkTheme } from "@uiresponse/renderer-react";
|
|
10
|
+
import "@openuidev/react-ui/index.css"; // their components' CSS — FIRST
|
|
11
|
+
import "@uiresponse/renderer-react/styles.css"; // ours — SECOND (layout, spacing, semantic states)
|
|
12
|
+
|
|
13
|
+
<UIResponsePage page={pageDoc} resolve={resolveFn} theme={dark ? darkTheme : undefined} />
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**The renderer paints no background.** The surface belongs to the host: a workroom canvas, a chat
|
|
17
|
+
thread, a home screen. Blocks are discrete tiles placed on it. A one-block page looks like one
|
|
18
|
+
element in space, not a page inside a page.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## The import contract (for `uir/demo/web`)
|
|
23
|
+
|
|
24
|
+
### Props
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Required | What it is |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| `page` | UIR page document (`uir:foundation/page/v1`) | ✅ | The whole document. Validate it first; this renderer trusts it. |
|
|
29
|
+
| `resolve` | `({page, dataset, params, pageDoc}) => Promise<{rows, columns?}>` | ✅ | **The only data surface.** See below. |
|
|
30
|
+
| `theme` | partial token object | — | Merged over `defaultTheme`. This is the per-tenant brand story. |
|
|
31
|
+
| `params` | `{ [name]: value }` | — | Host-supplied `url` and `session` parameter values. A page never carries identity (P8). |
|
|
32
|
+
| `onOpenRecord` | `({dataset, id, target}) => void` | — | Where a record lives. The renderer does not know. |
|
|
33
|
+
| `onNotice` | `(message) => void` | — | Told when a disabled verb is clicked, or a URL refused. |
|
|
34
|
+
| `confirm` | `(confirmSpec) => Promise<boolean>` | — | Replaces `window.confirm` for `action.confirm`. |
|
|
35
|
+
| `now` | `Date` | — | The instant `relative_time` and `$now` measure against. Pin it in tests. |
|
|
36
|
+
| `validated` | `boolean` (default `false`) | — | The host asserts the page passed Phase 2. Only then is the validation tick drawn — the renderer does not validate, and will not claim it did. |
|
|
37
|
+
| `concurrency` | `number` (default `2`) | — | Datasets in flight at once. |
|
|
38
|
+
| `inline` | `boolean` (default `false`) | — | Chat context: same artifact and data semantics, no title/shell chrome. |
|
|
39
|
+
| `resolvePage` | `(pageId) => pageDoc \| Promise` | — | Makes `navigate.mode: "overlay"` executable. The host returns a page it has VALIDATED; without this, overlay verbs draw disabled wearing their reason. |
|
|
40
|
+
| `revealDwellMs` | `number` (default `0`) | — | Skeleton-before-content floor: each data-bearing tile wears its skeleton at least this long, and contents flip in no faster than one per this, page-wide. `prefers-reduced-motion` forces 0. |
|
|
41
|
+
| `narration` | `[{after, text, slots}]` | — | Generation-time commentary painted after the blocks it points at, slots resolved against real rows by the same binding machinery. A line whose slot cannot resolve is withheld — narration understates, never invents. |
|
|
42
|
+
| `onAllRevealed` | `(settled: boolean) => void` | — | Reports the FACT that every data-bearing block on screen has revealed its content (or settled into a terminal error) — `true` on that moment, `false` when new unrevealed content appears. This is the seam a streaming host composes rhythm on (outro-after-content, honest composers): "revealed" is not "resolved" — the dwell above is timing only this component knows, so the renderer reports what a viewer can see and keeps the mechanism private. |
|
|
43
|
+
|
|
44
|
+
### `resolve` — the single host tool
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
const resolve = async ({ page, dataset, params, pageDoc }) => {
|
|
48
|
+
const res = await fetch("/api/uir_resolve", {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: { "content-type": "application/json" },
|
|
51
|
+
body: JSON.stringify({ pageDoc, dataset, params }),
|
|
52
|
+
});
|
|
53
|
+
const out = await res.json();
|
|
54
|
+
if (out.errors?.length) throw new Error(out.errors.map((e) => e.message).join("; "));
|
|
55
|
+
return out; // { rows, columns }
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This is the same shape as `uir/demo/web/src/uirResolve.js`'s `uir_resolve`, so the demo's existing
|
|
60
|
+
swap point drops straight in. Two rules the renderer relies on and the demo already honours:
|
|
61
|
+
|
|
62
|
+
- **Throw on resolver errors.** A dataset that failed is an **error**, never an empty result set. The
|
|
63
|
+
renderer draws "could not resolve" and says why. `{rows: []}` means *no matching rows*, and those
|
|
64
|
+
two must never look the same (DECISIONS **R9**).
|
|
65
|
+
- **Never import the resolver into the browser.** `connectors/postgrest.mjs` can reach a credential.
|
|
66
|
+
The browser asks for rows; the server decides what it may read.
|
|
67
|
+
|
|
68
|
+
### Theme
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
import { defaultTheme, darkTheme, makeTheme } from "@uiresponse/renderer-react";
|
|
72
|
+
const tenant = makeTheme({ color: { brand: "#0071E3" } });
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**`design/UIR-element-system.dc.html` is the design source of truth.** Every token in `tokens.js` was
|
|
76
|
+
extracted from its sheets; when the two disagree, the design wins and `tokens.js` changes. See
|
|
77
|
+
`design/NOTES-designsync.md` for what was converged, what is implemented ahead of the schema, and
|
|
78
|
+
where the renderer deliberately diverges.
|
|
79
|
+
|
|
80
|
+
The default theme is **universal**: zinc neutrals on a white ground, the system UI font, one quiet
|
|
81
|
+
accent (`#3B6FE6`) used almost nowhere. A page a model generated inside someone else's product must
|
|
82
|
+
not arrive wearing ours.
|
|
83
|
+
|
|
84
|
+
`themeToCssVars()` emits `--uir-*` and, in the **light theme, no `--openui-*` overrides at all**:
|
|
85
|
+
their components keep their own styling. In **dark** it remaps their variables to neutrals, because
|
|
86
|
+
they ship no dark palette and their charts would otherwise sit white on black.
|
|
87
|
+
|
|
88
|
+
Brand is not in the LLM's vocabulary — Principle 1 bans colour from the document. That is the good
|
|
89
|
+
consequence: **every generated page is on-brand by construction**, because brand lives in the theme
|
|
90
|
+
and nowhere else.
|
|
91
|
+
|
|
92
|
+
### Also exported
|
|
93
|
+
|
|
94
|
+
`defaultTheme`, `darkTheme`, `makeTheme`, `themeToCssVars`, and the pure semantic functions — `formatValue`,
|
|
95
|
+
`emptyRender`, `isEmptyValue`, `hidesBlock`, `evaluateCondition`, `resolveValue`, `resolveBinding`,
|
|
96
|
+
`bindingArity`, `aggregate`, `collectDatasets`, `collectParams`, `planDatasetOrder`, `flattenBlocks`,
|
|
97
|
+
`DISABLED_VERBS`, `isDisabledVerb`. A chat surface that wants to render one metric inline does not
|
|
98
|
+
need `<UIResponsePage>`.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## What it does that the Lang compiler could not
|
|
103
|
+
|
|
104
|
+
The compile-to-OpenUI-Lang path (X3) worked, and its demo is what proved it lossy. Every loss came
|
|
105
|
+
from the **Lang**, not from their components:
|
|
106
|
+
|
|
107
|
+
| UIR semantic | Lang path | Here |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `format` on a binding | dropped | `£1.2M`, `+24%`, `5 months ago`, `1h 2m`, `Q3 2026` |
|
|
110
|
+
| `empty` policy | dropped | absence is visible — a muted dash or a stated reason, never a fake `0` |
|
|
111
|
+
| `emphasis` | flattened | four levels, expressed as type scale and tile size |
|
|
112
|
+
| `intent.priority` | invisible | orders peers, and orders dataset loading |
|
|
113
|
+
| `visible_when` | partial | the full condition grammar, client-side |
|
|
114
|
+
| null Select option | inexpressible | clears the param, so an unset filter shows everything (R4) |
|
|
115
|
+
| `metric` | generic Card | the one number, on its own compact tile |
|
|
116
|
+
| progressive load | stream-shaped | top-down, first-use order, per-block paint |
|
|
117
|
+
|
|
118
|
+
## Progressive loading
|
|
119
|
+
|
|
120
|
+
The behaviour, not a simulation of it:
|
|
121
|
+
|
|
122
|
+
1. **Structure paints on frame one.** Every tile draws itself, with a skeleton, before a single row
|
|
123
|
+
exists. The shape of the answer is knowable from the document alone — that is what a semantic
|
|
124
|
+
block language buys.
|
|
125
|
+
2. **Datasets resolve in first-use order**, walking the tree top-down. `intent.priority` breaks ties.
|
|
126
|
+
3. **Each block paints when *its* datasets land.** A slow royalty aggregation does not hold the
|
|
127
|
+
contract queue hostage.
|
|
128
|
+
4. **A conditional block draws nothing while pending.** A skeleton is a promise that content is
|
|
129
|
+
coming; for a `visible_when`-gated block that promise is a coin flip. The header says
|
|
130
|
+
"resolving 2 of 3" once, instead of every block guessing.
|
|
131
|
+
|
|
132
|
+
## Actions
|
|
133
|
+
|
|
134
|
+
`set_param`, `refresh` and `open` execute. **`navigate` and `mutate` render disabled, wearing their
|
|
135
|
+
reason** — a write needs an authorizing resolver round-trip (A5 puts authorization outside UIR), and
|
|
136
|
+
there is no page router in v1. A disabled verb aborts its `then` chain: "approve, then refresh" must
|
|
137
|
+
never refresh without approving.
|
|
138
|
+
|
|
139
|
+
An `input`'s `on_change` may write `{"$self": true}` — the value the user just entered (DECISIONS
|
|
140
|
+
B12). The renderer supplies it as `scope.self` and then resolves what the page wrote; it no longer
|
|
141
|
+
substitutes the widget's value for the action's, which is what it used to do and should not have.
|
|
142
|
+
`$self` resolves to the field's string, the toggle's boolean, the date, or a select's chosen
|
|
143
|
+
`option_value_field` — `null` when the choice is cleared.
|
|
144
|
+
|
|
145
|
+
## Known gaps
|
|
146
|
+
|
|
147
|
+
- **Chart axis ticks are theirs.** A count axis can read `0, 0.75, 1.50, 2.25, 3`. Their chart props
|
|
148
|
+
expose no `tickFormatter`; fixing it means a PR upstream or a fork, and neither is worth it yet.
|
|
149
|
+
- **`chart.kind: "pie"` is ours, not theirs.** See `ATTRIBUTION.md`.
|
|
150
|
+
- **`chart.kind: "bar"` leaves empty frame with few categories.** Their `BarChart` gives each
|
|
151
|
+
category a fixed band width rather than stretching bands to fill. Capping the plot width pulls the
|
|
152
|
+
bars out and *breaks* the alignment between bars and x-labels — the sticky y-axis column is
|
|
153
|
+
measured out of a different box than the plot. A plain chart beats a chart whose labels sit under
|
|
154
|
+
the wrong bar, so the emptiness stays. Same upstream fix as above.
|
|
155
|
+
- **`metric.delta` has a schema but no design.** The element system shows no delta — no arrows, no
|
|
156
|
+
triangles, no signed percentages — so the status-tone chip is a stand-in. See NOTES D3.
|
|
157
|
+
- **Spotify embeds resolve to the provider's default surface.** An opaque `content_id` cannot say
|
|
158
|
+
track-vs-playlist-vs-episode. Widening that is a `content_type` prop or more enum members; encoding
|
|
159
|
+
it in the id would be a microsyntax. See DECISIONS MED4.
|
|
160
|
+
- **A `series[].kind` that differs from the chart's `kind` is drawn in the chart's kind**, and the
|
|
161
|
+
page says so in its notice. Their chart components derive one shape per chart. CH5 makes announcing
|
|
162
|
+
the substitution an obligation, so this is a stated degradation rather than a gap.
|
|
163
|
+
- **Clearing a `select` written the old way is a renderer contract, not a resolution.** A select whose
|
|
164
|
+
`on_change` binds the chosen option's row (`scope: "row"`, still legal) has no row to bind when the
|
|
165
|
+
choice is cleared, so the renderer writes `set_param(null)` — which is what clearing means (R4). A
|
|
166
|
+
select written with `{"$self": true}` needs none of this: its `self` is simply `null`.
|
|
167
|
+
- **`detail` `select: "first" | "last"`** is honoured, but the renderer cannot warn that the query
|
|
168
|
+
had no `sort`. That is Phase 2's job, and Phase 2 runs before this does.
|
|
169
|
+
|
|
170
|
+
## Developing
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
npm install
|
|
174
|
+
npm run preview:dev # http://localhost:5178 — the example pages, the block gallery, the one-block page
|
|
175
|
+
npm run build # dist/uir-renderer-web.js + .css
|
|
176
|
+
node tests.mjs # 74 checks over the semantic core
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The preview harness (`preview/`) is **not** part of the package. It runs the real `uir/resolver` and
|
|
180
|
+
seed connector behind a dev-server middleware, exactly as the demo does. `preview/connector.mjs`
|
|
181
|
+
supplies the one resource the seed connector does not yet serve, and rewrites the seed's unreachable
|
|
182
|
+
`artwork_url` to sleeves the harness serves locally; see its header.
|
|
183
|
+
|
|
184
|
+
Two preview pages are worth knowing about:
|
|
185
|
+
|
|
186
|
+
- **`gallery.page.json`** — every block type UIR v1 has, rendered once against real rows. The
|
|
187
|
+
permanent visual regression page; open this after any change to the composition model.
|
|
188
|
+
- **`pie-ask.page.json`** — a single generated block, as a router-assembled answer to one ask would
|
|
189
|
+
arrive. The acid test: it must look like an element in space, not a poster.
|
|
190
|
+
|
|
191
|
+
Licence and attribution for the wrapped components: [`ATTRIBUTION.md`](ATTRIBUTION.md).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.uir-page{--uir-row-pad: var(--uir-density-comfortable);color:var(--uir-color-ink);font-family:var(--uir-font-body);font-size:var(--uir-size-base);line-height:1.5;padding:var(--uir-space-lg);-webkit-font-smoothing:antialiased}.uir-page *,.uir-page *:before,.uir-page *:after{box-sizing:border-box}.uir-shell{display:flex;flex-direction:column;gap:var(--uir-space-xl);margin:0 auto;max-width:var(--uir-measure)}.uir-page--inline{padding:0}.uir-page--inline .uir-shell{gap:var(--uir-space-md);margin:0;max-width:none}.uir-num,.uir-mono{font-family:var(--uir-font-mono);font-feature-settings:"tnum" 1,"zero" 1;font-variant-numeric:tabular-nums}.uir-eyebrow{color:var(--uir-color-graphite);font-family:var(--uir-font-body);font-size:var(--uir-size-small);font-weight:400;letter-spacing:0;text-transform:none}.uir-header{display:flex;flex-direction:column;gap:3px}.uir-header__line{align-items:baseline;display:flex;flex-wrap:wrap;gap:var(--uir-space-md)}.uir-header__title{font-size:var(--uir-size-large);font-weight:600;letter-spacing:-.008em;line-height:1.2;margin:0}.uir-header__subtitle{color:var(--uir-color-graphite);font-size:var(--uir-size-small);margin:0;max-width:68ch}.uir-header[data-minimal] .uir-header__title{color:var(--uir-color-graphite);font-size:var(--uir-size-small);font-weight:500;letter-spacing:0}.uir-header__status{align-items:center;color:var(--uir-color-faint);display:inline-flex;font-size:var(--uir-size-micro);gap:.5em;margin-left:auto}.uir-header__status:before{animation:uir-pulse 1.4s ease-in-out infinite;background:var(--uir-color-graphite);border-radius:50%;content:"";height:5px;width:5px}.uir-header__status[data-settled]:before{animation:none;background:var(--uir-color-positive)}@keyframes uir-pulse{0%,to{opacity:.25}50%{opacity:1}}.uir-section{display:flex;flex-direction:column;gap:var(--uir-space-md)}.uir-section__label{color:var(--uir-color-graphite);font-size:var(--uir-size-small);font-weight:500;letter-spacing:0;margin:0}.uir-section[data-density=compact]{--uir-row-pad: var(--uir-density-compact)}.uir-section[data-editorial]{gap:var(--uir-space-lg)}.uir-section[data-editorial]>.uir-section__label{color:var(--uir-color-ink);font-size:var(--uir-size-primary-value);font-weight:600;letter-spacing:-.02em;line-height:1.1;margin-bottom:calc(var(--uir-space-md) * -1);max-width:24ch;text-wrap:balance}.uir-section[data-editorial]>.uir-arrange>.uir-block[data-type=text]:first-child .uir-text{color:var(--uir-color-graphite);font-size:var(--uir-size-large);font-weight:400;letter-spacing:0;line-height:1.5;max-width:64ch}.uir-section[data-editorial] .uir-posters[data-mode=rail]{margin-right:calc(var(--uir-space-lg) * -1);padding-right:var(--uir-space-lg)}.uir-arrange{align-items:start;display:grid;gap:var(--uir-space-md)}.uir-arrange[data-arrangement=stack]{grid-template-columns:minmax(0,1fr)}.uir-arrange[data-arrangement=row]{align-items:end;grid-auto-columns:minmax(0,1fr);grid-auto-flow:column}.uir-arrange[data-arrangement=grid]{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}.uir-arrange[data-arrangement=flow]{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.uir-arrange[data-arrangement=row]:has(>.uir-block[data-type=chart]),.uir-arrange[data-arrangement=row]:has(>.uir-block[data-type=table]),.uir-arrange[data-arrangement=row]:has(>.uir-block[data-type=timeline]){grid-auto-flow:row;grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}.uir-arrange:not([data-arrangement=stack])>.uir-block[data-type=chart],.uir-arrange:not([data-arrangement=stack])>.uir-block[data-type=timeline],.uir-arrange:not([data-arrangement=stack])>.uir-block[data-type=calendar],.uir-arrange:not([data-arrangement=stack])>.uir-block[data-type=table],.uir-arrange:not([data-arrangement=stack])>.uir-block[data-type=detail]{grid-column:1 / -1}@media(max-width:640px){.uir-arrange[data-arrangement=row]{grid-auto-columns:auto;grid-auto-flow:row}.uir-page{padding:var(--uir-space-md)}}.uir-block{min-width:0;position:relative}.uir-block[data-tile]{background:var(--uir-color-sheet);border:1px solid var(--uir-color-rule);border-radius:var(--uir-radius-lg);box-shadow:var(--uir-shadow);padding:var(--uir-tile-pad-y) var(--uir-tile-pad-x)}@keyframes uir-fold{0%{opacity:0;transform:translateY(7px) scale(.985)}to{opacity:1;transform:none}}.uir-block[data-tile][data-state=ready][data-arrived]{animation:uir-fold var(--uir-motion-fold)}.uir-block[data-tile][data-emphasis=hero]{grid-column:span 2}@media(max-width:520px){.uir-block[data-tile][data-emphasis=hero]{grid-column:1 / -1}}.uir-block[data-tile][data-emphasis=subtle]{background:transparent;border-color:transparent;box-shadow:none}.uir-arrange[data-arrangement=stack]>.uir-block[data-tile][data-type=metric]:not([data-emphasis=hero]),.uir-arrange[data-arrangement=stack]>.uir-block[data-tile][data-type=progress]:not([data-emphasis=hero]){max-width:300px}.uir-card{background:var(--uir-color-sheet);border:1px solid var(--uir-color-rule);border-radius:var(--uir-radius-lg);box-shadow:var(--uir-shadow);display:flex;flex-direction:column;gap:var(--uir-space-lg);padding:var(--uir-space-lg)}.uir-card__title{font-size:var(--uir-size-base);font-weight:600;letter-spacing:-.005em;margin:0}.uir-card--clickable{cursor:pointer;transition:border-color .12s ease}.uir-card--clickable:hover{border-color:var(--uir-color-brand)}.uir-block[data-type=card][data-emphasis=hero]{grid-column:1 / -1}.uir-card .uir-block[data-tile],.uir-card .uir-block[data-tile][data-emphasis=hero]{background:none;border:0;border-radius:0;grid-column:auto;padding:0}.uir-tabs{display:flex;flex-direction:column;gap:var(--uir-space-md)}.uir-tabs__list{align-self:start;background:var(--uir-color-sunk);border-radius:9px;display:inline-flex;gap:2px;max-width:100%;overflow-x:auto;padding:3px;scrollbar-width:none}.uir-tab{background:none;border:0;border-radius:7px;color:var(--uir-color-graphite);cursor:pointer;font-family:var(--uir-font-body);font-size:var(--uir-size-small);font-weight:500;padding:5px 14px;transition:background .12s ease,color .12s ease;white-space:nowrap}.uir-tab[aria-selected=true]{background:var(--uir-color-sheet);box-shadow:var(--uir-shadow);color:var(--uir-color-ink)}.uir-tab:hover:not([aria-selected=true]){color:var(--uir-color-ink)}.uir-metric{display:flex;flex-direction:column}.uir-metric__label{color:var(--uir-color-graphite);font-size:var(--uir-size-label);font-weight:400;letter-spacing:0;text-transform:none}.uir-metric__value{font-feature-settings:"tnum" 1;font-variant-numeric:tabular-nums;font-weight:600;margin-top:8px}.uir-metric__unit{color:var(--uir-color-faint);font-size:var(--uir-size-unit);font-weight:400;letter-spacing:0;margin-left:3px}.uir-metric[data-emphasis=hero] .uir-metric__value{font-size:var(--uir-size-hero-value);letter-spacing:-.02em;line-height:1.05}.uir-metric[data-emphasis=primary] .uir-metric__value{font-size:var(--uir-size-primary-value);letter-spacing:-.015em;line-height:1.1}.uir-metric[data-emphasis=normal] .uir-metric__value{font-size:var(--uir-size-normal-value);line-height:1.2}.uir-metric[data-emphasis=subtle] .uir-metric__value{color:var(--uir-color-muted);font-size:var(--uir-size-subtle-value);font-weight:500;line-height:1.3;margin-top:4px}.uir-metric__delta{align-items:center;align-self:flex-start;border-radius:999px;display:inline-flex;gap:4px;font-size:var(--uir-size-small);font-weight:500;margin-top:8px;padding:2px 8px}.uir-metric__delta[data-tone=positive]{background:var(--uir-color-positive-soft);color:var(--uir-color-positive)}.uir-metric__delta[data-tone=negative]{background:var(--uir-color-negative-soft);color:var(--uir-color-negative)}.uir-metric__delta[data-tone=neutral]{background:var(--uir-color-neutral-soft);color:var(--uir-color-neutral)}.uir-metric__delta em{font-style:normal;font-variant-numeric:tabular-nums}.uir-metric__delta span{color:var(--uir-color-graphite);font-weight:400}.uir-metric--clickable{background:none;border:0;cursor:pointer;padding:0;text-align:left;width:100%}.uir-metric--clickable:hover .uir-metric__value{color:var(--uir-color-brand)}.uir-absent{color:var(--uir-color-faint)}.uir-metric__value .uir-absent--dash{color:var(--uir-color-rule-strong)}.uir-absent--dash{font-variant-numeric:tabular-nums}.uir-absent--text{font-size:var(--uir-size-micro)}.uir-absent--zero{color:var(--uir-color-ink)}.uir-block[data-type=table][data-tile]{padding:var(--uir-tile-pad-y) 0 0}.uir-table__head{align-items:center;display:flex;justify-content:space-between;padding:0 var(--uir-tile-pad-x) var(--uir-space-md)}.uir-table__title{color:var(--uir-color-ink);font-size:var(--uir-size-large);font-weight:600;margin:0}.uir-density{background:var(--uir-color-sunk);border-radius:var(--uir-radius-md);display:inline-flex;gap:2px;padding:2px}.uir-density button{background:none;border:0;border-radius:var(--uir-radius-sm);color:var(--uir-color-graphite);cursor:pointer;font-family:inherit;font-size:var(--uir-size-small);font-weight:500;padding:4px 10px}.uir-density button[aria-pressed=true]{background:var(--uir-color-sheet);box-shadow:var(--uir-shadow);color:var(--uir-color-ink)}.uir-table-wrap{margin:0}.uir-table__scroll{overflow-x:auto;scrollbar-width:thin}.uir-table{border-collapse:collapse;font-size:var(--uir-size-base);width:100%}.uir-table[data-density=comfortable]{--uir-row-pad: var(--uir-density-comfortable)}.uir-table[data-density=compact]{--uir-row-pad: var(--uir-density-compact)}.uir-table thead th{border-top:1px solid var(--uir-color-rule-soft);color:var(--uir-color-faint);font-size:var(--uir-size-small);font-weight:500;letter-spacing:0;padding:7px var(--uir-tile-pad-x);text-align:left;text-transform:none;white-space:nowrap}.uir-table tbody td{border-top:1px solid var(--uir-color-rule-soft);padding:var(--uir-row-pad) var(--uir-tile-pad-x);vertical-align:middle}.uir-table tbody tr{--row-bg: var(--uir-color-sheet);position:relative}.uir-table tbody tr:hover{--row-bg: var(--uir-color-sunk);background:var(--uir-color-sunk)}.uir-table tbody tr[data-clickable=true]{cursor:pointer}.uir-table tbody tr[data-clickable=true]:hover{--row-bg: var(--uir-color-brand-soft);background:var(--uir-color-brand-soft)}.uir-table tbody td:first-child{color:var(--uir-color-ink);font-weight:500}.uir-table td[data-numeric=true],.uir-table th[data-numeric=true]{text-align:right}.uir-table td[data-numeric=true]{font-variant-numeric:tabular-nums}.uir-name-cell{align-items:center;display:inline-flex;gap:10px}.uir-initials{align-items:center;background:var(--uir-color-sunk);color:var(--uir-color-muted);display:inline-flex;flex:none;font-size:9.5px;font-weight:600;height:24px;justify-content:center;letter-spacing:.02em;width:24px}.uir-initials[data-shape=round]{border-radius:50%}.uir-initials[data-shape=square]{border-radius:var(--uir-radius-md);color:var(--uir-color-graphite);font-size:10px;height:32px;width:32px}.uir-thumb{background:var(--uir-color-sunk);border:1px solid var(--uir-color-rule);border-radius:var(--uir-radius-sm);display:block;height:26px;object-fit:cover;width:26px}.uir-table td:has(>.uir-thumb){padding-bottom:5px;padding-top:5px;width:1%}.uir-table__actions-cell{padding:0!important;width:0}.uir-table__actions{align-items:center;background:var(--row-bg);display:flex;gap:var(--uir-space-sm);opacity:0;padding:0 var(--uir-tile-pad-x) 0 var(--uir-space-sm);pointer-events:none;position:absolute;right:0;top:50%;transform:translateY(-50%);transition:opacity .11s ease}.uir-table__actions:before{background:linear-gradient(to right,transparent,var(--row-bg));bottom:0;content:"";left:-44px;position:absolute;top:0;width:44px}.uir-table tbody tr:hover .uir-table__actions,.uir-table tbody tr:focus-within .uir-table__actions{opacity:1;pointer-events:auto}.uir-table__actions .uir-button{position:relative}@media(hover:none){.uir-table__actions{opacity:1;padding-left:0;pointer-events:auto;position:static;transform:none}.uir-table__actions:before{display:none}.uir-table__actions-cell{padding:var(--uir-row-pad) var(--uir-tile-pad-x)!important;width:auto}}.uir-pagination{align-items:center;border-top:1px solid var(--uir-color-rule-soft);color:var(--uir-color-graphite);display:flex;font-size:var(--uir-size-small);font-variant-numeric:tabular-nums;justify-content:space-between;padding:10px var(--uir-tile-pad-x)}.uir-pagination>div{display:flex;gap:6px}.uir-pagination button{align-items:center;background:var(--uir-color-sheet);border:1px solid var(--uir-color-rule);border-radius:var(--uir-radius-sm);color:var(--uir-color-muted);cursor:pointer;display:inline-flex;font-family:inherit;font-size:18px;height:26px;justify-content:center;line-height:1;width:26px}.uir-pagination button:hover:not(:disabled){background:var(--uir-color-sunk-strong);color:var(--uir-color-ink)}.uir-pagination button:disabled{cursor:default;opacity:.35}.uir-list{display:flex;flex-direction:column;margin:0 calc(-1 * var(--uir-space-sm))}.uir-list__item{align-items:center;background:none;border:0;border-radius:var(--uir-radius-md);display:flex;gap:var(--uir-space-md);justify-content:space-between;padding:8px var(--uir-space-sm);text-align:left;width:100%}.uir-list__copy{display:flex;flex:1;flex-direction:column;min-width:0}.uir-list__item[data-clickable=true]{cursor:pointer}.uir-list__item[data-clickable=true]:hover{background:var(--uir-color-sunk-strong)}.uir-list__title{font-family:var(--uir-font-body);font-size:var(--uir-size-small);font-weight:500;letter-spacing:-.003em}.uir-list__subtitle{color:var(--uir-color-graphite);font-family:var(--uir-font-body);font-size:var(--uir-size-small);letter-spacing:0;white-space:nowrap}.uir-chevron{align-self:center;flex:none;stroke:var(--uir-color-faint);transition:transform .16s ease}.uir-list__item[data-clickable=true]:hover .uir-chevron{transform:translate(2px)}@media(prefers-reduced-motion:reduce){.uir-chevron{transition:none}.uir-list__item[data-clickable=true]:hover .uir-chevron{transform:none}}.uir-img{background-position:center;background-repeat:no-repeat;background-size:cover;transition:filter .4s ease,opacity .4s ease,transform .24s cubic-bezier(.2,.7,.3,1)}.uir-img[data-blurup=thumb]:not([data-loaded]){filter:blur(12px)}.uir-img[data-blurup=none]:not([data-loaded]){opacity:0}.uir-img[data-loaded]{filter:none;opacity:1}.uir-poster__art>.uir-img[data-blurup=thumb]:not([data-loaded]){transform:scale(1.06)}@media(prefers-reduced-motion:reduce){.uir-img{transition:none}.uir-poster__art>.uir-img[data-blurup=thumb]:not([data-loaded]){transform:none}}.uir-posters{display:grid;gap:var(--uir-space-lg)}.uir-posters[data-mode=grid]{grid-template-columns:repeat(auto-fit,minmax(150px,200px))}.uir-posters[data-mode=rail]{grid-auto-columns:minmax(150px,184px);grid-auto-flow:column;overflow-x:auto;overscroll-behavior-x:contain;padding-bottom:var(--uir-space-sm);scroll-snap-type:x proximity;scrollbar-width:thin}.uir-posters[data-mode=rail]>.uir-poster{scroll-snap-align:start}.uir-poster{background:none;border:0;display:flex;flex-direction:column;gap:2px;min-width:0;padding:0;text-align:left;width:100%}.uir-poster__art{aspect-ratio:1 / 1;background:var(--uir-color-sunk);border-radius:var(--uir-radius-md);display:block;margin-bottom:var(--uir-space-sm);overflow:hidden;position:relative;width:100%}.uir-poster__art:after{border-radius:inherit;box-shadow:inset 0 0 0 1px var(--uir-color-rule);content:"";top:0;right:0;bottom:0;left:0;pointer-events:none;position:absolute}.uir-poster__art>img{display:block;height:100%;object-fit:cover;width:100%}.uir-poster__art[data-empty]{background:var(--uir-color-sunk)}.uir-poster__title{color:var(--uir-color-ink);font-size:var(--uir-size-large);font-weight:600;letter-spacing:-.01em;line-height:1.25;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.uir-poster__caption{color:var(--uir-color-graphite);font-size:var(--uir-size-small);line-height:1.45;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.uir-poster[data-clickable=true]{cursor:pointer}.uir-poster[data-clickable=true]:hover .uir-poster__art>img{transform:scale(1.03)}.uir-poster[data-clickable=true]:hover .uir-poster__title{color:var(--uir-color-brand)}.uir-poster:focus-visible{border-radius:var(--uir-radius-md);outline:2px solid var(--uir-color-focus);outline-offset:3px}@media(prefers-reduced-motion:reduce){.uir-poster[data-clickable=true]:hover .uir-poster__art>img{transform:none}}.uir-arrange:not([data-arrangement=stack])>.uir-block[data-type=list]:has(.uir-posters){grid-column:1 / -1}.uir-block[data-tile][data-type=list]:has(.uir-posters){background:none;border:0;box-shadow:none;padding:0}.uir-detail{display:flex;flex-direction:column;gap:var(--uir-space-lg);margin:0}.uir-detail__head{display:flex;flex-direction:column;gap:var(--uir-space-sm);align-items:flex-start}.uir-detail__name{color:var(--uir-color-ink);font-size:var(--uir-size-normal-value);font-weight:600;letter-spacing:-.01em;line-height:1.15;margin:0;text-wrap:balance}.uir-detail[data-emphasis=hero] .uir-detail__name{font-size:var(--uir-size-hero-value);letter-spacing:-.02em;line-height:1.05}.uir-detail[data-emphasis=primary] .uir-detail__name{font-size:var(--uir-size-primary-value);letter-spacing:-.015em;line-height:1.1}.uir-detail[data-emphasis=subtle] .uir-detail__name{color:var(--uir-color-muted);font-size:var(--uir-size-subtle-value);font-weight:500}.uir-detail__badges{display:flex;flex-wrap:wrap;gap:var(--uir-space-sm)}.uir-detail__facts{display:grid;gap:0;margin:0}.uir-detail__row{align-items:baseline;display:grid;gap:var(--uir-space-md);grid-template-columns:minmax(110px,26%) 1fr;padding:7px 0}.uir-detail__row+.uir-detail__row{border-top:1px solid var(--uir-color-rule)}.uir-detail[data-headed] .uir-detail__row+.uir-detail__row{border-top-color:var(--uir-color-rule-soft)}.uir-detail dt{color:var(--uir-color-graphite);font-size:var(--uir-size-small)}.uir-detail__value{font-size:var(--uir-size-small);font-variant-numeric:tabular-nums;margin:0}.uir-detail__value[data-numeric=true]{font-variant-numeric:tabular-nums}.uir-detail__link{color:var(--uir-color-brand);text-decoration:none;overflow-wrap:anywhere}.uir-detail__link:hover{text-decoration:underline}.uir-section[data-density=compact] .uir-detail__row{padding:3px 0}.uir-section[data-density=compact] .uir-detail{gap:var(--uir-space-md)}.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail]){column-gap:var(--uir-space-xl);display:grid;grid-template-columns:minmax(150px,32%) 1fr;grid-template-rows:min-content min-content 1fr;min-height:232px;row-gap:var(--uir-space-md)}.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail])>.uir-block{grid-column:2;min-width:0}.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail])>.uir-block[data-type=media]{align-self:stretch;grid-column:1;grid-row:1 / -1;margin:calc(var(--uir-space-lg) * -1) 0 calc(var(--uir-space-lg) * -1) calc(var(--uir-space-lg) * -1)}.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail])>.uir-block[data-type=badge]{order:-1}.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail]) .uir-media{height:100%;position:relative}.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail]) .uir-media__image{border-radius:var(--uir-radius-lg) 0 0 var(--uir-radius-lg);height:100%;top:0;right:0;bottom:0;left:0;object-fit:cover;position:absolute;width:100%}@media(max-width:640px){.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail]){grid-template-columns:1fr;grid-template-rows:none;min-height:0}.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail])>.uir-block,.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail])>.uir-block[data-type=media]{grid-column:1;grid-row:auto}.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail])>.uir-block[data-type=media]{margin:calc(var(--uir-space-lg) * -1) calc(var(--uir-space-lg) * -1) 0}.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail]) .uir-media{height:auto;position:static}.uir-card>.uir-arrange:has(>.uir-block[data-type=media]):has(>.uir-block[data-type=detail]) .uir-media__image{aspect-ratio:1 / 1;border-radius:var(--uir-radius-lg) var(--uir-radius-lg) 0 0;height:auto;position:static}}.uir-timeline{position:relative}.uir-timeline__track{height:224px;position:relative}.uir-timeline__axis{background:var(--uir-color-rule);height:1px;left:0;position:absolute;right:0;top:50%}.uir-timeline__today-line{border-left:1px dashed var(--uir-color-rule-strong);bottom:50%;position:absolute;top:2px;width:0}.uir-timeline__today{color:var(--uir-color-faint);font-family:var(--uir-font-mono);font-size:9.5px;position:absolute;top:-6px;transform:translate(-50%)}.uir-timeline__ticks{bottom:16px;left:0;position:absolute;right:0}.uir-timeline__ticks span{color:var(--uir-color-faint);font-size:var(--uir-size-micro);position:absolute;transform:translate(-50%)}.uir-timeline__item{bottom:0;position:absolute;top:0;width:0}.uir-timeline__dot{background:var(--uir-color-brand);border:2px solid var(--uir-color-sheet);border-radius:50%;height:10px;left:0;position:absolute;top:50%;transform:translate(-50%,-50%);width:10px;z-index:2}.uir-timeline__span{background:var(--uir-color-brand);height:3px;opacity:.22;position:absolute;top:50%;transform:translateY(-50%)}.uir-timeline__stem{background:var(--uir-color-rule-strong);left:0;position:absolute;width:1px}.uir-timeline__item[data-side=above] .uir-timeline__stem{bottom:50%;height:16px}.uir-timeline__item[data-side=below] .uir-timeline__stem{height:16px;top:50%}.uir-timeline__item[data-lane="2"] .uir-timeline__stem,.uir-timeline__item[data-lane="3"] .uir-timeline__stem{height:58px}.uir-timeline__caption{position:absolute;text-align:center;transform:translate(-50%);width:104px}.uir-timeline__item[data-side=above] .uir-timeline__caption{bottom:calc(50% + 18px)}.uir-timeline__item[data-side=below] .uir-timeline__caption{display:flex;flex-direction:column-reverse;top:calc(50% + 18px)}.uir-timeline__item[data-lane="2"] .uir-timeline__caption{bottom:calc(50% + 60px)}.uir-timeline__item[data-lane="3"] .uir-timeline__caption{top:calc(50% + 60px)}.uir-timeline__label{-webkit-box-orient:vertical;-webkit-line-clamp:2;display:-webkit-box;overflow:hidden;font-family:var(--uir-font-body);font-size:var(--uir-size-small);font-weight:500;letter-spacing:-.003em;line-height:1.2}.uir-timeline__date{color:var(--uir-color-faint);font-size:var(--uir-size-micro);margin-top:2px;white-space:nowrap}.uir-timeline__item[data-side=below] .uir-timeline__date{margin:0 0 2px}.uir-progress__head{align-items:baseline;display:flex;gap:var(--uir-space-md);justify-content:space-between;margin-bottom:8px}.uir-progress__track{background:var(--uir-color-sunk);border-radius:999px;height:6px;overflow:hidden}.uir-progress__fill{animation:uir-bar-in var(--uir-motion-bar-in);background:var(--uir-color-brand);border-radius:999px;height:100%;transition:width .42s cubic-bezier(.2,.7,.2,1)}@keyframes uir-bar-in{0%{width:0}}.uir-progress__value{font-size:var(--uir-size-small);font-variant-numeric:tabular-nums;font-weight:500}.uir-progress__ratio{color:var(--uir-color-graphite);font-weight:400}.uir-badge{align-items:center;border-radius:999px;display:inline-flex;gap:.4em;font-family:var(--uir-font-body);font-size:var(--uir-size-micro);font-weight:500;letter-spacing:0;padding:3px 10px;text-transform:none;white-space:nowrap}.uir-badge--cell{font-size:var(--uir-size-micro);vertical-align:baseline}.uir-badge[data-tone=positive]{background:var(--uir-color-positive-soft);color:var(--uir-color-positive)}.uir-badge[data-tone=warning]{background:var(--uir-color-warning-soft);color:var(--uir-color-warning)}.uir-badge[data-tone=negative]{background:var(--uir-color-negative-soft);color:var(--uir-color-negative)}.uir-badge[data-tone=info]{background:var(--uir-color-info-soft);color:var(--uir-color-info)}.uir-badge[data-tone=neutral]{background:var(--uir-color-neutral-soft);color:var(--uir-color-neutral)}.uir-badge__label{color:var(--uir-color-graphite);font-weight:400}.uir-text{font-size:var(--uir-size-small);margin:0;max-width:70ch}.uir-text[data-emphasis=hero]{font-size:var(--uir-size-large);font-weight:600;letter-spacing:-.01em}.uir-text[data-emphasis=primary]{font-size:var(--uir-size-base);font-weight:500}.uir-text[data-emphasis=subtle]{color:var(--uir-color-graphite)}.uir-calendar__head{align-items:baseline;display:flex;justify-content:space-between;margin-bottom:var(--uir-space-md)}.uir-calendar__count{color:var(--uir-color-faint);font-size:var(--uir-size-micro)}.uir-calendar__grid{display:grid;gap:1px;grid-template-columns:repeat(7,minmax(0,1fr))}.uir-calendar__weekday{color:var(--uir-color-faint);font-size:var(--uir-size-micro);font-weight:500;padding-bottom:var(--uir-space-sm);text-align:center}.uir-calendar__cell{background:var(--uir-color-sheet);border-radius:var(--uir-radius-sm);display:flex;flex-direction:column;gap:2px;min-height:62px;outline:1px solid var(--uir-color-rule-soft);padding:var(--uir-space-sm)}.uir-calendar__cell[data-outside]{opacity:.38}.uir-calendar__cell[data-occupied]{background:var(--uir-color-brand-soft)}.uir-calendar__date{color:var(--uir-color-graphite);font-size:var(--uir-size-micro);font-variant-numeric:tabular-nums}.uir-calendar__item{color:var(--uir-color-ink);font-size:var(--uir-size-micro);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.uir-calendar[data-span=week] .uir-calendar__cell{min-height:96px}.uir-media{margin:0}.uir-media__image{border-radius:var(--uir-radius-md);display:block;max-width:100%}.uir-media__caption{color:var(--uir-color-graphite);font-size:var(--uir-size-small);margin-top:var(--uir-space-sm)}.uir-audio{align-items:center;display:flex;gap:var(--uir-space-md)}.uir-audio>audio{display:none}.uir-audio>button{align-items:center;background:var(--uir-color-ink);border:0;border-radius:50%;color:var(--uir-color-sheet);cursor:pointer;display:inline-flex;flex:none;height:32px;justify-content:center;padding:0;width:32px}.uir-audio>button:hover{background:var(--uir-color-muted)}.uir-audio>button span{font-size:10px;transform:translate(1px)}.uir-audio__body{flex:1;min-width:0}.uir-audio__line{align-items:baseline;display:flex;gap:10px;justify-content:space-between}.uir-audio__line strong{font-size:13.5px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.uir-audio__line span{color:var(--uir-color-graphite);flex:none;font-size:var(--uir-size-small);font-variant-numeric:tabular-nums}.uir-audio__track{background:var(--uir-color-rule-soft);border-radius:2px;height:3px;margin-top:8px;overflow:hidden}.uir-audio__track span{background:var(--uir-color-ink);display:block;height:100%}.uir-media__frame{background:var(--uir-color-sunk);border-radius:var(--uir-radius-md);overflow:hidden;width:100%}.uir-media__frame[data-aspect="9:16"]{max-width:320px}.uir-media__embed{border:0;display:block;height:100%;width:100%}.uir-embed-meta{align-items:center;display:flex;gap:9px;padding:11px 6px 2px}.uir-embed-meta__icon{align-items:center;background:var(--uir-color-sunk);border:1px solid var(--uir-color-rule);border-radius:4px;color:var(--uir-color-graphite);display:inline-flex;flex:none;font-size:9px;font-weight:600;height:16px;justify-content:center;width:16px}.uir-embed-meta__copy{display:flex;flex:1;flex-direction:column;min-width:0}.uir-embed-meta__copy strong{font-size:12.5px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.uir-embed-meta__copy span{color:var(--uir-color-faint);font-family:var(--uir-font-mono);font-size:10.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.uir-embed-meta a{color:var(--uir-color-graphite);flex:none;font-size:var(--uir-size-small);font-weight:500;text-decoration:none}.uir-embed-meta a:hover{color:var(--uir-color-ink)}.uir-field{display:flex;flex-direction:column;gap:5px;max-width:280px}.uir-input,.uir-select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--uir-color-sheet);border:1px solid var(--uir-color-rule-strong);border-radius:var(--uir-radius-md);color:var(--uir-color-ink);font-family:var(--uir-font-body);font-size:var(--uir-size-small);padding:7px 10px;width:100%}.uir-select{background-image:linear-gradient(45deg,transparent 50%,var(--uir-color-graphite) 50%),linear-gradient(135deg,var(--uir-color-graphite) 50%,transparent 50%);background-position:calc(100% - 16px) 50%,calc(100% - 11px) 50%;background-repeat:no-repeat;background-size:5px 5px,5px 5px;padding-right:30px}.uir-select-row{align-items:center;display:flex;gap:10px}.uir-select-row .uir-select{flex:1;min-width:0}.uir-select-clear{background:none;border:0;color:var(--uir-color-graphite);cursor:pointer;font-family:inherit;font-size:12.5px;font-weight:500;padding:4px 2px}.uir-select-clear:hover{color:var(--uir-color-ink)}.uir-input:focus-visible,.uir-select:focus-visible,.uir-button:focus-visible,.uir-tab:focus-visible,.uir-list__item:focus-visible,.uir-table tbody tr:focus-visible,.uir-metric--clickable:focus-visible{outline:2px solid var(--uir-color-focus);outline-offset:2px}.uir-toggle{align-items:center;display:flex;font-size:13.5px;justify-content:space-between;max-width:280px;padding:7px 0}.uir-toggle>button{background:var(--uir-color-rule);border:0;border-radius:11px;cursor:pointer;display:flex;height:22px;padding:2px;transition:background .15s ease;width:36px}.uir-toggle>button>span{background:var(--uir-color-sheet);border-radius:50%;box-shadow:0 1px 2px #0003;display:block;height:18px;transform:translate(0);transition:transform .15s ease;width:18px}.uir-toggle>button[aria-checked=true]{background:var(--uir-color-brand)}.uir-toggle>button[aria-checked=true]>span{transform:translate(14px)}.uir-button{background:var(--uir-color-ink);border:1px solid var(--uir-color-ink);border-radius:var(--uir-radius-md);color:var(--uir-color-sheet);cursor:pointer;font-family:var(--uir-font-body);font-size:var(--uir-size-small);font-weight:500;letter-spacing:0;padding:7px 14px;text-transform:none;transition:background .12s ease,border-color .12s ease}.uir-button:hover:not(:disabled){background:var(--uir-color-brand);border-color:var(--uir-color-brand)}.uir-button--ghost{background:var(--uir-color-sheet);border-color:var(--uir-color-rule-strong);color:var(--uir-color-ink)}.uir-button--ghost:hover:not(:disabled){background:var(--uir-color-brand-soft);border-color:var(--uir-color-brand);color:var(--uir-color-brand)}.uir-button--danger{background:var(--uir-color-sheet);border-color:var(--uir-color-rule);color:var(--uir-color-negative)}.uir-button--danger:hover:not(:disabled){background:var(--uir-color-negative-soft);border-color:var(--uir-color-negative);color:var(--uir-color-negative)}.uir-button--confirm{background:#d92d20;border-color:#d92d20;color:#fff}.uir-button--confirm:hover:not(:disabled){background:var(--uir-color-negative);border-color:var(--uir-color-negative)}.uir-button--small{font-size:var(--uir-size-micro);padding:4px 9px}.uir-button:disabled{cursor:not-allowed;opacity:.45}.uir-disabled-wrap{display:inline-flex;position:relative}.uir-disabled-wrap .uir-reason{background:var(--uir-color-ink);border-radius:var(--uir-radius-md);bottom:calc(100% + 6px);color:var(--uir-color-sheet);font-family:var(--uir-font-body);font-size:var(--uir-size-micro);letter-spacing:0;line-height:1.35;opacity:0;padding:7px 9px;pointer-events:none;position:absolute;right:0;text-transform:none;transition:opacity .11s ease,visibility .11s;visibility:hidden;max-width:260px;width:max-content;z-index:5}.uir-disabled-wrap:hover .uir-reason,.uir-disabled-wrap:focus-within .uir-reason{opacity:1;visibility:visible}.uir-confirm{align-items:center;display:flex;top:0;right:0;bottom:0;left:0;justify-content:center;position:fixed;z-index:60}.uir-confirm__scrim{background:#09090b47;border:0;top:0;right:0;bottom:0;left:0;padding:0;position:absolute}.uir-confirm__panel{background:var(--uir-color-sheet);border-radius:var(--uir-radius-lg);box-shadow:0 12px 32px #0000002e;box-sizing:border-box;padding:18px 18px 14px;position:relative;width:min(280px,calc(100vw - 32px))}.uir-confirm__panel:focus{outline:none}.uir-confirm__panel h2{font-size:14.5px;font-weight:600;margin:0}.uir-confirm__panel p{color:var(--uir-color-muted);font-size:12.5px;line-height:1.5;margin:6px 0 0}.uir-confirm__actions{display:flex;gap:8px;justify-content:flex-end;margin-top:14px}.uir-confirm__actions .uir-button{border-radius:7px;font-size:12.5px;padding:5px 12px}.uir-block[data-arrived] .uir-table tbody tr,.uir-block[data-arrived] .uir-list__item,.uir-block[data-arrived] .uir-poster{animation:uir-rise .34s cubic-bezier(.2,.7,.3,1) backwards;animation-delay:calc(var(--uir-i, 0) * 34ms)}@keyframes uir-rise{0%{opacity:0;transform:translateY(6px)}}@media(prefers-reduced-motion:reduce){.uir-block[data-arrived] .uir-table tbody tr,.uir-block[data-arrived] .uir-list__item,.uir-block[data-arrived] .uir-poster{animation:none}}@keyframes uir-shimmer{0%{background-position:-240px 0}to{background-position:240px 0}}.uir-skeleton{display:flex;flex-direction:column;padding-top:0}.uir-skeleton__bar,.uir-skeleton__value{animation:uir-shimmer var(--uir-motion-shimmer);background:linear-gradient(90deg,var(--uir-shimmer-from) 25%,var(--uir-shimmer-to) 40%,var(--uir-shimmer-from) 60%);background-size:480px 100%}.uir-skeleton__bar{border-radius:5px;height:10px;margin-top:14px}.uir-skeleton__bar:first-child{margin-top:0;width:88px}.uir-skeleton__bar--wide{width:100%}.uir-skeleton__bar--mid{width:62%}.uir-skeleton__bar--narrow{width:34%}.uir-skeleton__value{border-radius:6px;height:28px;margin-top:12px;width:150px}.uir-skeleton__heading{font-size:var(--uir-size-base);font-weight:600;letter-spacing:-.005em;margin:0}.uir-skeleton__label{color:var(--uir-color-graphite);font-size:var(--uir-size-label)}.uir-skeleton__cols{border-bottom:1px solid var(--uir-color-rule);color:var(--uir-color-faint);display:flex;font-size:var(--uir-size-small);gap:var(--uir-space-lg);margin-top:var(--uir-space-md);padding-bottom:var(--uir-space-sm)}.uir-loading-rule{display:none}.uir-block[data-stale=true]{opacity:.45;transition:opacity .14s ease}@keyframes uir-tick{0%{opacity:0;transform:scale(.6)}15%,75%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(1)}}.uir-tick{align-items:center;animation:uir-tick var(--uir-motion-tick);background:var(--uir-color-sheet);border-radius:9px;box-shadow:0 1px 4px #00000024;display:inline-flex;height:18px;justify-content:center;pointer-events:none;position:absolute;right:-7px;top:-7px;width:18px;z-index:3}.uir-tick svg{display:block}.uir-tick polyline{fill:none;stroke:var(--uir-color-tick);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}.uir-narration{animation:uir-fold .45s cubic-bezier(.2,.7,.3,1);color:var(--uir-color-ink);font-size:var(--uir-size-base);line-height:1.6;margin:2px 2px 4px;text-wrap:pretty}@media(prefers-reduced-motion:reduce){.uir-narration{animation:none}}@media(prefers-reduced-motion:reduce){.uir-tick{display:none}}.uir-error{align-items:flex-start;display:flex;gap:11px;min-width:0}.uir-error__mark{align-items:center;background:var(--uir-color-negative-soft);border-radius:9px;color:var(--uir-color-negative);display:inline-flex;flex:none;font-size:var(--uir-size-micro);font-weight:600;height:18px;justify-content:center;line-height:1;margin-top:1px;width:18px}.uir-error__body{display:flex;flex-direction:column;min-width:0}.uir-error__label{color:var(--uir-color-ink);font-size:var(--uir-size-label);font-weight:500}.uir-error__msg{-webkit-box-orient:vertical;-webkit-line-clamp:3;color:var(--uir-color-graphite);display:-webkit-box;font-size:var(--uir-size-small);line-height:1.45;margin-top:3px;overflow:hidden}.uir-error__msg code{background:none;font-family:var(--uir-font-mono);font-size:var(--uir-size-micro);padding:0}.uir-error__retry{align-self:flex-start;background:var(--uir-color-sheet);border:1px solid var(--uir-color-rule);border-radius:var(--uir-radius-sm);color:var(--uir-color-muted);cursor:pointer;font-family:inherit;font-size:var(--uir-size-small);font-weight:500;margin-top:9px;padding:3px 10px}.uir-error__retry:hover{background:var(--uir-color-sunk-strong);color:var(--uir-color-ink)}.uir-chart-frame{margin:0}.uir-chart__legend{display:flex;flex-wrap:wrap;gap:18px;margin:8px 0 10px}.uir-chart__legend-item{align-items:center;display:inline-flex;gap:6px}.uir-chart__swatch{border-radius:4px;height:8px;width:8px}.uir-chart__legend-label{color:var(--uir-color-muted);font-size:var(--uir-size-small)}.uir-chart__legend-value{color:var(--uir-color-ink);font-size:var(--uir-size-small);font-weight:600;font-variant-numeric:tabular-nums}.uir-chart-frame__inner{width:100%}.uir-pie{align-items:center;display:grid;gap:var(--uir-space-xl);grid-template-columns:auto minmax(0,auto);justify-content:start}@media(max-width:460px){.uir-pie{grid-template-columns:minmax(0,1fr);justify-items:center}}.uir-pie__legend{display:flex;flex-direction:column;gap:8px;min-width:0;width:min(340px,100%)}.uir-pie__row{align-items:center;display:grid;gap:var(--uir-space-sm);grid-template-columns:9px minmax(0,1fr) auto auto}.uir-pie__swatch{border-radius:2px;height:9px;width:9px}.uir-pie__label{color:var(--uir-color-ink);font-size:var(--uir-size-small);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.uir-pie__value{font-size:var(--uir-size-small);font-variant-numeric:tabular-nums;font-weight:500}.uir-pie__pct{color:var(--uir-color-faint);font-size:var(--uir-size-small);font-variant-numeric:tabular-nums;min-width:3.4em;text-align:right}@media(prefers-reduced-motion:reduce){.uir-page *,.uir-page *:before,.uir-page *:after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}.uir-loading-rule:after{transform:scaleX(.35)}}.uir-stages{display:flex;flex-wrap:wrap;gap:var(--uir-space-sm);list-style:none;margin:0;padding:0}.uir-stages__stage{align-items:center;background:var(--uir-color-sunk);border-radius:999px;color:var(--uir-color-graphite);display:inline-flex;font-size:var(--uir-size-micro);gap:5px;padding:4px 10px}.uir-stages__stage[data-done]{background:var(--uir-color-positive-soft);color:var(--uir-color-positive)}.uir-stages__stage[data-current]{box-shadow:inset 0 0 0 1px var(--uir-color-brand);color:var(--uir-color-ink)}.uir-stages__mark{font-size:10px;line-height:1}.uir-stages__label{font-weight:500}.uir-stages__state{opacity:.72}.uir-overlay{top:0;right:0;bottom:0;left:0;position:fixed;z-index:40}.uir-overlay__scrim{animation:uir-fade var(--uir-motion-fade);background:var(--uir-overlay-scrim);top:0;right:0;bottom:0;left:0;position:absolute}.uir-overlay__panel{animation:uir-slide-in var(--uir-motion-slide-in);background:var(--uir-color-sheet);border-left:1px solid var(--uir-color-rule);bottom:0;box-shadow:var(--uir-overlay-shadow);box-sizing:border-box;display:flex;flex-direction:column;max-width:100vw;position:absolute;right:0;top:0;width:var(--uir-overlay-width)}.uir-overlay__panel:focus{outline:none}.uir-overlay__head{align-items:center;display:flex;flex:none;justify-content:space-between;padding:18px 22px 14px}.uir-overlay__title{color:var(--uir-color-ink);font-size:15px;font-weight:600;letter-spacing:-.01em}.uir-overlay__close{align-items:center;background:var(--uir-color-sunk);border:none;border-radius:14px;cursor:pointer;display:inline-flex;height:28px;justify-content:center;width:28px}.uir-overlay__close:hover{background:var(--uir-color-sunk-strong)}.uir-overlay__close line{stroke:var(--uir-color-muted);stroke-linecap:round;stroke-width:1.5}.uir-overlay__body{flex:1;overflow-y:auto;padding:2px 22px 20px}.uir-page--nested{padding:0}.uir-page--nested .uir-shell{gap:var(--uir-space-md)}@keyframes uir-slide-in{0%{transform:translate(100%)}}@keyframes uir-fade{0%{opacity:0}}@media(prefers-reduced-motion:reduce){.uir-overlay__panel,.uir-overlay__scrim{animation:none}}@media(prefers-reduced-motion:reduce){.uir-progress__fill{animation:none;transition:none}.uir-block[data-tile]{animation:none}}
|