@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 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}}