Files
PaperClipAI/ui/src/index.css
T
DottaandPaperclip aa58fa1073 perf(ui): restore fast Coordinator task and inbox loading (#18323)
## Thinking Path

> - Paperclip lets people manage work done by agents.
> - Tasks, Inbox, and Needs You must stay usable as work history grows.
> - The Coordinator views bypassed some existing collection
optimizations.
> - Navigation fetched complete decision cards for a count, and Tasks
mounted every loaded row.
> - This PR uses the canonical attention rules for small count and
task-ID responses. It also restores bounded rendering and the optimized
Inbox selection.
> - The benefit is less data and less initial rendering work without a
second definition of pending work.

## Linked Issues or Issue Description

**What happened?**

Coordinator navigation fetched the full attention feed on every cold
collection visit. Tasks rendered the entire loaded collection.
Coordinator could also select the old Inbox loader when Streamlined UI
was off.

**Expected behavior**

Fetch only the projection needed by each surface. Start with a bounded
set of rows. Keep complete task membership, accurate counts, current
permissions, and stable loaded rows.

**Steps to reproduce**

1. Enable Coordinator and open Tasks or Inbox with an empty browser
query cache.
2. Observe a complete attention-card download for the navigation badge.
3. Open a large task list and observe that every loaded row mounts at
once.
4. Disable Streamlined UI while keeping Coordinator enabled, then open
Mine and observe three overlapping compact task reads.

Related work: #17756, #3744, #17983, and #18312.

## What Changed

- Add board-only attention count and complete task-ID projections. Reuse
canonical company, privacy, addressee, deduplication, dismissal, snooze,
and archive rules. Skip card bodies, plan/image reads, and presentation
roster lookup.
- Share the small count read across desktop and mobile navigation. Reuse
the page-owned response on Needs You and Needs attention. Keep the
newest known count through navigation, including zero.
- Use the task-ID index for Needs attention and keep task row hydration
in batches of at most 100.
- Render 100 task rows initially, across the whole expanded tree. Reveal
150 more at a time through scrolling or a button. Keep existing rows
through refresh and append. Defer offscreen leaf layout with shared
tokens.
- Keep Coordinator on the optimized Inbox when Streamlined UI is off.
Use index reads for expanded project recents.
- Add real-database, component, and production-build browser regression
checks. Update the access and feature maps.

## Verification

- All required CI checks pass on
`1c68ac8957490a6c8585ba0939355f536c8120bf`: typecheck, build,
server/workspace tests, serialized database suites, and eight E2E
shards. Greptile is **5/5**, with its row-retention finding fixed and
the review thread resolved. Storybook checks were intentionally skipped;
Paperclip Review completed neutral.
- Workspace typecheck and production build passed locally. The final
rebased source also passed UI typecheck and production build. Token
gates and map checks passed.
- The attention service suite passed all 34 tests on disposable
PostgreSQL. Final targeted checks cover compact projections,
complete-set counts, quality approvals, snoozes, private tasks, and
board/company access. Database response capture confirms compact
projections do not read plans or images. All 31 OpenAPI tests passed
after registering both endpoint contracts.
- Six production-build browser journeys passed: desktop, mobile,
Coordinator Inbox with Streamlined UI off, Needs You, Needs attention,
and an older server page followed by task open/Back. They verify the
initial 100-row budget, scrolling, retained DOM identity/height, one
owner per attention read, complete task membership, 220 retained rows on
Back, and less than 2 px of return-position difference.
- The row-budget and Inbox regressions fail against original source and
pass with this change. The final three affected UI suites passed 40
tests, and the broader Inbox/IssuesList suites passed 101 tests
serially.

### Controlled production-build comparison

Baseline `b31ec0a2dc079e32272a8f618425fe92b68bbaec` versus PR head
`1c68ac8957490a6c8585ba0939355f536c8120bf`. Isolated baseline asset
`index-CS9uPxMx.js`; candidate asset `index-BxJeTQ30.js`. Both UI builds
used the same warm disposable API server and database, with 220 tasks
and 110 pending board approvals. Chromium **151.0.7922.34**, 1440 × 900
viewport, **4× CPU throttle**, loopback with no added network latency,
alternating baseline/candidate order. Cold cases use a new browser
context with network cache disabled; warm cases navigate within the same
context. Other host workloads are uncontrolled.

Readiness requires the destination collection's first visible task row,
then two animation frames. Inbox readiness waits for its canonical Mine
route so previous-page rows cannot satisfy the measurement. Five paired
samples per case; cold Inbox has a second five-pair confirmation wave
because the first result was slower. Times below are **milliseconds**;
parentheses show min–max. Paired savings are the median of individual
before-minus-after pairs, not the difference between medians.

| Journey | Pairs | Baseline median (range) | Candidate median (range) |
Median paired saving | Faster pairs |
| --- | ---: | ---: | ---: | ---: | ---: |
| Cold Tasks | 5 | 2752 (2542–3025) | 2300 (2275–2524) | 328 | 5/5 |
| Cold Inbox | 10 | 2536 (2283–2927) | 2582 (2117–2856) | 37 | 5/10 |
| Warm Tasks navigation | 5 | 2054 (1826–2479) | 1448 (1419–1548) | 635
| 5/5 |
| Warm Inbox navigation | 5 | 2020 (1485–2254) | 1568 (1289–1848) | 350
| 5/5 |
| Task → Back to Tasks | 5 | 1747 (1609–1948) | 1327 (1238–1355) | 472 |
5/5 |

Cold Tasks, warm Tasks, warm Inbox, and Back improved in every measured
pair. **Cold Inbox latency is inconclusive:** the two waves pointed in
opposite directions, with the candidate faster in 5/10 pairs. This PR
claims less badge work there, not a proven cold Inbox latency
improvement.

The canonical badge response shrank from **200,039 to 359 decoded
bytes** (encoded HTTP transfer: **8,125 to 598 bytes**). The compact
response also skips card payloads, plans, images, and presentation-only
roster work. These are measured mechanism reductions, separate from
end-to-end latency. Needs You still owns its full card feed; Needs
attention owns its complete task-ID projection. Reproduction setup and
assertions are in `tests/perf/collections/README.md`.

<details>
<summary>Individual paired readiness measurements (ms)</summary>

| Journey | Pair | Baseline | Candidate |
| --- | ---: | ---: | ---: |
| Cold Tasks | 1 | 3025 | 2283 |
| Cold Tasks | 2 | 2542 | 2275 |
| Cold Tasks | 3 | 2752 | 2424 |
| Cold Tasks | 4 | 2783 | 2300 |
| Cold Tasks | 5 | 2641 | 2524 |
| Cold Inbox | 1 | 2927 | 2702 |
| Cold Inbox | 2 | 2422 | 2629 |
| Cold Inbox | 3 | 2409 | 2606 |
| Cold Inbox | 4 | 2621 | 2769 |
| Cold Inbox | 5 | 2539 | 2856 |
| Cold Inbox | 6 | 2283 | 2117 |
| Cold Inbox | 7 | 2466 | 2558 |
| Cold Inbox | 8 | 2674 | 2286 |
| Cold Inbox | 9 | 2696 | 2389 |
| Cold Inbox | 10 | 2533 | 2183 |
| Warm Tasks navigation | 1 | 2479 | 1423 |
| Warm Tasks navigation | 2 | 2228 | 1548 |
| Warm Tasks navigation | 3 | 2054 | 1419 |
| Warm Tasks navigation | 4 | 1826 | 1448 |
| Warm Tasks navigation | 5 | 1954 | 1518 |
| Warm Inbox navigation | 1 | 2020 | 1661 |
| Warm Inbox navigation | 2 | 2198 | 1848 |
| Warm Inbox navigation | 3 | 1733 | 1497 |
| Warm Inbox navigation | 4 | 2254 | 1568 |
| Warm Inbox navigation | 5 | 1485 | 1289 |
| Task → Back to Tasks | 1 | 1721 | 1330 |
| Task → Back to Tasks | 2 | 1747 | 1275 |
| Task → Back to Tasks | 3 | 1948 | 1355 |
| Task → Back to Tasks | 4 | 1609 | 1327 |
| Task → Back to Tasks | 5 | 1790 | 1238 |

</details>

### Failed attempts and limits

- The local full repository command hit embedded PostgreSQL startup
failures and 16 failures in the untouched native session executor suite;
it was stopped. The full local suite is not claimed green. CI on the
final revision provides the broad test result.
- A concurrent local UI run hit timing failures under host contention.
Inbox/IssuesList subsequently passed all 101 tests serially. No tests
were weakened to address these timeouts.
- CI caught two integration gaps introduced during this work: missing
OpenAPI entries for the new endpoints, and an attention fixture
incompatible with the routine filters added on master. Both were fixed;
the affected 31 OpenAPI and 40 UI tests pass.
- The final revision's first CI attempt failed two untouched tests:
scheduled-retry batch cancellation and the tool-access audience/archive
race. Both failed jobs passed when rerun without changing the revision.
Their original failed attempts remain visible in [the workflow
history](https://github.com/paperclipai/paperclip/actions/runs/41083379875).
- Browser evidence uses synthetic disposable data. This PR is not
deployed staging or production verification.

## Risks

- New attention sources must preserve canonical eligibility. Both
projections call the existing collector, so they share its permissions
and complete-set behavior.
- The count and task-ID caches must not stand in for full decision
cards. Each projection has its own key under the existing attention
invalidation prefix.
- A row budget can omit descendants if applied per parent. The tests
cover one global budget across a large expanded tree, refresh, append,
reset, and incremental loading.
- Local browser evidence uses synthetic data and is not staging
deployment proof.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use, and code
execution. The exact backend model deployment ID and context window were
not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (affected suites; broad-run
limitations are listed above)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-30 11:58:53 -05:00

5092 lines
239 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@import "./motion-tokens.css";
@import "tailwindcss";
@plugin "@tailwindcss/typography";
/* offline-recovery:start */
.paperclip-offline {
--offline-background: #171716;
--offline-foreground: #fafafa;
--offline-muted: #a3a3a3;
--offline-control: #262626;
--offline-control-hover: #333333;
--offline-border: #404040;
--offline-focus: #6995ff;
--offline-font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--offline-space: 1.5rem;
--offline-identity-gap: calc(var(--offline-space) * 0.8);
--offline-message-gap: calc(var(--offline-space) * 0.75);
--offline-copy-gap: 0.75rem;
--offline-title-size: 1.75rem;
--offline-body-size: 0.9375rem;
--offline-character-size: 9.6rem;
--offline-character-outer-start: #252525;
--offline-character-outer-end: #686c6e;
--offline-character-inner-start: #292929;
--offline-character-inner-end: #727679;
--offline-character-face: #333333;
--offline-character-tear: #f6fdff;
--offline-content-width: 32rem;
--offline-control-height: 2.75rem;
--offline-radius: 0.5rem;
--offline-stroke: 1px;
--offline-focus-width: 2px;
--offline-focus-offset: 4px;
--offline-weight: 600;
--offline-leading: 1.5;
--offline-heading-leading: 1.25;
--offline-tracking: -0.025em;
color-scheme: dark;
box-sizing: border-box;
margin: 0;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
padding: var(--offline-space);
background: var(--offline-background);
color: var(--offline-foreground);
font-family: var(--offline-font);
font-size: var(--offline-body-size);
line-height: var(--offline-leading);
text-align: center;
}
.paperclip-offline main {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--offline-space);
width: 100%;
max-width: var(--offline-content-width);
}
.paperclip-offline-message,
.paperclip-offline-identity {
display: flex;
flex-direction: column;
align-items: center;
}
.paperclip-offline-message {
gap: var(--offline-message-gap);
}
.paperclip-offline-identity {
gap: var(--offline-identity-gap);
}
.paperclip-offline-character {
width: var(--offline-character-size);
height: var(--offline-character-size);
}
.paperclip-offline h1 {
margin: 0;
font-size: var(--offline-title-size);
font-weight: var(--offline-weight);
line-height: var(--offline-heading-leading);
letter-spacing: var(--offline-tracking);
text-wrap: balance;
}
.paperclip-offline p {
margin: 0;
color: var(--offline-muted);
text-wrap: pretty;
}
.paperclip-offline button {
min-height: var(--offline-control-height);
padding: var(--offline-copy-gap) var(--offline-space);
border: var(--offline-stroke) solid var(--offline-border);
border-radius: var(--offline-radius);
background: var(--offline-control);
color: inherit;
font: inherit;
font-weight: var(--offline-weight);
cursor: pointer;
}
.paperclip-offline button:hover {
background: var(--offline-control-hover);
}
.paperclip-offline button:focus-visible {
outline: var(--offline-focus-width) solid var(--offline-focus);
outline-offset: var(--offline-focus-offset);
}
/* offline-recovery:end */
@font-face {
font-family: "InterVariable";
src: url("/fonts/InterVariable.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 100 900;
}
@font-face {
font-family: "InterVariable";
src: url("/fonts/InterVariable-Italic.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 100 900;
}
@font-face {
font-family: "Paper Mono";
src: url("/fonts/PaperMono-Variable.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 100 800;
}
@custom-variant dark (&:is(.dark *));
/* Shared file tree: preserve desktop indentation and leave room for labels on
narrow screens, including deeply nested skill packages. */
.file-tree-row {
padding-inline-start: min(calc(var(--file-tree-indent-base) + var(--file-tree-depth) * var(--file-tree-indent-step)), var(--file-tree-indent-max));
}
@media (width < 40rem) {
.file-tree-row {
--file-tree-indent-step: calc(var(--spacing) * 3);
--file-tree-indent-max: 25%;
}
}
/* One control column plus its gap: a child's checkbox aligns with its parent's
icon. Keep this rhythm at every depth and viewport width. */
.file-tree-row-explorer {
--file-tree-indent-step: calc(var(--spacing) * 6);
padding-inline-start: calc(var(--file-tree-indent-base) + var(--file-tree-depth) * var(--file-tree-indent-step));
}
@theme inline {
--font-sans: "InterVariable", "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-highlight: var(--muted-highlight);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
/* Multiplicative radius ladder (modern shadcn/Tailwind v4 mapping):
sm/md/lg/xl/2xl/3xl/4xl = 0.6/0.8/1.0/1.4/1.8/2.2/2.6 x the single
--radius knob (base anchor 0.5rem = 8px; rounded-lg IS the anchor).
Replaces the additive +/-px ladder (DECISION-SHEET B4 → Run 3 review):
retune the whole app's corner language by editing --radius alone. */
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) * 1.4);
--font-heading: var(--font-sans);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
}
/* Keep this font runtime-resolved: the proposal/sandbox theme substitutes
Paper Mono for every shared font-mono consumer, including portaled content. */
@theme {
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
:root {
color-scheme: light;
--font-paper-mono: "Paper Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
--file-tree-indent-base: calc(var(--spacing) * 2);
--file-tree-indent-step: calc(var(--spacing) * 6);
--file-tree-indent-max: 40%;
--native-select-caret-half: calc(var(--spacing) * 1.5);
--native-select-caret-inset: calc(var(--spacing) * 3);
--native-select-caret-stroke: 1.5px;
--native-select-end-padding: calc(var(--spacing) * 9);
--composer-effort-track-height: calc(var(--spacing) * 3);
--composer-effort-thumb-size: calc(var(--spacing) * 7);
--composer-effort-hover-scale: 1.08;
--composer-effort-press-scale: 1.16;
--composer-effort-halo: 0 0 0 calc(var(--spacing) * 1.5) color-mix(in oklab, var(--tc-mode-ask) 14%, transparent);
--radius: 0.5rem;
--background: oklch(1 0 0);
--background-alt: var(--muted);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--collection-filter-active: var(--color-blue-600);
--collection-filter-badge: var(--color-blue-600);
--collection-filter-badge-foreground: var(--color-white);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-highlight: var(--muted);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--focus-ring-stroke-width: 1px;
--focus-ring-halo-width: 3px;
--focus-ring-halo: color-mix(in srgb, var(--ring) 20%, transparent);
--focus-ring-shadow: 0 0 0 var(--focus-ring-halo-width) var(--focus-ring-halo);
--scrollbar-thumb: oklch(0.7 0 0);
--scrollbar-thumb-hover: oklch(0.6 0 0);
--scrollbar-size: 8px;
--scrollbar-radius: 999px;
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--sidebar: oklch(0.985 0 0);
--primary-sidebar-background: color-mix(in srgb, var(--border) 50%, var(--background));
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.88 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--chip-match-title-bg: oklch(0.97 0.02 265);
--chip-match-title-fg: oklch(0.4 0.13 265);
--chip-match-title-border: oklch(0.85 0.05 265);
--chip-match-comment-bg: oklch(0.97 0.02 145);
--chip-match-comment-fg: oklch(0.4 0.10 145);
--chip-match-comment-border: oklch(0.85 0.05 145);
--chip-match-document-bg: oklch(0.97 0.02 295);
--chip-match-document-fg: oklch(0.4 0.10 295);
--chip-match-document-border: oklch(0.85 0.05 295);
--chip-match-identifier-bg: var(--muted);
--chip-match-identifier-fg: var(--muted-foreground);
--chip-match-identifier-border: var(--border);
--paperclip-doc-annotation-highlight-source: oklch(0.78 0.16 89);
--paperclip-doc-annotation-highlight-open: color-mix(in srgb, var(--paperclip-doc-annotation-highlight-source) 58%, var(--background));
--paperclip-doc-annotation-highlight-focused: color-mix(in srgb, var(--paperclip-doc-annotation-highlight-source) 60%, var(--background));
--paperclip-doc-annotation-highlight-stale: color-mix(in srgb, var(--paperclip-doc-annotation-highlight-source) 48%, var(--background));
--paperclip-doc-annotation-highlight-resolved: color-mix(in srgb, var(--paperclip-doc-annotation-highlight-source) 28%, var(--background));
--paperclip-doc-annotation-highlight-x-outset: 0.25rem;
--paperclip-doc-annotation-highlight-y-outset: 0.125rem;
--paperclip-doc-annotation-highlight-radius: 0.1875rem;
/* Brand agent-capsule gradient stops (paperclip.ing/brand/).
"The capsule is the agent" — each agent gets a 2-stop gradient,
--agent-Na (top) → --agent-Nb (bottom). Brand-fixed hex, identical
across themes. Indices 1–10; consumed by AgentCapsule. */
--agent-1a: #f7cfdc;
--agent-1b: #1f7a3a;
--agent-2a: #c9a9e8;
--agent-2b: #ee79a1;
--agent-3a: #28164b;
--agent-3b: #7a1530;
--agent-4a: #f3e6c4;
--agent-4b: #e3a21a;
--agent-5a: #1f4dd6;
--agent-5b: #3aa35c;
--agent-6a: #e94b27;
--agent-6b: #5a1122;
--agent-7a: #7eb6e3;
--agent-7b: #ee79a1;
--agent-8a: #9ce8a7;
--agent-8b: #bd7ff0;
--agent-9a: #f3b49e;
--agent-9b: #1f4ed4;
--agent-10a: #f2d95f;
--agent-10b: #4fbcba;
/* Onboarding mascot artwork (brand-fixed, theme-independent). Keeping the
SVG palette in the token layer preserves the source asset while allowing
components to remain token-only. */
--pill-guy-eye: #060606;
--pill-guy-dormant-top: #626262;
--pill-guy-dormant-bottom: #101010;
--pill-guy-tuft: #2d200d;
--pill-guy-alive-top: #3028aa;
--pill-guy-alive-bottom: #ff0000;
/* Status colors — one base hue per status, consumed by the agent / task
status chips and the task status icons. Mode-independent: the `.status-chip`,
`.status-fill` and inline icon helpers derive the light vs dark
fill/text/border from these via color-mix, so a single hue covers both
modes. Values mirror the PAP-75 brand palette (gray inert, blue liveness,
amber queued, violet review, green done, red blocked). */
--status-agent-idle: #a8aeb2;
--status-agent-running: #2563eb;
--status-agent-paused: #f59e0b;
--status-agent-error: #dc2626;
/* Dashboard agent card: translucent running treatment from the Paper design. */
--dashboard-run-accent: oklch(62.3% 0.214 259.815);
--dashboard-run-background: color-mix(in oklab, var(--dashboard-run-accent) 4%, transparent);
--dashboard-run-border: color-mix(in oklab, var(--dashboard-run-accent) 25%, transparent);
--dashboard-list-leading-width: calc(var(--spacing) * 6);
--dashboard-list-id-width: calc(var(--spacing) * 20);
--dashboard-list-time-width: calc(var(--spacing) * 16);
--dashboard-task-list-columns: var(--dashboard-list-leading-width) minmax(0, 1fr) calc(var(--spacing) * 36) var(--dashboard-list-id-width) var(--dashboard-list-time-width);
--dashboard-activity-list-columns: var(--dashboard-list-leading-width) minmax(0, 1fr) var(--dashboard-list-id-width) var(--dashboard-list-time-width);
--status-task-backlog: #a8aeb2;
--status-task-todo: #f59e0b;
--status-task-in_progress: #2563eb;
--status-task-in_review: #7c3aed;
--status-task-done: #22c55e;
--status-task-blocked: #dc2626;
--status-task-cancelled: #a8aeb2;
/* Task status ICON hues (PAP-238 3b) — AA-tuned (every glyph ≥ 3:1 against
its surface, WCAG 2.1 AA, both modes). Distinct from the
`--status-task-*` chip base hues: a status chip carries its own bg/text
tint, but a bare glyph sitting next to text on the page needs a stronger
hue to clear 3:1. Where the chip base already passes (blue liveness, red
blocked, light violet review) the icon var simply re-points at it; the
gray / amber / green statuses are pinned to the board-approved AA hexes.
`in_queue` is the BLOCKED shape recoloured to the in_progress blue
(replaces the bespoke teal "covered" state). Light values here; the
`.dark` block below overrides the hues that need a mode-specific value. */
--sz-protocol-notice-icon: calc(var(--spacing) * 3.5);
--status-task-icon-backlog: #52585d; /* gray darkened — 7.21:1 on paper */
--status-task-icon-todo: #cc7a00; /* amber darkened — clears 3:1 */
--status-task-icon-in_progress: var(--color-blue-600); /* matches nav dots and live labels */
--status-task-icon-in_review: var(--status-task-in_review); /* #7c3aed (light) */
--status-task-icon-done: #16a34a; /* green darkened — clears 3:1 */
--status-task-icon-blocked: var(--status-task-blocked); /* #dc2626 both modes */
--status-task-icon-cancelled: #52585d;
--status-task-icon-in_queue: var(--status-task-icon-in_progress); /* blocked shape, same mode-aware blue */
--folder-color-indigo: #6366f1;
--folder-color-violet: #8b5cf6;
--folder-color-emerald: #10b981;
--folder-color-cyan: #06b6d4;
--folder-color-amber: #f59e0b;
--folder-color-slate: #64748b;
--gtc-folder-row: 1rem minmax(0, 1fr) auto;
--gtc-folder-row-actions: 1rem minmax(0, 1fr) auto auto;
--sz-folder-rail: 13.25rem;
--sz-folder-sheet-max: 80dvh;
--sz-tweak-panel-max: 70vh; /* Task Chat Redesign dev tweak panel scroll cap. */
/* New-task dialog geometry inputs and gap tokens. The derived top/height
calculations live on the dialog element so runtime Visual Viewport
overrides participate in computed-value resolution. */
--new-issue-visual-viewport-height: 100dvh;
--new-issue-visual-viewport-offset-top: 0px;
--new-issue-dialog-top-gap: max(1rem, env(safe-area-inset-top));
--new-issue-dialog-bottom-gap: max(1rem, env(safe-area-inset-bottom));
/* Connection banner offset: clear the notch/status bar under viewport-fit=cover. */
--connection-banner-top: calc(0.5rem + env(safe-area-inset-top, 0px));
--mobile-entity-picker-visual-viewport-height: 100dvh;
--mobile-entity-picker-backdrop: color-mix(in oklab, var(--foreground) 50%, transparent);
/* Deliberate scroll distances (CSS pixels) and shared nav/composer motion. */
--mobile-nav-scroll-top-zone: 24px;
--mobile-nav-scroll-hide-distance: 32px;
--mobile-nav-scroll-show-distance: 16px;
--motion-mobile-nav-enter: var(--motion-duration-slow);
--motion-mobile-nav-exit: var(--motion-duration-base);
/* ────────────────────────────────────────────────────────────────────────
Motion tokens — chat-style task thread (default; classic legacy view sits
behind enableClassicTaskInterface).
These are the single source of truth for the redesigned thread's motion.
Kept in :root (NOT @theme inline) on purpose: @theme inline bakes literals
at build time, so the dev tweak panel — which writes these live via
element.style.setProperty — could not move a baked value. Rationale for
each transition point lives in DESIGN.md; VALUES here are tunable
placeholders the human tunes live via the tweak panel, then pastes back.
Reuses the two house easing curves already used across the app. Any new
motion in the redesigned thread MUST reference one of these tokens — no
hardcoded ms / cubic-bezier in components (enforced by a check script).
──────────────────────────────────────────────────────────────────────── */
/* Shared easing and instant/fast duration primitives are in the imported
motion-tokens.css so JavaScript motion uses the same defaults.
--motion-ease-out is declared by the decision/quicklook block below. */
/* Duration primitives (tunable) */
--motion-duration-base: 240ms;
--motion-duration-slow: 360ms;
--motion-duration-deliberate: 520ms;
/* State/component-scoped — grouped for the tweak panel (tunable) */
--motion-marker-enter: var(--motion-duration-base);
--motion-bubble-enter: var(--motion-duration-fast);
--motion-cot-line-stagger: 40ms;
--motion-cot-collapse: var(--motion-duration-base);
--motion-tool-enter: var(--motion-duration-base);
--motion-diff-reveal: var(--motion-duration-base);
--motion-status-enter: var(--motion-duration-fast);
--motion-status-exit: var(--motion-duration-fast);
--motion-approval-pulse: 1.6s;
--motion-plan-entry-stagger: 40ms;
--motion-plan-check: var(--motion-duration-fast);
--motion-question-page-enter: var(--motion-duration-instant);
--motion-count-tween: var(--motion-duration-slow);
--motion-streaming-cursor-blink: 1.1s;
/* Synthetic thread playback cadence; no provider or runtime is involved. */
--motion-sandbox-stream-tick: 40ms;
--motion-sandbox-word-tick: 60ms;
--motion-conversation-word-fade: 320ms;
--conversation-word-start-opacity: 0.08;
--motion-conversation-tool-shimmer: 2.5s;
--motion-sandbox-playback-step: 4s;
--motion-turn-fold: 380ms; /* finished-turn activity folding into its summary line */
--motion-conversation-handoff-fade: 180ms;
--motion-conversation-handoff-fold: 480ms;
--motion-turn-meta: var(--motion-duration-fast); /* finished-turn hover metadata reveal */
--motion-line-scroll: 240ms; /* completed interstitial line sliding out of the 1lh live-line viewport */
--motion-interstitial-dwell: 4000ms; /* interstitial update hold: minimum before the next swaps in, and how long a finished update sits before sliding away */
--motion-scroll-pill-enter: 200ms; /* scroll-to-latest pill entry */
--motion-scroll-pill-exit: 400ms; /* scroll-to-latest pill exit */
--motion-scrollbar-idle-delay: 600ms; /* keep the task-thread thumb visible briefly after scrolling settles */
--motion-ease-scroll-pill-in: cubic-bezier(0.23, 1, 0.32, 1);
--motion-ease-scroll-pill-out: cubic-bezier(0.7, 0, 0.84, 0);
--motion-pane-glide: 320ms; /* properties-pane maximize/restore glide */
--motion-pane-reveal: var(--motion-duration-slow);
--motion-ease-pane-reveal: var(--motion-ease-out-expo);
--motion-pane-width-reset: 200ms;
--motion-ease-pane-width-reset: ease-in-out;
--motion-side-panel-tab: var(--motion-duration-fast);
/* Portable side-panel geometry and hierarchy. These stay centralized so
task, project, agent, and future hosts render the same Codex-like chrome. */
--side-panel-header-height: var(--sz-60px);
--side-panel-tab-height: var(--sz-30px);
--side-panel-mobile-tabs-width: min(calc(100vw - var(--spacing) * 4), 24rem);
--side-panel-mobile-tabs-max-height: 60dvh;
--side-panel-streamlined-tab-min-width: var(--sz-100px);
--side-panel-streamlined-tab-max-width: 125px;
--side-panel-streamlined-tab-collapse-floor: var(--sz-48px);
--side-panel-streamlined-tab-inline-space: calc(var(--spacing) * 1.5);
--side-panel-streamlined-tab-padding-left: calc(var(--spacing) * 1.5 + var(--sz-4px));
--side-panel-tab-edge-fade-width: var(--sz-32px);
--side-panel-tab-label-max-width: var(--sz-100px);
--side-panel-tab-label-expanded-max-width: calc(var(--side-panel-tab-label-max-width) + var(--sz-18px));
--side-panel-tab-label-fade-width: var(--sz-32px);
--sidebar-nav-action-fade-width: calc(var(--spacing) * 4);
--side-panel-tab-radius: var(--radius-xl);
--side-panel-control-radius: var(--radius-xl);
--side-panel-launcher-width: var(--sz-320px);
--side-panel-bg: var(--background);
--side-panel-fg: var(--foreground);
--streamlined-task-detail-bg: var(--background);
--side-panel-tab-active-bg: color-mix(in oklab, var(--accent) 88%, var(--background));
--side-panel-tab-hover-bg: color-mix(in oklab, var(--accent) 58%, transparent);
--side-panel-streamlined-tab-hover-bg: oklch(0.9675 0 0);
--side-panel-streamlined-tab-active-bg: oklch(0.9581 0 0);
--radius-task-composer: 16px;
/* Artifacts V1 (doc/plans/2026-10-22-artifacts-mvp-v1.md): the artifact
directions lab's measurements, from the PCLP-Core mocks. Light values
here; .dark overrides the fills and shadows. */
--artifact-font-mono: var(--font-paper-mono);
--artifact-paper: #fafafa; /* the sheet and the print's border, both themes */
--artifact-paper-ink: #262626; /* a preview sheet's title */
--artifact-ink: #333333; /* an icon sheet's type label, the play disc */
--artifact-ink-faint: #d2d2d2; /* 20% ink over the paper, kept opaque so stroked glyphs do not darken where they overlap */
--artifact-sheet-radius: 12px;
--artifact-sheet-stroke: rgba(26, 26, 26, 0.1); /* 1px inner stroke on sheets and backings */
--artifact-sheet-inset-stroke: inset 0 0 0 1px var(--artifact-sheet-stroke);
--artifact-print-radius: 6px;
--artifact-shadow-sheet: drop-shadow(0 3.406px 10px rgba(26, 26, 26, 0.0924));
--artifact-shadow-print: 0 3.406px 20px rgba(26, 26, 26, 0.0924);
--artifact-sheet-title-size: 15.248px;
--artifact-sheet-title-leading: 18px;
--artifact-sheet-title-tracking: -0.6172px;
--artifact-icon-label-size: 24px;
--artifact-icon-label-leading: 54px;
--artifact-grid-min: 330px;
--artifact-grid: repeat(auto-fill, minmax(min(100%, var(--artifact-grid-min)), 1fr)); /* one column still fits a 320px phone */
--artifact-frame-h: 254px;
--artifact-tile-h: 370px;
--artifact-tile-radius: 12px;
--artifact-tile-hover: var(--accent);
--artifact-pill: var(--muted);
--artifact-pill-hover: var(--border); /* the light hover fill is --muted's value, so the pill steps down while hovered */
--artifact-pill-ink: var(--foreground);
--artifact-row: var(--muted);
--artifact-row-hover: #eeeeee;
--artifact-row-radius: 6.4px;
--artifact-row-gap: 12px;
--artifact-row-ink: #fafafa; /* the type label and glyph on the row's document icon */
--artifact-row-icon-label-size: 11px;
--artifact-row-icon-label-leading: 15px;
--artifact-play-scrim: rgba(0, 0, 0, 0.55);
--artifact-hover-ease: 300ms cubic-bezier(0.3, 0, 0.1, 1);
--tc-interaction-card-max-h: 50dvh;
--shadow-task-composer: 0 2px 8px -2px color-mix(in oklab, var(--foreground) 7%, transparent),
0 12px 32px -8px color-mix(in oklab, var(--foreground) 12%, transparent);
--profile-popover-width: 248px;
--profile-popover-header-height: 76px;
--profile-popover-row-height: 38px;
--profile-popover-row-gap: 9px;
--profile-popover-label-line-height: 18px;
--profile-popover-meta-line-height: 15px;
--shadow-profile-popover: 0 18px 45px oklch(0 0 0 / 18%);
--organization-popover-width: 248px;
--organization-popover-header-height: 48px;
--organization-popover-company-row-height: 44px;
--organization-popover-action-row-height: 38px;
--organization-popover-avatar-size: 30px;
--organization-popover-row-gap: 9px;
--organization-popover-name-line-height: 17px;
--organization-popover-prefix-line-height: 13px;
--organization-popover-action-line-height: 18px;
/* Composer mode-chip hues (v5–v7 decisions): agent + plan reuse the task
status hues; ask is the v5 blue. Consumed as `--sc` seeds through the
.status-chip color-mix recipe. */
--tc-mode-agent: var(--status-task-backlog);
--tc-mode-plan: var(--status-task-todo);
--tc-mode-ask: #3b82f6;
/* Agent-comment comparison fill used by TaskChatLab. The chosen production
treatment sits directly on the page surface; this token keeps the darker
card available as an explicit lab alternative. */
--bubble-agent: oklch(0.97 0 0);
--bubble-agent-card-padding-inline: 0.875rem;
/* Redesigned thread viewport bound: lets the thread own its scroll (auto-
follow + pinned composer) inside the page flow. The issue header lives
INSIDE the scroll viewport (it scrolls away with the messages), so the
only chrome outside the thread is the top bar plus page padding ≈ 8rem —
this is what keeps the composer pinned near the viewport bottom. */
--tc-thread-max-h: calc(100dvh - 8rem);
/* Redesigned chat-shell column cap (phase 2c): one token caps the page
wrapper, thread body, and pinned composer so they widen in lockstep. */
--tc-shell-max-w: 60rem;
--sz-66px: 66px;
/* Active-turn status island and its checklist overlay. */
--sz-turn-status-island: min(100%, 42rem);
--sz-turn-status-popover: min(32rem, calc(100vw - 2rem));
/* Mobile pinned-composer bottom offset (PAP-495). The visible-nav offset
leaves 6px below the dock; its pb-1 adds 4px for a 10px card-to-nav gap.
Desktop and the classic thread use the safe-area default. */
--tc-composer-bottom: var(--sz-calc-8);
--mobile-chat-viewport-height: 100dvh;
--tc-composer-editor-max-height: min(var(--sz-28dvh), calc(var(--mobile-chat-viewport-height) * 0.4));
--tc-composer-visible-nav-offset: calc(4rem + var(--spacing) * 1.5 + var(--sz-safe-bottom));
/* With the dock's pb-1, leave 12px below the card when the nav hides. */
--tc-composer-hidden-nav-offset: calc(var(--sz-safe-bottom) + var(--spacing) * 2);
}
.dark {
--background: oklch(0.205 0 0);
--foreground: oklch(0.985 0 0);
--artifact-shadow-sheet: drop-shadow(0 3.406px 10px rgba(26, 26, 26, 0.18));
--artifact-shadow-print: 0 3.406px 20px rgba(26, 26, 26, 0.18);
--artifact-tile-hover: #202020;
--artifact-pill: #333333;
--artifact-pill-hover: #333333;
--artifact-row: #262626;
--artifact-row-hover: #2c2c2c;
--side-panel-bg: var(--background);
--side-panel-fg: oklch(0.985 0 0);
--streamlined-task-detail-bg: var(--background);
--side-panel-streamlined-tab-hover-bg: var(--side-panel-tab-hover-bg);
--side-panel-streamlined-tab-active-bg: color-mix(in oklab, color-mix(in oklab, var(--accent) 92%, var(--background)) 97%, var(--foreground) 3%);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--collection-filter-active: var(--color-blue-400);
--bubble-agent: oklch(0.185 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-highlight: #333333; /* Paper: --dark-color-muted-highlight. */
--background-alt: oklch(0.145 0 0); /* Paper: --dark-color-background-alt. */
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.637 0.237 25.331); /* Gallery feedback round 1: user preferred master's original dark destructive red over the preset's softer 0.704 0.191 22.216. */
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--scrollbar-thumb: oklch(0.4 0 0);
--scrollbar-thumb-hover: oklch(0.5 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--sidebar: oklch(0.205 0 0);
--primary-sidebar-background: var(--muted);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.32 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
--chip-match-title-bg: oklch(0.27 0.04 265 / 0.5);
--chip-match-title-fg: oklch(0.78 0.10 265);
--chip-match-title-border: oklch(0.4 0.06 265);
--chip-match-comment-bg: oklch(0.27 0.04 145 / 0.5);
--chip-match-comment-fg: oklch(0.78 0.08 145);
--chip-match-comment-border: oklch(0.4 0.05 145);
--chip-match-document-bg: oklch(0.27 0.04 295 / 0.5);
--chip-match-document-fg: oklch(0.78 0.08 295);
--chip-match-document-border: oklch(0.4 0.05 295);
--chip-match-identifier-bg: var(--muted);
--chip-match-identifier-fg: var(--muted-foreground);
--chip-match-identifier-border: var(--border);
/* Dark-mode status-icon hues. The lighter progress blue gives bare glyphs
contrast comparable to the neighboring gray, purple, and green icons.
Blocked retains its base red; in_queue follows the progress icon blue. */
--status-task-icon-backlog: #9a958a;
--status-task-icon-todo: #fbbf24;
--status-task-icon-in_progress: var(--color-blue-400); /* matches nav dots and live labels */
--status-task-icon-in_review: #9474f0; /* deeper than violet-400, reads purple */
--status-task-icon-done: #34d06f;
--status-task-icon-cancelled: #9a958a;
}
.streamlined-task-detail-surface {
--side-panel-bg: var(--streamlined-task-detail-bg);
background-color: var(--streamlined-task-detail-bg);
}
::highlight(paperclip-doc-annotation-open) {
background-color: transparent;
color: inherit;
}
::highlight(paperclip-doc-annotation-focused) {
background-color: transparent;
color: inherit;
}
::highlight(paperclip-doc-annotation-stale) {
background-color: transparent;
color: inherit;
}
::highlight(paperclip-doc-annotation-resolved) {
background-color: transparent;
color: inherit;
}
.paperclip-doc-annotation-highlight::before {
position: absolute;
inset:
calc(-1 * var(--paperclip-doc-annotation-highlight-y-outset))
calc(-1 * var(--paperclip-doc-annotation-highlight-x-outset));
border-radius: var(--paperclip-doc-annotation-highlight-radius);
background-color: var(--paperclip-doc-annotation-highlight-open);
content: "";
transition-property: background-color;
transition-duration: var(--motion-duration-fast);
transition-timing-function: var(--motion-ease-standard);
}
.paperclip-doc-annotation-highlight[data-focused="true"]:not([data-anchor-state="stale"]):not([data-status="resolved"])::before,
.paperclip-doc-annotation-highlight[data-hovered="true"]:not([data-anchor-state="stale"]):not([data-status="resolved"])::before {
background-color: var(--paperclip-doc-annotation-highlight-focused);
}
.paperclip-doc-annotation-highlight[data-anchor-state="stale"]:not([data-status="resolved"])::before {
background-color: var(--paperclip-doc-annotation-highlight-stale);
}
.paperclip-doc-annotation-highlight[data-status="resolved"]::before {
background-color: var(--paperclip-doc-annotation-highlight-resolved);
}
@layer base {
/*
Autofill, in the app's own colours.
Chrome paints an autofilled field with a fixed blue-tinted fill of its own —
it ignores `background-color` entirely, which is why nothing in the token
layer reached it and a filled email field came out blue on an otherwise
monochrome screen. `box-shadow` is the one property that does reach it: an
inset shadow thick enough to cover the field repaints the surface, and
`-webkit-text-fill-color` does the same job for the text.
The transition delay is the standard trick for keeping it: Chrome re-applies
its own fill on interaction, and a delay long enough to outlast the frame is
what stops it flashing back.
*/
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active,
textarea:-webkit-autofill,
select:-webkit-autofill {
-webkit-text-fill-color: var(--foreground);
caret-color: var(--foreground);
box-shadow: inset 0 0 0 1000px var(--muted);
transition: background-color 100000s ease-in-out 0s;
}
* {
@apply border-border outline-ring/50;
}
html {
height: 100%;
-webkit-tap-highlight-color: color-mix(in oklab, var(--foreground) 20%, transparent);
@apply font-sans;
}
body {
@apply antialiased bg-background text-foreground;
font-family: var(--font-sans);
height: 100%;
overflow: hidden;
}
h1,
h2,
h3 {
text-wrap: balance;
}
/* Prevent double-tap-to-zoom on interactive elements for mobile */
a,
button,
[role="button"],
input,
select,
textarea,
label {
touch-action: manipulation;
}
@media (pointer: coarse) {
input,
select,
textarea,
[contenteditable=""],
[contenteditable="true"],
[contenteditable="plaintext-only"] {
font-size: max(var(--text-base), 1em);
}
}
/* Let font-mono (utilities layer) override for monospace editors */
.paperclip-mdxeditor [class*="_placeholder_"],
.paperclip-mdxeditor-content {
font-family: inherit;
}
}
/* Native single-value dropdowns share one inset caret. Padding alone does not
move the browser's arrow. Keep native menus/keyboard behavior and leave
listboxes and controls with their own appearance-none icon untouched.
This rule is outside layers so per-field px-* utilities cannot erase the
space reserved for the caret. currentColor follows every app theme. */
select:where(:not([multiple]):not(.appearance-none)):where(:not([size]), [size="0"], [size="1"]) {
appearance: none;
padding-inline-end: var(--native-select-end-padding);
background-image:
linear-gradient(45deg, transparent calc(50% - var(--native-select-caret-stroke) / 2), currentColor calc(50% - var(--native-select-caret-stroke) / 2), currentColor calc(50% + var(--native-select-caret-stroke) / 2), transparent calc(50% + var(--native-select-caret-stroke) / 2)),
linear-gradient(135deg, transparent calc(50% - var(--native-select-caret-stroke) / 2), currentColor calc(50% - var(--native-select-caret-stroke) / 2), currentColor calc(50% + var(--native-select-caret-stroke) / 2), transparent calc(50% + var(--native-select-caret-stroke) / 2));
background-position: right calc(var(--native-select-caret-inset) + var(--native-select-caret-half)) center, right var(--native-select-caret-inset) center;
background-size: var(--native-select-caret-half) var(--native-select-caret-half);
background-repeat: no-repeat;
}
select:where(:not([multiple]):not(.appearance-none)):where(:not([size]), [size="0"], [size="1"]):dir(rtl) {
background-position: left var(--native-select-caret-inset) center, left calc(var(--native-select-caret-inset) + var(--native-select-caret-half)) center;
}
@media (forced-colors: active) {
select:where(:not([multiple]):not(.appearance-none)):where(:not([size]), [size="0"], [size="1"]) {
appearance: auto;
background-image: none;
}
}
@media (pointer: coarse) {
/* iOS Safari zooms when focusing text smaller than 16px. Floor the small
typography tokens on editable controls, preserving larger title sizes. */
input,
select,
textarea,
[contenteditable=""],
[contenteditable="true"],
[contenteditable="plaintext-only"] {
--text-nano: var(--text-base);
--text-micro: var(--text-base);
--text-xs: var(--text-base);
--text-compact: var(--text-base);
--text-sm: var(--text-base);
}
/* Rich-text editors can inherit a smaller size from their surrounding
document styles rather than using a typography utility. Keep this more
specific than the editor's content class. */
html [contenteditable=""],
html [contenteditable="true"],
html [contenteditable="plaintext-only"] {
font-size: max(var(--text-base), 1em);
}
button,
[role="button"],
input,
select,
textarea,
[data-slot="select-trigger"] {
min-height: 44px;
}
/* Small inline widgets keep their design size on touch devices —
forcing 44px here stretches checkboxes, chip menus, and icon buttons
that live inside dense rows (sidebar headers, issue rows, filter
popovers). The surrounding row provides the touch area. */
[data-slot="toggle"],
[data-slot="checkbox"],
[data-slot="icon-button"],
[data-slot="task-chat-mode-trigger"],
[data-slot="new-issue-compact-control"],
[data-size="xs"],
[data-size="icon-xs"],
[data-size="icon-sm"],
input[type="checkbox"],
input[type="radio"],
/* Auto-resizing title fields are vertically centered inside their own bar,
which already provides the touch area; the 44px floor would top-align the
single line of text and push it too high (notably on mobile Safari). */
textarea[data-autosize-title] {
min-height: 0;
}
}
/* Keep the nav on the compositor; the composer follows the same easing and
duration while the page retains its reserved bottom space. */
.mobile-bottom-nav {
transform: translateY(0);
transition: transform var(--motion-mobile-nav-enter) var(--motion-ease-out-expo);
will-change: transform;
}
.mobile-bottom-nav[data-visible="false"] {
transform: translateY(100%);
transition-duration: var(--motion-mobile-nav-exit);
transition-timing-function: var(--motion-ease-standard);
pointer-events: none;
}
.mobile-nav-composer {
transition: bottom var(--mobile-nav-motion-duration, var(--motion-mobile-nav-enter)) var(--mobile-nav-motion-ease, var(--motion-ease-out-expo));
}
/* Mobile conversations use the same single message scroller + normal footer as
desktop. Opt in from the rendered task shell, so classic tasks, lists, and
the empty agent-chat picker retain their existing document layout. */
html:has(.paperclip-app-shell[data-mobile="true"] [data-task-chat-shell]),
html:has(.paperclip-app-shell[data-mobile="true"] [data-task-chat-shell]) body {
position: fixed;
inset: 0;
width: 100%;
}
html:has(.paperclip-app-shell[data-mobile="true"] [data-task-chat-shell]),
html:has(.paperclip-app-shell[data-mobile="true"] [data-task-chat-shell]) body,
html:has(.paperclip-app-shell[data-mobile="true"] [data-task-chat-shell]) #root {
height: 100%;
overflow: hidden !important;
overscroll-behavior: none;
}
.paperclip-app-shell[data-mobile="true"]:has([data-task-chat-shell]) {
position: fixed;
inset: 0;
display: flex;
flex-direction: column;
height: var(--mobile-chat-viewport-height);
min-height: 0;
overflow: hidden;
}
.paperclip-app-shell[data-mobile="true"]:has([data-task-chat-shell]) :is(.app-main-row, .app-content-row) {
display: flex;
flex: 1;
min-height: 0;
overflow: hidden;
}
.paperclip-app-shell[data-mobile="true"]:has([data-task-chat-shell]) .app-content-column {
flex: 1;
min-height: 0;
}
.paperclip-app-shell[data-mobile="true"]:has([data-task-chat-shell]) .app-breadcrumb {
flex-shrink: 0;
}
.paperclip-app-shell[data-mobile="true"]:has([data-task-chat-shell]) #main-content {
display: flex;
flex-direction: column;
min-height: 0;
overflow: hidden;
padding-bottom: var(--tc-composer-visible-nav-offset);
}
.paperclip-app-shell[data-mobile="true"][data-keyboard-open="true"]:has([data-task-chat-shell]) #main-content {
padding-bottom: 0;
}
@media (prefers-reduced-motion: reduce) {
:root {
--motion-mobile-nav-enter: 0ms;
--motion-mobile-nav-exit: 0ms;
}
}
/* Mobile collection toolbars and square agent actions. */
@media (width < 40rem) {
[role="toolbar"][aria-label="Inbox controls"] [data-slot="tabs-list"][data-variant="line"] [data-slot="tabs-trigger"]::after {
bottom: var(--spacing);
}
[role="toolbar"][aria-label="Inbox controls"] button[data-size="icon"],
[role="group"][aria-label="Agent view"] > button {
width: var(--sz-44px);
height: var(--sz-44px);
min-height: var(--sz-44px);
padding: 0;
flex-shrink: 0;
}
.paperclip-task-list-toolbar > :first-child {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
}
.paperclip-task-list-toolbar [data-slot="collection-toolbar-search"] {
grid-column: 1;
grid-row: 1;
}
.paperclip-task-list-toolbar [data-slot="collection-toolbar-search"] input {
height: var(--sz-44px);
}
.paperclip-task-list-toolbar [data-slot="collection-toolbar-context"] {
grid-column: 2;
grid-row: 1;
}
.paperclip-task-list-toolbar [data-slot="collection-toolbar-context"] button,
.paperclip-task-list-toolbar [data-slot="collection-toolbar-controls"] button {
width: var(--sz-44px);
height: var(--sz-44px);
min-height: var(--sz-44px);
padding: 0;
flex-shrink: 0;
}
.paperclip-task-list-toolbar > :first-child > :last-child {
grid-column: 1 / -1;
grid-row: 2;
}
}
/* Dark mode scrollbars */
.dark {
color-scheme: dark;
}
.dark *::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.dark *::-webkit-scrollbar-track {
background: oklch(0.205 0 0);
}
.dark *::-webkit-scrollbar-thumb {
background: oklch(0.4 0 0);
border-radius: 4px;
}
.dark *::-webkit-scrollbar-thumb:hover {
background: oklch(0.5 0 0);
}
/* Auto-hide scrollbar: thin, stable gutter with the thumb visible only on hover */
.scrollbar-auto-hide {
scrollbar-gutter: stable;
scrollbar-width: thin;
scrollbar-color: transparent transparent;
}
.scrollbar-auto-hide::-webkit-scrollbar {
width: 8px !important;
height: 8px !important;
background: transparent !important;
}
.scrollbar-auto-hide::-webkit-scrollbar-track {
background: transparent !important;
}
.scrollbar-auto-hide::-webkit-scrollbar-thumb {
background: transparent !important;
}
/* Task thread: keep the gutter available without painting a persistent rail.
Scroll events temporarily add data-scroll-active; hovering alone stays idle. */
.scrollbar-while-scrolling {
scrollbar-width: thin;
scrollbar-color: transparent transparent;
}
.scrollbar-while-scrolling::-webkit-scrollbar {
width: var(--scrollbar-size);
height: var(--scrollbar-size);
background: transparent;
}
.scrollbar-while-scrolling::-webkit-scrollbar-track,
.scrollbar-while-scrolling::-webkit-scrollbar-thumb {
background: transparent;
}
.scrollbar-while-scrolling[data-scroll-active="true"] {
scrollbar-color: var(--scrollbar-thumb) transparent;
}
.scrollbar-while-scrolling[data-scroll-active="true"]::-webkit-scrollbar-thumb {
background: var(--scrollbar-thumb);
border-radius: var(--scrollbar-radius);
}
.scrollbar-while-scrolling[data-scroll-active="true"]::-webkit-scrollbar-thumb:hover {
background: var(--scrollbar-thumb-hover);
}
/* The task-chat shell owns its desktop scroll viewport. Release Layout's
otherwise-useful reserved main scrollbar gutter so that viewport can meet
the properties-pane border instead of stopping one scrollbar-width early. */
#main-content:has([data-task-chat-shell]) {
scrollbar-gutter: auto;
}
/* Paper task-detail properties rhythm: values share the 12px label scale and
each section gets a full 24px breathing interval before the next heading. */
.task-detail-properties [data-property-section="true"] {
padding-bottom: calc(var(--spacing) * 6);
}
.task-detail-properties [data-property-value="true"],
.task-detail-properties [data-property-value="true"] .text-sm {
font-size: var(--text-xs);
line-height: var(--text-xs--line-height);
}
/* The generic line-tab variant deliberately keeps active tabs transparent.
The task-detail sidebar uses a filled active tab instead; the pane slot ID
gives this contextual treatment enough specificity without affecting other
line-tab surfaces. */
#properties-pane-header-slot .task-detail-pane-tab[data-state="active"] {
background-color: var(--muted);
}
/* Codex-style pane tabs soften clipped labels into the tab surface. The mask
stays on the label itself so short names remain visually untouched. */
.task-detail-pane-tab-label {
-webkit-mask-image: linear-gradient(
to right,
var(--foreground) 0,
var(--foreground) calc(100% - calc(var(--spacing) * 4)),
transparent 100%
);
mask-image: linear-gradient(
to right,
var(--foreground) 0,
var(--foreground) calc(100% - calc(var(--spacing) * 4)),
transparent 100%
);
}
/* Keep every streamlined tab at its preferred width until the row must compress.
The usable floor leaves the close action accessible before horizontal scrolling. */
[data-tab-sizing="compact"] {
--side-panel-streamlined-tab-width: var(--side-panel-streamlined-tab-min-width);
}
[data-tab-sizing="wide"] {
--side-panel-streamlined-tab-width: var(--side-panel-streamlined-tab-max-width);
}
.side-panel-streamlined-tab {
min-width: var(--side-panel-streamlined-tab-collapse-floor);
width: max(
var(--side-panel-streamlined-tab-collapse-floor),
min(
var(--side-panel-streamlined-tab-width),
calc(100% / var(--side-panel-streamlined-tab-count) - var(--side-panel-streamlined-tab-inline-space))
)
);
}
/* Keep continuation tabs readable as they pass beneath the fixed add control.
The mask is enabled only while content remains beyond the right edge. */
.side-panel-tabs-scroll[data-scroll-end-fade="true"] {
-webkit-mask-image: linear-gradient(
to right,
var(--foreground) 0,
var(--foreground) calc(100% - var(--side-panel-tab-edge-fade-width)),
transparent 100%
);
mask-image: linear-gradient(
to right,
var(--foreground) 0,
var(--foreground) calc(100% - var(--side-panel-tab-edge-fade-width)),
transparent 100%
);
}
/* Light mode scrollbar on hover */
.scrollbar-auto-hide:hover {
scrollbar-color: oklch(0.7 0 0) transparent;
}
.scrollbar-auto-hide:hover::-webkit-scrollbar-track {
background: transparent !important;
}
.scrollbar-auto-hide:hover::-webkit-scrollbar-thumb {
background: oklch(0.7 0 0) !important;
border-radius: 999px !important;
}
.scrollbar-auto-hide:hover::-webkit-scrollbar-thumb:hover {
background: oklch(0.6 0 0) !important;
}
/* Dark mode scrollbar on hover */
.dark .scrollbar-auto-hide:hover {
scrollbar-color: oklch(0.4 0 0) transparent;
}
.dark .scrollbar-auto-hide:hover::-webkit-scrollbar-track {
background: transparent !important;
}
.dark .scrollbar-auto-hide:hover::-webkit-scrollbar-thumb {
background: oklch(0.4 0 0) !important;
}
.dark .scrollbar-auto-hide:hover::-webkit-scrollbar-thumb:hover {
background: oklch(0.5 0 0) !important;
}
/* Typing indicator — three bouncing dots, used inside an agent chat bubble */
.typing-dots {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 4px 0;
}
.typing-dots span {
width: 5px;
height: 5px;
border-radius: 999px;
background: var(--muted-foreground);
animation: typing-bounce 1.2s ease-in-out infinite;
}
.typing-dots span:nth-child(2) {
animation-delay: 0.15s;
}
.typing-dots span:nth-child(3) {
animation-delay: 0.3s;
}
@keyframes typing-bounce {
0%,
60%,
100% {
transform: translateY(0);
opacity: 0.4;
}
30% {
transform: translateY(-3px);
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.typing-dots span {
animation: none;
opacity: 0.6;
}
}
/* Task Chat Redesign motion tokens collapse to instant under reduced motion.
Zeroing the primitives cascades to every scoped token that references them;
the literal-valued scoped tokens (staggers, loops) are zeroed explicitly. */
@media (prefers-reduced-motion: reduce) {
:root {
--motion-duration-instant: 0ms;
--motion-duration-fast: 0ms;
--motion-duration-base: 0ms;
--motion-duration-slow: 0ms;
--motion-duration-deliberate: 0ms;
--motion-cot-line-stagger: 0ms;
--motion-approval-pulse: 0ms;
--motion-plan-entry-stagger: 0ms;
--motion-streaming-cursor-blink: 0ms;
--motion-turn-fold: 0ms;
--motion-line-scroll: 0ms;
/* --motion-interstitial-dwell is deliberately NOT zeroed: it is a pacing
hold, not a movement — zeroing it would make the interstitial line churn
faster under reduced motion. Swaps themselves snap (line-scroll: 0). */
/* --motion-scrollbar-idle-delay is also a visibility hold, not movement;
keeping it lets reduced-motion users see the thumb while scrolling. */
--motion-scroll-pill-enter: 0ms;
--motion-scroll-pill-exit: 0ms;
--motion-pane-glide: 0ms;
--motion-pane-width-reset: 0ms;
}
}
/* ────────────────────────────────────────────────────────────────────────
Chat-style task thread animation utilities (default view; classic legacy
view sits behind enableClassicTaskInterface).
These classes are the ONLY place the redesigned thread's motion is applied;
every duration/easing is a var(--motion-*) reference (never a literal), so
(a) the no-hardcoded-timing gate passes and (b) the dev tweak panel retunes
them live by writing the tokens on :root. Keep timing here, in tokens — the
components only add class names.
──────────────────────────────────────────────────────────────────────── */
@keyframes tc-fade-rise {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: none; }
}
@keyframes tc-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes tc-status-in {
from { opacity: 0; transform: translateY(2px) scale(0.98); }
to { opacity: 1; transform: none; }
}
@keyframes tc-approval-pulse {
0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklab, var(--primary) 0%, transparent); }
50% { box-shadow: 0 0 0 3px color-mix(in oklab, var(--primary) 18%, transparent); }
}
.tc-question-option { transition-duration: var(--motion-duration-instant); }
.tc-question-page { animation: tc-fade-in var(--motion-question-page-enter) var(--motion-ease-standard) both; }
@keyframes tc-cursor-blink {
0%, 45% { opacity: 1; }
55%, 100% { opacity: 0; }
}
.tc-enter-bubble { animation: tc-fade-rise var(--motion-bubble-enter) var(--motion-ease-out-expo) both; }
.tc-enter-marker { animation: tc-fade-in var(--motion-marker-enter) var(--motion-ease-out) both; }
.tc-enter-tool { animation: tc-fade-rise var(--motion-tool-enter) var(--motion-ease-out-expo) both; }
.tc-enter-status { animation: tc-status-in var(--motion-status-enter) var(--motion-ease-out-expo) both; }
.mobile-task-side-panel { animation-duration: var(--motion-duration-fast); }
.tc-enter-cot-line { animation: tc-fade-rise var(--motion-duration-fast) var(--motion-ease-standard) both; }
.tc-notice-chevron { transition-duration: var(--motion-duration-fast); }
.tc-reveal-diff { animation: tc-fade-in var(--motion-diff-reveal) var(--motion-ease-standard) both; }
.tc-approval { animation: tc-approval-pulse var(--motion-approval-pulse) var(--motion-ease-standard) infinite; }
.tc-cursor { animation: tc-cursor-blink var(--motion-streaming-cursor-blink) step-end infinite; }
.tc-enter-plan-entry { animation: tc-fade-rise var(--motion-plan-check) var(--motion-ease-out-expo) both; }
/* ────────────────────────────────────────────────────────────────────────
Agent-bubble treatment explorations (PAP-501). Scoped to the TaskChatLab
stage via [data-bubble-variant]; the live thread never sets that attribute,
so these overrides remain lab-only comparisons. Feedback: the former
dark-mode agent card (oklch 0.24) read too light against the near-black page
for the bulk of the text. The production treatment now sits directly on
the page background; the card alternatives remain available for comparison.
──────────────────────────────────────────────────────────────────────── */
/* Former production treatment, retained as a direct before/after comparison
for the darker default. */
[data-bubble-variant="former"] { --bubble-agent: oklch(0.955 0 0); }
.dark [data-bubble-variant="former"] { --bubble-agent: oklch(0.24 0 0); }
/* B · Hairline flush — near-page fill + a 1px hairline for gentle definition
without any brightness lift. */
[data-bubble-variant="hairline"] { --bubble-agent: var(--background); }
[data-bubble-variant="hairline"] [data-testid="task-chat-agent-bubble"] {
border: 1px solid var(--border);
}
.dark [data-bubble-variant="hairline"] { --bubble-agent: oklch(0.17 0 0); }
.dark [data-bubble-variant="hairline"] [data-testid="task-chat-agent-bubble"] {
border-color: oklch(1 0 0 / 8%);
}
/* Card variants restore the shared card geometry that production no longer
applies to agent prose. */
[data-bubble-variant="former"] [data-testid="task-chat-agent-bubble"],
[data-bubble-variant="darker"] [data-testid="task-chat-agent-bubble"],
[data-bubble-variant="hairline"] [data-testid="task-chat-agent-bubble"] {
background: var(--bubble-agent);
border-radius: var(--radius-2xl);
padding-left: var(--bubble-agent-card-padding-inline);
padding-right: var(--bubble-agent-card-padding-inline);
max-width: var(--pct-85);
}
/* Live-line viewport (PAP-361): exactly one line-height tall, clipping the
wrapped interstitial block. On wrap, the inner block translates up whole
line-heights (set inline as translateY(-(n−1)·1lh)) so the completed line
slides out the top — transform-only, same motion grammar as tool rows
animating in. */
.tc-line-scroll { display: block; height: 1lh; overflow: hidden; }
.tc-line-scroll-inner { transition: transform var(--motion-line-scroll) var(--motion-ease-standard); will-change: transform; }
/* Scroll-to-latest pill (stock shadcn MessageScrollerButton motion, tokenized).
The pill is centered with translateX(-50%), so the keyframes carry the X
offset through the whole transform. */
@keyframes tc-pill-in {
from { opacity: 0; transform: translate(-50%, 8px) scale(0.9); }
to { opacity: 1; transform: translate(-50%, 0) scale(1); }
}
@keyframes tc-pill-out {
from { opacity: 1; transform: translate(-50%, 0) scale(1); }
to { opacity: 0; transform: translate(-50%, 8px) scale(0.9); }
}
.tc-scroll-pill-in { animation: tc-pill-in var(--motion-scroll-pill-enter) var(--motion-ease-scroll-pill-in) both; }
.tc-scroll-pill-out { animation: tc-pill-out var(--motion-scroll-pill-exit) var(--motion-ease-scroll-pill-out) both; }
/* Finished-turn fold: grid-rows 1fr→0fr animates height without measuring;
the single grid child must be overflow-hidden for 0fr to clip. */
.tc-turn-fold {
display: grid;
grid-template-rows: 1fr;
transition: grid-template-rows var(--motion-turn-fold) var(--motion-ease-standard),
opacity var(--motion-turn-fold) var(--motion-ease-standard);
}
.tc-turn-fold[data-folded="true"] { grid-template-rows: 0fr; opacity: 0; }
.tc-turn-fold > * { overflow: hidden; min-height: 0; }
/* Hover-revealed activity carets remain discoverable on touch-only devices. */
@media (hover: none) {
.tc-hover-disclosure-caret { opacity: 1; }
}
/* Settled-turn metadata remains available on hover/focus and while expanded,
but its collapsed grid track consumes no width at rest. */
.tc-turn-metrics {
display: grid;
grid-template-columns: 0fr;
opacity: 0;
transition: grid-template-columns var(--motion-turn-meta) var(--motion-ease-standard),
opacity var(--motion-turn-meta) var(--motion-ease-standard);
}
[data-testid="task-chat-turn-summary"]:hover .tc-turn-metrics,
[data-testid="task-chat-turn-summary"]:focus-visible .tc-turn-metrics,
.tc-turn-metrics[data-visible="true"] {
grid-template-columns: 1fr;
opacity: 1;
}
/* Properties-pane maximize/restore glide (left animates between docked and
sidebar-flush while the pane is position:fixed). */
.tc-pane-glide { transition: left var(--motion-pane-glide) var(--motion-ease-standard); }
.properties-panel-docked {
transition: width var(--motion-pane-reveal) var(--motion-ease-pane-reveal),
opacity var(--motion-pane-reveal) var(--motion-ease-pane-reveal);
}
.sidebar-shell[data-width-resetting="true"],
.sidebar-shell[data-width-resetting="true"] > .sidebar-shell-panel { transition: width var(--motion-pane-width-reset) var(--motion-ease-pane-width-reset); }
.design-page-aside[data-width-resetting="true"] { transition: flex-basis var(--motion-pane-width-reset) var(--motion-ease-pane-width-reset); }
.side-panel-tab-motion {
transition-property: color, background-color, border-color, opacity, box-shadow, transform;
transition-duration: var(--motion-side-panel-tab);
transition-timing-function: var(--motion-ease-standard);
}
.side-panel-tab-close-motion {
transition-property: color, background-color, border-color, box-shadow, transform;
transition-duration: var(--motion-side-panel-tab);
transition-timing-function: var(--motion-ease-standard);
}
/* Sidebar actions cover the trailing live label without moving row content,
fading into the row surface like the task-detail tab close control. */
.primary-sidebar-surface {
--sidebar-action-rest-surface: var(--primary-sidebar-background);
background-color: var(--sidebar-action-rest-surface);
}
.sidebar-action-row {
--sidebar-action-surface: var(--sidebar-action-rest-surface, var(--background));
}
.sidebar-action-row:is(:hover, :focus-within),
.sidebar-action-row:has(.sidebar-action-link[aria-current="page"]),
.sidebar-action-row:has(.sidebar-action-menu[data-state="open"]) {
--sidebar-action-surface: var(--sidebar-accent);
}
.sidebar-action-link {
background-color: var(--sidebar-action-surface);
}
.sidebar-action-row:is(:hover, :focus-within) .sidebar-action-link,
.sidebar-action-row:has(.sidebar-action-menu[data-state="open"]) .sidebar-action-link {
color: var(--sidebar-accent-foreground);
}
.sidebar-action-menu {
isolation: isolate;
}
.sidebar-action-menu::before {
content: "";
position: absolute;
inset: 0;
left: calc(-1 * var(--sidebar-nav-action-fade-width));
z-index: -1;
border-radius: inherit;
/* Animate a solid surface in sync with the link; gradient color stops switch
immediately and flash the resting sidebar color during the opacity fade. */
background-color: var(--sidebar-action-surface);
mask-image: linear-gradient(to right, transparent, black var(--sidebar-nav-action-fade-width));
transition: background-color var(--default-transition-duration) var(--default-transition-timing-function);
pointer-events: none;
}
.side-panel-tab-label-fade {
-webkit-mask-image: linear-gradient(to right, black calc(100% - var(--side-panel-tab-label-fade-width)), transparent);
mask-image: linear-gradient(to right, black calc(100% - var(--side-panel-tab-label-fade-width)), transparent);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
}
@media (prefers-reduced-motion: reduce) {
.tc-enter-bubble,
.tc-enter-marker,
.tc-enter-tool,
.tc-enter-status,
.tc-enter-cot-line,
.tc-reveal-diff,
.tc-scroll-pill-in,
.tc-scroll-pill-out,
.tc-enter-plan-entry { animation: none; }
.tc-turn-fold,
.tc-turn-metrics,
.tc-line-scroll-inner,
.tc-pane-glide,
.properties-panel-docked,
.side-panel-tab-motion,
.side-panel-tab-close-motion { transition: none; }
/* The pill keyframes carry the X-centering; with animation:none restore it. */
.tc-scroll-pill-in,
.tc-scroll-pill-out { transform: translate(-50%, 0); }
.tc-approval { animation: none; box-shadow: 0 0 0 2px color-mix(in oklab, var(--primary) 18%, transparent); }
.tc-cursor { animation: none; opacity: 1; }
}
/* Expandable dialog transition for max-width changes */
[data-slot="dialog-content"] {
transition: max-width 200ms cubic-bezier(0.16, 1, 0.3, 1);
}
/* ---------------------------------------------------------------------------
Motion tokens
---------------------------------------------------------------------------
The first named motion values in the system. Durations and easings were
previously written inline at each call site; these give the "snappy, eases
in and out" behaviour one definition so surfaces stay in step.
Exit is deliberately shorter than enter: a thing appearing wants to be
followed, a thing leaving just needs to get out of the way.
The enter curve is the same cubic-bezier the dialog max-width transition
already used — decelerating hard at the end, which is what reads as "snappy"
rather than "slow start". Consumers: the decision-card disclosure and the
task quicklook, both below.
--------------------------------------------------------------------------- */
:root {
--motion-duration-enter: 160ms;
--motion-duration-exit: 110ms;
--motion-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--motion-ease-in: cubic-bezier(0.4, 0, 1, 1);
/* A crossfading pair must share one duration and one curve. Two elements
swapping places both change height, and the container shows the SUM. Give
them different timing — the outgoing on a 110ms ease-in against the
incoming on a 160ms ease-out — and that sum stops being monotonic: the
container bulges or dips mid-swap, which reads as a lurch even though each
half is individually smooth. Run both off the same clock and the same
curve and the sum collapses to a clean interpolation between the two
states, because at every moment progress is identical on both sides. */
--motion-duration-swap: 150ms;
}
/* ---------------------------------------------------------------------------
Decision-card disclosure (See more / See less)
---------------------------------------------------------------------------
Radix Collapsible measures the panel and publishes its height, so the card
can grow and shrink to a real number instead of snapping. The content
unmounts once the exit finishes, so an expanded row's resolver is not left
running behind a collapsed card.
--------------------------------------------------------------------------- */
@keyframes decision-disclosure-open {
from {
height: 0;
opacity: 0;
}
to {
height: var(--radix-collapsible-content-height);
opacity: 1;
}
}
@keyframes decision-disclosure-close {
from {
height: var(--radix-collapsible-content-height);
opacity: 1;
}
to {
height: 0;
opacity: 0;
}
}
[data-slot="collapsible-content"][data-decision-disclosure] {
overflow: hidden;
}
[data-slot="collapsible-content"][data-decision-disclosure][data-state="open"] {
animation: decision-disclosure-open var(--motion-duration-swap) var(--motion-ease-out);
}
[data-slot="collapsible-content"][data-decision-disclosure][data-state="closed"] {
/* Same duration and same curve as the open side — see --motion-duration-swap.
The two run against each other, so matched timing is what keeps the card's
height a straight interpolation instead of a bulge. */
animation: decision-disclosure-close var(--motion-duration-swap) var(--motion-ease-out);
}
/* ---------------------------------------------------------------------------
Task quicklook: a small scale-up out of the trigger, not a fade in place.
---------------------------------------------------------------------------
The shadcn PopoverContent class string carries `animate-in`, `zoom-in-95`
and friends, but those utilities come from the tailwindcss-animate plugin,
which this project does not install — there is no `enter`/`exit` keyframe in
the build, so they are inert. Rather than take on that dependency for one
surface (it would newly animate every dialog, dropdown and tooltip in the
app), the quicklook defines its own motion here.
The scale is deliberately shallow. `--radix-popover-content-transform-origin`
pins the origin to the corner nearest the trigger, so the card reads as
growing out of the task key that opened it rather than swelling in place.
--------------------------------------------------------------------------- */
@keyframes quicklook-open {
from {
opacity: 0;
transform: scale(0.96);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes quicklook-close {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.96);
}
}
[data-slot="popover-content"][data-quicklook][data-state="open"] {
animation: quicklook-open var(--motion-duration-enter) var(--motion-ease-out);
}
[data-slot="popover-content"][data-quicklook][data-state="closed"] {
animation: quicklook-close var(--motion-duration-exit) var(--motion-ease-in);
}
@media (prefers-reduced-motion: reduce) {
[data-slot="collapsible-content"][data-decision-disclosure][data-state="open"],
[data-slot="collapsible-content"][data-decision-disclosure][data-state="closed"],
[data-slot="popover-content"][data-quicklook][data-state="open"],
[data-slot="popover-content"][data-quicklook][data-state="closed"] {
animation: none;
}
}
/* Shared rhythm for dashboard activity and recent-task rows. */
.dashboard-list-row {
min-height: calc(var(--spacing) * 12);
padding: calc(var(--spacing) * 3) calc(var(--spacing) * 4);
}
/* Dashboard activity row entry motion */
@keyframes dashboard-activity-enter {
0% {
opacity: 0;
transform: translateY(-14px) scale(0.985);
filter: blur(4px);
}
62% {
opacity: 1;
transform: translateY(2px) scale(1.002);
filter: blur(0);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
filter: blur(0);
}
}
@keyframes dashboard-activity-highlight {
0% {
box-shadow: inset 2px 0 0 var(--primary);
background-color: color-mix(in oklab, var(--accent) 55%, transparent);
}
100% {
box-shadow: inset 0 0 0 transparent;
background-color: transparent;
}
}
.activity-row-enter {
animation:
dashboard-activity-enter 520ms cubic-bezier(0.16, 1, 0.3, 1),
dashboard-activity-highlight 920ms cubic-bezier(0.16, 1, 0.3, 1);
}
@media (prefers-reduced-motion: reduce) {
.activity-row-enter {
animation: none;
}
}
/* Chain-of-thought reasoning line ticker animations.
Pure translate, no opacity — the overflow-hidden container clips.
Both keyframes share the same easing so the two spans move in lockstep. */
@keyframes cot-line-slide-in {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
@keyframes cot-line-slide-out {
from { transform: translateY(0); }
to { transform: translateY(-100%); }
}
.cot-line-enter {
animation: cot-line-slide-in 300ms cubic-bezier(0.4, 0, 0.2, 1) both;
}
.cot-line-exit {
animation: cot-line-slide-out 300ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@media (prefers-reduced-motion: reduce) {
.cot-line-enter,
.cot-line-exit {
animation: none;
}
}
/* RunTranscriptView live-tail motion (PAP-461, A3). The experimental chat-style
view streams its live output through RunTranscriptView; these token-driven
classes replace the raw Tailwind `animate-in … duration-300` / `animate-ping`
the tail used so its streaming affordances honor --motion-* retuning AND the
reduced-motion override below, exactly like the rest of the redesign. */
@keyframes tc-stream-block-in {
from { opacity: 0; transform: translateY(0.25rem); }
to { opacity: 1; transform: translateY(0); }
}
.tc-stream-block-enter {
animation: tc-stream-block-in var(--motion-tool-enter) var(--motion-ease-standard) both;
}
@keyframes tc-live-ping {
75%, 100% { transform: scale(2); opacity: 0; }
}
.tc-live-ping {
animation: tc-live-ping var(--motion-approval-pulse) var(--motion-ease-standard) infinite;
}
.paperclip-markdown.task-chat-reasoning-markdown,
.paperclip-markdown.task-chat-reasoning-markdown :is(h1, h2, h3, h4, h5, h6, strong) {
font-size: inherit;
font-weight: inherit;
line-height: inherit;
}
/* Activity rows stay on one bounded line until explicitly expanded. The mask
gives long provider text a quieter edge than an ellipsis, while the shared
expanded treatment safely wraps paths, identifiers, and unbroken tokens. */
.task-chat-collapsed-line-fade {
overflow: hidden;
white-space: nowrap;
text-overflow: clip;
-webkit-mask-image: linear-gradient(
to right,
black 0,
black calc(100% - 2rem),
transparent 100%
);
mask-image: linear-gradient(
to right,
black 0,
black calc(100% - 2rem),
transparent 100%
);
}
.task-chat-expanded-line-wrap,
.paperclip-markdown.task-chat-reasoning-markdown,
.paperclip-markdown.task-chat-reasoning-markdown :is(p, strong, em, a, code) {
overflow-wrap: anywhere;
word-break: break-word;
}
@media (prefers-reduced-motion: reduce) {
.tc-stream-block-enter,
.tc-live-ping {
animation: none;
}
}
/* Shimmer text effect for active "Working" state — Cursor-style sweep */
@keyframes shimmer-text-slide {
0% { background-position: 100% center; }
100% { background-position: 0% center; }
}
.shimmer-text {
--shimmer-base: var(--foreground);
--shimmer-highlight: color-mix(in oklch, var(--foreground) 35%, transparent);
background: linear-gradient(
90deg,
var(--shimmer-base) 0%,
var(--shimmer-base) 40%,
var(--shimmer-highlight) 50%,
var(--shimmer-base) 60%,
var(--shimmer-base) 100%
);
background-size: 200% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer-text-slide 2.5s linear infinite;
}
.paperclip-thinking-icon {
transform-origin: 50% 50%;
}
@keyframes paperclip-thinking-draw {
0% {
stroke-dasharray: 0 85.717;
stroke-dashoffset: -85.717;
opacity: 1;
animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955);
}
39.0625% {
stroke-dasharray: 85.717 85.717;
stroke-dashoffset: 0;
opacity: 1;
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
78.125% {
stroke-dasharray: 0 85.717;
stroke-dashoffset: 0;
opacity: 1;
}
78.225% {
opacity: 0;
}
100% {
stroke-dasharray: 0 85.717;
stroke-dashoffset: 0;
opacity: 0;
}
}
.paperclip-thinking-icon-path {
animation: paperclip-thinking-draw 1s linear infinite;
}
/* Muted variant (task-chat v7 activity rows): the sweep runs over the
muted-foreground tier so live "Thinking…"/"Working…" labels stay subordinate
to real content. */
.shimmer-text-muted {
--shimmer-base: var(--muted-foreground);
--shimmer-highlight: color-mix(in oklch, var(--muted-foreground) 30%, transparent);
}
/* Utilities expected by shadcn base components (ui/attachment.tsx). Declared
with @utility so variant forms (e.g. group-data-[state=uploading]:shimmer)
compile under Tailwind v4. */
@utility shimmer {
--shimmer-base: var(--foreground);
--shimmer-highlight: color-mix(in oklch, var(--foreground) 35%, transparent);
background: linear-gradient(
90deg,
var(--shimmer-base) 0%,
var(--shimmer-base) 40%,
var(--shimmer-highlight) 50%,
var(--shimmer-base) 60%,
var(--shimmer-base) 100%
);
background-size: 200% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer-text-slide 2.5s linear infinite;
}
@utility scrollbar-none {
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
@utility scroll-fade-x {
mask-image: linear-gradient(
to right,
transparent,
black 12px,
black calc(100% - 12px),
transparent
);
}
@media (prefers-reduced-motion: reduce) {
.shimmer-text {
animation: none;
-webkit-text-fill-color: unset;
background: none;
}
.paperclip-thinking-icon-path {
animation: none;
opacity: 1;
stroke-dasharray: 85.717 85.717;
stroke-dashoffset: 0;
}
}
/* Agent heartbeat capsule motion (PAP-75).
`hb-pulse` marks a running agent (live); `hb-blink` marks a broken agent
(error). The blink alternates the fill between brand red and the page
background so the capsule reads as a faltering heartbeat. */
@keyframes hb-pulse {
0%, 100% { opacity: 0.4; transform: scale(0.85); }
50% { opacity: 1; transform: scale(1); }
}
@keyframes hb-blink {
0%, 49.9% { background-color: #DC2626; }
50%, 100% { background-color: var(--background); }
}
.hb-pulse {
animation: hb-pulse 1.6s ease-in-out infinite;
}
.hb-blink {
animation: hb-blink 1.2s step-end infinite;
}
@media (prefers-reduced-motion: reduce) {
.hb-pulse,
.hb-blink {
animation: none;
}
}
/* Agent capsule motif (PAP-119) — "the capsule is the agent".
Three states realized in the AgentCapsule component:
- slot: dashed outline gently pulsing (an empty agent slot)
- configured: solid stroke (named/model picked, not yet live)
- online: brand agent-gradient liquid rises to fill + green online-pulse
prefers-reduced-motion skips the liquid rise + pulses and renders the
final state. */
@keyframes agent-cap-slot-pulse {
0%, 100% { opacity: 0.5; }
50% { opacity: 1; }
}
@keyframes agent-cap-rise {
0% { height: 0; }
100% { height: 100%; }
}
@keyframes agent-cap-online-pulse {
0%, 100% {
box-shadow: 0 0 0 0 color-mix(in oklab, #22c55e 0%, transparent);
transform: scale(1);
}
50% {
box-shadow: 0 0 0 6px color-mix(in oklab, #22c55e 18%, transparent);
transform: scale(1.03);
}
}
.agent-cap-slot {
animation: agent-cap-slot-pulse 1.6s ease-in-out infinite;
}
.agent-cap-liquid {
animation: agent-cap-rise 1.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.agent-cap-online {
animation: agent-cap-online-pulse 1.8s ease-in-out infinite;
}
/* Blue online-glow variant (PAP-125) — the Option 4 wizard's review step.
Same breathing ring as the green pulse, recoloured to brand blue (#2563eb). */
@keyframes agent-cap-online-pulse-blue {
0%, 100% {
box-shadow: 0 0 0 0 color-mix(in oklab, #2563eb 0%, transparent);
transform: scale(1);
}
50% {
box-shadow: 0 0 0 6px color-mix(in oklab, #2563eb 22%, transparent);
transform: scale(1.03);
}
}
.agent-cap-online-blue {
animation: agent-cap-online-pulse-blue 1.8s ease-in-out infinite;
}
/* Cross-fade the stacked dashed/solid border layers as the capsule evolves
in place (border-style itself is not animatable). */
.agent-cap-layer {
transition: opacity 0.5s ease;
}
@media (prefers-reduced-motion: reduce) {
.agent-cap-slot,
.agent-cap-online,
.agent-cap-online-blue {
animation: none;
}
.agent-cap-layer {
transition: none;
}
.agent-cap-liquid {
animation: none;
height: 100%;
}
}
/* MDXEditor theme integration */
.paperclip-mdxeditor-scope,
.paperclip-mdxeditor {
--baseBase: var(--background);
--baseBg: transparent;
--baseBgSubtle: color-mix(in oklab, var(--accent) 35%, transparent);
--baseLine: var(--border);
--baseSolid: var(--muted-foreground);
--baseSolidHover: var(--foreground);
--baseText: var(--muted-foreground);
--baseBorderColor: var(--border);
--baseBorder: var(--border);
--baseBorderHover: var(--ring);
--baseTextContrast: var(--foreground);
--baseTextContrastMuted: var(--muted-foreground);
--baseTextEmphasis: var(--foreground);
--basePageBg: var(--background);
--baseRadius: var(--radius);
--baseLineHeight: 1.5;
--accentBorder: color-mix(in oklab, var(--primary) 35%, var(--border));
--accentSolid: var(--primary);
--accentSolidHover: var(--primary);
--accentLine: color-mix(in oklab, var(--primary) 20%, transparent);
--accentBg: var(--accent);
--accentBgHover: color-mix(in oklab, var(--accent) 80%, var(--background));
--accentBgActive: color-mix(in oklab, var(--accent) 72%, var(--background));
--accentText: var(--accent-foreground);
font-family: inherit;
font-size: 1rem;
line-height: 1.5;
color: var(--foreground);
}
@media (min-width: 768px) {
.paperclip-mdxeditor-scope,
.paperclip-mdxeditor {
font-size: 0.875rem;
}
}
.paperclip-mdxeditor-scope [class*="_iconButton_"],
.paperclip-mdxeditor [class*="_iconButton_"] {
color: var(--baseText);
}
.paperclip-mdxeditor-scope [class*="_iconButton_"]:hover,
.paperclip-mdxeditor [class*="_iconButton_"]:hover {
color: var(--baseTextContrast);
}
.paperclip-mdxeditor .mdxeditor-root-contenteditable {
min-height: 2.5rem;
padding: 0;
line-height: 1.5;
}
.paperclip-mdxeditor [class*="_contentEditable_"] {
padding: 0.375rem 0.625rem !important;
}
.paperclip-mdxeditor--borderless [class*="_contentEditable_"] {
padding: 0 !important;
}
.paperclip-mdxeditor [class*="_placeholder_"] {
font-size: inherit;
line-height: 1.5;
color: var(--muted-foreground);
}
/* MDXEditor defaults placeholders to a single clipped line. The task-chat
composer uses longer, assignee-aware copy, so keep it inside the editor by
giving the absolutely positioned placeholder a wrapping inline size. */
.paperclip-task-chat-composer .paperclip-mdxeditor [class*="_placeholder_"] {
display: block;
width: 100%;
overflow-wrap: anywhere;
white-space: normal;
}
.paperclip-mdxeditor-content {
font-size: inherit;
line-height: inherit;
color: inherit;
}
.paperclip-edit-in-place-content {
font-size: 0.9375rem;
line-height: 1.75rem;
}
.paperclip-mdxeditor-content > *:first-child {
margin-top: 0;
}
.paperclip-mdxeditor-content > *:last-child {
margin-bottom: 0;
}
.paperclip-mdxeditor-content p {
margin: 0;
line-height: inherit;
}
.paperclip-mdxeditor-content p + p {
margin-top: 1.1em;
}
.paperclip-mdxeditor-content a:not(.paperclip-mention-chip):not(.paperclip-project-mention-chip) {
color: color-mix(in oklab, var(--foreground) 76%, #0969da 24%);
text-decoration: underline;
text-underline-offset: 0.15em;
cursor: pointer;
}
.dark .paperclip-mdxeditor-content a:not(.paperclip-mention-chip):not(.paperclip-project-mention-chip) {
color: color-mix(in oklab, var(--foreground) 80%, #58a6ff 20%);
}
.paperclip-mdxeditor-content a.paperclip-mention-chip,
.paperclip-mdxeditor-content a.paperclip-project-mention-chip {
display: inline-flex;
align-items: center;
gap: 0.25rem;
margin: 0 0.1rem;
padding: 2px 0.625rem;
border: 1px solid var(--border);
border-radius: 999px;
font-size: 0.75rem;
line-height: 1.25;
text-decoration: none;
vertical-align: middle;
position: relative;
top: -1px;
white-space: nowrap;
user-select: none;
}
/* Strip the MDXEditor's default inline-code styling from the text inside chips
(the link label otherwise picks up a monospace font + gray tint). */
.paperclip-mdxeditor-content a.paperclip-mention-chip code,
.paperclip-mdxeditor-content a.paperclip-project-mention-chip code {
font-family: inherit;
background: none;
color: inherit;
padding: 0;
}
.paperclip-mdxeditor-content a.paperclip-mention-chip::before,
a.paperclip-mention-chip::before {
content: "";
flex: none;
}
.paperclip-mdxeditor-content a.paperclip-mention-chip[data-mention-kind="project"]::before,
a.paperclip-mention-chip[data-mention-kind="project"]::before {
width: 0.45rem;
height: 0.45rem;
border-radius: 999px;
background-color: var(--paperclip-mention-project-color, currentColor);
}
.paperclip-mdxeditor-content a.paperclip-mention-chip[data-mention-kind="agent"]::before,
a.paperclip-mention-chip[data-mention-kind="agent"]::before {
width: 1rem;
height: 1rem;
background-image: var(--paperclip-mention-avatar-image);
background-position: center;
background-repeat: no-repeat;
background-size: contain;
}
.paperclip-mdxeditor-content ul,
.paperclip-mdxeditor-content ol {
margin: 1.1em 0;
padding-left: 1.6em;
}
.paperclip-mdxeditor-content ul {
list-style: disc;
}
.paperclip-mdxeditor-content ol {
list-style: decimal;
}
.paperclip-mdxeditor-content li {
display: list-item;
margin: 0.3em 0;
line-height: inherit;
}
.paperclip-mdxeditor-content li::marker {
color: var(--muted-foreground);
}
.paperclip-mdxeditor-content h1 {
margin: 1.4em 0 0.9em;
font-size: 1.75em;
font-weight: var(--font-weight-bold);
line-height: 1.2;
}
.paperclip-mdxeditor-content h2 {
margin: 1.3em 0 0.85em;
font-size: 1.35em;
font-weight: var(--font-weight-bold);
line-height: 1.3;
}
.paperclip-mdxeditor-content h3 {
margin: 1.2em 0 0.8em;
font-size: 1.15em;
font-weight: 600;
line-height: 1.35;
}
.paperclip-mdxeditor-content img {
max-height: 18rem;
border-radius: calc(var(--radius) - 2px);
}
.paperclip-mdxeditor-content blockquote {
margin: 1.2em 0;
padding-left: 1em;
border-left: 3px solid var(--border);
color: var(--muted-foreground);
line-height: inherit;
}
.paperclip-mdxeditor-content code {
font-family: var(--font-mono);
font-size: 1em;
}
.paperclip-mdxeditor-content pre {
margin: 0.4rem 0;
padding: 0;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background: var(--muted);
color: var(--foreground);
overflow-x: auto;
}
/* Dark theme for CodeMirror code blocks inside the MDXEditor.
Overrides the default cm6-theme-basic-light that MDXEditor bundles. */
.paperclip-mdxeditor .cm-editor {
background-color: #1e1e2e !important;
color: #cdd6f4 !important;
font-size: 1em;
}
.paperclip-mdxeditor .cm-gutters {
background-color: #181825 !important;
color: #585b70 !important;
border-right: 1px solid #313244 !important;
}
.paperclip-mdxeditor .cm-activeLineGutter {
background-color: #1e1e2e !important;
}
.paperclip-mdxeditor .cm-activeLine {
background-color: color-mix(in oklab, #cdd6f4 5%, transparent) !important;
}
.paperclip-mdxeditor .cm-cursor,
.paperclip-mdxeditor .cm-dropCursor {
border-left-color: #cdd6f4 !important;
}
.paperclip-mdxeditor .cm-selectionBackground {
background-color: color-mix(in oklab, #89b4fa 25%, transparent) !important;
}
.paperclip-mdxeditor .cm-focused .cm-selectionBackground {
background-color: color-mix(in oklab, #89b4fa 30%, transparent) !important;
}
.paperclip-mdxeditor .cm-content {
caret-color: #cdd6f4;
}
/* MDXEditor code block language selector – show on hover only */
.paperclip-mdxeditor-content [class*="_codeMirrorWrapper_"] {
position: relative;
}
.paperclip-mdxeditor-content [class*="_codeMirrorToolbar_"],
.paperclip-mdxeditor-content [class*="_codeBlockToolbar_"] {
position: absolute;
top: 0.25rem;
right: 0.25rem;
z-index: 2;
opacity: 0;
transition: opacity 150ms ease;
}
.paperclip-mdxeditor-content [class*="_codeMirrorToolbar_"] select,
.paperclip-mdxeditor-content [class*="_codeBlockToolbar_"] select {
background-color: #313244;
color: #cdd6f4;
border-color: #45475a;
}
.paperclip-mdxeditor-content [class*="_codeMirrorWrapper_"]:hover [class*="_codeMirrorToolbar_"],
.paperclip-mdxeditor-content [class*="_codeMirrorWrapper_"]:hover [class*="_codeBlockToolbar_"],
.paperclip-mdxeditor-content [class*="_codeMirrorWrapper_"]:focus-within [class*="_codeMirrorToolbar_"],
.paperclip-mdxeditor-content [class*="_codeMirrorWrapper_"]:focus-within [class*="_codeBlockToolbar_"] {
opacity: 1;
}
/* Rendered markdown code blocks & inline code (prose/MarkdownBody context).
Theme-aware: the block rides --muted/--foreground/--border so it follows
light and dark instead of pinning the old Catppuccin #1e1e2e/#cdd6f4 in
both modes. Override prose CSS variables so prose-invert can't revert to
defaults. */
.paperclip-markdown {
--tw-prose-pre-bg: var(--muted);
--tw-prose-pre-code: var(--foreground);
--tw-prose-code: var(--foreground);
--tw-prose-invert-pre-bg: var(--muted);
--tw-prose-invert-pre-code: var(--foreground);
--tw-prose-invert-code: var(--foreground);
}
.paperclip-markdown pre {
border: 1px solid var(--border) !important;
border-radius: var(--radius-lg) !important;
background-color: var(--muted) !important;
color: var(--foreground) !important;
padding: 0.5rem 0.65rem !important;
margin: 0.4rem 0 !important;
font-size: 1em !important;
overflow-x: auto;
white-space: pre;
}
.paperclip-markdown code {
font-family: var(--font-mono);
font-size: 1em;
}
.paperclip-markdown pre code {
font-size: inherit;
color: inherit;
background: none;
}
/* Actions for fenced and indented preformatted markdown blocks */
.paperclip-markdown-codeblock {
position: relative;
}
.paperclip-markdown-codeblock-actions {
position: absolute;
top: 0.4rem;
right: 0.4rem;
display: inline-flex;
align-items: center;
gap: 0.25rem;
opacity: 0;
transition: opacity 0.12s ease;
}
.paperclip-markdown-codeblock-action {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.25rem;
min-height: 1.55rem;
padding: 0.2rem 0.4rem;
border-radius: calc(var(--radius) - 4px);
border: 1px solid color-mix(in oklab, var(--foreground) 14%, transparent);
background-color: color-mix(in oklab, var(--muted) 92%, var(--background) 8%);
color: var(--muted-foreground);
font-size: 0.7rem;
line-height: 1;
cursor: pointer;
transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.paperclip-markdown-codeblock:hover .paperclip-markdown-codeblock-actions,
.paperclip-markdown-codeblock-actions:focus-within,
.paperclip-markdown-codeblock-actions[data-active] {
opacity: 1;
}
.paperclip-markdown-codeblock-action:hover {
background-color: var(--accent);
color: var(--accent-foreground);
}
.paperclip-markdown-codeblock-action[data-active],
.paperclip-markdown-codeblock-action[data-copied] {
border-color: color-mix(in oklab, var(--primary) 38%, transparent);
color: var(--primary);
}
.paperclip-markdown-codeblock-action[data-failed] {
color: var(--destructive);
}
.paperclip-markdown-codeblock-action-label {
font-weight: var(--font-weight-medium);
}
/* Remove backtick pseudo-elements from inline code (prose default adds them) */
.prose code::before,
.prose code::after {
content: none;
}
/* Inline code background (not inside a code block) */
.prose :not(pre) > code {
background-color: color-mix(in oklab, var(--accent) 60%, transparent);
padding: 0.15em 0.35em;
border-radius: 3px;
font-weight: var(--font-weight-medium);
}
.dark .prose :not(pre) > code {
background-color: #ffffff0f;
}
.paperclip-markdown.paperclip-markdown-on-accent :not(pre) > code {
background-color: color-mix(in oklab, currentColor 24%, transparent);
color: currentColor;
}
.paperclip-markdown {
color: var(--foreground);
font-size: 0.9375rem;
line-height: 1.6;
}
.paperclip-markdown.paperclip-markdown-on-accent {
color: inherit;
--tw-prose-body: currentColor;
--tw-prose-headings: currentColor;
--tw-prose-lead: currentColor;
--tw-prose-links: currentColor;
--tw-prose-bold: currentColor;
--tw-prose-counters: currentColor;
--tw-prose-bullets: currentColor;
--tw-prose-hr: color-mix(in oklab, currentColor 24%, transparent);
--tw-prose-quotes: currentColor;
--tw-prose-quote-borders: color-mix(in oklab, currentColor 56%, transparent);
--tw-prose-captions: color-mix(in oklab, currentColor 84%, transparent);
--tw-prose-code: currentColor;
--tw-prose-th-borders: color-mix(in oklab, currentColor 34%, transparent);
--tw-prose-td-borders: color-mix(in oklab, currentColor 22%, transparent);
--tw-prose-invert-body: currentColor;
--tw-prose-invert-headings: currentColor;
--tw-prose-invert-lead: currentColor;
--tw-prose-invert-links: currentColor;
--tw-prose-invert-bold: currentColor;
--tw-prose-invert-counters: currentColor;
--tw-prose-invert-bullets: currentColor;
--tw-prose-invert-hr: color-mix(in oklab, currentColor 24%, transparent);
--tw-prose-invert-quotes: currentColor;
--tw-prose-invert-quote-borders: color-mix(in oklab, currentColor 56%, transparent);
--tw-prose-invert-captions: color-mix(in oklab, currentColor 84%, transparent);
--tw-prose-invert-code: currentColor;
--tw-prose-invert-th-borders: color-mix(in oklab, currentColor 34%, transparent);
--tw-prose-invert-td-borders: color-mix(in oklab, currentColor 22%, transparent);
}
.paperclip-markdown > :first-child {
margin-top: 0;
}
.paperclip-markdown > :last-child {
margin-bottom: 0;
}
.paperclip-markdown :where(p, ul, ol, blockquote, pre, .paperclip-markdown-table-scroll) {
margin-top: 0.7rem;
margin-bottom: 0.7rem;
}
.paperclip-markdown :where(ul, ol) {
padding-left: 1.5rem;
}
.paperclip-markdown ul {
list-style-type: disc;
}
.paperclip-markdown ol {
/* Enough inline start room to hold outside markers without clipped digits
when markdown lists sit flush against an overflow-constrained container. */
list-style-type: decimal;
padding-left: 2.5rem;
}
.paperclip-markdown li {
margin: 0.14rem 0;
padding-left: 0.2rem;
}
@media (width < 48rem) {
.paperclip-mobile-thread .paperclip-markdown :where(ul, ol) {
padding-left: calc(var(--spacing) * 5);
}
}
.paperclip-markdown li > :where(p, ul, ol) {
margin-top: 0.3rem;
margin-bottom: 0.3rem;
}
.paperclip-markdown li::marker {
color: var(--muted-foreground);
}
.paperclip-markdown.paperclip-markdown-on-accent li::marker {
color: currentColor;
}
.paperclip-markdown h1,
.paperclip-markdown h2,
.paperclip-markdown h3,
.paperclip-markdown h4 {
margin-top: 1.75rem;
margin-bottom: 0.45rem;
color: var(--foreground);
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.3;
}
.paperclip-markdown h1 {
font-size: 1.5rem;
}
.paperclip-markdown h2 {
font-size: 1.25rem;
}
.paperclip-markdown h3 {
font-size: 1.05rem;
}
.paperclip-markdown h4 {
font-size: 0.95rem;
}
.paperclip-markdown.paperclip-markdown-on-accent :where(h1, h2, h3, h4, strong, b) {
color: currentColor;
}
.paperclip-markdown :where(strong, b) {
color: var(--foreground);
font-weight: 600;
}
.paperclip-markdown a {
color: color-mix(in oklab, var(--foreground) 76%, #0969da 24%);
text-decoration: underline;
text-underline-offset: 0.15em;
cursor: pointer;
}
.paperclip-markdown.paperclip-markdown-on-accent :where(a, a:visited) {
color: currentColor;
text-decoration-color: color-mix(in oklab, currentColor 72%, transparent);
}
.paperclip-markdown.paperclip-markdown-on-accent :where(a:hover, a:focus-visible) {
color: currentColor;
text-decoration-color: currentColor;
}
.paperclip-markdown a.paperclip-mention-chip {
text-decoration: none;
}
/* Document cards underline only their label span; the card itself must not
inherit the markdown link underline, or the label shows two lines. */
.paperclip-markdown a[data-sidebar-document-link] {
text-decoration: none;
}
.paperclip-markdown a.paperclip-mention-chip[data-mention-kind="issue"] {
border-color: color-mix(in oklab, var(--foreground) 14%, var(--border) 86%);
background: color-mix(in oklab, var(--accent) 42%, transparent);
}
/* Inline issue references in markdown: no pill chrome, just a status icon
beside the link label — keeps the pair from splitting across lines. */
.paperclip-markdown-issue-ref {
display: inline;
white-space: nowrap;
}
/* External object URL decorations follow the same pattern: the host renders
the icon prefix, the link text is left untouched (Postel's Law). */
.paperclip-markdown-external-ref {
display: inline;
white-space: nowrap;
}
.dark .paperclip-markdown a {
color: color-mix(in oklab, var(--foreground) 80%, #58a6ff 20%);
}
.dark .paperclip-markdown.paperclip-markdown-on-accent :where(a, a:visited) {
color: currentColor;
}
.paperclip-markdown blockquote {
margin-left: 0;
padding-left: 0.95rem;
border-left: 0.24rem solid color-mix(in oklab, var(--border) 84%, var(--muted-foreground) 16%);
color: var(--muted-foreground);
}
.paperclip-markdown.paperclip-markdown-on-accent blockquote {
border-left-color: color-mix(in oklab, currentColor 56%, transparent);
color: color-mix(in oklab, currentColor 88%, transparent);
}
.paperclip-markdown.paperclip-markdown-on-accent .paperclip-workspace-file-link {
border-color: color-mix(in oklab, currentColor 34%, transparent);
background-color: color-mix(in oklab, currentColor 16%, transparent);
color: currentColor;
}
.paperclip-markdown.paperclip-markdown-on-accent .paperclip-workspace-file-link:hover {
background-color: color-mix(in oklab, currentColor 24%, transparent);
}
.paperclip-markdown.paperclip-markdown-on-accent :where(a.paperclip-mention-chip, a.paperclip-project-mention-chip) {
border-color: color-mix(in oklab, currentColor 34%, transparent) !important;
background: color-mix(in oklab, currentColor 16%, transparent) !important;
color: currentColor !important;
}
.paperclip-markdown hr {
margin: 1.25rem 0;
border-color: var(--border);
}
.paperclip-markdown img {
border-radius: calc(var(--radius) + 2px);
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--foreground) 10%, transparent);
}
.paperclip-markdown-table-scroll {
max-width: 100%;
overflow-x: auto;
overscroll-behavior-x: contain;
-webkit-overflow-scrolling: touch;
}
.paperclip-markdown-table-scroll:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
.paperclip-markdown-table-scroll table {
width: max-content;
min-width: 100%;
margin: 0;
}
.paperclip-markdown-table-scroll :where(th, td) {
min-width: 8rem;
max-width: 18rem;
vertical-align: top;
}
.paperclip-markdown th {
font-weight: 600;
text-align: left;
}
.paperclip-mermaid {
margin: 0.5rem 0;
padding: 0.45rem 0.55rem;
border: 1px solid var(--border);
border-radius: calc(var(--radius) - 3px);
background-color: color-mix(in oklab, var(--accent) 35%, transparent);
overflow-x: auto;
}
.paperclip-mermaid svg {
display: block;
width: max-content;
max-width: none;
min-width: 100%;
height: auto;
}
.paperclip-mermaid-status {
margin: 0 0 0.45rem;
font-size: 0.75rem;
color: var(--muted-foreground);
}
.paperclip-mermaid-status-error {
color: var(--destructive);
}
.paperclip-mermaid-source {
margin: 0;
padding: 0;
border: 0;
background: transparent;
}
/* Mention chips rendered inline in prose (MarkdownBody or inline anchors) */
a.paperclip-mention-chip,
a.paperclip-project-mention-chip,
span.paperclip-mention-chip,
span.paperclip-project-mention-chip {
display: inline-flex;
align-items: center;
gap: 0.25rem;
margin: 0 0.1rem;
padding: 2px 0.625rem;
border: 1px solid var(--border);
border-radius: 999px;
font-size: 0.75rem;
line-height: 1.25;
text-decoration: none;
/* Center the pill on the surrounding x-height so it sits on the text line
instead of hanging below it. inline-flex baseline alignment is unreliable
across browsers, so use vertical-align: middle for a predictable result.
Nudge up 1px so it visually centers with the cap height of the text. */
vertical-align: middle;
position: relative;
top: -1px;
white-space: nowrap;
}
/* Touch target floor: WCAG 2.5.5 expects ≥24px on coarse pointers. The base
chip is 22px; bump it on touch viewports without disturbing inline metrics. */
@media (pointer: coarse) {
a.paperclip-mention-chip,
a.paperclip-project-mention-chip,
span.paperclip-mention-chip,
span.paperclip-project-mention-chip {
min-height: 24px;
}
}
/* External object pills: variant marker the host renders for known-resolved
external URLs. Inline status icon is rendered by ExternalObjectStatusIcon. */
a.paperclip-mention-chip[data-mention-kind="external-object"],
span.paperclip-mention-chip[data-mention-kind="external-object"] {
/* Inherit base chip styling; status colour comes from inline classes so the
host can vary tone per category without defining a CSS variant per state. */
}
/* When the identifier inside a chip is backtick-wrapped in markdown, strip the
inline-code monospace/gray styling so the pill label reads cleanly. */
.paperclip-markdown a.paperclip-mention-chip code,
.paperclip-markdown a.paperclip-project-mention-chip code,
.paperclip-markdown span.paperclip-mention-chip code,
.paperclip-markdown span.paperclip-project-mention-chip code {
font-family: inherit;
background: none;
color: inherit;
padding: 0;
font-size: inherit;
}
/* Keep MDXEditor popups above app dialogs, even when they portal to <body>. */
[class*="_popupContainer_"] {
z-index: 81 !important;
}
[class*="_dialogOverlay_"] {
z-index: 80;
}
[class*="_dialogContent_"],
[class*="_largeDialogContent_"],
[class*="_popoverContent_"],
[class*="_linkDialogPopoverContent_"],
[class*="_tableColumnEditorPopoverContent_"],
[class*="_toolbarButtonDropdownContainer_"],
[class*="_toolbarNodeKindSelectContainer_"] {
z-index: 81 !important;
}
/* Status chip/fill helpers.
Each consumer sets a local `--sc` to the status' base hue (e.g.
`--sc: var(--status-task-in_progress)`); these rules derive the rendered
fill / text / border from it. `.status-chip` is the bordered chip (agent +
task chips); `.status-fill` a solid swatch (heartbeat capsule). Light mixes
the hue toward white for a soft fill; dark layers the hue at low alpha so it
reads on dark surfaces. A single hue covers both modes. */
.status-chip {
background-color: color-mix(in srgb, var(--sc) 15%, white);
color: color-mix(in srgb, var(--sc) 82%, black);
border-color: var(--sc);
}
.dark .status-chip {
background-color: color-mix(in srgb, var(--sc) 22%, transparent);
/* Keep small chip labels above AA contrast on their tinted dark fill. */
color: color-mix(in srgb, var(--sc) 70%, white);
border-color: color-mix(in srgb, var(--sc) 48%, transparent);
}
.status-fill {
background-color: var(--sc);
}
/* ── Extracted verbatim tokens (Phase 2, design/token-extraction) ── */
/* Batch 1/4: color literals only. Reused-from-existing-token sites (see
TOKEN-AUDIT.md section 1.1) are NOT duplicated here — they reference
--status-task-in_progress / --status-task-done directly at the call site.
Allowlist (sites intentionally left as hardcoded / functional literals,
NOT converted to tokens — each also carries an inline
`token-extraction: allowlisted` comment at the site):
- pages/CompanyEnvironments.tsx — xterm.js terminal theme config; functional JS values, third-party.
- pages/CompanySettings.tsx — <input type="color"> value; functional form control, not a rendered value. REMOVED: the company brand-color setting was deleted, so the control no longer exists.
- components/issue-properties/IssueProperties.tsx (newLabelColor) — color-picker seed persisted into label-create payload.
- pages/CompanySkills.tsx (DISCOVERY_ACCENTS) — persisted/compared skill.color JS data, not just rendered.
- components/IssueColumns.tsx (accentColor fallback) — also feeds pickTextColorForPillBg() contrast math.
- components/CompanyPatternIcon.tsx — canvas fillStyle computed at runtime from numeric props, not a static literal.
- pages/InviteUxLab.tsx (brandColor prop, x2) — demo/showcase-only prop feeding CompanyPatternIcon's hexToHue() color math, not a rendered CSS value. REMOVED: the brandColor prop was deleted, so the props no longer exist.
- components/FileViewerSheet.tsx — half-migrated var(--paperclip-code-highlight-*, fallback) pattern at the time of Batch 1; RESOLVED in Batch 4 via *-resolved wrapper tokens (see the Batch 4 MISC token block below) — no longer allowlisted.
*/
:root {
--hex-959596: #959596; /* Muted feed actor/verb/title text (ActivityFeed.tsx, FeedCard.tsx) — PRIOR-ART-flagged gap cluster, no existing token match. */
--hex-1d1d1d: #1d1d1d; /* OnboardingWizard.tsx dark decorative panel background, singleton. */
--hex-22d3ee: #22d3ee; /* OrgChart.tsx agent status dot — 'running' (independent palette from --status-agent-*, see TOKEN-AUDIT.md 1.2). */
--hex-4ade80: #4ade80; /* OrgChart.tsx agent status dot — 'active'. */
--hex-facc15: #facc15; /* OrgChart.tsx agent status dot — 'paused' / 'idle' (shared value). */
--hex-f87171: #f87171; /* OrgChart.tsx agent status dot — 'error'. */
--hex-a3a3a3: #a3a3a3; /* OrgChart.tsx agent status dot — 'terminated' + defaultDotColor fallback. */
--hex-ef4444: #ef4444; /* ActivityCharts.tsx — priority 'critical'; also the <0.5 success-rate bar tint and the run-activity 'failed' segment. (Chart STATUS hues re-pointed at --status-task-* in DECISION-SHEET.md B5; priority/success series stay independent.) */
--hex-f97316: #f97316; /* ActivityCharts.tsx — priority 'high'. */
--hex-eab308: #eab308; /* ActivityCharts.tsx — priority 'medium'; also the 0.5-0.8 success-rate bar tint. */
--hex-6b7280: #6b7280; /* ActivityCharts.tsx — priority 'low' + unknown-status statusColors fallback. */
--hex-10b981: #10b981; /* ActivityCharts.tsx — >=0.8 success-rate bar tint; also the run-activity 'succeeded' segment. */
--hex-737373: #737373; /* ActivityCharts.tsx — run activity 'other' segment tint. */
--project-none: #64748b; /* Semantic rename of --hex-64748b (DECISION-SHEET.md A3, value unchanged). 'No project assigned' muted-slate fallback (TOKEN-AUDIT.md 1.3) across Routines/MarkdownEditor/RoutineRunVariablesDialog/RoutineList/IssueColumns/editable-sections; also ActivityCharts.tsx status 'backlog'. */
--project-seed: #6366f1; /* Semantic rename of --hex-6366f1 (DECISION-SHEET.md A3, value unchanged). Project-color-fallback indigo seed default (ProjectDetail/IssueProperties/NewIssueDialog) — new-project-color-picker-seed family per TOKEN-AUDIT.md 1.3. */
--liveness-blue: #2563eb; /* DECISION-SHEET.md A6: IssueChatThread.tsx human-message 'liveness blue' bubble (PAP-95 rev 5). Same value as --status-task-in_progress by coincidence, decoupled on purpose so a future status-hue change doesn't drag the chat bubble along. */
--app-logo-tile-1: #ff4f00;
--app-logo-tile-2: #24292f;
--app-logo-tile-3: #4a154b;
--app-logo-tile-4: #0f172a;
--app-logo-tile-5: #5e6ad2;
--app-logo-tile-6: #1a73e8;
--app-logo-tile-7: #0f9d58;
--app-logo-tile-8: #ea4335;
--gradient-extract-1: linear-gradient(180deg,rgba(255,80,80,0.12),rgba(255,255,255,0.02)); /* Dashboard.tsx budget-alert card gradient. */
--gradient-extract-2: linear-gradient(180deg,rgba(255,255,255,0.05),rgba(255,255,255,0.02)); /* Costs.tsx subtle white card gradient. */
--gradient-extract-3: radial-gradient(circle at top left,rgba(244,114,182,0.08),transparent 35%),radial-gradient(circle at bottom right,rgba(56,189,248,0.1),transparent 32%); /* AccountingModelCard.tsx decorative overlay. */
--gradient-extract-4: linear-gradient(180deg,rgba(255,70,70,0.10),rgba(255,255,255,0.02)); /* BudgetIncidentCard.tsx incident-card gradient. */
--gradient-extract-7: radial-gradient(circle at top right,rgba(255,255,255,0.22),transparent 34%),radial-gradient(circle at bottom left,rgba(255,255,255,0.08),transparent 36%); /* ProfileSettings.tsx:162 decorative overlay. */
}
/* ── Extracted verbatim TYPE tokens (Phase 2 Batch 2, design/token-extraction) ── */
/* Batch 2/4: font-size + letter-spacing + line-height literals, verbatim
(no normalizing — 9/10/11/12/13/14/15px and 0.08-0.24em all stay distinct;
the human scale-collapse decision comes later per DESIGN.md/TOKEN-AUDIT.md).
Allowlist (sites intentionally left as hardcoded / functional literals,
NOT converted to tokens — each also carries an inline
`token-extraction: allowlisted` comment at the site):
- pages/CompanyEnvironments.tsx (fontSize: 12) — xterm.js terminal theme
config; functional third-party numeric option, not a rendered CSS value.
Same allowlisted object as Batch 1's color entry for this file.
- pages/CompanySkills.tsx (fontSize: Math.round(size * 0.42)) — computed at
runtime from a prop; not a static literal, nothing to extract.
*/
:root {
/* ── Named type ladder (DECISION-SHEET.md B3, preset-tune session) ──
Collapses the 8 verbatim --fs-* font-size tokens and 9 --ls-*
letter-spacing tokens (extracted in Batch 2 above) into named steps.
Codemod: scripts/codemod-type-ladder.mjs. Mapping:
9px + 10px -> --text-nano (9 -> 10 bump per locked ladder)
11px + 0.7rem -> --text-micro (0.7rem = 11.2px -> 11px)
12px -> Tailwind `text-xs` class (12px exact match; scale
class preferred over a redundant token per DESIGN.md)
13px -> --text-compact (PRIOR-ART named this tier "sm 13",
but that collides with Tailwind text-sm = 14px,
hence "compact")
14px + 15px -> Tailwind `text-sm` class (14px; 15 -> 14)
Letter-spacing, nearest-step mapping:
0.08em, 0.1em -> --tracking-label
0.12em, 0.14em, 0.16em -> --tracking-eyebrow
0.18em, 0.2em, 0.22em, 0.24em -> --tracking-caps
NOTE: sites moved to text-xs/text-sm also pick up the Tailwind scale
line-height (text-(length:--x) set font-size only) — intentional,
reviewed on the post-preset contact sheet. */
--text-nano: 10px;
--text-micro: 11px;
--text-compact: 13px;
/* The onboarding arc's question. A quarter larger than the 36px it was, so
the step's one question carries the screen it is alone on. Off Tailwind's
scale (text-4xl is 36, text-5xl 48), hence a token rather than a step. */
--text-onboarding-title: 45px;
/* Its own leading, because `text-(length:…)` sets only the size: the 36px
step carried 40px of line height with it, and inheriting the body's would
have loosened a question that wraps onto two lines. */
--leading-onboarding-title: 1.1;
--tracking-label: 0.08em;
--tracking-eyebrow: 0.14em;
--tracking-caps: 0.2em;
}
/* ── Extracted verbatim SIZE/SPACING/RADIUS/SHADOW tokens (Phase 2 Batch 3, design/token-extraction) ── */
/* Batch 3/4: width/height/min/max, padding/margin/gap/inset/translate,
radius, ring/outline-width, and shadow literals, verbatim (no
normalizing — the human scale-collapse decision comes later per
DESIGN.md/TOKEN-AUDIT.md). --sz-* is ONE shared family across
w/h/p/m/gap/inset/translate so identical values dedupe regardless of
which property used them. --rad-* is likewise shared across
rounded, ring, and outline widths.
Allowlist (sites intentionally left as hardcoded / functional literals
or var()-only passthrough with no new token minted):
- components/ui/scroll-area.tsx (rounded-[inherit]) — CSS keyword, not a
literal value; nothing to extract.
- Bracket values that only wrap var(--radix-*-trigger-width/height) or
var(--new-issue-dialog-height) etc. are rewritten to the bare paren
form directly (e.g. w-(--radix-popover-trigger-width)) — these are
runtime library/component variables, not design values, so no new
--sz-* token is minted for them (see TOKEN-AUDIT.md extraction log
for the full site list).
*/
:root {
--sz-execution-blocker-inline: calc(var(--spacing) * 4);
--sz-execution-blocker-block: calc(var(--spacing) * 2);
/* Ten single-line repository rows, including the gaps between them. */
--sz-github-repository-list: calc(10lh + 9 * var(--spacing) * 2);
--sz-320px: 320px; /* Extracted from ui/src/components/ActiveAgentsPanel.tsx (h-[320px]). */
--sz-390px: 390px;
--sz-64px: 64px;
--sz-3px: 3px; /* Extracted from ui/src/components/ActivityCharts.tsx (gap-[3px]). */
--sz-calc-1: calc(100% - 1.5rem); /* Extracted from ui/src/components/ActivityFeed.tsx (w-[calc(100%-1.5rem)]). */
--sz-calc-2: calc(100% - 0.75rem); /* Extracted from ui/src/components/ActivityFeed.tsx (w-[calc(100%-0.75rem)]). */
--sz-16rem: 16rem; /* Extracted from ui/src/components/ActivityFeed.tsx (max-w-[16rem]). */
--sz-20rem: 20rem; /* AgentSkillReleasePicker.tsx release menu width cap. */
--sz-18px: 18px; /* Extracted from ui/src/components/ActivityFeed.tsx (p-[18px]). */
--sz-44px: 44px; /* Extracted from ui/src/components/AgentConfigForm.tsx (min-h-[44px]). */
/* The width one source tile has when the connect step's row holds two of
them. The row collapses to the chosen source once a sign-in starts, and the
survivor has to keep the width it already had — letting `flex-1` take the
whole row would make the tile grow as it travelled, which reads as a
different tile arriving rather than the one just pressed being kept. */
--sz-source-tile-two-up: calc((100% - 0.75rem) / 2);
/* The connect step's sign-in card, held at one height across its own states.
It opens on a spinner and then fills with an instruction and a field, and
the height is fixed so that arrival is the only movement — a card that grew
as its contents landed would push the footer twice for one event. 108px is
16 padding + a 16 line + a 16 gap + a 44 field + 16 padding. */
--sz-108px: 108px;
--sz-88px: 88px; /* Extracted from ui/src/components/AgentConfigForm.tsx (min-h-[88px]). */
--sz-240px: 240px; /* Extracted from ui/src/components/AgentConfigForm.tsx (max-h-[240px]). */
--sz-10_5rem: 10.5rem; /* Extracted from ui/src/components/BlockedInboxView.tsx (w-[10.5rem]). */
--sz-150px: 150px; /* Extracted from ui/src/components/BlockedInboxView.tsx (w-[150px]). */
--sz-5_75rem: 5.75rem; /* Extracted from ui/src/components/BlockedInboxView.tsx (w-[5.75rem]). */
--sz-12rem: 12rem; /* Extracted from ui/src/components/BlockedInboxView.tsx (max-w-[12rem]). */
--sz-200px: 200px; /* Extracted from ui/src/components/ChatComposer.test.tsx (max-h-[200px]). */
--sz-22px: 22px; /* Extracted from ui/src/components/ChatComposer.test.tsx (max-h-[22px]). */
--sz-60px: 60px; /* Extracted from ui/src/components/CommentThread.tsx (min-h-[60px]). */
--sz-220px: 220px; /* Extracted from ui/src/components/CompanySwitcher.tsx (w-[220px]). */
--sz-88vh: 88vh; /* Extracted from ui/src/components/DocumentAnnotationPanel.tsx (max-h-[88vh]). */
--sz-80vh: 80vh; /* Extracted from ui/src/components/DocumentAnnotationPanel.tsx (max-h-[80vh]). */
--sz-360px: 360px; /* Extracted from ui/src/components/DocumentAnnotationPanel.tsx (w-[360px]). */
--sz-85vh: 85vh; /* Extracted from ui/src/components/DocumentDiffModal.tsx (max-h-[85vh]). */
--sz-6rem: 6rem; /* Extracted from ui/src/components/EntityRow.tsx (min-w-[6rem]). */
--sz-1px: 1px; /* Extracted from ui/src/components/EntityRow.tsx (top-[1px]). */
--sz-100px: 100px; /* Extracted from ui/src/components/ExecutionParticipantPicker.tsx (max-w-[100px]). */
--sz-calc-3: min(840px,calc(100dvh - 2rem)); /* Extracted from ui/src/components/FileViewerSheet.tsx (h-[min(840px,calc(100dvh-2rem))]). */
--sz-calc-4: calc(100vw - 2rem); /* Extracted from ui/src/components/FileViewerSheet.tsx (w-[calc(100vw-2rem)]). */
--sz-calc-5: min(1280px,calc(100vw - 2rem)); /* Extracted from ui/src/components/FileViewerSheet.tsx (max-w-[min(1280px,calc(100vw-2rem))]). */
--sz-94vw: 94vw; /* Extracted from ui/src/components/FileViewerSheet.tsx (w-[94vw]). */
--sz-1280px: 1280px; /* Extracted from ui/src/components/FileViewerSheet.tsx (max-w-[1280px]). */
--sz-60vh: 60vh; /* Used by ui/src/pages/SkillStudio.tsx and others (min-h/max-h-[60vh]). */
--sz-52vh: 52vh;
--sz-40rem: 40rem;
--sz-44rem: 44rem;
--sz-36rem: 36rem;
--sz-neg-1_25rem: -1.25rem;
--sz-85pct: 85%;
--sz-30pct: 30%;
--sz-24pct: 24%;
--sz-18pct: 18%;
--sz-12pct: 12%;
--sz-calc-6: min(20rem,calc(100vw - 2rem)); /* Extracted from ui/src/components/InlineEntitySelector.tsx (w-[min(20rem,calc(100vw-2rem))]). */
--sz-18rem: 18rem; /* Extracted from ui/src/components/IssueBlockedNotice.tsx (max-w-[18rem]). */
--sz-70vh: 70vh; /* Extracted from ui/src/components/IssueChatThread.test.tsx (max-h-[70vh]). */
--sz-calc-7: calc(100% - 0.5rem); /* Extracted from ui/src/components/IssueChatThread.test.tsx (max-w-[calc(100%-0.5rem)]). */
--sz-28dvh: 28dvh; /* Extracted from ui/src/components/IssueChatThread.test.tsx (max-h-[28dvh]). */
--sz-72px: 72px; /* Extracted from ui/src/components/IssueChatThread.test.tsx (min-h-[72px]). */
--sz-15px: 15px; /* Extracted from ui/src/components/IssueChatThread.test.tsx (p-[15px]). */
--sz-calc-8: calc(env(safe-area-inset-bottom) + 20px); /* Extracted from ui/src/components/IssueChatThread.test.tsx (bottom-[calc(env(safe-area-inset-bottom)+20px)]). */
--sz-300px: 300px; /* Extracted from ui/src/components/IssueColumns.tsx (w-[300px]). */
--sz-calc-9: min(80vh,42rem); /* Extracted from ui/src/components/IssueFiltersPopover.test.tsx (max-h-[min(80vh,42rem)]). */
--sz-calc-10: min(780px,calc(100vw - 2rem)); /* Extracted from ui/src/components/IssueFiltersPopover.tsx (w-[min(780px,calc(100vw-2rem))]). */
--sz-24ch: 24ch; /* Extracted from ui/src/components/IssuePlanDecompositionsSection.tsx (max-w-[24ch]). */
--sz-calc-13: calc(0.75rem + 0.5rem); /* Extracted from ui/src/components/IssueRow.tsx (ml-[calc(theme(spacing.3)+theme(spacing.2))]). */
--sz-140px: 140px; /* Extracted from ui/src/components/JsonSchemaForm.tsx (min-h-[140px]). */
--sz-52px: 52px; /* Extracted from ui/src/components/KanbanBoard.tsx (w-[52px]). */
--sz-48px: 48px; /* Extracted from ui/src/components/KanbanBoard.tsx (min-w-[48px]). */
--sz-260px: 260px; /* Extracted from ui/src/components/KanbanBoard.tsx (min-w-[260px]). */
--sz-120px: 120px; /* Extracted from ui/src/components/KanbanBoard.tsx (min-h-[120px]). */
--sz-safe-top: env(safe-area-inset-top); /* Extracted from ui/src/components/Layout.tsx (pt-[env(safe-area-inset-top)]). */
--sz-calc-14: calc(5rem + env(safe-area-inset-bottom)); /* Extracted from ui/src/components/Layout.tsx (pb-[calc(5rem+env(safe-area-inset-bottom))]). */
--sz-38ch: 38ch; /* Extracted from ui/src/components/MarkdownBody.test.tsx (max-w-[38ch]). */
--sz-208px: 208px; /* Extracted from ui/src/components/MarkdownEditor.test.tsx (max-h-[208px]). */
--sz-180px: 180px; /* Extracted from ui/src/components/MarkdownEditor.tsx (min-w-[180px]). */
--sz-calc-15: calc(100vw - 16px); /* Extracted from ui/src/components/MarkdownEditor.tsx (max-w-[calc(100vw-16px)]). */
--sz-66px: 66px; /* Extracted from ui/src/components/MembershipAction.tsx (w-[66px]). */
--sz-safe-bottom: env(safe-area-inset-bottom); /* Extracted from ui/src/components/MobileBottomNav.tsx (pb-[env(safe-area-inset-bottom)]). */
--sz-calc-16: min(calc(100dvh - 2rem),46rem); /* Extracted from ui/src/components/NewAgentDialog.tsx (max-h-[min(calc(100dvh-2rem),46rem)]). */
--sz-24rem: 24rem; /* Extracted from ui/src/components/NewAgentDialog.tsx (h-[24rem]). */
--sz-50vh: 50vh; /* Extracted from ui/src/components/NewGoalDialog.tsx (max-h-[50vh]). */
--sz-8_5rem: 8.5rem; /* Extracted from ui/src/components/NewIssueDialog.tsx (min-w-[8.5rem]). */
--sz-160px: 160px; /* Extracted from ui/src/components/NewProjectDialog.tsx (max-w-[160px]). */
--sz-80px: 80px; /* Extracted from ui/src/components/OnboardingWizard.tsx (min-h-[80px]). */
--sz-calc-17: calc(100vh - 4rem); /* Extracted from ui/src/components/PageSkeleton.tsx (h-[calc(100vh-4rem)]). */
--sz-2px: 2px; /* Extracted from ui/src/components/QuotaBar.tsx (w-[2px]). */
--sz-calc-18: calc(100dvh - 2rem); /* Extracted from ui/src/components/RoutineRunVariablesDialog.test.tsx (max-h-[calc(100dvh-2rem)]). */
--sz-calc-19: calc(1rem + env(safe-area-inset-bottom)); /* Extracted from ui/src/components/RoutineRunVariablesDialog.test.tsx (pb-[calc(1rem+env(safe-area-inset-bottom))]). */
--sz-calc-20: min(calc(100dvh - 2rem),42rem); /* Extracted from ui/src/components/RoutineRunVariablesDialog.tsx (max-h-[min(calc(100dvh-2rem),42rem)]). */
--sz-100dvh: 100dvh; /* Extracted from ui/src/components/RoutineSubSidebar.tsx (max-h-[100dvh]). */
--sz-calc-21: calc(1.5rem + 5rem + env(safe-area-inset-bottom)); /* Extracted from ui/src/components/ScrollToBottom.tsx (bottom-[calc(1.5rem+5rem+env(safe-area-inset-bottom))]). */
--sz-calc-22: calc(320px + 1.5rem); /* Extracted from ui/src/components/ScrollToBottom.tsx (right-[calc(320px+1.5rem)]). */
--sz-calc-23: min(32rem,calc(100vw - 2rem)); /* Extracted from ui/src/components/SearchableSelect.tsx (max-w-[min(32rem,calc(100vw-2rem))]). */
--sz-277px: 277px; /* Extracted from ui/src/components/SidebarAccountMenu.test.tsx (w-[277px]). */
--sz-calc-24: calc(100vw - 1rem); /* Extracted from ui/src/components/SidebarAccountMenu.tsx (max-w-[calc(100vw-1rem)]). */
--sz-4_5rem: 4.5rem; /* Extracted from ui/src/components/SidebarAgents.tsx (min-w-[4.5rem]). */
--sz-36px: 36px; /* Extracted from ui/src/components/WorkspaceFileBrowser.tsx (min-h-[36px]). */
--sz-32px: 32px; /* Extracted from ui/src/components/WorkspaceFileBrowser.tsx (min-h-[32px]). */
--sz-30px: 30px; /* Extracted from ui/src/components/WorkspaceFileBrowser.tsx (min-h-[30px]). */
--sz-8px: 8px; /* Extracted from ui/src/components/artifacts/ArtifactGroupCard.tsx (translate-x-[8px]). */
--sz-4px: 4px; /* Extracted from ui/src/components/artifacts/ArtifactGroupCard.tsx (translate-x-[4px]). */
--sz-34px: 34px; /* Extracted from ui/src/components/environment-variables-editor/SecretPicker.tsx (h-[34px]). */
--sz-11rem: 11rem; /* Extracted from ui/src/components/issue-properties/IssueProperties.tsx (min-w-[11rem]). */
--sz-32rem: 32rem; /* Extracted from ui/src/components/issue-properties/IssueProperties.tsx (w-[32rem]). */
--sz-280px: 280px; /* Extracted from ui/src/components/timeline/WorkTimelineChart.tsx (max-w-[280px]). */
--sz-7px: 7px; /* Extracted from ui/src/components/transcript/RunTranscriptView.tsx (mt-[7px]). */
--sz-calc-25: calc(100% - 2rem); /* Extracted from ui/src/components/ui/alert-dialog.tsx (max-w-[calc(100%-2rem)]). */
--sz-calc-26: max(1rem,env(safe-area-inset-top)); /* Extracted from ui/src/components/ui/alert-dialog.tsx (top-[max(1rem,env(safe-area-inset-top))]). */
--sz-8rem: 8rem; /* Extracted from ui/src/components/ui/dropdown-menu.tsx (min-w-[8rem]). */
--sz-calc-27: calc(100% - 1px); /* Extracted from ui/src/components/ui/tabs.tsx (h-[calc(100%-1px)]). */
--sz-neg-5px: -5px; /* Extracted from ui/src/components/ui/tabs.tsx (bottom-[-5px]). */
--sz-calc-28: calc(-50% - 2px); /* Extracted from ui/src/components/ui/tooltip.tsx (translate-y-[calc(-50%_-_2px)]). */
--sz-420px: 420px; /* Extracted from ui/src/pages/AgentDetail.tsx (min-h-[420px]). */
--sz-30rem: 30rem; /* Extracted from ui/src/pages/AgentDetail.tsx (w-[30rem]). */
--sz-38rem: 38rem; /* Extracted from ui/src/pages/AgentDetail.tsx (max-h-[38rem]). */
--sz-7rem: 7rem; /* Extracted from ui/src/pages/Agents.test.tsx (min-w-[7rem]). */
--sz-calc-29: calc(100% + 3rem); /* Extracted from ui/src/pages/BoardChat.tsx (h-[calc(100%+3rem)]). */
--sz-22rem: 22rem; /* Extracted from ui/src/pages/CompanyEnvironments.tsx (h-[22rem]). */
--sz-560px: 560px; /* Extracted from ui/src/pages/CompanyExport.tsx (min-h-[560px]). */
--sz-520px: 520px; /* Extracted from ui/src/pages/CompanyExport.tsx (min-h-[520px]). */
--sz-480px: 480px; /* Extracted from ui/src/pages/CompanyExport.tsx (max-h-[480px]). */
--sz-calc-30: calc(100vh - 12rem); /* Extracted from ui/src/pages/CompanyExport.tsx (h-[calc(100vh-12rem)]). */
--sz-calc-31: calc(100vh - 16rem); /* Extracted from ui/src/pages/CompanyImport.tsx (h-[calc(100vh-16rem)]). */
--sz-calc-32: min(28rem,70vh); /* Extracted from ui/src/pages/CompanySkills.tsx (max-h-[min(28rem,70vh)]). */
--sz-11_5rem: 11.5rem; /* Extracted from ui/src/pages/CompanySkills.tsx (min-h-[11.5rem]). */
--sz-11_75rem: 11.75rem; /* SkillStudio.tsx file/input browser cap: five min-h-9 rows plus p-1 vertical padding. */
--sz-calc-33: calc(100dvh - 6rem); /* Extracted from ui/src/pages/CompanySkills.tsx (h-[calc(100dvh-6rem)]). */
--sz-calc-34: clamp(14rem,45vh,28rem); /* Extracted from ui/src/pages/CompanySkills.tsx (h-[clamp(14rem,45vh,28rem)]). */
--sz-400px: 400px; /* Extracted from ui/src/pages/CompanySkills.tsx (min-h-[400px]). */
--sz-calc-35: min(34rem,55vw); /* Extracted from ui/src/pages/CompanySkills.tsx (max-w-[min(34rem,55vw)]). */
--sz-640px: 640px; /* Extracted from ui/src/pages/DesignGuide.tsx (max-w-[640px]). */
--sz-28rem: 28rem; /* Extracted from ui/src/pages/DesignGuide.tsx (w-[28rem]). */
--sz-170px: 170px; /* Extracted from ui/src/pages/Inbox.tsx (w-[170px]). */
--sz-calc-36: min(28rem,65vh); /* Extracted from ui/src/pages/InstanceExperimentalSettings.tsx (max-h-[min(28rem,65vh)]). */
--sz-calc-37: min(100%,22rem); /* Extracted from ui/src/pages/IssueDetail.tsx (w-[min(100%,22rem)]). */
--sz-85dvh: 85dvh; /* Extracted from ui/src/pages/IssueDetail.tsx (max-h-[85dvh]). */
--sz-calc-38: calc(100dvh - 9rem); /* Extracted from ui/src/pages/OrgChart.tsx (h-[calc(100dvh-9rem)]). */
--sz-720px: 720px; /* Extracted from ui/src/pages/RunTranscriptUxLab.tsx (max-h-[720px]). */
--sz-460px: 460px; /* Extracted from ui/src/pages/RunTranscriptUxLab.tsx (max-h-[460px]). */
--sz-960px: 960px; /* Extracted from ui/src/pages/Search.tsx (max-w-[960px]). */
--sz-70px: 70px; /* Extracted from ui/src/pages/Secrets.tsx (min-h-[70px]). */
--sz-calc-41: min(520px,calc(100vw - 2rem)); /* Extracted from ui/src/pages/Secrets.tsx (w-[min(520px,calc(100vw-2rem))]). */
--sz-calc-42: min(80vh,34rem); /* Extracted from ui/src/pages/Secrets.tsx (max-h-[min(80vh,34rem)]). */
--sz-calc-43: min(98vw,90rem); /* RunnerInspector sheet width. */
--sz-14rem: 14rem; /* Extracted from ui/src/pages/TeamCatalog.tsx (max-w-[14rem]). */
--rad-3: 3px; /* Extracted from ui/src/components/ExternalObjectPill.tsx (ring-[3px]). */
--rad-4: 4px; /* Extracted from ui/src/components/IssueChatThread.tsx (rounded-br-[4px]). */
--rad-2: 2px; /* Extracted from ui/src/components/SidebarCompanyMenu.tsx (ring-[2px]). */
--rad-14: 14px; /* Extracted from ui/src/pages/CompanySettings.tsx (rounded-[14px]). */
--rad-28: 28px; /* Extracted from ui/src/pages/InviteUxLab.tsx (rounded-[28px]). */
--rad-24: 24px; /* Extracted from ui/src/pages/InviteUxLab.tsx (rounded-[24px]). */
--rad-32: 32px; /* Extracted from ui/src/pages/InviteUxLab.tsx (rounded-[32px]). */
--pct-90: 90%; /* Extracted from ui/src/components/DocumentDiffModal.tsx (max-w-[90%]). */
--pct-80: 80%; /* TaskChatThread composer width (PAP-498 follow-up). */
--pct-50: 50%; /* Extracted from ui/src/components/ImageGalleryModal.tsx (max-w-[50%]). */
--pct-85: 85%; /* Extracted from ui/src/components/IssueChatThread.test.tsx (max-w-[85%]). */
--pct-70: 70%; /* Extracted from ui/src/components/CaseFieldsPanel.tsx (max-w-[70%]). */
--pct-neg-50: -50%; /* Extracted from ui/src/components/ui/alert-dialog.tsx (translate-x-[-50%]). */
--pct-72: 72%; /* Extracted from ui/src/pages/IssueDetail.tsx (w-[72%]). */
--shadow-extract-1: 0 16px 40px rgba(37,99,235,0.08); /* ActiveAgentsPanel.tsx live-box glow. Gallery feedback r2: liveness glow recolored cyan->status blue (value edit, site unchanged; originally extracted as rgba(6,182,212,0.08)). */
--shadow-extract-2: 0 20px 80px -40px rgba(0,0,0,0.55); /* Extracted from ui/src/components/BudgetPolicyCard.tsx (shadow-[0_20px_80px_-40px_rgba(0,0,0,0.55)]). */
--shadow-extract-3: 0 0 0 1px rgba(255,255,255,0.08); /* Extracted from ui/src/components/BudgetSidebarMarker.tsx (shadow-[0_0_0_1px_rgba(255,255,255,0.08)]). */
--shadow-extract-4: 0 -12px 28px rgba(15,23,42,0.08); /* Extracted from ui/src/components/ChatComposer.test.tsx (shadow-[0_-12px_28px_rgba(15,23,42,0.08)]). */
--shadow-extract-5: 0 -12px 28px rgba(0,0,0,0.28); /* Extracted from ui/src/components/ChatComposer.test.tsx (shadow-[0_-12px_28px_rgba(0,0,0,0.28)]). */
--shadow-extract-6: 0 0 0 1px var(--color-background); /* Extracted from ui/src/components/DocumentAnnotationLayer.tsx (shadow-[0_0_0_1px_var(--color-background)]). */
--shadow-extract-7: 0 -12px 28px rgba(15,23,42,0.08),0 0 0 1px color-mix(in oklab, var(--primary) 16%, transparent); /* Extracted from ui/src/components/IssueChatThread.tsx; corrected for full-color semantic tokens. */
--shadow-extract-8: 0 1px 0 rgba(15,23,42,0.02); /* Extracted from ui/src/components/IssueRecoveryActionCard.tsx (shadow-[0_1px_0_rgba(15,23,42,0.02)]). */
--shadow-extract-9: 0 18px 42px rgba(15,23,42,0.06); /* Extracted from ui/src/components/IssueThreadInteractionCard.tsx (shadow-[0_18px_42px_rgba(15,23,42,0.06)]). */
--shadow-extract-10: 0 1px 0 1px var(--border); /* Extracted from ui/src/components/KeyboardShortcutsCheatsheet.tsx; corrected for full-color semantic tokens. */
--shadow-extract-11: 0 18px 50px rgba(37,99,235,0.08); /* LiveRunWidget.tsx glow. Gallery feedback r2: liveness glow recolored cyan->status blue (value edit, site unchanged; originally extracted as rgba(6,182,212,0.08)). */
--shadow-extract-12: 0 0 0 2px var(--background); /* Production sidebar icon-badge outline. */
--shadow-sidebar-icon-badge: 0 0 0 2px var(--muted); /* Streamlined sidebar icon-badge outline; matches the sidebar surface. */
--shadow-extract-13: 0 0 0 3px rgba(245,158,11,0.18); /* Extracted from ui/src/components/environment-variables-editor/index.tsx (shadow-[0_0_0_3px_rgba(245,158,11,0.18)]). */
--shadow-extract-14: 0 0 12px rgba(37,99,235,0.08); /* AgentDetail.tsx live-card glow. Gallery feedback r2: liveness glow recolored cyan->status blue (value edit, site unchanged; originally extracted as rgba(6,182,212,0.08)). */
--shadow-extract-18: 0 18px 44px -28px rgba(0,0,0,0.45); /* Extracted from ui/src/pages/ProfileSettings.tsx (shadow-[0_18px_44px_-28px_rgba(0,0,0,0.45)]). */
}
/* ── Extracted verbatim MISC tokens (Phase 2 Batch 4, design/token-extraction) ── */
/* Batch 4/4 (final sweep): grid track lists, transition-property
lists, z-index, scale, easing, vertical-align, stroke-width, blur,
drop-shadow, and half-migrated var(x, fallback) color forms,
verbatim (no normalizing - the human scale-collapse decision comes
later per DESIGN.md/TOKEN-AUDIT.md). --gtc-* and --gtr-* and --tp-* are
sequentially numbered / slugged since their content (track lists,
property lists) is not safely nameable by value alone.
drop-shadow (a CSS filter function) gets its own
--drop-shadow-extract-* family, kept separate from Batch 3's
--shadow-extract-* box-shadow family per that batch's logged
human-decision note.
*-resolved wrapper tokens hold a verbatim var(--x, fallback)
expression for sites where Tailwind v4's paren-with-fallback-comma
shorthand does not parse (confirmed unsupported in this batch's
Step 0 syntax spike). theme(colors.a.b) fallbacks were resolved to
the equivalent var(--token) form the Tailwind build already
compiles them to today (inspected byte-for-byte from the built
storybook-static CSS before this codemod ran: theme(colors.muted.DEFAULT)
-> var(--muted), theme(colors.muted.foreground) -> var(--muted-foreground)).
--gradient-extract-* here CONTINUES Batch 1's counter (Batch 1 minted
1 through 24) rather than restarting it - these 2 sites use
hsl(var(...))/color-mix(...) CSS-native color functions rather than raw
hex/rgb literals, which is why Batch 1's hand-audited color-literal
table did not catch them, but they are still value-bearing gradient
brackets in scope for this batch's final sweep.
Allowlist (sites intentionally left as-is - see ALLOWLIST doc-comment
at the end of this file for the machine-readable list consumed by
scripts/check-token-gates.mjs):
allow ui/src/components/ui/dialog.tsx — tw-animate-css plugin utilities (zoom-in-[0.97] etc.) are dead/no-op classes today (plugin not installed, verified via built-CSS grep); nothing to tokenize without visually changing a currently-inert class
allow ui/src/components/ui/alert-dialog.tsx — same tw-animate-css dead-class situation as dialog.tsx
*/
:root {
--gtc-1: 56px 56px 24px minmax(0,1fr); /* Extracted from ui/src/components/DocumentDiffModal.tsx (grid-cols-[56px_56px_24px_minmax(0,1fr)]). */
--gtc-case-revisions: 16rem 1fr; /* Extracted from ui/src/components/CaseRevisionRail.tsx (grid-cols-[16rem_1fr]). */
--gtc-task-chat-details: 6rem minmax(0,1fr);
--gtc-2: auto minmax(0,1fr) 2.25rem; /* Extracted from ui/src/components/FileTree.tsx (grid-cols-[auto_minmax(0,1fr)_2.25rem]). */
--gtc-3: minmax(0,1fr) 2.25rem; /* Extracted from ui/src/components/FileTree.tsx (grid-cols-[minmax(0,1fr)_2.25rem]). */
--gtc-4: minmax(0,1fr); /* Extracted from ui/src/components/FileTree.tsx (grid-cols-[minmax(0,1fr)]). */
--gtc-5: auto minmax(0,1fr); /* Extracted from ui/src/components/FileViewerSheet.tsx (grid-cols-[auto_minmax(0,1fr)]). */
--gtc-6: auto minmax(0,1fr) auto; /* Extracted from ui/src/components/FileViewerSheet.tsx (grid-cols-[auto_minmax(0,1fr)_auto]). */
--gtc-7: 7.5rem minmax(0,1fr); /* Extracted from ui/src/components/IssueChatThread.tsx (grid-cols-[7.5rem_minmax(0,1fr)]). */
--gtc-8: 7.5rem 1fr; /* Extracted from ui/src/components/IssueRecoveryActionCard.tsx (grid-cols-[7.5rem_1fr]). */
--gtc-9: 300px minmax(0,1fr); /* Extracted from ui/src/components/RoutineHistoryTab.tsx (grid-cols-[300px_minmax(0,1fr)]). */
--gtc-10: 10rem 1fr; /* Extracted from ui/src/components/SourceResolvedFoldCallout.tsx (grid-cols-[10rem_1fr]). */
--gtc-11: 12rem 1fr; /* Extracted from ui/src/components/SourceResolvedFoldCallout.tsx (grid-cols-[12rem_1fr]). */
--gtc-12: minmax(0,0.75fr) minmax(0,1fr); /* Extracted from ui/src/components/TrustPresetSection.tsx (grid-cols-[minmax(0,0.75fr)_minmax(0,1fr)]). */
--gtc-13: minmax(0,1fr) auto; /* Extracted from ui/src/components/environment-variables-editor/Row.tsx (grid-cols-[minmax(0,1fr)_auto]). */
--gtc-14: minmax(160px,2fr) minmax(240px,3fr) 32px; /* Extracted from ui/src/components/environment-variables-editor/Row.tsx (grid-cols-[minmax(160px,2fr)_minmax(240px,3fr)_32px]). */
--gtc-15: 6rem 1fr; /* Extracted from ui/src/components/issue-properties/IssueProperties.tsx (grid-cols-[6rem_1fr]). */
--gtc-16: auto 1fr; /* Extracted from ui/src/components/transcript/RunTranscriptView.tsx (grid-cols-[auto_1fr]). */
--gtc-17: 1fr auto; /* Extracted from ui/src/components/ui/card.tsx (grid-cols-[1fr_auto]). */
--gtc-18: auto minmax(0,1fr) minmax(12rem,0.65fr); /* Extracted from ui/src/pages/AgentDetail.tsx (grid-cols-[auto_minmax(0,1fr)_minmax(12rem,0.65fr)]). */
--gtc-19: 260px minmax(0,1fr); /* Extracted from ui/src/pages/AgentDetail.tsx (grid-cols-[260px_minmax(0,1fr)]). */
/* --gtc-20 through --gtc-23 retired with the removed Cloud Upstream page. */
--gtc-24: minmax(0,1.5fr) 120px 120px 180px; /* Extracted from ui/src/pages/CompanyAccess.tsx (grid-cols-[minmax(0,1.5fr)_120px_120px_180px]). */
--gtc-25: 19rem minmax(0,1fr); /* Extracted from ui/src/pages/CompanyExport.tsx (grid-cols-[19rem_minmax(0,1fr)]). */
--gtc-26: 7rem minmax(0,1fr); /* Extracted from ui/src/pages/CompanySkills.tsx (grid-cols-[7rem_minmax(0,1fr)]). */
--gtc-27: minmax(0,1fr) auto auto; /* Extracted from ui/src/pages/CompanySkills.tsx (grid-cols-[minmax(0,1fr)_auto_auto]). */
--gtc-28: 13rem minmax(0,1fr); /* Extracted from ui/src/pages/CompanySkills.tsx (grid-cols-[13rem_minmax(0,1fr)]). */
--gtc-29: minmax(0,1fr) 18rem; /* Extracted from ui/src/pages/CompanySkills.tsx (grid-cols-[minmax(0,1fr)_18rem]). */
--gtc-30: 14rem minmax(0,1fr); /* Extracted from ui/src/pages/CompanySkills.tsx (grid-cols-[14rem_minmax(0,1fr)]). */
--gtc-31: 1.3fr,1fr; /* Extracted from ui/src/pages/Costs.tsx (grid-cols-[1.3fr,1fr]). */
--gtc-32: 1.25fr,0.95fr; /* Extracted from ui/src/pages/Costs.tsx (grid-cols-[1.25fr,0.95fr]). */
--gtc-33: 1.2fr,0.95fr; /* Extracted from ui/src/pages/Costs.tsx (grid-cols-[1.2fr,0.95fr]). */
--gtc-34: 320px minmax(0,1fr); /* Extracted from ui/src/pages/InstanceAccess.tsx (grid-cols-[320px_minmax(0,1fr)]). */
--gtc-35: minmax(10rem,14rem) 1fr; /* Extracted from ui/src/pages/InstanceExperimentalSettings.tsx (grid-cols-[minmax(10rem,14rem)_1fr]). */
--gtc-36: minmax(0,1.15fr) minmax(360px,0.85fr); /* Extracted from ui/src/pages/InviteLanding.tsx (grid-cols-[minmax(0,1.15fr)_minmax(360px,0.85fr)]). */
--gtc-37: minmax(0,1.15fr) minmax(320px,0.85fr); /* Extracted from ui/src/pages/InviteUxLab.tsx (grid-cols-[minmax(0,1.15fr)_minmax(320px,0.85fr)]). */
--gtc-38: minmax(0,0.92fr) minmax(0,1.08fr); /* Extracted from ui/src/pages/InviteUxLab.tsx (grid-cols-[minmax(0,0.92fr)_minmax(0,1.08fr)]). */
--gtc-39: minmax(0,1.2fr) 320px; /* Extracted from ui/src/pages/InviteUxLab.tsx (grid-cols-[minmax(0,1.2fr)_320px]). */
--gtc-40: minmax(0,1fr) 320px; /* Extracted from ui/src/pages/IssueChatLongThreadPerf.tsx (grid-cols-[minmax(0,1fr)_320px]). */
--gtc-52: minmax(0,1.4fr) minmax(220px,0.8fr); /* Extracted from ui/src/pages/PluginSettings.tsx (grid-cols-[minmax(0,1.4fr)_minmax(220px,0.8fr)]). */
--gtc-53: minmax(0,1.4fr) minmax(18rem,0.9fr); /* Extracted from ui/src/pages/ProjectWorkspaceDetail.tsx (grid-cols-[minmax(0,1.4fr)_minmax(18rem,0.9fr)]). */
--gtc-54: minmax(12rem,2.4fr) minmax(5.25rem,0.75fr) minmax(7rem,1fr) minmax(5rem,0.7fr) 2.75rem; /* Extracted from ui/src/pages/Secrets.tsx (grid-cols-[minmax(12rem,2.4fr)_minmax(5.25rem,0.75fr)_minmax(7rem,1fr)_minmax(5rem,0.7fr)_2.75rem]). */
--gtc-55: 8rem minmax(0,1fr); /* Extracted from ui/src/pages/Secrets.tsx (grid-cols-[8rem_minmax(0,1fr)]). */
--gtc-56: minmax(0,1fr) minmax(14rem,18rem); /* Extracted from ui/src/pages/TeamCatalog.tsx (grid-cols-[minmax(0,1fr)_minmax(14rem,18rem)]). */
--gtc-57: repeat(14,minmax(0,1fr)); /* Extracted from ui/src/pages/UserProfile.tsx (grid-cols-[repeat(14,minmax(0,1fr))]). */
--gtc-58: auto 1fr auto; /* Extracted from ui/src/pages/UserProfile.tsx (grid-cols-[auto_1fr_auto]). */
--gtc-59: 8rem 1fr;
--gtc-60: 1fr 1fr;
--gtc-61: 1fr 1fr 1fr;
--gtc-62: 1fr minmax(0,18rem);
--gtc-63: 280px 1fr;
--gtc-64: minmax(0,20rem) 1fr;
--gtc-65: minmax(8rem,0.38fr) minmax(0,0.62fr) auto;
--gtc-runner-inspector-stage: 1.25rem minmax(0,1fr);
--gtc-runner-inspector-filters: minmax(14rem,1fr) repeat(5,minmax(8rem,auto));
--gtc-runner-inspector-pipeline: 21rem minmax(0,1fr);
--gtc-runner-inspector-fields: 8rem 1fr;
--gtc-runner-inspector-presentation: minmax(18rem,0.8fr) minmax(20rem,1.2fr);
--gtc-runner-inspector-trace: 20rem minmax(0,1fr);
--gtr-1: auto auto; /* Extracted from ui/src/components/ui/card.tsx (grid-rows-[auto_auto]). */
--gtr-2: 1fr; /* Extracted from ui/src/pages/CompanySkills.tsx (grid-rows-[1fr]). */
--gtr-3: 0fr; /* Extracted from ui/src/pages/CompanySkills.tsx (grid-rows-[0fr]). */
--tp-background-color-border-color: background-color,border-color; /* Extracted from ui/src/components/ActivityFeed.tsx (transition-[background-color,border-color]). */
--tp-width-background-color: width,background-color; /* Extracted from ui/src/components/BudgetPolicyCard.tsx (transition-[width,background-color]). */
--tp-width: width; /* Extracted from ui/src/components/ClaudeSubscriptionPanel.tsx (transition-[width]). */
--tp-background-color-color-opacity: background-color,color,opacity; /* Extracted from ui/src/components/FileTree.tsx (transition-[background-color,color,opacity]). */
--tp-max-height: max-height; /* Extracted from ui/src/components/FoldCurtain.tsx (transition-[max-height]). */
--tp-border-color-background-color-box-shadow: border-color,background-color,box-shadow; /* Extracted from ui/src/components/IssueChatThread.tsx (transition-[border-color,background-color,box-shadow]). */
--tp-width-min-width: width,min-width; /* Extracted from ui/src/components/KanbanBoard.tsx (transition-[width,min-width]). */
--tp-background-color-right: background-color,right; /* Extracted from ui/src/components/ScrollToBottom.tsx (transition-[background-color,right]). */
--tp-background-color-border-color-color: background-color,border-color,color; /* Extracted from ui/src/components/SystemNotice.tsx (transition-[background-color,border-color,color]). */
--tp-transform-opacity: transform,opacity; /* Extracted from ui/src/components/ToastViewport.tsx (transition-[transform,opacity]). */
--tp-color-box-shadow: color,box-shadow; /* Extracted from ui/src/components/ui/badge.tsx (transition-[color,box-shadow]). */
--tp-color-background-color-border-color-box-shadow-opacity: color,background-color,border-color,box-shadow,opacity; /* Extracted from ui/src/components/ui/button.tsx (transition-[color,background-color,border-color,box-shadow,opacity]). */
--tp-border-color-background-color: border-color,background-color; /* Extracted from ui/src/components/ui/radio-card.tsx (transition-[border-color,background-color]). */
--tp-color-background-color-border-color-box-shadow: color,background-color,border-color,box-shadow; /* Extracted from ui/src/components/ui/tabs.tsx (transition-[color,background-color,border-color,box-shadow]). */
--tp-grid-template-rows-opacity: grid-template-rows,opacity; /* Extracted from ui/src/pages/CompanySkills.tsx (transition-[grid-template-rows,opacity]). */
--tp-box-shadow-border-color: box-shadow,border-color; /* Extracted from ui/src/pages/OrgChart.tsx (transition-[box-shadow,border-color]). */
--tp-box-shadow: box-shadow; /* Extracted from ui/src/pages/ProjectDetail.tsx (transition-[box-shadow]). */
--tp-transform-box-shadow: transform,box-shadow; /* Extracted from ui/src/pages/ProjectDetail.tsx (transition-[transform,box-shadow]). */
--z-2: 2; /* Extracted from ui/src/components/DocumentAnnotationLayer.tsx (z-[2]). */
--z-20: 20; /* Anchored document annotation popover. */
--z-60: 60; /* Extracted from ui/src/components/DocumentAnnotationPanel.tsx (z-[60]). */
--z-1: 1; /* Extracted from ui/src/components/IssueDocumentAnnotations.tsx (z-[1]). */
--z-200: 200; /* Extracted from ui/src/components/Layout.tsx (z-[200]). */
--z-9999: 9999; /* Extracted from ui/src/components/MarkdownEditor.tsx (z-[9999]). */
--z-120: 120; /* Extracted from ui/src/components/ToastViewport.tsx (z-[120]). */
--s-0_98: 0.98; /* Extracted from ui/src/pages/Inbox.tsx (scale-[0.98]). */
--s-1_02: 1.02; /* Extracted from ui/src/components/CaseAttachmentsGallery.tsx (scale-[1.02]). */
--e-cubic-bezier-0_16-1-0_3-1: cubic-bezier(0.16,1,0.3,1); /* Extracted from ui/src/components/ui/alert-dialog.tsx (ease-[cubic-bezier(0.16,1,0.3,1)]). */
--va-0_125em: -0.125em; /* Extracted from ui/src/components/ExternalObjectStatusIcon.tsx (align-[-0.125em]). */
--sw-2_3: 2.3; /* Extracted from ui/src/components/MobileBottomNav.tsx (stroke-[2.3]). */
--blur-1px: 1px; /* Extracted from ui/src/components/ChatComposer.tsx (backdrop-blur-[1px]). */
--blur-2px: 2px; /* Extracted from ui/src/components/IssueChatThread.tsx (backdrop-blur-[2px]). */
--drop-shadow-extract-1: 0 1px 2px rgba(0,0,0,0.65); /* Extracted from ui/src/components/CompanyPatternIcon.tsx (drop-shadow-[0_1px_2px_rgba(0,0,0,0.65)]). */
--code-bg-resolved: var(--paperclip-code-bg, var(--muted)); /* Extracted from ui/src/components/FileViewerSheet.tsx (bg-[var(--paperclip-code-bg,theme(colors.muted.DEFAULT))]). */
--paperclip-code-highlight-bg: rgba(250,204,21,0.12); /* DECISION-SHEET.md A5: FileViewerSheet.tsx referenced this var without it ever being defined, relying on the var() fallback; minted here at exactly the former fallback value (zero pixels). */
--paperclip-code-highlight-border: rgb(234,179,8); /* DECISION-SHEET.md A5: same as --paperclip-code-highlight-bg — minted at the former fallback value. */
--code-highlight-bg-resolved: var(--paperclip-code-highlight-bg); /* Extracted from ui/src/components/FileViewerSheet.tsx; fallback dropped in A5 now that --paperclip-code-highlight-bg is a real token (was var(--paperclip-code-highlight-bg, rgba(250,204,21,0.12))). */
--code-gutter-fg-resolved: var(--paperclip-code-gutter-fg, var(--muted-foreground)); /* Extracted from ui/src/components/FileViewerSheet.tsx (text-[var(--paperclip-code-gutter-fg,theme(colors.muted.foreground))]). */
--code-highlight-border-resolved: var(--paperclip-code-highlight-border); /* Extracted from ui/src/components/FileViewerSheet.tsx; fallback dropped in A5 now that --paperclip-code-highlight-border is a real token (was var(--paperclip-code-highlight-border, rgb(234,179,8))). */
--gradient-extract-25: linear-gradient(135deg,var(--primary) 0%,var(--accent) 55%,var(--muted) 100%); /* Extracted from ui/src/components/SidebarAccountMenu.tsx; corrected for full-color semantic tokens. */
--gradient-extract-26: linear-gradient(135deg,var(--primary) 0%,var(--accent) 58%,color-mix(in oklab,var(--background) 76%,white 24%) 100%); /* Extracted from ui/src/pages/ProfileSettings.tsx; corrected for full-color semantic tokens. */
}
/* ── ALLOWLIST (Phase 2, design/token-extraction) ──────────────────────────
Canonical, MACHINE-READABLE allowlist consumed by
scripts/check-token-gates.mjs. This block consolidates every site the
Batch 1-4 codemods intentionally left un-tokenized (functional/third-party
values, CSS keywords, and currently-inert dead classes) into one list the
gate script parses directly, rather than re-deriving it from each batch's
prose log above. The per-batch prose blocks above are kept as historical
narrative (why each decision was made); this block is the source of truth
for WHICH sites are exempt.
CRITERIA UPDATE (Run 2 review, DECISION-SHEET.md B1 user ruling): the
allowlist criteria now also include FIRST-PARTY INTENTIONAL ONE-OFF
DECORATION — deliberate, never-reused decorative gradients/shadows on
demo/UX-lab surfaces stay inline (spirit over letter of DESIGN.md
principle 2) rather than being minted as singleton --gradient-extract-*
and --shadow-extract-* tokens. Production-surface decoration tokens were
KEPT; only tokens whose sole consumers were *UxLab.tsx demo pages were
reverted to their original literal form under this rule.
Format (one line per entry, do not reflow):
* allow <repo-relative-path> — <reason>
A path may appear more than once if it has more than one distinct
allowlisted concern. Paths are matched by the gate script as a substring
of the file being checked (repo-relative, POSIX-style).
* allow ui/src/pages/CompanyEnvironments.tsx — xterm.js terminal theme config (background/foreground/cursor/cursorAccent/selectionBackground hex, fontSize: 12); functional third-party option object, not a rendered CSS value
* allow ui/src/components/issue-properties/IssueProperties.tsx — newLabelColor color-picker seed state persisted into the label-create payload sent to the backend; a var() string would corrupt that payload
* allow ui/src/pages/CompanySkills.tsx — DISCOVERY_ACCENTS palette array feeds skill.color, persisted/compared JS data (SkillCreateDraft), not just a rendered value; also fontSize: Math.round(size * 0.42), computed at runtime from a prop, not a static literal
* allow ui/src/components/IssueColumns.tsx — accentColor fallback also feeds pickTextColorForPillBg() contrast math (lib/color-contrast.ts), which needs a real hex string to compute luminance
* allow ui/src/components/CompanyPatternIcon.tsx — canvas 2D fillStyle built from a runtime-computed template literal, not a static literal at all
* allow ui/src/components/onboarding/PillGuy.tsx — inline rendering of the approved pill-1 dormant/alive brand SVG assets; their fixed eye, body, tuft, and gradient fills are the illustration artwork rather than themeable interface colors
* allow ui/src/components/ui/scroll-area.tsx — rounded-[inherit] is a CSS keyword, not a literal value; nothing to extract
* allow ui/src/components/ui/dialog.tsx — tw-animate-css plugin utilities (zoom-in-[0.97], zoom-out-[0.97], slide-in-from-top-[1%], slide-out-to-top-[1%], animate-in, animate-out, fade-in-0, fade-out-0) are dead/no-op classes today (the tw-animate-css plugin is not installed and no matching @utility exists in this file; confirmed via grep of the built storybook-static CSS — none of these class names emit any rule); nothing to tokenize without visually changing a currently-inert class
* allow ui/src/components/ui/alert-dialog.tsx — same tw-animate-css dead-class situation as dialog.tsx (zoom-out-[0.97], zoom-in-[0.97])
* allow ui/src/components/ProjectWorkspaceSummaryCard.tsx — min-[420px]: is a Tailwind arbitrary BREAKPOINT variant (responsive container-query-style prefix), not a value applied to a property; the variant position cannot reference a CSS custom property, so it cannot be tokenized without changing Tailwind's own variant syntax
* allow ui/src/components/FileTree.tsx — max-[480px]: arbitrary breakpoint variant (x2), same reasoning as min-[420px] above
* allow ui/src/pages/RunTranscriptUxLab.tsx — first-party intentional one-off decoration (demo/UX-lab page): hero/card gradients + shadows reverted from singleton tokens per DECISION-SHEET.md B1 user ruling
* allow ui/src/pages/InviteUxLab.tsx — first-party intentional one-off decoration (demo/UX-lab page): hero/card gradients + shadows reverted from singleton tokens per DECISION-SHEET.md B1 user ruling
* allow ui/src/pages/IssueChatUxLab.tsx — first-party intentional one-off decoration (demo/UX-lab page): hero/card gradients + shadows reverted from singleton tokens per DECISION-SHEET.md B1 user ruling
* allow ui/src/pages/SystemNoticeUxLab.tsx — first-party intentional one-off decoration (demo/UX-lab page): hero/card gradients + shadows reverted from singleton tokens per DECISION-SHEET.md B1 user ruling
Test-fixture hex-literal policy (Batch 4, resolving TOKEN-AUDIT.md section
8 item 10 "Needs human decision" for these specific sites): the following
*.test.tsx files pass hex-literal strings as MOCK PROP / MOCK API-RESPONSE
DATA (label/project/company/brand `color` fields, xterm.js theme-mock
assertions) — not as Tailwind class strings or CSS-in-JS style
declarations. These are the test-file counterpart of the same
"functional/persisted data, not a rendered CSS value" category Batch 1
already allowlisted in production source (see CompanySkills.tsx,
IssueProperties.tsx, IssueColumns.tsx above); tokenizing them would
replace realistic-looking mock data with `var()` strings that no longer
resemble what a real API response contains, without touching any
rendered pixel. Each file is listed individually for reviewability.
* allow ui/src/components/IssueFiltersPopover.test.tsx — mock label color prop
* allow ui/src/components/IssueProperties.test.tsx — mock label/project color fixtures
* allow ui/src/components/MarkdownBody.test.tsx — mock project color fed through a mention-href builder
* allow ui/src/components/MarkdownEditor.test.tsx — mock projectColor mention fixture
* allow ui/src/components/NewIssueDialog.test.tsx — mock label color fixtures
* allow ui/src/components/ProjectTile.test.tsx — mock project color prop
* allow ui/src/components/RoutineRunVariablesDialog.test.tsx — mock label color fixture
* allow ui/src/components/SidebarProjects.test.tsx — mock project color fixture
* allow ui/src/components/SidebarStarredProjects.test.tsx — mock project color fixture
* allow ui/src/pages/CompanyEnvironments.test.tsx — xterm.js theme-mock assertion values (mirrors the allowlisted xterm.js config in the CompanyEnvironments.tsx source above)
* allow ui/src/pages/ExecutionWorkspaceDetail.test.tsx — mock project color fixture
* allow ui/src/pages/InviteLanding.test.tsx — "#NNNN" pull-request references inside explanatory comments, not color literals
* allow ui/src/pages/IssueDetail.test.tsx — mock project color fixture
* allow ui/src/pages/ProjectDetail.test.tsx — mock project color fixture
* allow ui/src/pages/ProjectWorkspaceDetail.test.tsx — mock project color fixture
* allow ui/src/pages/Projects.test.tsx — mock project color fixture
* allow ui/src/pages/Routines.test.tsx — mock routine/label color fixtures
── end allowlist ── */
/* Agent settings composition, shared with Storybook. */
.agent-yaml-editor .cm-editor {
height: var(--sz-480px);
font-family: var(--font-mono);
font-size: var(--text-sm);
background: var(--background);
color: var(--foreground);
}
.agent-yaml-editor .cm-scroller { overflow: auto; }
.agent-yaml-editor .cm-gutters {
background: var(--muted);
color: var(--muted-foreground);
border-color: var(--border);
}
.agent-yaml-editor .cm-activeLine,
.agent-yaml-editor .cm-activeLineGutter { background: var(--accent); }
.agent-yaml-editor .cm-cursor { border-left-color: var(--foreground); }
.agent-yaml-editor .cm-selectionBackground,
.agent-yaml-editor .cm-focused .cm-selectionBackground { background: var(--accent); }
.agent-yaml-editor .cm-focused { outline-color: var(--ring); }
.agent-settings-form > .relative {
@apply flex flex-col gap-8;
}
.agent-settings-form [data-config-section] {
@apply my-0;
}
.agent-settings-form [data-config-section] > .rounded-lg {
@apply bg-card p-5;
}
.agent-settings-form [data-config-section] h3 {
@apply text-sm font-semibold;
}
.agent-settings-form [data-config-section="identity"] > .rounded-lg {
@apply grid gap-5 sm:grid-cols-2;
}
.agent-settings-form [data-config-section="identity"] > .rounded-lg > * {
@apply m-0;
}
.agent-settings-form
[data-config-section="identity"]
> .rounded-lg
> :nth-child(n + 3) {
@apply sm:col-span-2;
}
.agent-settings-overview section,
.agent-settings-permissions .rounded-lg,
.agent-settings-api-keys .rounded-lg,
.agent-settings-revisions .rounded-md {
@apply bg-card;
}
.agent-settings-content > .max-w-3xl {
@apply max-w-none;
}
.agent-settings-form [data-config-section="permissions"] > .rounded-lg {
@apply grid items-start gap-5 sm:grid-cols-2;
}
.agent-settings-form [data-config-section="permissions"] > .rounded-lg > * {
@apply m-0 min-w-0;
}
.agent-settings-form
[data-config-section="permissions"]
> .rounded-lg
> [data-config-field],
.agent-settings-form [data-config-section="permissions"] > .rounded-lg > p,
.agent-settings-form
[data-config-section="permissions"]
> .rounded-lg
> div:has(textarea),
.agent-settings-form
[data-config-section="permissions"]
> .rounded-lg
> div:has([role="switch"]) {
@apply sm:col-span-2;
}
/* Task feeds own their row anchor and follow position. Browser anchoring must
not race the same correction after a prepend or transcript reconciliation. */
.task-chat-scroll-viewport {
overflow-anchor: none;
scrollbar-gutter: stable;
}
.task-chat-loading-shell .animate-pulse {
animation: none;
}
/* Concealed mobile history must not grow the document between responses.
Restore normal flow and position the finished conversation before paint. */
.task-chat-history-pending {
position: relative;
height: var(--tc-thread-max-h);
overflow: hidden;
}
.task-chat-history-pending [data-testid="task-chat-history-content"] {
position: absolute;
inset: 0;
overflow: hidden;
}
.task-chat-history-pending > [data-testid="task-chat-composer-dock"] {
position: absolute;
inset-inline: 0;
}
/* Connection and recovery cards share one surface and spacing scale. */
.task-context-notice {
padding: calc(var(--spacing) * 3);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background-color: var(--muted);
color: var(--foreground);
font-size: var(--text-sm);
line-height: var(--text-sm--line-height);
}
/* The runner keeps one fixed-height activity row between commentary updates. */
@keyframes runner-activity-roll-in {
from { transform: translateY(100%); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes runner-activity-roll-out {
from { transform: translateY(0); opacity: 1; }
to { transform: translateY(-100%); opacity: 0; }
}
.runner-activity-roll-in { animation: runner-activity-roll-in var(--motion-line-scroll) var(--motion-ease-standard) both; }
.runner-activity-roll-out { animation: runner-activity-roll-out var(--motion-line-scroll) var(--motion-ease-standard) both; }
@media (prefers-reduced-motion: reduce) {
.runner-activity-roll-in { animation: none; }
.runner-activity-roll-out { display: none; }
}
/* Keep entity-picker search controls visible when a narrow viewport opens its
software keyboard. The fixed sheet also avoids scale motion during mobile
viewport resizing. */
@media (max-width: 40rem) {
[data-radix-popper-content-wrapper]:has(> [data-mobile-entity-picker]) {
inset: 0 !important;
width: 100% !important;
height: var(--mobile-entity-picker-visual-viewport-height) !important;
min-width: 0 !important;
transform: none !important;
will-change: auto !important;
pointer-events: none;
}
[data-radix-popper-content-wrapper]:has(> [data-mobile-entity-picker])::before {
content: "";
position: absolute;
inset: 0;
background: var(--mobile-entity-picker-backdrop);
/* Hit the popper wrapper so Radix dismisses this sheet, while the parent
task dialog recognizes the tap as belonging to its nested picker. */
pointer-events: auto;
}
[data-mobile-entity-picker] {
position: fixed !important;
inset:
max(calc(var(--spacing) * 4), env(safe-area-inset-top))
calc(var(--spacing) * 4)
auto !important;
width: auto !important;
max-width: none !important;
height: calc(
var(--mobile-entity-picker-visual-viewport-height)
- max(calc(var(--spacing) * 4), env(safe-area-inset-top))
- max(calc(var(--spacing) * 4), env(safe-area-inset-bottom))
) !important;
max-height: none !important;
transform: none !important;
translate: none !important;
transform-origin: bottom center !important;
animation: none !important;
pointer-events: auto;
display: flex;
flex-direction: column;
padding: 0 !important;
overflow: hidden;
}
[data-mobile-entity-picker-header] {
display: flex;
}
[data-mobile-entity-picker] > [data-slot="command"] {
min-height: 0;
flex: 1;
}
[data-mobile-entity-picker] [data-slot="command-list"],
[data-mobile-entity-picker-list] {
max-height: none !important;
min-height: 0;
flex: 1;
}
}
:root {
--announcement-width: 22rem;
--announcement-available-width: calc(100vw - var(--spacing) * 6);
--announcement-image-height: 8.5rem;
--announcement-image-mobile-height: 6rem;
--announcement-mobile-bottom: calc(var(--spacing) * 19 + env(safe-area-inset-bottom, 0px));
--announcement-max-height: calc(100dvh - var(--spacing) * 6);
--announcement-mobile-max-height: calc(100dvh - var(--announcement-mobile-bottom) - var(--spacing) * 3);
}
.announcement-well {
animation: announcement-enter var(--motion-duration-fast) var(--motion-ease-standard) both;
}
@keyframes announcement-enter {
from { opacity: 0; }
to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.announcement-well { animation: none; }
}
/* Native disclosure marker is replaced by the connection-health chevron. */
.chat-connection-health-summary::-webkit-details-marker {
display: none;
}
/* Immutable ClipLab cap-v1 artwork tokens. New artwork gets a new version. */
:root {
--agent-cap-v1-bubblegum-sky-a: #8bd4ff;
--agent-cap-v1-bubblegum-sky-b: #ff51bf;
--agent-cap-v1-pink-lemonade-a: #ff26a8;
--agent-cap-v1-pink-lemonade-b: #fff78a;
--agent-cap-v1-orchid-peach-a: #e771ff;
--agent-cap-v1-orchid-peach-b: #ffd87c;
--agent-cap-v1-coral-mint-a: #beffe8;
--agent-cap-v1-coral-mint-b: #ff797b;
--agent-cap-v1-lime-lagoon-a: #b4ffa4;
--agent-cap-v1-lime-lagoon-b: #26dfff;
--agent-cap-v1-arctic-blue-a: #97fff3;
--agent-cap-v1-arctic-blue-b: #0084ff;
--agent-cap-v1-solar-flare-a: #ecca5c;
--agent-cap-v1-solar-flare-b: #fe3c3f;
--agent-cap-v1-violet-ember-a: #ff5c43;
--agent-cap-v1-violet-ember-b: #6262ff;
--agent-cap-v1-deep-tide-a: #003d60;
--agent-cap-v1-deep-tide-b: #32fffc;
--agent-cap-v1-coral-current-a: #26e2ff;
--agent-cap-v1-coral-current-b: #ff6666;
--agent-cap-v1-golden-hour-a: #ffcd4f;
--agent-cap-v1-golden-hour-b: #fde5ba;
--agent-cap-v1-tangerine-cobalt-a: #ffad32;
--agent-cap-v1-tangerine-cobalt-b: #4169ff;
--agent-cap-v1-electric-grove-a: #b4ff32;
--agent-cap-v1-electric-grove-b: #008d58;
--agent-cap-v1-flamingo-jade-a: #ff4f9a;
--agent-cap-v1-flamingo-jade-b: #36edaa;
--agent-cap-v1-cherry-pop-a: #ff69db;
--agent-cap-v1-cherry-pop-b: #d51c46;
--agent-cap-v1-turquoise-cherry-a: #37f0db;
--agent-cap-v1-turquoise-cherry-b: #ff4f64;
--agent-cap-v1-ultraviolet-tide-a: #7516cf;
--agent-cap-v1-ultraviolet-tide-b: #00e8d1;
--agent-cap-v1-muted-dream-a: #a6aaad;
--agent-cap-v1-muted-dream-b: #44464a;
}
/* Persistent plugin surface. The host reserves mobile navigation/safe-area
space; each extension owns its accessible panel and focus lifecycle. */
:root {
--plugin-shell-gap: calc(var(--spacing) * 3);
--plugin-shell-inset: calc(var(--spacing) * 4);
--plugin-shell-mobile-bottom: calc(var(--sz-calc-14) + var(--sz-safe-bottom) + var(--plugin-shell-inset));
}
.plugin-app-shell-overlays {
position: fixed;
inset-inline-end: max(var(--plugin-shell-inset), env(safe-area-inset-right));
bottom: max(var(--plugin-shell-inset), var(--sz-safe-bottom));
z-index: var(--z-20);
display: flex;
flex-direction: column;
align-items: flex-end;
gap: var(--plugin-shell-gap);
max-width: calc(100vw - var(--plugin-shell-inset) * 2);
pointer-events: none;
}
.plugin-app-shell-overlay { pointer-events: auto; }
@media (max-width: 767px) {
.plugin-app-shell-overlays { bottom: var(--plugin-shell-mobile-bottom); }
}
:root {
/* Browser Use live viewer chrome: 30px tab strip + 40px navigation bar.
Provider integration measurement (2026-09-29), not Paperclip spacing.
Keep navigation visible; the cross-origin viewer exposes no sizing message. */
--browser-use-viewer-chrome-height: 70px;
}
/* GitHub skill scans: real counters with a quiet indeterminate sweep until the tree is known. */
.skill-import-enter {
animation: tc-fade-rise var(--motion-duration-enter) var(--motion-ease-out) both;
}
.skill-import-sweep {
width: 40%;
animation: skill-import-sweep var(--motion-approval-pulse) var(--motion-ease-standard) infinite;
}
@keyframes skill-import-sweep {
from { transform: translateX(-100%); }
to { transform: translateX(350%); }
}
.skill-import-progress {
appearance: none;
overflow: hidden;
border: none;
border-radius: var(--radius-sm);
background: var(--muted);
color: var(--foreground);
}
.skill-import-progress::-webkit-progress-bar { background: var(--muted); }
.skill-import-progress::-webkit-progress-value {
background: var(--foreground);
border-radius: var(--radius-sm);
transition: width var(--motion-line-scroll) var(--motion-ease-standard);
}
.skill-import-progress::-moz-progress-bar { background: var(--foreground); }
@media (prefers-reduced-motion: reduce) {
.skill-import-enter, .skill-import-sweep { animation: none; }
.skill-import-progress::-webkit-progress-value { transition: none; }
}
/* Agent setup handoff: the same logo stack travels from trigger to prompt. */
:root {
--agent-setup-flight-duration: var(--motion-duration-deliberate);
--agent-setup-copied-hold: 2400ms;
--agent-setup-popover-width: 21rem;
--agent-setup-collision-padding: 12;
--agent-setup-preview-height: clamp(6rem, 27dvh, 12rem);
--agent-setup-logo-size: calc(var(--spacing) * 7);
--agent-setup-logo-overlap: calc(var(--spacing) * -1.5);
--agent-setup-logo-lift: calc(var(--spacing) * -1.5);
--agent-setup-logo-tilt: 14deg;
}
.agent-setup-trigger-logos {
display: inline-flex;
flex-shrink: 0;
width: calc(var(--agent-setup-logo-size) * 3 + var(--agent-setup-logo-overlap) * 2);
height: var(--agent-setup-logo-size);
margin-left: auto;
}
.agent-setup-trigger-logos[data-single="true"] {
width: var(--agent-setup-logo-size);
}
.agent-setup-logos { display: inline-flex; isolation: isolate; }
.agent-setup-logos[data-hidden="true"] { visibility: hidden; }
.agent-setup-logo-slot { display: inline-flex; position: relative; }
.agent-setup-logo-slot + .agent-setup-logo-slot { margin-left: var(--agent-setup-logo-overlap); }
.agent-setup-flight-layer { position: fixed; inset: 0; z-index: 51; pointer-events: none; }
.agent-setup-flight-layer .agent-setup-flying-logo { position: absolute; transition: none; }
.agent-setup-logo {
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--agent-setup-logo-size);
height: var(--agent-setup-logo-size);
border-radius: var(--radius-lg);
border: 1px solid var(--border);
background: var(--popover);
box-shadow: var(--shadow-xs);
transition: transform var(--motion-duration-slow) var(--motion-ease-out-expo);
}
.agent-setup-trigger:is(:hover, :focus-visible) .agent-setup-logo-slot:nth-child(1) .agent-setup-logo {
transform: translateY(var(--agent-setup-logo-lift)) rotate(calc(-1 * var(--agent-setup-logo-tilt)));
}
.agent-setup-trigger:is(:hover, :focus-visible) .agent-setup-logo-slot:nth-child(2) .agent-setup-logo {
transform: translateY(calc(var(--agent-setup-logo-lift) * 1.6));
}
.agent-setup-trigger:is(:hover, :focus-visible) .agent-setup-logo-slot:nth-child(3) .agent-setup-logo {
transform: translateY(calc(var(--agent-setup-logo-lift) * 0.6)) rotate(var(--agent-setup-logo-tilt));
}
.agent-setup-popover {
width: min(var(--agent-setup-popover-width), calc(100vw - var(--spacing) * 6));
max-height: var(--radix-popover-content-available-height);
box-shadow: var(--shadow-profile-popover);
}
.agent-setup-popover[data-state="open"] { animation: tc-fade-in var(--motion-duration-base) var(--motion-ease-out-expo); }
.agent-setup-popover[data-state="closed"] { animation: none; }
.agent-setup-preview {
height: var(--agent-setup-preview-height);
max-height: max(3rem, calc(var(--radix-popover-content-available-height) - 12rem));
scrollbar-width: thin;
}
.agent-setup-preview-logos { transform: translateY(-25%); }
.agent-setup-preview-logos .agent-setup-logo-slot:first-child .agent-setup-logo { transform: rotate(calc(var(--agent-setup-logo-tilt) * -0.5)); }
.agent-setup-preview-logos .agent-setup-logo-slot:last-child .agent-setup-logo { transform: rotate(calc(var(--agent-setup-logo-tilt) * 0.5)); }
.agent-setup-check { animation: agent-setup-check var(--motion-duration-slow) var(--motion-ease-out-expo) both; }
.agent-setup-confirmation { animation: tc-fade-rise var(--motion-duration-base) var(--motion-ease-out-expo) both; }
.agent-setup-copy[aria-disabled="true"] { opacity: 0.5; }
@keyframes agent-setup-check {
from { opacity: 0; transform: scale(0.6) rotate(-20deg); }
to { opacity: 1; transform: scale(1) rotate(0); }
}
@media (prefers-reduced-motion: reduce) {
.agent-setup-logo { transition: none; transform: none !important; }
.agent-setup-popover, .agent-setup-check, .agent-setup-confirmation { animation: none !important; }
}
@media (max-height: 420px) {
.agent-setup-popover { overflow-y: auto; }
}
/* Task-row design specimen. Opt-in until visual review; production defaults stay
unchanged. Values live here so both densities and every story share one kit. */
:root, .dark {
--task-row-height-comfortable: calc(var(--spacing) * 11);
--task-row-height-compact: calc(var(--spacing) * 8);
--task-row-recovery-width: calc(var(--spacing) * 32);
--task-row-external-width: calc(var(--spacing) * 10);
--task-row-padding-inline: calc(var(--spacing) * 4);
--task-row-gap: calc(var(--spacing) * 2);
--task-row-indent-step: calc(var(--spacing) * 6);
--task-row-control-size: calc(var(--spacing) * 4);
--task-row-caret-size: calc(var(--spacing) * 3.5);
--task-row-title-size: var(--text-sm);
--task-row-title-leading: calc(var(--spacing) * 4);
--task-row-font-mono: var(--font-paper-mono);
--task-row-title-weight-read: var(--font-weight-normal);
--task-row-title-weight-unread: var(--font-weight-semibold);
--task-row-title-color-read: var(--muted-foreground);
--task-row-title-color-active: var(--foreground);
--task-row-surface: transparent;
--task-row-surface-hover: var(--accent);
--task-row-surface-selected: color-mix(in oklab, var(--accent) 50%, transparent);
--task-row-surface-current: color-mix(in oklab, var(--primary) 5%, transparent);
--task-row-rule-color: var(--border);
--task-row-rule-width: 1px;
--task-row-radius: 0px;
--task-row-focus-width: 2px;
--task-row-focus-color: var(--ring);
--task-row-guide-offset: calc(var(--task-row-indent-step) + var(--task-row-control-size) / 2);
--task-row-identifier-width: calc(var(--spacing) * 20);
--task-row-time-width: calc(var(--spacing) * 24);
--task-row-title-marquee-delay: var(--motion-duration-fast);
--task-row-title-marquee-step-duration: 25ms;
--task-row-title-marquee-ease: linear;
--cell-marquee-fade-width: calc(var(--spacing) * 3);
--cell-marquee-mask-solid: #000; /* Alpha mask only; reveals the actual surface beneath the cell. */
}
.task-row-group {
background: var(--task-list-surface);
border-block: var(--task-row-rule-width) solid var(--task-row-rule-color);
}
.task-row-group + .task-row-group { border-top: 0; }
.task-row-deferred {
content-visibility: auto;
contain-intrinsic-block-size: auto var(--task-row-height-comfortable);
}
.task-row-deferred[data-density="compact"] {
contain-intrinsic-block-size: auto var(--task-row-height-compact);
}
.task-row-stable, .task-row-skeleton {
height: var(--task-row-height-comfortable);
min-height: var(--task-row-height-comfortable);
}
.task-row-stable { align-items: center; }
.task-row-stable [data-slot="task-row-leading"] { align-items: center; padding-top: 0; }
.task-row-stable [data-slot="task-row-title"] {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.task-row-stable [data-slot="task-row-recovery"] {
width: var(--task-row-control-size);
flex-shrink: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.task-row-stable [data-slot="task-row-external"] {
width: var(--task-row-external-width);
flex-shrink: 0;
}
.task-row-stable [data-slot="task-row-metadata"] {
width: var(--task-row-time-width);
flex-shrink: 0;
overflow: hidden;
white-space: nowrap;
}
.task-row-stable [data-slot="task-row-content"] { align-self: center; }
@media (min-width: 640px) {
.task-row-stable [data-slot="task-row-recovery"] { width: var(--task-row-recovery-width); }
}
.task-row-skeleton { display: flex; align-items: center; gap: var(--task-row-gap); padding-inline: var(--task-row-padding-inline); }
.task-row-refreshed {
--task-row-height: var(--task-row-height-comfortable);
--task-row-guide-end-gap: calc((var(--task-row-height) - var(--task-row-control-size)) / 2);
height: var(--task-row-height);
min-height: var(--task-row-height);
align-items: center;
gap: var(--task-row-gap);
padding: 0 var(--task-row-padding-inline);
border: 0;
border-radius: var(--task-row-radius);
background: var(--task-row-surface);
font-family: var(--font-sans);
font-size: var(--task-row-title-size);
line-height: var(--task-row-title-leading);
}
.task-row-refreshed[data-density="compact"] {
--task-row-height: var(--task-row-height-compact);
}
.task-row-refreshed * {
font-size: var(--task-row-title-size);
line-height: var(--task-row-title-leading);
}
.task-row-status-tooltip {
font-size: var(--task-row-title-size);
line-height: var(--task-row-title-leading);
}
.task-row-refreshed:hover { background: var(--task-row-surface-hover); }
.task-row-refreshed[data-selected="true"] { background: var(--task-row-surface-selected); }
.task-row-refreshed[data-current-step="true"] { background: var(--task-row-surface-current); }
.task-row-refreshed .task-row-link { border-radius: var(--task-row-radius); }
.task-row-refreshed .task-row-link:focus-visible {
outline: var(--task-row-focus-width) solid var(--task-row-focus-color);
outline-offset: calc(var(--task-row-focus-width) * -1);
box-shadow: none;
}
.task-row-refreshed [data-slot="task-row-leading"] {
align-items: center;
gap: var(--task-row-gap);
padding-top: 0;
}
.task-row-status-link {
position: relative;
z-index: 10;
display: inline-flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: var(--task-row-control-size);
height: var(--task-row-control-size);
}
.task-row-status-link svg { pointer-events: none; }
.task-row-status-link:focus-visible {
outline: var(--task-row-focus-width) solid var(--task-row-focus-color);
outline-offset: var(--task-row-rule-width);
border-radius: var(--radius-sm);
}
.task-row-refreshed [data-slot="task-row-leading-control"] {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: var(--task-row-control-size);
height: var(--task-row-control-size);
}
.task-row-refreshed .task-row-disclosure {
width: var(--task-row-control-size);
height: var(--task-row-control-size);
min-height: 0;
padding: 0;
color: var(--task-row-title-color-read);
}
.task-row-refreshed .task-row-disclosure svg {
width: var(--task-row-caret-size);
height: var(--task-row-caret-size);
}
.task-row-refreshed [data-slot="task-row-tree-guide"] {
width: calc(var(--task-row-indent-step) - var(--task-row-gap));
}
.task-row-refreshed [data-slot="task-row-tree-connector"] {
inset-block: 0;
left: var(--task-row-guide-offset);
width: var(--task-row-rule-width);
background: transparent;
pointer-events: none;
}
/* Match the space below the parent status glyph at the group's lower boundary.
Clip the paint so any disclosure gap stays centered within its original row. */
.task-row-group > .task-row-refreshed:last-child [data-slot="task-row-tree-guide"]:first-child [data-slot="task-row-tree-connector"] {
clip-path: inset(0 0 var(--task-row-guide-end-gap) 0);
}
.task-row-refreshed [data-slot="task-row-tree-line"] {
background: var(--task-row-rule-color);
}
.task-row-refreshed [data-slot="task-row-tree-gap"] {
height: var(--task-row-control-size);
}
.task-row-refreshed [data-slot="task-row-content"] {
flex-direction: row;
align-items: center;
gap: var(--task-row-gap);
}
.task-row-refreshed [data-slot="task-row-title-cluster"] { align-items: center; }
.task-row-refreshed [data-slot="task-row-title"] {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
-webkit-line-clamp: unset;
font-size: var(--task-row-title-size);
line-height: var(--task-row-title-leading);
font-weight: var(--task-row-title-weight-read);
color: var(--task-row-title-color-read);
}
.task-row-refreshed[data-unread="true"] [data-slot="task-row-title"] {
font-weight: var(--task-row-title-weight-unread);
color: var(--task-row-title-color-active);
}
/* Each cell measures its own overflow. Row overlay links retain navigation;
only the cell under the pointer (or keyboard focus) reveals its contents. */
.cell-marquee {
display: block;
min-width: 0;
width: 100%;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cell-marquee-content {
display: block;
pointer-events: none;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transform: translateX(0);
transition: none;
}
.cell-marquee-viewport { display: block; width: 100%; overflow: hidden; pointer-events: none; }
/* Mask content rather than painting a background-colored overlay, so text and
chips fade into every real row surface, including hover and selection. */
.cell-marquee[data-truncated="true"] > .cell-marquee-viewport {
mask-image: linear-gradient(to right, var(--cell-marquee-mask-solid) calc(100% - var(--cell-marquee-fade-width)), transparent);
}
.cell-marquee[data-revealing="true"] > .cell-marquee-viewport {
mask-image: linear-gradient(to right, transparent, var(--cell-marquee-mask-solid) var(--cell-marquee-fade-width), var(--cell-marquee-mask-solid) calc(100% - var(--cell-marquee-fade-width)), transparent);
}
.cell-marquee[data-reveal-complete="true"] > .cell-marquee-viewport {
mask-image: linear-gradient(to right, transparent, var(--cell-marquee-mask-solid) var(--cell-marquee-fade-width));
}
.cell-marquee[data-revealing="true"] { text-overflow: clip; }
.cell-marquee[data-revealing="true"] > .cell-marquee-viewport > .cell-marquee-content {
width: max-content;
overflow: visible;
transform: translateX(calc(var(--cell-marquee-overflow) * -1px));
transition: transform calc(var(--cell-marquee-overflow) * var(--task-row-title-marquee-step-duration)) var(--task-row-title-marquee-ease) var(--task-row-title-marquee-delay);
}
.cell-marquee:focus-visible { outline: var(--task-row-focus-width) solid var(--task-row-focus-color); outline-offset: calc(var(--task-row-focus-width) * -1); }
@media (prefers-reduced-motion: reduce) {
.cell-marquee[data-revealing="true"] > .cell-marquee-viewport > .cell-marquee-content { transition: none; }
.cell-marquee[data-revealing="true"] > .cell-marquee-viewport {
mask-image: linear-gradient(to right, transparent, var(--cell-marquee-mask-solid) var(--cell-marquee-fade-width));
}
}
.task-row-refreshed:is(:hover, :focus-within) [data-slot="task-row-title"] {
color: var(--task-row-title-color-active);
}
.task-row-refreshed [data-slot="task-row-trailing"] { gap: var(--task-row-gap); }
.task-row-refreshed [data-slot="task-row-identifier"] {
width: var(--task-row-identifier-width);
font-family: var(--task-row-font-mono, var(--font-mono));
}
.task-row-refreshed [data-slot="task-row-timestamp"] { width: var(--task-row-time-width); }
/* A narrow summary reuses the standalone row anatomy without a disclosure
gutter. Container breakpoints live here in the token/style layer: hide the
identifier below 28rem, then time below 20rem; never change type or row density. */
.task-summary-list {
container: task-summary / inline-size;
min-width: 0;
margin: 0;
padding: 0;
list-style: none;
}
.task-summary-list > li + li { border-top: var(--task-row-rule-width) solid var(--task-row-rule-color); }
.task-summary-row [data-slot="task-row-leading-control"] { display: none; }
.task-summary-row [data-slot="task-row-trailing"] { display: flex; }
.task-summary-row [data-slot="task-row-title-cluster"] { min-width: 0; }
.activity-row-refreshed { display: flex; min-width: 0; text-decoration: none; }
.activity-row-refreshed:focus-visible { outline: var(--task-row-focus-width) solid var(--task-row-focus-color); outline-offset: calc(var(--task-row-focus-width) * -1); }
.activity-row-details:empty { display: none; }
.activity-row-details:not(:empty) { padding: 0 var(--task-row-padding-inline) var(--task-row-gap); }
.task-row-responsive-container { container: task-summary / inline-size; }
.task-row-responsive-container [data-slot="task-row-trailing"] { display: flex; }
@container task-summary (width < 28rem) {
.task-summary-row [data-slot="task-row-identifier"],
.task-row-responsive-container [data-slot="task-row-identifier"],
.task-row-responsive-container [data-slot="task-row-metadata"] { display: none; }
}
@container task-summary (width < 20rem) {
.task-summary-row [data-slot="task-row-timestamp"],
.task-row-responsive-container [data-slot="task-row-timestamp"] { display: none; }
}
/* Complete task-list column contract. Header and row cells share the same
dimensions; horizontal scrolling preserves every field and title legibility. */
:root, .dark {
--task-list-surface: var(--background);
--task-list-header-height: var(--task-row-height-comfortable);
--task-list-edge-fade-width: calc(var(--spacing) * 6);
--task-list-edge-fade-surface: var(--task-list-surface);
--task-list-controls-width: var(--task-row-height-comfortable);
--task-list-no-controls-space: 0px;
--task-list-controls-space: var(--task-list-no-controls-space);
--task-list-sort-icon-size: calc(var(--spacing) * 3);
--task-list-sort-gap: var(--spacing);
--task-list-sort-padding: var(--spacing);
--task-list-sort-padding-inline: calc(var(--spacing) * 2);
--task-list-sort-radius: calc(infinity * 1px);
--task-list-options-width: calc(var(--spacing) * 50);
--task-list-options-padding: var(--flyout-padding);
--task-list-options-heading-padding: var(--flyout-header-padding-inline);
--task-list-options-gap: var(--flyout-option-gap);
--task-list-options-item-height: var(--flyout-option-height);
--task-list-options-item-radius: var(--flyout-option-radius);
--task-list-options-check-size: var(--flyout-check-size);
--task-list-options-heading-size: var(--flyout-type-size);
--task-list-options-leading: var(--flyout-type-leading);
--task-list-options-surface: var(--task-list-surface);
--task-list-options-highlight: var(--muted-highlight);
--task-row-tag-background: var(--muted-highlight);
--task-row-tag-foreground: var(--muted-foreground);
--task-list-column-status: calc(var(--task-row-control-size) * 2 + var(--task-row-gap));
--task-list-column-title-min: calc(var(--spacing) * 80);
--task-list-column-task: calc(var(--task-list-column-status) + var(--task-row-gap) + var(--task-list-column-title-min));
--task-list-column-task-id: calc(var(--spacing) * 20);
--task-list-column-created-by: calc(var(--spacing) * 28);
--task-list-column-assignee: calc(var(--spacing) * 28);
--task-list-column-project: calc(var(--spacing) * 32);
--task-list-column-parent: calc(var(--spacing) * 36);
--task-list-column-tags: calc(var(--spacing) * 32);
--task-list-column-updated: calc(var(--spacing) * 26);
--task-list-column-worktree: calc(var(--spacing) * 38);
--task-list-detail-columns: var(--task-list-column-task-id) var(--task-list-column-created-by) var(--task-list-column-assignee) var(--task-list-column-project) var(--task-list-column-parent) var(--task-list-column-tags) var(--task-list-column-worktree) var(--task-list-column-updated);
--task-list-columns: var(--task-list-column-status) minmax(var(--task-list-column-title-min), 1fr) var(--task-list-detail-columns);
--task-list-min-width: calc(var(--task-list-column-status) + var(--task-list-column-title-min) + var(--task-list-column-task-id) + var(--task-list-column-created-by) + var(--task-list-column-assignee) + var(--task-list-column-project) + var(--task-list-column-parent) + var(--task-list-column-tags) + var(--task-list-column-updated) + var(--task-list-column-worktree) + var(--task-row-gap) * 9 + var(--task-row-padding-inline) * 2);
}
.task-row-design-preview { background: var(--task-list-surface); }
/* Proposal typography is shared by Storybook and the opt-in sandbox. Aliases
migrate existing production-component recipes without restyling the ordinary
app or stories outside the proposal theme. */
html:has(.design-proposal-theme) {
--font-mono: var(--font-paper-mono);
--default-mono-font-family: var(--font-paper-mono);
--font-weight-medium: var(--font-weight-normal);
--font-weight-bold: var(--font-weight-semibold);
--text-micro: var(--text-xs);
--profile-popover-meta-line-height: calc(var(--text-xs) * var(--text-xs--line-height));
}
/* Legacy micro utilities get the full xs size/leading recipe. New proposal
code uses text-xs directly; shared component defaults stay compatible. */
html:has(.design-proposal-theme) [class~="text-(length:--text-micro)"] {
font-size: var(--text-xs);
line-height: var(--text-xs--line-height);
}
/* Tailwind prose and preflight also contain literal medium/bolder weights.
Preserve explicit strong-heading rules and compact reasoning inheritance. */
html:has(.design-proposal-theme) :where(b, strong) { font-weight: var(--font-weight-semibold); }
html:has(.design-proposal-theme) .paperclip-markdown :where(a, code, blockquote) { font-weight: var(--font-weight-normal); }
/* Conversation proposal: two size/leading recipes, shared by saved threads,
synthetic previews and the composer. Page headers and other prose are outside
these explicit regions. Removing the proposal theme restores ordinary UI. */
html:has(.design-proposal-theme) [data-conversation-typography] {
--conversation-human-foreground: #fff;
--conversation-body-size: var(--text-sm);
--conversation-body-leading: calc(var(--text-sm) * var(--text-sm--line-height));
--conversation-meta-size: var(--text-xs);
--conversation-meta-leading: calc(var(--text-xs) * var(--text-xs--line-height));
font-size: var(--conversation-body-size);
line-height: var(--conversation-body-leading);
color: var(--muted-foreground);
}
/* Collapse inherited contrast tiers without changing semantic status colors,
SVG icons, action-button variants or keyboard-focus treatments. */
html:has(.design-proposal-theme) [data-conversation-typography] :where([class*="text-muted-foreground"], [class*="text-foreground"]):where(:not(svg, [data-slot="button"])) {
color: inherit;
}
html:has(.design-proposal-theme) [data-conversation-typography] :is(
[data-conversation-meta], [data-conversation-message], [data-testid="task-chat-agent-identity"],
[data-testid="task-chat-turn-summary"], [data-testid="task-chat-activity-phase"],
[data-testid="task-chat-tool-card"], [data-testid="task-chat-protocol-activity-row"],
[data-testid="task-chat-live-run-pill"], [data-testid="task-chat-current-activity"],
[data-testid="task-chat-activity-unavailable"], [data-testid="task-chat-collapsible-marker"],
[data-testid="task-chat-system-notice"], [data-testid="task-chat-runner-activity-detail"],
[data-testid="task-chat-composer-actions"]
) {
font-size: var(--conversation-meta-size);
line-height: var(--conversation-meta-leading);
color: var(--muted-foreground);
}
html:has(.design-proposal-theme) [data-conversation-typography] :is(
[data-testid="task-chat-agent-identity"], [data-testid="task-chat-human-identity"]
) { font-weight: var(--font-weight-normal); }
/* Nested text utilities and tool output share their owning role's rhythm. */
html:has(.design-proposal-theme) [data-conversation-typography] :where(
[class~="text-xs"], [class~="text-sm"], [class~="text-(length:--text-micro)"],
[class~="leading-relaxed"], [class~="leading-4"], [class~="leading-none"]
):where(:not([data-slot="button"])) {
font-size: inherit;
line-height: inherit;
}
html:has(.design-proposal-theme) [data-conversation-typography] :where(
[data-conversation-text], [data-testid="task-chat-thinking"] .paperclip-markdown,
[data-testid="task-chat-composer-input"],
[data-testid="task-chat-live-run-pill"] > span:first-child,
[data-testid="task-chat-turn-status-header"]
) {
font-size: var(--conversation-body-size);
line-height: var(--conversation-body-leading);
color: var(--muted-foreground);
}
html:has(.design-proposal-theme) [data-conversation-typography] [data-conversation-text="final"] { color: var(--foreground); }
html:has(.design-proposal-theme) [data-conversation-typography] [data-conversation-text="human"] { color: var(--conversation-human-foreground); }
html:has(.design-proposal-theme) [data-conversation-typography] .paperclip-markdown {
font-size: inherit;
line-height: inherit;
color: inherit;
}
html:has(.design-proposal-theme) [data-conversation-typography] .paperclip-markdown :is(h1, h2, h3, h4, h5, h6, p, ul, ol, li, blockquote, a, code, strong, b, em, th, td) {
font-size: inherit;
line-height: inherit;
color: inherit;
letter-spacing: normal;
}
html:has(.design-proposal-theme) [data-conversation-typography] .paperclip-markdown :is(h1, h2, h3, h4, h5, h6) { font-weight: var(--font-weight-normal); }
html:has(.design-proposal-theme) [data-conversation-typography] .paperclip-markdown :is(strong, b) { font-weight: var(--font-weight-semibold); }
/* Existing shared code-block rules use !important; retain their layout while
allowing authored message and diagnostic roles to own size and contrast. */
html:has(.design-proposal-theme) [data-conversation-typography] .paperclip-markdown pre {
font-size: inherit !important;
line-height: inherit;
color: inherit !important;
}
html:has(.design-proposal-theme) [data-conversation-typography] .paperclip-markdown :is(a, a:hover, a:focus-visible) { color: inherit; }
html:has(.design-proposal-theme) [data-conversation-typography] .shimmer-text {
--shimmer-base: var(--foreground);
--shimmer-highlight: color-mix(in oklch, var(--foreground) 35%, transparent);
/* Two identical waves make the endpoints coincide without a blank dwell. */
background-image: linear-gradient(90deg, var(--shimmer-base) 0%, var(--shimmer-highlight) 25%, var(--shimmer-base) 50%, var(--shimmer-highlight) 75%, var(--shimmer-base) 100%);
animation-duration: var(--motion-conversation-tool-shimmer);
}
/* One text-clipped background spans both Inter and Paper Mono. Descendants
inherit its text fill, rather than starting a separate sweep for each font.
Removing the lifecycle hook restores ordinary muted metadata and hover. */
html:has(.design-proposal-theme) [data-conversation-typography] [data-conversation-tool-running="true"] {
color: var(--foreground);
--shimmer-base: var(--foreground);
--shimmer-highlight: color-mix(in oklch, var(--foreground) 35%, transparent);
background-image: linear-gradient(90deg, var(--shimmer-base) 0%, var(--shimmer-highlight) 25%, var(--shimmer-base) 50%, var(--shimmer-highlight) 75%, var(--shimmer-base) 100%);
background-size: 200% 100%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer-text-slide var(--motion-conversation-tool-shimmer) linear infinite;
}
html:has(.design-proposal-theme) [data-conversation-typography] [data-conversation-tool-running="true"] > span {
background: none;
animation: none;
-webkit-text-fill-color: inherit;
}
/* Agent identity uses the body size with foreground semibold emphasis. */
html:has(.design-proposal-theme) [data-conversation-typography] :is([data-testid="task-chat-agent-identity"], [data-testid="task-chat-agent-identity"] > span:last-child) {
font-size: var(--conversation-body-size);
line-height: var(--conversation-body-leading);
font-weight: var(--font-weight-semibold);
color: var(--foreground);
}
/* Restore the neutral interactive metadata treatment suppressed by contrast
normalization. Preserve semantic status accents and action-button variants. */
html:has(.design-proposal-theme) [data-conversation-typography] :is(
button[data-testid="task-chat-turn-summary"],
button[data-testid="task-chat-activity-phase-toggle"],
[data-activity-item-id] > button,
[data-testid="task-chat-tool-card"] > button,
[data-testid="task-chat-thinking"] > button,
[data-testid="task-chat-protocol-activity-row"] button[aria-expanded],
[data-testid="task-chat-system-notice"] button[aria-expanded],
[data-testid="task-chat-collapsible-marker"] button[aria-expanded]
):is(:hover, :focus-visible):not(:disabled) {
color: var(--foreground);
background-color: color-mix(in srgb, var(--muted) 40%, transparent);
border-radius: var(--radius-sm);
}
@keyframes conversation-word-in {
from { opacity: var(--conversation-word-start-opacity); }
to { opacity: 1; }
}
.conversation-handoff-activity {
display: grid;
grid-template-rows: 1fr;
opacity: 1;
transition: opacity var(--motion-conversation-handoff-fade) var(--motion-ease-out),
grid-template-rows var(--motion-conversation-handoff-fold) var(--motion-ease-standard) var(--motion-conversation-handoff-fade);
}
.conversation-handoff-activity[data-collapsing="true"] {
grid-template-rows: 0fr;
opacity: 0;
}
[data-handoff-state="settled"] [data-testid="task-chat-bubble-attached-turn"] {
animation: conversation-word-in var(--motion-conversation-handoff-fade) var(--motion-ease-out) both;
}
html:has(.design-proposal-theme) [data-conversation-typography] .conversation-stream-word {
animation: conversation-word-in var(--motion-conversation-word-fade) var(--motion-ease-out) both;
}
@media (prefers-reduced-motion: reduce) {
.conversation-handoff-activity { transition: none; }
[data-handoff-state="settled"] [data-testid="task-chat-bubble-attached-turn"] { animation: none; }
html:has(.design-proposal-theme) [data-conversation-typography] .conversation-stream-word { animation: none; }
html:has(.design-proposal-theme) [data-conversation-typography] [data-conversation-tool-running="true"] {
animation: none;
background-image: none;
-webkit-text-fill-color: currentColor;
}
}
/* Paper color foundation, scoped to proposed stories. The other six semantic
colors already match Paper's dark tokens in .dark above. Scope at html so
Radix portals inherit the same values; removing the story marker restores
the existing production theme. Source: Paper Dark mode frame 3LXU-2. */
html.dark:has(.design-proposal-theme) {
--background: #1a1a1a;
--muted-alt: #202020;
--card: var(--background);
--card-foreground: var(--muted-foreground);
--popover: var(--background);
--accent: var(--muted-highlight);
--input: var(--border);
/* Focus follows foreground independently of neutral pane-divider interactions. */
--ring: var(--foreground);
--sidebar-ring: var(--ring);
--pane-resize-highlight: oklch(0.556 0 0);
--task-row-surface-hover: var(--muted);
background: var(--background);
}
/* Light proposals inherit production semantic colors. Proposal-only surface
names alias existing production tokens rather than introducing a palette. */
html:not(.dark):has(.design-proposal-theme) {
--muted-alt: var(--secondary);
--design-sidebar-surface: var(--primary-sidebar-background);
--design-sidebar-item-foreground: color-mix(in oklab, var(--foreground) 80%, transparent);
--design-sidebar-item-highlight: var(--sidebar-accent);
--design-sidebar-item-active-foreground: var(--sidebar-accent-foreground);
}
.design-proposal-theme { color: var(--muted-foreground); }
html.dark:has(.design-proposal-theme) .pane-resize-handle { --ring: var(--pane-resize-highlight); }
html:has(.design-proposal-theme) :is(body, #storybook-root) { background: var(--background); }
html:has(.design-proposal-theme) [data-slot="input"] { background: var(--background-alt); border-color: var(--border); color: var(--muted-foreground); }
html:has(.design-proposal-theme) [data-slot="input"]:focus-visible { border-color: var(--ring); }
/* Bright edge with a softer outer halo, matching the production focus treatment.
Trigger wrappers replace Button's slot, so include those identities too.
Resize controls keep their separate neutral interaction color. */
html:has(.design-proposal-theme) :where([data-slot="input"], [data-slot="textarea"], [data-slot="button"], [data-slot="tooltip-trigger"], [data-slot="dropdown-menu-trigger"], [data-slot="popover-trigger"], [data-slot="select-trigger"], [data-slot="checkbox"], [data-slot="tabs-trigger"], [role="tab"], select):focus-visible:where(:not([aria-invalid="true"], [data-variant="destructive"], .pane-resize-handle, .pane-resize-handle *, .design-task-search input)) {
--tw-ring-color: var(--focus-ring-halo);
--tw-ring-shadow: var(--focus-ring-shadow);
outline: var(--focus-ring-stroke-width) solid var(--ring);
outline-offset: calc(-1 * var(--focus-ring-stroke-width));
box-shadow: var(--focus-ring-shadow);
}
html:has(.design-proposal-theme) :where(.task-row-link, .task-row-status-link, .cell-marquee, .activity-row-refreshed, .task-list-sort-button, .design-page-content, .design-sidebar-recent-list, .design-sidebar a, .task-filter-search input):focus-visible {
outline: var(--focus-ring-stroke-width) solid var(--ring);
outline-offset: calc(-1 * var(--focus-ring-stroke-width));
box-shadow: var(--focus-ring-shadow);
}
html:has(.design-proposal-theme) [data-slot="card"] { border-color: var(--border); }
html:has(.design-proposal-theme) [data-slot="card"][data-elevation="middle"] { background: var(--muted-alt); }
html:has(.design-proposal-theme) [data-slot="card"][data-elevation="high"] { background: var(--muted); }
html:has(.design-proposal-theme) [data-slot="card-title"] { color: var(--foreground); }
.task-list-columns-scroll { overflow-x: auto; isolation: isolate; }
.task-list-viewport,
.horizontal-scroll-viewport { position: relative; min-width: 0; background: var(--task-list-surface); }
.horizontal-scroll-area { overflow-x: auto; }
.horizontal-scroll-content { width: max-content; min-width: 100%; }
.task-list-viewport::before,
.task-list-viewport::after,
.horizontal-scroll-viewport::before,
.horizontal-scroll-viewport::after {
content: "";
position: absolute;
inset-block: 0;
width: var(--task-list-edge-fade-width);
pointer-events: none;
z-index: 1;
opacity: 0;
}
.task-list-viewport::before,
.horizontal-scroll-viewport::before {
left: 0;
background: linear-gradient(to right, var(--task-list-edge-fade-surface), transparent);
}
.task-list-viewport::after,
.horizontal-scroll-viewport::after {
right: 0;
background: linear-gradient(to left, var(--task-list-edge-fade-surface), transparent);
}
.task-list-viewport[data-overflow-left="true"]::before,
.task-list-viewport[data-overflow-right="true"]::after,
.horizontal-scroll-viewport[data-overflow-left="true"]::before,
.horizontal-scroll-viewport[data-overflow-right="true"]::after { opacity: 1; }
.task-list-columns-content { min-width: var(--task-list-min-width); }
.task-list-column-header {
display: grid;
grid-template-columns: var(--task-list-columns);
align-items: center;
gap: var(--task-row-gap);
height: var(--task-list-header-height);
padding-inline: var(--task-row-padding-inline) calc(var(--task-row-padding-inline) + var(--task-list-controls-space));
font-size: var(--task-row-title-size);
line-height: var(--task-row-title-leading);
font-weight: var(--font-weight-medium);
color: var(--muted-foreground);
}
.task-row-refreshed[data-columns="all"] [data-slot="task-row-trailing"] { display: contents; }
.task-row-refreshed[data-columns="all"] {
display: grid;
grid-template-columns: var(--task-list-columns);
grid-template-rows: minmax(0, 1fr);
padding-inline-end: calc(var(--task-row-padding-inline) + var(--task-list-controls-space));
}
.task-row-refreshed[data-columns="all"] [data-slot="task-row-leading"] { grid-column: var(--task-list-task-start); grid-row: 1; }
.task-row-refreshed[data-columns="all"] [data-slot="task-row-content"] {
grid-column: calc(var(--task-list-task-start) + 1);
grid-row: 1;
padding-inline-start: calc(var(--task-row-depth) * var(--task-row-indent-step));
}
.task-row-refreshed[data-title-hidden="true"] [data-slot="task-row-content"] { display: none; }
.task-row-refreshed[data-title-hidden="true"] [data-slot="task-row-leading"] { flex: 1; }
.task-row-refreshed[data-columns="all"] [data-slot="task-row-trailing"]:empty { display: none; }
.task-row-columns {
display: contents;
font-size: var(--task-row-title-size);
line-height: var(--task-row-title-leading);
color: var(--muted-foreground);
}
.task-row-column {
grid-row: 1;
align-self: center;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-row-column > * { max-width: 100%; }
.task-row-columns [data-column="tags"] [data-slot="badge"] {
color: var(--task-row-tag-foreground);
background-color: var(--task-row-tag-background);
border-color: var(--task-row-tag-background);
}
.task-row-columns :is([data-column="taskId"], [data-column="worktree"]),
.task-row-columns [data-column="parentTask"] .font-mono {
font-family: var(--task-row-font-mono);
}
.task-list-column-header [role="columnheader"] { min-width: 0; }
.task-list-column-header [data-dragging="true"] { position: relative; z-index: 10; }
.task-list-column-header .task-list-sort-button {
display: inline-flex;
align-items: center;
gap: var(--task-list-sort-gap);
margin-inline-start: calc(var(--task-list-sort-padding-inline) * -1);
padding: var(--task-list-sort-padding) var(--task-list-sort-padding-inline);
height: calc(var(--task-row-title-leading) + var(--task-list-sort-padding) * 2);
border-radius: var(--task-list-sort-radius);
font-size: var(--task-row-title-size);
line-height: var(--task-row-title-leading);
color: var(--muted-foreground);
white-space: nowrap;
cursor: pointer;
}
.task-list-sort-button svg { width: var(--task-list-sort-icon-size); height: var(--task-list-sort-icon-size); flex-shrink: 0; }
.task-list-column-header [data-column="task"] {
padding-inline-start: calc(var(--task-list-column-status) + var(--task-row-gap));
}
.task-list-sort-button[data-active="true"] { background: var(--muted-highlight); color: var(--foreground); }
.task-list-sort-button:hover { background: var(--muted-highlight); color: var(--foreground); }
.task-list-sort-button[data-reorderable="true"] { cursor: grab; touch-action: pan-y; }
.task-list-column-header [data-dragging="true"] .task-list-sort-button { cursor: grabbing; }
.task-list-sort-button:focus-visible {
outline: var(--task-row-focus-width) solid var(--task-row-focus-color);
outline-offset: var(--task-row-rule-width);
box-shadow: none;
}
.task-list-header-controls {
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
width: var(--task-list-controls-width);
height: var(--task-list-header-height);
background: var(--task-list-surface);
}
.task-list-settings-trigger { color: var(--muted-foreground); }
.task-list-settings-trigger:is(:hover, [data-state="open"]) { background: var(--muted-highlight); color: var(--foreground); }
.task-list-options {
width: var(--task-list-options-width);
padding: 0;
border: var(--task-row-rule-width) solid var(--border);
border-radius: var(--radius-lg);
background: var(--task-list-options-surface);
box-shadow: none;
font-size: var(--task-row-title-size);
line-height: var(--task-list-options-leading);
}
.task-list-options-heading {
padding: var(--task-list-options-padding) var(--task-list-options-heading-padding);
border-bottom: var(--task-row-rule-width) solid var(--border);
font-size: var(--task-list-options-heading-size);
line-height: var(--task-list-options-leading);
font-weight: var(--font-weight-semibold);
color: var(--foreground);
}
.task-list-options-items { display: flex; flex-direction: column; gap: var(--task-list-options-gap); padding: var(--task-list-options-padding); }
.task-list-options-item {
height: var(--task-list-options-item-height);
flex-shrink: 0;
padding: var(--task-list-options-gap) var(--task-list-options-padding);
padding-inline-end: calc(var(--task-list-options-padding) + var(--task-list-options-check-size) + var(--task-row-gap));
border-radius: var(--task-list-options-item-radius);
font-size: var(--task-row-title-size);
line-height: var(--task-list-options-leading);
font-weight: var(--font-weight-normal);
color: var(--muted-foreground);
}
.task-list-options-item > span { left: auto; right: var(--task-list-options-padding); }
.task-list-options-item svg { width: var(--task-list-options-check-size); height: var(--task-list-options-check-size); }
.task-list-options-item[data-highlighted] { background: var(--task-list-options-highlight); color: var(--foreground); }
.task-list-options-reset {
height: var(--task-list-options-item-height);
flex-shrink: 0;
justify-content: center;
padding: var(--task-list-options-gap) var(--task-list-options-padding);
border: 0;
outline: var(--task-row-rule-width) solid var(--task-list-options-highlight);
border-radius: var(--task-list-options-item-radius);
background: var(--muted);
color: var(--foreground);
font-size: var(--task-list-options-heading-size);
line-height: var(--task-list-options-leading);
font-weight: var(--font-weight-semibold);
}
.task-list-options-reset[data-highlighted] { background: var(--task-list-options-highlight); }
/* One opt-in appearance for the existing Radix dropdown and popover primitives.
Context follows portals; default production surfaces retain their styling. */
:root, .dark {
--flyout-surface: var(--background);
--flyout-border: var(--border);
--flyout-highlight: var(--muted-highlight);
--flyout-foreground: var(--foreground);
--flyout-item-foreground: var(--muted-foreground);
--flyout-radius: var(--radius-lg);
--flyout-width: calc(var(--spacing) * 50);
--flyout-padding: calc(var(--spacing) * 3);
--flyout-header-padding-inline: calc(var(--spacing) * 6);
--flyout-option-gap: var(--spacing);
--flyout-option-height: var(--task-row-height-compact);
--flyout-option-radius: var(--spacing);
--flyout-check-size: var(--task-row-caret-size);
--flyout-type-size: var(--text-sm);
--flyout-type-leading: calc(var(--spacing) * 5);
--shadow-flyout: 0 4px 16px rgb(0 0 0 / 20%);
--task-preview-width: calc(var(--spacing) * 70);
--task-preview-padding: calc(var(--spacing) * 4);
--task-preview-gap: calc(var(--spacing) * 3);
--task-preview-meta-gap: calc(var(--spacing) * 2);
--task-preview-meta-size: var(--text-xs);
--task-preview-meta-leading: calc(var(--spacing) * 4);
--task-preview-title-size: var(--text-sm);
--task-preview-content-leading: calc(var(--spacing) * 4.5);
--task-preview-title-lines: 2;
--task-preview-description-lines: 3;
}
[data-flyout-appearance="refreshed"] {
min-height: 0;
padding: 0;
border: var(--task-row-rule-width) solid var(--flyout-border);
border-radius: var(--flyout-radius);
background: var(--flyout-surface);
color: var(--flyout-item-foreground);
box-shadow: var(--shadow-flyout);
font-size: var(--flyout-type-size);
line-height: var(--flyout-type-leading);
}
/* Task previews share the popover surface and shadow, but their content is a
single padded card rather than a menu with separately padded sections. */
[data-quicklook][data-flyout-appearance="refreshed"] {
width: var(--task-preview-width);
max-width: var(--radix-popover-content-available-width, 100vw);
padding: var(--task-preview-padding);
}
.task-hover-card { display: flex; flex-direction: column; gap: var(--task-preview-gap); }
.task-hover-card [data-slot="task-preview-meta"],
.task-hover-card [data-slot="task-preview-identity"] { gap: var(--task-preview-meta-gap); }
.task-hover-card [data-slot="task-preview-id"],
.task-hover-card [data-slot="task-preview-time"] {
color: var(--muted-foreground);
font-size: var(--task-preview-meta-size);
line-height: var(--task-preview-meta-leading);
}
.task-hover-card [data-slot="task-preview-title"] {
color: var(--foreground);
font-size: var(--task-preview-title-size);
font-weight: var(--font-weight-semibold);
line-height: var(--task-preview-content-leading);
-webkit-line-clamp: var(--task-preview-title-lines);
}
.task-hover-card [data-slot="task-preview-description"] {
color: var(--muted-foreground);
font-size: var(--task-preview-meta-size);
line-height: var(--task-preview-content-leading);
-webkit-line-clamp: var(--task-preview-description-lines);
}
[data-flyout-appearance="refreshed"] [data-flyout-header] {
height: auto;
padding: var(--flyout-padding) var(--flyout-header-padding-inline);
border: 0;
border-bottom: var(--task-row-rule-width) solid var(--flyout-border);
color: var(--flyout-foreground);
font-size: var(--flyout-type-size);
line-height: var(--flyout-type-leading);
font-weight: var(--font-weight-semibold);
}
[data-flyout-appearance="refreshed"] [data-flyout-header] [data-slot="dropdown-menu-label"] { padding: 0; font: inherit; color: inherit; }
[data-flyout-appearance="refreshed"] [data-flyout-header] > button { color: var(--flyout-item-foreground); }
[data-flyout-appearance="refreshed"] [data-flyout-header] > button:hover { background: var(--flyout-highlight); color: var(--flyout-foreground); }
[data-flyout-appearance="refreshed"] [data-flyout-profile] { padding: var(--flyout-header-padding-inline); }
[data-flyout-appearance="refreshed"] [data-flyout-profile] > a:hover { background: var(--flyout-highlight); }
[data-flyout-appearance="refreshed"] [data-flyout-options] {
display: flex;
flex: none;
flex-direction: column;
gap: var(--flyout-option-gap);
padding: var(--flyout-padding);
border-top: 0;
}
[data-flyout-appearance="refreshed"] [data-flyout-options] + [data-flyout-options] { border-top: var(--task-row-rule-width) solid var(--flyout-border); }
[data-flyout-appearance="refreshed"] :is([data-slot="dropdown-menu-item"], [data-slot="dropdown-menu-checkbox-item"], [data-slot="dropdown-menu-radio-item"], [data-flyout-options] > a, [data-flyout-options] > button) {
min-height: var(--flyout-option-height);
border-radius: var(--flyout-option-radius);
padding-inline: var(--flyout-padding);
color: var(--flyout-item-foreground);
font-size: var(--flyout-type-size);
line-height: var(--flyout-type-leading);
font-weight: var(--font-weight-normal);
}
[data-flyout-appearance="refreshed"] :is([data-slot="dropdown-menu-checkbox-item"], [data-slot="dropdown-menu-radio-item"]) { padding-inline-end: calc(var(--flyout-padding) + var(--flyout-check-size) + var(--flyout-option-gap) * 2); }
[data-flyout-appearance="refreshed"] [data-flyout-options] > :is(a, button) { height: var(--flyout-option-height); }
[data-flyout-appearance="refreshed"] :is([data-slot="dropdown-menu-checkbox-item"], [data-slot="dropdown-menu-radio-item"]) > span { left: auto; right: var(--flyout-padding); }
[data-flyout-appearance="refreshed"] :is([data-slot="dropdown-menu-checkbox-item"], [data-slot="dropdown-menu-radio-item"]) svg { width: var(--flyout-check-size); height: var(--flyout-check-size); }
[data-flyout-appearance="refreshed"] :is([data-slot="dropdown-menu-item"], [data-slot="dropdown-menu-checkbox-item"], [data-slot="dropdown-menu-radio-item"], [data-flyout-options] > a, [data-flyout-options] > button) :is(.text-muted-foreground, .text-foreground, svg) { color: inherit; }
[data-flyout-appearance="refreshed"] :is([data-slot="dropdown-menu-item"], [data-slot="dropdown-menu-checkbox-item"], [data-slot="dropdown-menu-radio-item"], [data-flyout-options] > a, [data-flyout-options] > button):is([data-highlighted], :hover, :focus-visible) {
background: var(--flyout-highlight);
color: var(--flyout-foreground);
}
[data-flyout-appearance="refreshed"] [data-flyout-options] .task-list-options-reset { font-weight: var(--font-weight-semibold); }
/* Full-page proposal: compose the reviewed collection with the shared sidebar
shell and controls. These values stay opt-in until page-level approval. */
:root, .dark {
--design-page-height: 100dvh;
--design-page-header-height: calc(var(--spacing) * 15);
--design-page-aside-width: var(--sz-320px);
--design-page-aside-max-width: 30%;
--design-page-aside-min-width: var(--sz-260px);
--design-page-main-min-width: var(--sz-320px);
--design-page-aside-resize-step: 16px;
--design-page-aside-resize-hit-width: calc(var(--spacing) * 2);
--design-page-inset: calc(var(--spacing) * 6);
--design-page-section-gap: calc(var(--spacing) * 8);
--design-page-toolbar-gap: calc(var(--spacing) * 4);
--design-page-control-height: var(--task-row-height-comfortable);
--design-page-control-padding: calc(var(--spacing) * 3);
--design-page-control-radius: var(--radius-md);
--design-page-search-width: calc(var(--spacing) * 68);
--design-page-search-min: calc(var(--spacing) * 40);
--design-page-search-surface: var(--background-alt);
--design-sidebar-surface: var(--muted); /* Paper: --dark-color-muted. */
--design-sidebar-border: var(--border); /* Paper: --dark-color-border. */
--design-sidebar-item-foreground: var(--muted-foreground);
--design-sidebar-item-highlight: var(--muted-highlight);
--design-sidebar-item-active-foreground: var(--foreground);
--design-page-control-surface: color-mix(in oklab, var(--foreground) 4%, var(--task-list-surface));
/* Opaque so the search field's darker fill doesn't change the visible border. */
--design-page-control-border: color-mix(in srgb, var(--foreground) 15%, var(--design-page-control-surface));
--design-page-icon-size: calc(var(--spacing) * 4);
--design-page-control-gap: calc(var(--spacing) * 2);
--design-page-action-gap: var(--spacing);
--design-page-type-size: var(--text-sm);
--design-page-type-leading: calc(var(--spacing) * 5);
--design-page-heading-tracking: 0.05em;
--design-sidebar-inset: calc(var(--spacing) * 3);
--design-sidebar-item-inset: calc(var(--spacing) * 2);
--design-sidebar-label-leading: 1.5;
--design-sidebar-item-height: calc(var(--text-sm) * var(--design-sidebar-label-leading) + var(--spacing) * 3);
--design-sidebar-item-gap: calc(var(--spacing) * 0.5);
--design-sidebar-heading-tracking: 0.1em;
--design-sidebar-label-size: var(--text-sm);
--design-page-focus-width: var(--task-row-focus-width);
}
.design-page-shell { display: flex; position: relative; height: var(--design-page-height); overflow: hidden; color: var(--foreground); }
.design-page-main { display: flex; flex: 1; flex-direction: column; min-width: 0; background: var(--task-list-surface); }
.design-page-aside { position: relative; display: flex; flex-direction: column; flex: 0 0 clamp(var(--design-page-aside-min-width), var(--design-page-aside-user-width, min(var(--design-page-aside-width), var(--design-page-aside-max-width))), var(--design-page-aside-available-width, 100%)); min-width: 0; background: var(--background); border-inline-start: var(--task-row-rule-width) solid var(--border); }
.design-page-aside-resize { left: calc(var(--design-page-aside-resize-hit-width) / -2); width: var(--design-page-aside-resize-hit-width); }
.design-page-shell[data-aside-maximized="true"] > .design-page-main { display: none; }
.design-page-shell[data-aside-maximized="true"] > .design-page-aside { flex: 1; }
.design-page-header-actions { display: flex; align-items: center; margin-inline-start: auto; }
.design-page-header.design-page-aside-header { min-width: 0; gap: var(--spacing); padding-inline: calc(var(--spacing) * 2); }
.design-page-aside-tabs { display: flex; flex: 1; align-self: stretch; min-width: 0; }
.design-page-aside-controls { display: flex; flex-shrink: 0; align-items: center; gap: var(--spacing); }
.design-page-header { display: flex; align-items: center; height: var(--design-page-header-height); flex-shrink: 0; padding-inline: var(--design-page-inset); border-bottom: var(--task-row-rule-width) solid var(--border); }
.design-page-header :is(h1, h2) { font-size: var(--design-page-type-size); line-height: var(--design-page-type-leading); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: var(--design-page-heading-tracking); }
.design-page-content { display: flex; flex: 1; flex-direction: column; gap: var(--design-page-section-gap); min-height: 0; overflow-y: auto; padding: var(--design-page-inset); outline: none; }
.design-page-aside .design-page-content { overscroll-behavior-y: contain; }
.design-page-aside .design-page-content:focus-visible { outline: var(--design-page-focus-width) solid var(--ring); outline-offset: calc(-1 * var(--design-page-focus-width)); }
.design-page-skip-link { position: absolute; z-index: var(--z-60); top: var(--design-page-inset); left: var(--design-page-inset); padding: var(--design-page-control-padding); border-radius: var(--design-page-control-radius); background: var(--foreground); color: var(--background); transform: translateY(calc(-1 * var(--design-page-header-height) * 2)); }
.design-page-skip-link:focus { transform: none; }
/* Keep the muted fill out of the translucent divider. The divider composites
over the app background, matching the main-pane header and pane boundaries. */
.design-sidebar { display: flex; flex-direction: column; height: 100%; min-height: 0; border-inline-end: var(--task-row-rule-width) solid var(--design-sidebar-border); background: var(--design-sidebar-surface); background-clip: padding-box; }
.design-sidebar-brand { display: flex; align-items: center; gap: var(--design-page-control-gap); flex-shrink: 0; height: var(--design-page-header-height); padding-inline: var(--design-sidebar-inset); font-size: var(--design-page-type-size); font-weight: var(--font-weight-semibold); }
.design-sidebar-search { flex-shrink: 0; color: var(--muted-foreground); }
.design-sidebar-search svg { width: var(--design-page-icon-size); height: var(--design-page-icon-size); }
.design-sidebar-search:hover { background: var(--muted-highlight); color: var(--foreground); }
.design-sidebar-navigation { display: flex; flex: 1; flex-direction: column; min-height: 0; gap: var(--design-page-toolbar-gap); overflow-y: auto; padding: var(--design-sidebar-item-inset) var(--design-sidebar-inset); }
.design-sidebar-primary { display: flex; flex-direction: column; gap: var(--design-sidebar-item-gap); }
.design-sidebar-navigation [class~="group/sidebar-section"] > div { min-height: 0; }
/* Navigation and streamlined properties intentionally share this exact label
recipe: sans, nano, normal, uppercase, 150% leading and 0.1em tracking. */
html:has(.design-proposal-theme) :is([data-slot="sidebar-group-label"], [data-slot="property-section-label"][data-streamlined="true"]),
.coordinator-sidebar [data-slot="sidebar-group-label"],
html:has(.design-proposal-theme) .design-navigation-group-label {
font-family: var(--font-sans);
font-size: var(--text-nano);
font-weight: var(--font-weight-normal);
line-height: var(--design-sidebar-label-leading);
letter-spacing: var(--design-sidebar-heading-tracking);
text-transform: uppercase;
color: color-mix(in oklab, var(--muted-foreground) 60%, transparent);
}
.design-sidebar .design-sidebar-item { min-height: var(--design-sidebar-item-height); margin-inline: var(--design-sidebar-item-inset); padding-inline: var(--design-sidebar-item-inset); color: var(--design-sidebar-item-foreground); font-size: var(--design-sidebar-label-size); line-height: var(--design-sidebar-label-leading); }
.design-sidebar .design-sidebar-item:is(:hover, [aria-current="page"]) { background: var(--design-sidebar-item-highlight); color: var(--design-sidebar-item-active-foreground); }
.design-sidebar-new-task { display: flex; justify-content: flex-start; width: calc(100% - var(--design-sidebar-item-inset) * 2); height: var(--design-sidebar-item-height); margin-inline: var(--design-sidebar-item-inset); padding-inline: var(--design-sidebar-item-inset); gap: calc(var(--spacing) * 2.5); font-size: var(--design-sidebar-label-size); line-height: var(--design-sidebar-label-leading); font-weight: var(--font-weight-medium); color: var(--design-sidebar-item-foreground); }
.design-sidebar-new-task svg { width: var(--design-page-icon-size); height: var(--design-page-icon-size); }
.design-sidebar-new-task:hover { background: var(--design-sidebar-item-highlight); color: var(--design-sidebar-item-active-foreground); }
.design-sidebar-recents .design-sidebar-item { font-weight: var(--font-weight-normal); }
.design-sidebar-navigation > :not(.design-sidebar-recents) { flex-shrink: 0; }
.design-sidebar-recents { display: flex; flex: 1; min-height: 0; }
.design-sidebar-recents > div { display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; }
.design-sidebar-recents > div > :first-child { flex-shrink: 0; }
.design-sidebar-recents > div > :last-child { flex: 1; min-height: 0; }
.design-sidebar-recent-list { display: flex; flex-direction: column; gap: var(--design-sidebar-item-gap); min-height: 0; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; }
.design-sidebar-recent-list:focus-visible { outline: var(--design-page-focus-width) solid var(--ring); outline-offset: calc(-1 * var(--design-page-focus-width)); }
.design-sidebar-recent-list .design-sidebar-item { flex-shrink: 0; }
.design-sidebar-account { flex-shrink: 0; }
.design-sidebar-account [aria-label="Open account menu"] { font-size: var(--design-sidebar-label-size); line-height: var(--design-sidebar-label-leading); color: var(--foreground); }
.design-sidebar-account [data-slot="avatar-fallback"] { background: var(--muted-highlight); color: var(--muted-foreground); }
.design-page-toolbar { display: flex; align-items: center; flex-wrap: wrap; flex-shrink: 0; gap: var(--design-page-toolbar-gap); }
.design-page-toolbar .design-toolbar-button,
.design-toolbar-filter > button { height: var(--design-page-control-height); border-radius: var(--design-page-control-radius); padding-inline: var(--design-page-control-padding); gap: var(--design-page-control-gap); font-size: var(--design-page-type-size); line-height: var(--design-page-type-leading); box-shadow: none; }
.design-page-toolbar .design-toolbar-button:not(.design-new-task),
.design-toolbar-filter > button { background: var(--design-page-control-surface); border-color: var(--design-page-control-border); color: var(--muted-foreground); }
.design-page-toolbar .design-toolbar-button:is(:hover, [aria-pressed="true"], [data-active="true"], [data-state="open"]),
.design-toolbar-filter > button:hover { background: var(--muted-highlight); color: var(--foreground); }
.design-page-toolbar .design-toolbar-button svg,
.design-toolbar-filter > button svg,
.design-task-search > svg { width: var(--design-page-icon-size); height: var(--design-page-icon-size); flex-shrink: 0; }
.design-page-toolbar .design-toolbar-icon,
.design-toolbar-filter > button { width: var(--design-page-control-height); padding: 0; }
.design-page-toolbar .design-view-trigger { justify-content: space-between; min-width: calc(var(--spacing) * 28); }
.design-task-search { display: flex; align-items: center; gap: var(--design-page-control-gap); width: var(--design-page-search-width); min-width: var(--design-page-search-min); height: var(--design-page-control-height); padding-inline: var(--design-page-control-padding); border: var(--task-row-rule-width) solid var(--design-page-control-border); border-radius: var(--design-page-control-radius); color: var(--muted-foreground); background: var(--design-page-search-surface); }
.design-task-search:focus-within { border-color: var(--ring); outline: none; box-shadow: var(--focus-ring-shadow); }
.design-task-search input { min-width: 0; height: 100%; padding: 0; border: none; border-radius: 0; background: transparent; color: var(--foreground); box-shadow: none; font-size: var(--design-page-type-size); line-height: var(--design-page-type-leading); }
.design-task-search input:focus-visible { outline: none; box-shadow: none; }
.design-task-search input::-webkit-search-cancel-button { display: none; }
.design-view-toggle { display: flex; border: var(--task-row-rule-width) solid var(--design-page-control-border); border-radius: var(--design-page-control-radius); }
.design-view-toggle .design-toolbar-button { height: calc(var(--design-page-control-height) - var(--task-row-rule-width) * 2); border-radius: 0; background: transparent; }
.design-view-toggle .design-toolbar-button:first-child { border-start-start-radius: var(--design-page-control-radius); border-end-start-radius: var(--design-page-control-radius); }
.design-view-toggle .design-toolbar-button:last-child { border-start-end-radius: var(--design-page-control-radius); border-end-end-radius: var(--design-page-control-radius); }
.design-view-toggle .design-toolbar-button:focus-visible { position: relative; z-index: var(--z-1); }
.design-toolbar-actions { display: flex; align-items: center; gap: var(--design-page-action-gap); margin-inline-start: auto; }
.design-page-toolbar .design-new-task { margin-inline-start: var(--design-page-toolbar-gap); background: var(--foreground); color: var(--background); font-weight: var(--font-weight-semibold); }
.design-page-toolbar .design-new-task:hover { background: color-mix(in oklab, var(--foreground) 90%, var(--task-list-surface)); color: var(--background); }
.design-task-results { margin-block-end: calc(-1 * var(--design-page-toolbar-gap)); font-size: var(--design-page-type-size); line-height: var(--design-page-type-leading); color: var(--muted-foreground); }
.design-task-list-panel { min-width: 0; }
.design-task-list-panel[hidden] { display: none; }
.design-task-board { min-width: 0; }
/* Paper filter IA: five peer groups, with searchable option lists. Surface,
highlight and shadow inherit the shared refreshed popover appearance. */
:root, .dark {
--task-filter-column-width: calc(var(--spacing) * 50);
--task-filter-column-gap: calc(var(--spacing) * 4);
--task-filter-panel-width: calc(var(--task-filter-column-width) * 5 + var(--task-filter-column-gap) * 4 + var(--flyout-padding) * 2 + var(--task-row-rule-width) * 2);
--task-filter-row-height: calc(var(--spacing) * 7);
--task-filter-row-gap: calc(var(--spacing) * 0.5);
--task-filter-row-padding: var(--spacing);
--task-filter-row-padding-inline: calc(var(--spacing) * 2);
--task-filter-icon-size: var(--task-row-control-size);
--task-filter-search-height: var(--task-row-height-compact);
--task-filter-search-icon-size: calc(var(--spacing) * 3);
--task-filter-search-inset: calc(var(--spacing) * 7);
--task-filter-label-dot-size: calc(var(--spacing) * 2.5);
--task-filter-checkbox-surface: color-mix(in srgb, var(--flyout-foreground) 4%, var(--flyout-surface));
}
.task-filters-popover {
display: flex;
flex-direction: column;
width: var(--task-filter-panel-width);
max-width: calc(100vw - var(--flyout-padding) * 2);
max-height: min(var(--radix-popover-content-available-height, 100dvh), var(--sz-calc-9));
overflow: hidden;
}
.task-filters-popover [data-flyout-header] { display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; padding: var(--flyout-padding); }
.task-filters-popover [data-flyout-header] > button { height: var(--flyout-type-leading); padding-inline: var(--task-filter-row-padding-inline); font-size: var(--flyout-type-size); font-weight: var(--font-weight-normal); }
.task-filters-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--task-filter-column-width)), 1fr)); align-items: start; gap: var(--task-filter-column-gap); min-height: 0; padding: var(--flyout-padding); overflow-y: auto; overscroll-behavior: contain; }
.task-filter-group { display: flex; flex-direction: column; min-width: 0; gap: var(--task-filter-column-gap); }
.task-filter-group h3 { margin: 0; color: var(--flyout-foreground); font-size: var(--flyout-type-size); line-height: var(--flyout-type-leading); font-weight: var(--font-weight-semibold); }
.task-filter-group-body { display: flex; flex-direction: column; min-width: 0; gap: var(--flyout-padding); }
.task-filter-options { display: flex; flex-direction: column; gap: var(--task-filter-row-gap); }
.task-filter-option { display: flex; align-items: center; min-width: 0; min-height: var(--task-filter-row-height); padding: var(--task-filter-row-padding) var(--task-filter-row-padding-inline); gap: var(--task-filter-row-padding-inline); border-radius: var(--flyout-option-radius); color: var(--flyout-item-foreground); cursor: pointer; font-size: var(--flyout-type-size); line-height: var(--flyout-type-leading); }
.task-filter-option:is(:hover, :focus-within) { background: var(--flyout-highlight); color: var(--flyout-foreground); }
.task-filter-option-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-filter-option [data-slot="checkbox"] { width: var(--task-filter-icon-size); height: var(--task-filter-icon-size); border-color: var(--flyout-border); background: var(--task-filter-checkbox-surface); color: inherit; box-shadow: none; }
.task-filter-option [data-slot="checkbox"][data-state="checked"] { background: var(--flyout-highlight); }
.task-filter-icon { display: flex; align-items: center; justify-content: center; flex-shrink: 0; width: var(--task-filter-icon-size); height: var(--task-filter-icon-size); }
.task-filter-icon > svg { width: var(--task-filter-icon-size); height: var(--task-filter-icon-size); }
.task-filter-label-dot { flex-shrink: 0; width: var(--task-filter-label-dot-size); height: var(--task-filter-label-dot-size); border-radius: calc(var(--task-filter-label-dot-size) / 2); background: var(--task-filter-label-color); }
.task-filter-search { position: relative; }
.task-filter-search > svg { position: absolute; left: var(--task-filter-row-padding-inline); top: 50%; transform: translateY(-50%); width: var(--task-filter-search-icon-size); height: var(--task-filter-search-icon-size); color: var(--flyout-item-foreground); pointer-events: none; }
.task-filter-search input { height: var(--task-filter-search-height); min-width: 0; padding-inline: var(--task-filter-search-inset) var(--flyout-padding); border: var(--task-row-rule-width) solid var(--flyout-border); border-radius: var(--radius-md); background: var(--background-alt); color: var(--flyout-foreground); font-size: var(--flyout-type-size); line-height: var(--flyout-type-leading); box-shadow: none; }
.task-filter-search input:focus-visible { outline: var(--task-row-focus-width) solid var(--ring); outline-offset: var(--task-row-rule-width); }
@media (pointer: coarse) {
.task-filter-search input { font-size: var(--text-base); }
}
.task-filter-empty { padding: var(--task-filter-row-padding) var(--task-filter-row-padding-inline); color: var(--flyout-item-foreground); font-size: var(--flyout-type-size); line-height: var(--flyout-type-leading); }
.task-list-group-heading { margin-block: var(--design-page-toolbar-gap) var(--design-page-control-gap); }
/* Canonical collection toolbars: shared geometry, opt-in proposal styling.
Search uses ToolbarSearch; only Tasks puts its view/heading before search. */
:root {
--collection-toolbar-height: var(--design-page-control-height);
--collection-toolbar-gap: calc(var(--spacing) * 2);
--collection-toolbar-radius: var(--design-page-control-radius);
--collection-toolbar-border: var(--design-page-control-border);
--collection-toolbar-padding: var(--design-page-control-padding);
}
html:has(.design-proposal-theme) [data-canonical-toolbar] {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: flex-start;
column-gap: var(--collection-toolbar-gap);
row-gap: var(--design-page-toolbar-gap);
min-width: 0;
}
html:has(.design-proposal-theme) [data-canonical-toolbar] :is(.design-toolbar-controls, .design-toolbar-actions, [role="tablist"]:not([data-canonical-tabs])) {
display: flex;
flex-wrap: wrap;
gap: var(--collection-toolbar-gap);
}
html:has(.design-proposal-theme) [data-canonical-toolbar] :is(button:not([data-toolbar-clear], .canonical-tab), [data-toolbar-control]) {
height: var(--collection-toolbar-height);
border: var(--task-row-rule-width) solid var(--collection-toolbar-border);
border-radius: var(--collection-toolbar-radius);
padding-inline: var(--collection-toolbar-padding);
gap: var(--design-page-control-gap);
font-size: var(--design-page-type-size);
line-height: var(--design-page-type-leading);
font-weight: var(--font-weight-normal);
background: var(--design-page-control-surface);
color: var(--muted-foreground);
}
html:has(.design-proposal-theme) [data-canonical-toolbar] button:is([data-size="icon"], [data-size="icon-sm"], .design-toolbar-icon) {
width: var(--collection-toolbar-height);
padding-inline: 0;
}
html:has(.design-proposal-theme) [data-canonical-toolbar] :is(button:not([data-toolbar-clear], .canonical-tab), [data-toolbar-control]):is(:hover, [aria-pressed="true"], [aria-selected="true"], [data-active="true"], [data-state="open"]) {
color: var(--foreground);
background: var(--muted-highlight);
}
html:has(.design-proposal-theme) [data-canonical-toolbar] button:is([data-toolbar-primary], .design-new-task) {
margin-inline-start: 0;
background: var(--foreground);
color: var(--background);
font-weight: var(--font-weight-semibold);
}
html:has(.design-proposal-theme) [data-canonical-toolbar] button:is([data-toolbar-primary], .design-new-task):hover {
background: color-mix(in oklab, var(--foreground) 90%, var(--task-list-surface));
color: var(--background);
}
html:has(.design-proposal-theme) [data-canonical-toolbar] button > svg {
width: var(--design-page-icon-size);
height: var(--design-page-icon-size);
margin-inline: 0;
}
html:has(.design-proposal-theme) [data-canonical-toolbar] .design-view-toggle {
height: var(--collection-toolbar-height);
overflow: visible;
}
html:has(.design-proposal-theme) [data-canonical-toolbar] .design-view-toggle button {
height: calc(var(--collection-toolbar-height) - var(--task-row-rule-width) * 2);
border: 0;
border-radius: 0;
}
html:has(.design-proposal-theme) [data-canonical-toolbar] .design-view-toggle button:first-child {
border-start-start-radius: var(--collection-toolbar-radius);
border-end-start-radius: var(--collection-toolbar-radius);
}
html:has(.design-proposal-theme) [data-canonical-toolbar] .design-view-toggle button:last-child {
border-start-end-radius: var(--collection-toolbar-radius);
border-end-end-radius: var(--collection-toolbar-radius);
}
html:has(.design-proposal-theme) [data-canonical-toolbar] [data-toolbar-search] {
flex-shrink: 1;
max-width: 100%;
}
html:has(.design-proposal-theme) [data-canonical-toolbar] [data-toolbar-search] input {
background: transparent;
}
/* Canonical pill tabs: Compact (12px) and Standard (14px).
Shared geometry applies to Radix content tabs and exclusive collection filters. */
:root {
--canonical-tab-compact-height: calc(var(--spacing) * 6);
--canonical-tab-standard-height: calc(var(--spacing) * 8);
--canonical-tab-padding: calc(var(--spacing) * 2.5);
--canonical-tab-radius: var(--radius-md);
--canonical-tab-compact-gap: calc(var(--spacing) * 1.5);
--canonical-tab-standard-gap: calc(var(--spacing) * 2);
--canonical-tab-disabled-opacity: 0.5;
--canonical-tab-standard-leading: calc(var(--spacing) * 4.5);
}
[data-canonical-tabs].canonical-tab-list {
display: flex;
flex-wrap: wrap;
width: fit-content;
max-width: 100%;
height: auto;
justify-content: flex-start;
align-items: center;
padding: var(--spacing);
gap: var(--canonical-tab-standard-gap);
background: transparent;
color: var(--muted-foreground);
}
[data-canonical-tabs="compact"].canonical-tab-list { gap: var(--canonical-tab-compact-gap); }
[data-canonical-tabs] .canonical-tab {
position: relative;
display: inline-flex;
flex: none;
align-items: center;
justify-content: center;
height: var(--canonical-tab-standard-height);
padding: 0 var(--canonical-tab-padding);
border: 0;
border-radius: var(--canonical-tab-radius);
background: transparent;
color: var(--muted-foreground);
font-family: var(--font-sans);
font-size: var(--text-sm);
line-height: var(--canonical-tab-standard-leading);
font-weight: var(--font-weight-normal);
white-space: nowrap;
box-shadow: none;
transition: background-color var(--default-transition-duration) var(--default-transition-timing-function), color var(--default-transition-duration) var(--default-transition-timing-function);
}
[data-canonical-tabs="compact"] .canonical-tab {
height: var(--canonical-tab-compact-height);
font-size: var(--text-xs);
line-height: calc(var(--text-xs) * var(--text-xs--line-height));
}
[data-canonical-tabs] .canonical-tab::after { display: none; }
[data-canonical-tabs] .canonical-tab:is([data-state="active"], [aria-checked="true"]) {
background: var(--muted-highlight);
color: var(--foreground);
box-shadow: none;
}
[data-canonical-tabs] .canonical-tab:hover:not(:disabled) {
background: var(--muted-highlight);
color: var(--foreground);
}
[data-canonical-tabs] .canonical-tab:focus-visible {
outline: var(--focus-ring-stroke-width) solid var(--ring);
outline-offset: var(--focus-ring-stroke-width);
box-shadow: var(--focus-ring-shadow);
}
[data-canonical-tabs] .canonical-tab:disabled { opacity: var(--canonical-tab-disabled-opacity); pointer-events: none; }
html:has(.design-proposal-theme) [data-canonical-toolbar] .collection-toolbar-tabs select {
height: var(--collection-toolbar-height);
border-radius: var(--collection-toolbar-radius);
border-color: var(--collection-toolbar-border);
}
/* ── Artifacts V1 component rules (ui/src/components/artifacts/v1) ──────────
Sheets, prints, the task pill and the hover movement. Movement only for
people who have not asked for reduced motion; colour fades stay. */
.artifact-sheet {
width: 180px;
height: 216px;
border-radius: var(--artifact-sheet-radius);
filter: var(--artifact-shadow-sheet);
}
.artifact-sheet::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
box-shadow: var(--artifact-sheet-inset-stroke);
pointer-events: none;
}
.artifact-print {
border-style: solid;
border-color: var(--artifact-paper);
border-radius: var(--artifact-print-radius);
background: var(--artifact-paper);
box-shadow: var(--artifact-shadow-print);
}
.artifact-backing {
filter: var(--artifact-shadow-sheet);
}
.artifact-pill {
font-family: var(--artifact-font-mono);
background: var(--artifact-pill);
color: var(--artifact-pill-ink);
}
.artifact-tile:is(:hover, :focus-visible) .artifact-pill {
background: var(--artifact-pill-hover);
}
[data-artifact-rotate] {
transform: rotate(var(--artifact-base, 0deg));
}
@media (prefers-reduced-motion: no-preference) {
.artifact-tile :is([data-artifact-rotate][data-artifact-spread], [data-artifact-front]) {
transition: transform var(--artifact-hover-ease);
}
.artifact-tile:hover [data-artifact-front] {
transform: translateY(-6px);
}
.artifact-tile:hover [data-artifact-spread="left"] {
transform: rotate(calc(var(--artifact-base, 0deg) + 5deg));
}
.artifact-tile:hover [data-artifact-spread="right"] {
transform: rotate(calc(var(--artifact-base, 0deg) - 5deg));
}
}
/* Coordinator workspace: the approved Precision palette with Editorial
reading rhythm. This scope leaves the classic interface and its tokens intact. */
.coordinator-workspace {
--coordinator-body-size: var(--text-sm);
--coordinator-body-leading: var(--text-sm--line-height);
--coordinator-card-radius: 14px;
--coordinator-card-inset: calc(var(--spacing) * 7);
--coordinator-card-shadow: 0 5px 22px #00000008;
--coordinator-composer-shadow: 0 8px 30px #0000000a;
--background: #fafafa;
--foreground: #262626;
--card: #ffffff;
--card-foreground: var(--foreground);
--sidebar: #f1f1f1;
--sidebar-accent: #e3e3e3;
--sidebar-accent-foreground: #202020;
--border: #dedede;
--muted: #eeeeee;
--muted-foreground: #6d6d6d;
--accent: #ebebeb;
--accent-foreground: var(--foreground);
--bubble-human-bg: var(--liveness-blue);
--bubble-human-fg: var(--color-white);
--streamlined-task-detail-bg: var(--background);
--side-panel-bg: var(--card);
--side-panel-streamlined-tab-active-bg: var(--sidebar-accent);
--tc-shell-max-w: 58rem;
font-family: var(--font-sans);
}
.dark .coordinator-workspace {
--background: #191919;
--foreground: #ecece8;
--card: #222222;
--sidebar: #202020;
--sidebar-accent: #343432;
--sidebar-accent-foreground: #f1f1ed;
--border: #3a3a38;
--muted: #2a2a28;
--muted-foreground: #a5a5a0;
--accent: #30302e;
--bubble-human-bg: var(--liveness-blue);
--coordinator-card-shadow: 0 5px 22px #00000020;
--coordinator-composer-shadow: 0 8px 30px #00000026;
}
/* Reuse proposal geometry locally; the Coordinator navigation and conversation
hierarchy remain independent of the global Design Proposals theme. */
html:not(.dark) .coordinator-workspace [data-sidebar-account] { background: var(--primary-sidebar-background); }
html.dark .coordinator-workspace [data-sidebar-account] { background: var(--primary-sidebar-background); }
.coordinator-sidebar, .coordinator-task-list {
--font-weight-medium: var(--font-weight-normal);
--font-mono: var(--font-paper-mono);
}
html:not(.dark) .coordinator-sidebar {
--design-sidebar-surface: var(--primary-sidebar-background);
--design-sidebar-item-foreground: color-mix(in oklab, var(--foreground) 80%, transparent);
--design-sidebar-item-highlight: var(--sidebar-accent);
--design-sidebar-item-active-foreground: var(--sidebar-accent-foreground);
}
.coordinator-workspace [data-conversation-text] { font-size: var(--coordinator-body-size); line-height: var(--coordinator-body-leading); }
.coordinator-workspace [data-conversation-text="progress"] { font-size: var(--text-sm); line-height: var(--text-sm--line-height); }
.coordinator-workspace [data-conversation-text] .paperclip-markdown { font-size: inherit; line-height: inherit; }
.coordinator-workspace [data-conversation-text="human"] { background: var(--bubble-human-bg); color: var(--bubble-human-fg); }
.coordinator-workspace [data-question-answer] { background: var(--card); }
.dark .coordinator-workspace [data-question-answer] { background: var(--muted); }
/* Only the composer surface obscures history; its surrounding dock is clear. */
.coordinator-workspace [data-testid="task-chat-composer-dock"] {
flex-shrink: 0;
background: transparent;
backdrop-filter: none;
pointer-events: none;
}
.coordinator-workspace [data-testid="task-chat-composer-dock"] > * { pointer-events: auto; }
@media (min-width: 768px) {
.coordinator-workspace [data-testid="task-chat-thread"] { position: relative; }
.coordinator-workspace [data-testid="task-chat-composer-dock"] { position: absolute; inset-inline: 0; bottom: 0; }
.coordinator-workspace [data-testid="task-chat-scroller"] > .paperclip-mobile-thread {
padding-bottom: calc(var(--task-composer-dock-height, 0px) + var(--spacing) * 4);
}
.coordinator-workspace [data-testid="task-chat-scroller"] ~ button {
bottom: calc(var(--task-composer-dock-height, 0px) + var(--spacing) * 3);
}
}
.coordinator-workspace [data-testid="task-chat-composer-dock"] > :last-child { border-radius: var(--coordinator-card-radius); }
.coordinator-workspace [cmdk-root] { height: auto; flex: none; }
.coordinator-workspace [cmdk-list] { flex: none; }
.coordinator-decision-card { border: 1px solid var(--border); border-radius: var(--coordinator-card-radius, var(--radius-xl)); padding: var(--coordinator-card-inset, calc(var(--spacing) * 6)); background: var(--card); box-shadow: var(--coordinator-card-shadow); }
.coordinator-decision-card > .rounded-lg { border: 0; padding: 0; background: transparent; box-shadow: none; }
.coordinator-decision-card [data-testid="interaction-status-badge"], .coordinator-decision-card [data-testid="interaction-addressee-badge"] { display: none; }
.coordinator-decision-card :is(h2,h3) { font-size: var(--text-lg); line-height: var(--text-lg--line-height); font-weight: var(--font-weight-medium); }
.coordinator-decision-card :is(p, label) { font-size: var(--text-sm); line-height: var(--text-sm--line-height); }
.coordinator-decision-card [data-decision-prompt] { font-size: var(--text-lg); line-height: var(--text-lg--line-height); font-weight: var(--font-weight-medium); }
.coordinator-decision-card [id^="interaction-"]:focus { outline-color: var(--ring); }
@media (max-width: 767px) {
.coordinator-workspace { --coordinator-card-inset: calc(var(--spacing) * 5); }
}
/* Coordinator commitments: event-triggered, bounded motion; settled content is
always usable. Values live here rather than in presentation components. */
.coordinator-task-commitment {
--coordinator-task-arrive-duration: 880ms;
--coordinator-task-light-duration: 1400ms;
--coordinator-task-morph-duration: 480ms;
--coordinator-task-content-duration: 320ms;
--coordinator-task-ease: cubic-bezier(.22, .65, .3, 1);
--coordinator-task-arrive-distance: calc(var(--spacing) * 5);
--coordinator-task-radius: 14px;
--coordinator-task-light-cool: oklch(.65 .18 250);
--coordinator-task-light-warm: oklch(.76 .16 75);
position: relative;
width: 100%;
min-width: 0;
overflow: hidden;
isolation: isolate;
border: 1px solid var(--border);
border-radius: var(--coordinator-task-radius);
background: var(--card);
}
.coordinator-task-commitment-content { position: relative; min-width: 0; }
.coordinator-decision-motion { border-radius: var(--coordinator-card-radius, var(--radius-xl)); box-shadow: var(--coordinator-card-shadow); }
.coordinator-decision-motion > .coordinator-task-commitment-content > :is(.coordinator-decision-card, .coordinator-decision-receipt) { border: 0; box-shadow: none; }
.coordinator-task-commitment[data-motion="arrive"] { animation: coordinator-task-arrive var(--coordinator-task-arrive-duration) var(--coordinator-task-ease) both; }
.coordinator-task-light { display: none; position: absolute; inset: 1px; border-radius: inherit; overflow: hidden; pointer-events: none; }
.coordinator-task-commitment[data-motion="gather"] > .coordinator-task-light { display: block; background: radial-gradient(ellipse at 25% 20%, color-mix(in oklab, var(--foreground) 8%, transparent), transparent 75%); animation: coordinator-task-gather var(--coordinator-task-light-duration) var(--coordinator-task-ease) both; }
.coordinator-task-light::before { content: ""; position: absolute; inset: 0; background: linear-gradient(110deg, transparent 15%, color-mix(in oklab, var(--coordinator-task-light-cool) 18%, transparent) 37%, color-mix(in oklab, var(--foreground) 20%, transparent) 50%, color-mix(in oklab, var(--coordinator-task-light-warm) 18%, transparent) 63%, transparent 85%); animation: coordinator-task-light-pass var(--coordinator-task-light-duration) var(--coordinator-task-ease) both; }
.coordinator-created-task, .coordinator-decision-receipt { display: flex; align-items: center; gap: calc(var(--spacing) * 3); padding: calc(var(--spacing) * 4); color: var(--foreground); transition: background-color var(--motion-fast, 120ms); }
.coordinator-created-task:hover, .coordinator-decision-receipt:hover { background: var(--accent); }
.coordinator-created-task:focus-visible, .coordinator-decision-receipt:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
.coordinator-decision-receipt { border: 1px solid var(--border); border-radius: 14px; background: var(--card); padding-block: calc(var(--spacing) * 3); }
@keyframes coordinator-task-arrive { from { opacity: 0; transform: translateY(var(--coordinator-task-arrive-distance)); } to { opacity: 1; transform: none; } }
@keyframes coordinator-task-gather { from { opacity: 0; } 20%, 45% { opacity: 1; } to { opacity: 0; } }
@keyframes coordinator-task-light-pass { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) {
.coordinator-task-commitment[data-motion] { animation: none; }
.coordinator-task-commitment > .coordinator-task-light { display: none; }
.coordinator-created-task, .coordinator-decision-receipt { transition: none; }
}