blob: 29a234f4500cf3b9c23de96c218ab9a9f142340e [file]
/* Maka product compositions shared across desktop renderer surfaces. Astryx
owns generic component chrome; these selectors own only product semantics. */
.maka-quote-chip-remove { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; border: 0; color: var(--muted-foreground); background: transparent; transition: color var(--duration-quick) var(--ease-out-strong), background var(--duration-quick) var(--ease-out-strong); }
.maka-quote-chip-remove:hover { color: var(--foreground); background: var(--state-hover-bg); }
.maka-quote-chip-remove:focus-visible { color: var(--foreground); background: var(--state-hover-bg); outline: var(--focus-ring-width) solid var(--focus-ring); outline-offset: 1px; }
.maka-page-header-title,
.maka-page-header-eyebrow,
.maka-page-header-subtitle { margin: 0; }
.maka-page-header-content { min-width: 0; }
.maka-page-header-heading-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.maka-stat-tile { display: flex; min-width: 0; flex-direction: column; align-items: flex-start; gap: 4px; padding: 12px; }
/* Visual System 2.0 (T4): flat. DESIGN.md §5 says default surfaces are flat and
depth comes from the ladder, then a line, then shadow only for something that
genuinely floats — a stat tile sits on the plate. It already states its edge
with a border, so the resting shadow was also a second separator on the same
edge (§4's One Means Rule). Dropping it retires --card-shadow's last consumer
rather than promoting it to --elevation-raised, which would have made a
stationary tile float harder than it did before. */
.maka-stat-tile-outline { border: 1px solid var(--card-border-color, var(--border)); border-radius: var(--radius-surface); background: var(--card-bg, var(--background)); }
.maka-stat-tile-filled { border-radius: var(--radius-control); background: var(--foreground-5); }
.maka-stat-tile-border-info { border-color: oklch(from var(--info) l c h / 0.24); }
.maka-stat-tile-border-success { border-color: oklch(from var(--success) l c h / 0.24); }
.maka-stat-tile-border-warning { border-color: oklch(from var(--warning) l c h / 0.28); }
.maka-stat-tile-border-destructive { border-color: oklch(from var(--destructive) l c h / 0.3); }
.maka-stat-tile-empty { opacity: var(--opacity-disabled); }
.maka-stat-tile-value-outline { font: var(--maka-text-heading-1); }
.maka-stat-tile-value-filled { font: var(--maka-text-heading-4); }
/* After the two role rules, not before them. The `font:` shorthand resets
`font-variant-numeric` along with the four axes the role names, and both
modifiers land on this same element — so declared first, the tabular figures
these tiles exist to align were being reset away by the role. Ordering is
the fix rather than repeating the declaration on each modifier: it is one
authority, and it is the general rule for any sub-property the roles do not
carry. */
.maka-stat-tile-value { display: block; min-width: 0; max-width: 100%; overflow-wrap: anywhere; color: var(--foreground); font-variant-numeric: tabular-nums; white-space: normal; }
.maka-stat-tile-value-info { color: var(--info-text); }
.maka-stat-tile-value-success { color: var(--success-text); }
.maka-stat-tile-value-warning { color: var(--warning-text); }
.maka-stat-tile-value-destructive { color: var(--destructive-text); }
.maka-stat-tile-label {
font: var(--maka-text-supporting);
color: var(--foreground-secondary); letter-spacing: 0.025em; }
.maka-stat-tile-detail {
font: var(--maka-text-supporting);
color: var(--muted-foreground); }
.maka-quote-chip { display: inline-flex; gap: 4px; padding-left: 8px; background: var(--foreground-alpha-6); box-shadow: inset 0 0 0 1px var(--foreground-alpha-12); }
.maka-quote-chip-expanded { width: 100%; max-width: 100%; align-items: flex-start; padding-block: 4px; border-radius: var(--radius-surface); }
/* #1879: measured 24px natural — a 20px line box plus 2 x 2px padding — and
`--h-control-sm` IS 24, so the pin is pixel-neutral today and load-bearing
the next time a leading moves. This is the collapsed state only; the
expanded twin above wraps to many lines and must keep growing. The chip's
type lives on `.maka-quote-chip-text`, which is why the derived scan needed
two arms rather than three to see it at all. */
.maka-quote-chip-collapsed { height: var(--h-control-sm); max-width: 240px; align-items: center; padding-block: 2px; border-radius: var(--radius-pill); }
.maka-composer-running-actions { display: inline-flex; align-items: center; gap: var(--space-1); }
.maka-quote-chip-removable { padding-right: 2px; }
.maka-quote-chip-readonly { padding-right: 8px; }
.maka-quote-chip-icon { width: var(--icon-meta); height: var(--icon-meta); flex: 0 0 auto; color: var(--muted-foreground); }
.maka-quote-chip-icon-expanded { margin-top: 2px; }
.maka-quote-chip-text {
font: var(--maka-text-supporting);
min-width: 0;
min-height: 0;
flex: 1;
height: auto;
border: 0;
padding: 0;
color: var(--foreground-secondary);
background: transparent;
text-align: left;
box-shadow: none;
}
.maka-quote-chip-text-body {
display: block;
min-width: 0;
max-width: 100%;
}
.maka-quote-chip-text-expanded { max-height: 128px; overflow-y: auto; overflow-wrap: break-word; white-space: pre-wrap; }
.maka-quote-chip-text-clipped { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.maka-quote-chip-label { color: var(--muted-foreground); }
.maka-quote-chip-remove {
width: 16px;
height: 16px;
min-width: 16px;
min-height: 16px;
border-radius: 50%;
padding: 0;
}
.maka-quote-chip-remove > svg,
.maka-quote-chip-remove svg { width: var(--icon-meta); height: var(--icon-meta); }
.maka-task-ledger-empty {
margin: 0;
}
/* The meta row holds two different kinds of thing, so it gets two different
rules. The time is content — a fact about the message — and stays put; gating
it on hover is what hid it from touch and from assistive tech. The actions are
chrome and only surface on hover or focus. Astryx already splits these into a
`timestamp` and a `footer` slot; the gate belongs on the slot, not on the row
that contains both.
The `· ` between the two slots is Astryx's own separator, rendered whenever
the timestamp has something to be separated FROM. It is dropped rather than
gated: the two slots already read as separate at rest, since one is text and
the other a row of controls that fades in beside it, so the bullet adds a mark
without adding a distinction. The slots carry no class of their own, so it is
addressed positionally — the timestamp is the first child span and the
separator the one right after it (the footer's own children are buttons).
A transparent action still takes clicks, here and on the assistant footer.
`pointer-events: none` looks like the fix and is not obviously safe: it makes
the pointer fall through to the ancestor whose `:hover` is what restores the
button, so being clickable again depends on the browser redoing hit-testing
on a later mouse event. Continuous movement gets there; a jump that lands and
stops was not something this surface could be made to reproduce. Left as-is
rather than traded for a failure mode that could not be demonstrated. */
.maka-message-meta > span + span {
display: none;
}
.maka-message-meta .maka-turn-footer-action {
opacity: 0;
transition: opacity var(--duration-quick) var(--ease-out-strong);
}
.maka-user-message:hover .maka-message-meta .maka-turn-footer-action,
.maka-user-message:focus-within .maka-message-meta .maka-turn-footer-action {
opacity: 1;
}
.maka-user-quotes { display: flex; max-width: 100%; flex-wrap: wrap; align-items: flex-start; gap: 4px; }
.maka-assistant-answer-content { display: flex; width: 100%; min-width: 0; flex-direction: column; gap: 8px; }
.maka-subagent-session-label,
.maka-subagent-session-end {
display: flex;
align-items: center;
gap: var(--space-2);
}
.maka-subagent-session-label { width: 100%; min-width: 0; }
.maka-subagent-session-summary { min-width: 0; flex: 1 1 auto; }
.maka-subagent-session-end { color: var(--muted-foreground); }
.maka-live-turn-footer-placeholder { height: 32px; margin-top: 2px; }
.maka-turn-processing,
.maka-turn-status {
font: var(--maka-text-body);
display: flex; align-items: center; gap: 8px; padding-block: 2px; color: var(--muted-foreground); }
/* The live status is the one thing in the turn that is about to happen rather
than something that already did, so it gets air the blocks above it do not
(they sit on the column's own 8px). Hugging the last tool card read as part
of that card. */
.maka-turn-processing { margin-top: 8px; }
.maka-turn-indicator-text { display: inline-flex; min-width: 0; align-items: center; gap: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Duration must match `WORKING_PHRASE_FADE_MS` in chat-turn.tsx: the swap is a
fade out, then a text change, then a fade in, and the JS timer is what waits
out this transition before rewriting the word. */
.maka-turn-working-phrase { font-weight: var(--font-weight-medium); transition: opacity var(--duration-large) var(--ease-out-strong); }
.maka-turn-working-phrase[data-fading="true"] { opacity: 0; }
.maka-turn-status-separator { color: var(--foreground-alpha-16); }
/* Tabular figures so a ticking clock changes glyphs without changing width. */
.maka-turn-elapsed { font-variant-numeric: tabular-nums; }
/* #1879: an Astryx `Badge variant="yellow"` now — the warning tone it used to
draw by hand (hairline + 5% wash) is a variant, and the box comes with it.
Product CSS keeps only what is layout: it is a block-level note under the
bubble, so it needs its own line and the gap above it. */
.maka-turn-truncation-badge { display: flex; width: fit-content; margin-top: 6px; cursor: help; }
.maka-deep-thinking { min-width: 0; }
/* The reasoning body inside the Astryx ChatReasoning disclosure. The official
atoms own no white-space (the shell assumes children are pre-rendered), so
the inherited `white-space: normal` collapses every newline in thinking
text. Restore the pre-wrap reading contract the pre-Astryx disclosure had
(dropped in the #1748 migration). `word-break` keeps long tokens from
overflowing the box, as the old body did. */
.maka-chat-reasoning-content { white-space: pre-wrap; word-break: break-word; }
@keyframes maka-spin { to { transform: rotate(360deg); } }
.maka-spin { animation: maka-spin 1s linear infinite; }
/* ---------------------------------------------------------------------------
Transcript markdown rhythm — the single authority for compact prose spacing.
Astryx's `density` only reaches the blocks Markdown renders itself. Lists are
delegated to the List control, and Markdown hands it a hardcoded
`density="compact"` in BOTH modes — so a markdown list is spaced as a
clickable row (4px block padding, the RFC #839 control value), not as prose.
The result under `density="compact"` was a broken order: list items sat 10px
apart while paragraphs sat 4px apart, so same-level items read as further
apart than different paragraphs.
This table restores one rule — visual distance rises with semantic distance —
by owning every compact prose gap in one place:
4px list items (same level)
8px blocks (paragraph, list, quote, table, code)
16px section (h3-h6, and either side of an `hr`)
24px chapter (h1, h2)
Three deliberate choices:
- Only `[data-density="compact"]`. The document mode is not broken (its
lists sit 10px apart against 12px paragraphs, which is already in order),
and the Daily Review renders through it. Fixing only the broken half keeps
that surface out of the blast radius.
- Scoped to Astryx's own `data-density`, not to a `.maka-turn` ancestor.
`themeProps()` reflects every visual prop as a data attribute for exactly
this ("consumers target stable data-attribute selectors"), so the rhythm
follows the `density` prop instead of where the markdown happens to sit.
Storybook then reproduces the transcript by passing the prop, which the
old ancestor-scoped rules could not.
- Top level only. `>` keeps every gap on the document's own children, so
blocks nested inside a list item or a blockquote keep Astryx's own compact
4px instead of the 8px block gap. That tier difference is the point, not
an oversight: a list item should read as one unit, so two paragraphs
inside it belong closer together than two paragraphs in the transcript.
The ladder extends downward (4px nested < 8px block) rather than
inverting, which is the invariant that matters.
A gap is a relation BETWEEN two blocks, so every gap rule is an adjacent
sibling rule. Two consequences, both load-bearing:
- The first block matches no gap rule at all, so it needs no reset. The
reset form (`> *` for the gap, `> :first-child` to take it back) looks
equivalent and is not: `:first-child` scores (0,4,0) against the heading
rules' (0,5,0), so a turn that OPENS with a heading — which is most of
them — kept the 24px chapter gap above its first line and pushed itself
off the top of the bubble. Expressing the relation directly is what makes
that unrepresentable rather than merely fixed.
- Gaps are margin-top only, on top of a blanket `margin-block: 0` that
clears Astryx's own margins. Nothing collapses, so the distance between
two blocks is the value declared here. */
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] {
--md-gap-list: var(--space-1);
--md-gap-block: var(--space-2);
--md-gap-section: var(--space-4);
--md-gap-chapter: var(--space-6);
}
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] > * {
margin-block: 0;
}
/* `:not(dialog)` because a gap is a relation between two blocks in the document
flow, and a dialog is not one. Astryx's `Dialog` does not portal, so the
Mermaid fullscreen overlay renders as a direct child of the markdown document
while being `position: fixed; inset: 0` — an adjacent-sibling margin there
pushes the whole viewport-filling overlay off the top of the screen rather
than spacing anything (caught by the Mermaid E2E journey, 8px off). The
exclusion belongs on the side that RECEIVES the margin; an overlay sitting
between two blocks is fine as the `+` antecedent. */
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] > * + *:not(dialog) {
margin-block-start: var(--md-gap-block);
}
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] > * + .astryx-markdown-heading:is([data-level="1"], [data-level="2"]) {
margin-block-start: var(--md-gap-chapter);
}
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] > * + .astryx-markdown-heading:not([data-level="1"], [data-level="2"]) {
margin-block-start: var(--md-gap-section);
}
/* An `hr` is the only break the author typed by hand. Left on the generic block
step it reads no wider than the paragraph boundary above it, so writing one
changes nothing — which is the same failure as the flattened heading scale,
in a different place. It sits at the section step because that is what it
separates. A following h1/h2 still wins on specificity ((0,5,0) beats
(0,3,1)) and keeps the chapter gap, so `hr` before a major heading is
unaffected. */
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] > * + hr,
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] > hr + *:not(dialog) {
margin-block-start: var(--md-gap-section);
}
/* Prose list, not a control list: drop the ListItem row padding and express the
row rhythm as a gap, so it stays below the block gap above. */
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] .astryx-list-item {
padding-block: 0;
}
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] .astryx-list {
gap: var(--md-gap-list);
}
/* Heading scale: size says "this is a heading", weight and colour say which
level. Astryx's document ladder (20/18/16/14) is a page scale and an agent
turn emits `##` every few lines, so the transcript keeps two size steps —
but it keeps TWO, not one. The previous rule flattened h2-h6 onto a single
size, which left h2 and h3 identical in size, weight AND colour: three
levels of structure rendered as three identical bold lines.
Cursor (1.43/1.32/1.21), Claude Code (1.14/1.07/1.0) and Codex
(1.71/1.43/1.21) all flatten here relative to their own document styles, and
opencode goes further — 17/15/13 with h4+ dropped to a muted colour. Nobody
flattens to zero. Two steps plus colour sits inside that range.
RISK, not just a choice: these are the only rules here that are typography
rather than spacing, and Astryx's density RFC (facebook/astryx#839) draws
the line at "density shifts heights and spacing, not typography". Keying
them on `data-density` is honest only while `compact` and "transcript" name
the same set — true today, since the transcript is the only caller asking
for compact and the Daily Review renders a document at the default. A
second compact surface would silently inherit transcript heading sizes and
dimmed deep headings. Giving typography its own surface attribute would
decouple them, but nothing needs it yet; the assumption is held by a test
instead (packages/ui/src/__tests__/markdown-rhythm-dom-contract.test.tsx,
"keeps compact markdown a transcript-only surface"), which fails the moment
the sets diverge and says what the choice is. */
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] .astryx-markdown-heading:is([data-level="1"], [data-level="2"]) {
font: var(--maka-text-heading-3);
}
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] .astryx-markdown-heading:not([data-level="1"], [data-level="2"]) {
font: var(--maka-text-heading-4);
}
/* Below h3 the size ladder is spent — h4-h6 already sit at body size, and
growing them back would re-create the slabs the flattening exists to avoid.
Colour is the one axis left that costs no vertical space, so the deepest
levels step down in ink instead. opencode makes the same call at the same
point (13px h4-h6 dropped to muted); it is why a turn can carry four levels
of structure inside two type sizes. */
[data-maka-contract="markdown"] .astryx-markdown[data-density="compact"] .astryx-markdown-heading:not([data-level="1"], [data-level="2"], [data-level="3"]) {
color: var(--foreground-secondary);
}
/* Markdown code renderers are custom components so Mermaid can be loaded only
for settled Mermaid fences. Astryx skips its own spacing wrapper when
`components.code` is set, so the custom block sits directly in the document
flow — the compact rhythm above already carries it, and only the document
mode needs its margins declared here. */
.maka-markdown-code { min-width: 0; }
.maka-markdown-code-default { margin-block: var(--space-3) var(--space-4); }
[role="document"] > .maka-markdown-code:first-child { margin-block-start: 0; }
[role="document"] > .maka-markdown-code:last-child { margin-block-end: 0; }
.maka-mermaid-diagram {
display: flex;
max-width: 100%;
min-height: 0;
flex-direction: column;
overflow: hidden;
container-type: inline-size;
border: 1px solid var(--border);
border-radius: var(--radius-surface);
background: var(--background);
}
.maka-mermaid-toolbar {
min-height: 36px;
flex: 0 0 auto;
border-bottom: 1px solid var(--border);
background: var(--foreground-3);
}
.maka-mermaid-title { font: var(--maka-text-supporting); color: var(--muted-foreground); }
.maka-mermaid-actions { display: flex; align-items: center; gap: 2px; }
.maka-mermaid-zoom-actions { display: flex; align-items: center; gap: 2px; }
.maka-mermaid-zoom-level {
font: var(--maka-text-code);
min-width: 4.5ch;
color: var(--muted-foreground);
text-align: center;
font-variant-numeric: tabular-nums;
}
.maka-mermaid-viewport {
position: relative;
display: grid;
min-width: 0;
align-items: center;
overflow: auto;
padding: var(--space-3);
cursor: default;
outline: none;
overscroll-behavior: auto;
touch-action: pan-y;
}
.maka-mermaid-viewport:focus-visible { box-shadow: inset 0 0 0 2px var(--focus-ring); }
.maka-mermaid-viewport[data-maka-mermaid-pannable="horizontal"] { cursor: grab; overscroll-behavior-x: contain; }
.maka-mermaid-viewport[data-maka-mermaid-pannable="vertical"],
.maka-mermaid-viewport[data-maka-mermaid-pannable="both"] {
cursor: grab;
overscroll-behavior: contain;
touch-action: none;
}
.maka-mermaid-viewport[data-maka-mermaid-panning="true"] { cursor: grabbing; user-select: none; }
.maka-mermaid-canvas { min-width: 0; margin-inline: auto; }
.maka-mermaid-svg { display: flex; width: 100%; height: 100%; min-width: 0; justify-content: center; }
.maka-mermaid-svg > svg {
display: block;
width: 100%;
max-width: none !important;
height: 100%;
overflow: visible;
}
/* Legal square (DESIGN.md §6): the expanded diagram fills its viewport with
margin 0 on every side, so it is full-bleed rather than a card that lost its
corners. The collapsed state above keeps its radius. */
.maka-mermaid-diagram-expanded {
width: 100%;
height: 100%;
max-width: none;
margin: 0 !important;
border: 0;
border-radius: 0;
}
.maka-mermaid-diagram-expanded .maka-mermaid-toolbar {
padding-left: max(var(--space-3), var(--maka-titlebar-area-x, 0px));
padding-right: calc(var(--space-1) + var(--maka-titlebar-overlay-right-width, 0px));
}
.maka-mermaid-diagram-expanded .maka-mermaid-actions { -webkit-app-region: no-drag; }
.maka-mermaid-diagram-expanded .maka-mermaid-viewport { min-height: 0; flex: 1 1 auto; }
.maka-mermaid-dialog { -webkit-app-region: no-drag; }
.maka-mermaid-source {
max-height: min(40%, 320px);
flex: 0 1 auto;
overflow: auto;
padding: var(--space-1) var(--space-3) var(--space-2);
border-top: 1px solid var(--border);
}
.maka-mermaid-fallback { display: grid; gap: var(--space-1-5); }
.maka-mermaid-fallback-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-2);
}
.maka-mermaid-status {
font: var(--maka-text-supporting);
color: var(--muted-foreground);
}
@container (max-width: 520px) {
.maka-mermaid-title { display: none; }
}
@container (max-width: 360px) {
.maka-mermaid-diagram:not(.maka-mermaid-diagram-expanded) .maka-mermaid-zoom-actions { display: none; }
}
.maka-tool-output-panel { display: grid; gap: 8px; margin-top: 4px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius-surface); background: var(--foreground-3); }
.maka-tool-output-command {
font: var(--maka-text-code);
display: block; min-width: 0; color: var(--foreground); font-variant-ligatures: none; white-space: pre-wrap; word-break: break-word; }
/* The command reads as the surface's header, not as the first line of the
output: it keeps the foreground colour the body gives up, and a hairline
separates the two. The rule is on the row rather than the panel so a
command with nothing under it (a failed launch) does not end in a stray
line — the panel's own 8px gap supplies the space below. */
.maka-tool-output-command-row { display: flex; min-width: 0; align-items: flex-start; gap: 8px; }
.maka-tool-output-command-row:not(:last-child) { padding-bottom: 8px; border-bottom: 1px solid var(--border); }
.maka-tool-output-command-row .maka-tool-output-command { flex: 1 1 auto; }
.maka-tool-output-command-actions { display: flex; flex: 0 0 auto; align-items: center; gap: 2px; }
.maka-tool-output-command-copy { flex: 0 0 auto; }
.maka-tool-output-command-copy[data-copy-feedback="pending"] { cursor: progress; }
.maka-tool-output-command-copy[data-copy-feedback="copied"] { color: var(--link); }
.maka-tool-output-command-copy[data-copy-feedback="failed"] { color: var(--destructive); }
.maka-tool-output-body {
font: var(--maka-text-code);
max-height: 256px; margin: 0; overflow-y: auto; color: var(--muted-foreground); font-variant-ligatures: none; scroll-behavior: auto; white-space: pre-wrap; word-break: break-word; }
.maka-tool-output-note {
font: var(--maka-text-body);
margin: 0; color: var(--muted-foreground); }
.maka-tool-output-wrap { min-width: 0; overflow-wrap: anywhere; }
.maka-tool-output-destructive { color: var(--destructive); }
.maka-tool-output-warning { color: var(--warning-text, var(--info-text)); }
.maka-tool-output-warning-border { border-color: oklch(from var(--warning) l c h / 0.32); }
.maka-tool-output-destructive-border { border-color: oklch(from var(--destructive) l c h / 0.28); }
.maka-tool-output-stack { display: flex; min-width: 0; flex-direction: column; gap: 6px; margin-top: 4px; }
.maka-pty-shell { gap: 0; overflow: hidden; padding: 0; }
.maka-pty-shell-header { display: flex; min-width: 0; align-items: center; padding: 10px 12px 4px; }
.maka-pty-shell-title {
font: var(--maka-text-label);
color: var(--foreground-secondary); }
.maka-pty-shell-body { display: grid; min-width: 0; gap: 8px; padding: 6px 12px; }
.maka-pty-shell-footer {
font: var(--maka-text-body);
display: flex; min-height: 32px; align-items: center; justify-content: flex-end; gap: 6px; padding: 4px 12px 10px; color: var(--muted-foreground); }
.maka-pty-terminal-body { overflow: auto; white-space: pre; word-break: normal; }
.maka-web-result { display: grid; gap: 8px; }
.maka-web-result-header { display: grid; gap: 2px; }
.maka-web-result-header strong {
font: var(--maka-text-heading-4);
color: var(--foreground); }
.maka-web-result-header small,
.maka-web-result-list small {
font: var(--maka-text-body);
color: var(--muted-foreground); }
.maka-web-result-list { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
.maka-web-result-list li { display: grid; gap: 2px; }
.maka-web-result-list a {
font: var(--maka-text-label);
color: var(--link); }
.maka-web-result-list p {
font: var(--maka-text-body);
margin: 0; color: var(--foreground-secondary); }
.maka-web-result-error { gap: 6px; }
.maka-web-result-error-message,
.maka-web-result-repair {
font: var(--maka-text-body);
margin: 0; }
.maka-web-result-error-message { color: var(--destructive); }
.maka-web-result-repair { color: var(--muted-foreground); }
.maka-tool-call-detail { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
/* A CodeBlock title (a file path, a result headline) sits at the same tier as
the command header beside it. Astryx hard-codes that title to the supporting
tier, which is 12px against the block's own 14px code — rebinding the role
token reaches it through inheritance, the same move chat-message.css makes
on the tool and reasoning rows. Only the size: `medium` on a header is
Astryx's intent and the surface command's foreground colour carries the same
weight of emphasis without it. */
.maka-tool-call-detail .astryx-codeblock {
/* Geist Mono ligates `--`, `!==` and friends into single glyphs that sit
outside the cell they started in, so a `---` line looked like it had
escaped the block's left edge — measured, its box starts on the same x as
every other line. The product's own wells all disable this; Astryx's
CodeBlock does not, and tool output is transcript text where the literal
characters are the point. */
font-variant-ligatures: none;
--text-supporting-size: var(--text-code-size);
/* Unitless, so it has to move with the size: left at the supporting leading
it would multiply the larger size into a 23px line box and give the header
a rhythm no other row in the panel has. */
--text-supporting-leading: var(--text-code-leading);
}
.maka-tool-output-chunk { display: contents; }
.maka-tool-output-chunk-stderr { color: var(--destructive); }
.maka-tool-output-chunk-redacted { opacity: 0.65; }
.maka-tool-output-redacted { display: inline; margin-left: 2px; color: var(--warning-text, var(--info-text)); }
.maka-sandbox-blocked-banner { margin-bottom: 10px; }
.maka-sandbox-blocked-description {
font: var(--maka-text-supporting);
display: flex; flex-direction: column; gap: 4px; white-space: pre-wrap; word-break: break-word; }
.maka-sandbox-blocked-error {
font: var(--maka-text-supporting);
}
.maka-sandbox-blocked-copy { align-self: start; }
.maka-sandbox-blocked-copy[data-pending="true"] { cursor: progress; }
.maka-sandbox-blocked-copy[data-copy-feedback="copied"] { border-color: oklch(from var(--link) l c h / 0.35); color: var(--link); }
.maka-sandbox-blocked-copy[data-copy-feedback="failed"] { border-color: oklch(from var(--destructive) l c h / 0.35); color: var(--destructive); }
.maka-turn-status-icon { flex: 0 0 auto; }
.maka-turn-aborted-marker,
.maka-turn-host-origin,
.maka-turn-failed-banner {
font: var(--maka-text-supporting);
display: inline-flex;
width: fit-content;
align-items: center;
margin-inline: 0;
border-radius: var(--radius-control);
}
.maka-turn-aborted-marker {
gap: var(--space-1);
margin-block: var(--space-0-5) var(--space-1);
padding: var(--space-0-5) var(--space-1-5);
background: var(--foreground-5);
color: var(--foreground-secondary);
font-style: italic;
}
.maka-turn-aborted-marker em { font-style: italic; }
.maka-turn-host-origin {
align-self: flex-end;
gap: var(--space-1);
margin-bottom: var(--space-1);
padding: var(--space-0-5) var(--space-1-5);
background: var(--foreground-5);
color: var(--muted-foreground);
}
.maka-turn-failed-banner {
flex-wrap: wrap;
gap: var(--space-1-5);
margin-block: var(--space-0-5) var(--space-1-5);
padding: var(--space-1) var(--space-2);
border: 1px solid oklch(from var(--destructive) l c h / 0.28);
background: oklch(from var(--destructive) l c h / 0.1);
color: var(--destructive);
}
.maka-turn-failed-icon { display: inline-flex; align-items: center; }
.maka-turn-failed-recovery { color: var(--text-muted); }
.maka-turn-failed-recovery::before {
margin-right: var(--space-1-5);
color: var(--foreground-alpha-16);
content: "·";
}
.maka-turn-lineage-row,
.maka-turn-footer {
display: flex;
width: 100%;
max-width: var(--maka-chat-measure, 680px);
flex-wrap: wrap;
align-items: center;
justify-content: flex-start;
gap: var(--space-0-5);
margin-left: 0;
margin-right: auto;
}
.maka-turn-lineage-row {
margin-top: var(--space-0-5);
margin-bottom: var(--space-1);
opacity: 0.82;
}
.maka-turn-lineage-row-reverse { margin-top: var(--space-1); }
.maka-turn-lineage-badge {
border: 0;
border-radius: var(--radius-pill);
background: oklch(from var(--foreground) l c h / 0.05);
color: var(--muted-foreground);
}
.maka-turn-lineage-badge[data-direction="forward"] {
background: oklch(from var(--info) l c h / 0.06);
color: oklch(from var(--info-text) calc(l - 0.06) c h);
}
.maka-turn-lineage-badge[data-direction="reverse"] {
background: oklch(from var(--brand-deep) l c h / 0.06);
color: oklch(from var(--brand-deep) calc(l - 0.04) c h);
}
.maka-turn-footer {
margin-top: var(--space-0-5);
padding: 0;
opacity: 0;
transition: opacity var(--duration-quick) var(--ease-out-strong);
}
.maka-assistant-answer:hover .maka-turn-footer,
.maka-turn-footer:focus-within { opacity: 1; }
.maka-turn-footer-action {
border: 0;
border-radius: var(--radius-surface);
color: var(--muted-foreground);
}
.maka-turn-footer-action[data-pending="true"] {
cursor: progress;
opacity: 0.78;
}
.maka-turn-footer-action[aria-disabled="true"][data-pending="true"] { opacity: 0.78; }
.maka-turn-footer-action[data-copy-feedback="copied"] { color: var(--link); }
.maka-turn-footer-action[data-copy-feedback="failed"] { color: var(--destructive); }
.maka-overlay-preview {
font: var(--maka-text-supporting);
max-height: 180px;
margin: var(--space-1) 0 0;
overflow: auto;
font-variant-ligatures: none;
white-space: pre-wrap;
word-break: break-word;
}
.maka-overlay-close { justify-self: end; }
.maka-tool-diff,
.maka-tool-terminal {
display: grid;
gap: 0;
padding: 0;
border-radius: var(--radius-surface);
background: var(--background);
box-shadow: var(--ring-soft);
white-space: normal;
}
.maka-tool-diff-paths,
.maka-tool-terminal-head {
font: var(--maka-text-supporting);
display: flex;
flex-wrap: wrap;
gap: var(--space-1-5);
padding: var(--space-1) var(--space-2);
border-bottom: 1px solid var(--border);
background: var(--foreground-2);
}
.maka-tool-terminal-head { align-items: center; font-variant-ligatures: none; }
.maka-tool-diff-paths code { color: var(--foreground-secondary); background: transparent; }
.maka-tool-diff-body {
font: var(--maka-text-supporting);
max-height: 320px;
margin: 0;
padding-block: var(--space-1);
overflow: auto;
font-variant-ligatures: none;
white-space: pre;
word-break: normal;
}
/* Only the chat consumer moved into `.maka-tool-output-panel`, where the diff
sits beside a command on the code tier and a 12px body next to 14px reads as
a different kind of text. `apps/desktop`'s artifact pane still draws this as
a standalone card at the supporting tier its container asks for, so the tier
belongs to the panel, not to the shared class. */
.maka-tool-output-panel .maka-tool-diff-body {
font: var(--maka-text-code);
/* The `font` shorthand resets `font-variant-ligatures`, and this rule
outranks the `none` the shared class declares — so the panel's diff was
the one code surface in the app rendering `===` as `⩶` and `=>` as `⇒`.
Every other `font: var(--maka-text-code)` rule restates it for the same
reason; this one had not. */
padding-block: 0;
font-variant-ligatures: none;
}
.maka-tool-diff-line { display: block; padding-inline: var(--space-2); white-space: pre; }
.maka-tool-diff-gutter {
display: inline-block;
box-sizing: content-box;
text-align: right;
padding-right: var(--space-2);
margin-right: var(--space-1);
border-right: 1px solid var(--border);
color: var(--muted-foreground);
user-select: none;
}
/* The marker column: two character cells, the sign and a blank, so an
addition's code starts in the same place as the context line above it and
the sign is not crowding the first token. `ch` rather than a px gap because
this sits in a monospace body — one more cell keeps the code on the same
grid the file itself is written on, where 4px would leave it between two.
The column keeps the line's tint colour — the sign is what the colour is
naming — while `.maka-tool-diff-code` hands the code back to syntax
colouring. Every row gets the cell, including the markerless ones: a
`\ No newline at end of file` that hugged column 0 while the code beside it
did not was the same misalignment on a different row. */
.maka-tool-diff-marker {
display: inline-block;
width: 2ch;
}
/* Neutral base under the `astryx-token-*` spans. Without it an added line's
uncoloured text would stay green and a deleted line's red, which is the
flat single-tint reading the tokens exist to break up; the row's background
tint and its marker carry add/del on their own.
This is also what brings context lines up from `--foreground-secondary` to
full foreground, which is deliberate rather than collateral: once a context
line's keywords and strings are syntax-coloured, dimming only the text
between them reads as damage, not as de-emphasis. The row's flat background
is what says "unchanged" now. */
.maka-tool-diff-code { color: var(--foreground); }
.maka-tool-diff-line[data-line="meta"] .maka-tool-diff-code { color: inherit; }
.maka-tool-diff-line[data-line="add"] { background: oklch(from var(--success) l c h / 0.1); color: var(--success-text); }
.maka-tool-diff-line[data-line="del"] { background: oklch(from var(--destructive) l c h / 0.1); color: var(--destructive); }
.maka-tool-diff-line[data-line="hunk"] {
font: var(--maka-text-heading-5);
background: oklch(from var(--link) l c h / 0.08); color: var(--foreground-secondary); }
.maka-tool-diff-line[data-line="meta"] { color: var(--muted-foreground); }
.maka-tool-diff-line[data-line="ctx"] { color: var(--foreground-secondary); }
.maka-tool-terminal-cwd { color: var(--muted-foreground); background: transparent; }
.maka-tool-terminal-cmd {
font: var(--maka-text-heading-5);
min-width: 0; flex: 1 1 auto; overflow: hidden; background: transparent; color: var(--foreground); text-overflow: ellipsis; white-space: nowrap; }
/* Pinned like every other pill in the scan, even though the whole
`.maka-tool-terminal-*` family is currently unreachable:
`previewVariants({part: 'terminal-*'})` has no call site — `TerminalPreview`
renders `maka-tool-output-stack` / `ShellOutputBody` / `ToolCodeBlock`
instead — and `check-dead-css` cannot see that because the literals still
exist in `previewVariants`. The alternative is an exemption whose reason is
"nothing renders this", which is a fact about call sites recorded in a
stylesheet and would outlive the day someone wires the family back up.
Deleting the family is its own change. */
.maka-tool-terminal-exit {
font: var(--maka-text-heading-5);
height: var(--h-control-sm); display: inline-flex; align-items: center; white-space: nowrap; padding: 1px var(--space-1-5); border-radius: var(--radius-pill); background: var(--foreground-5); color: var(--foreground-secondary); letter-spacing: var(--tracking-wide); }
.maka-tool-terminal-exit[data-ok="true"] { background: oklch(from var(--success) l c h / 0.14); color: var(--success); }
.maka-tool-terminal-exit[data-ok="false"] { background: oklch(from var(--destructive) l c h / 0.14); color: var(--destructive); }
.maka-tool-terminal-empty {
font: var(--maka-text-supporting);
margin: 0; padding: var(--space-2); color: var(--muted-foreground); font-style: italic; }
.maka-tool-terminal-stream {
font: var(--maka-text-supporting);
max-height: 180px; margin: 0; padding: var(--space-1-5) var(--space-2); overflow: auto; font-variant-ligatures: none; white-space: pre-wrap; word-break: break-word; }
.maka-tool-terminal-stream[data-stream="stdout"] { color: var(--foreground); }
.maka-tool-terminal-stream[data-stream="stderr"] { border-top: 1px solid var(--border); background: oklch(from var(--destructive) l c h / 0.04); color: var(--destructive); }
.maka-tool-terminal-truncated-note {
font: var(--maka-text-supporting);
display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); padding: var(--space-1-5) var(--space-2); border-top: 1px solid var(--border); background: oklch(from var(--warning) l c h / 0.06); color: var(--foreground-secondary); }
.maka-tool-terminal-truncated-note > span { min-width: 0; }
.maka-tool-terminal-copy,
.maka-agent-preview-copy { flex: 0 0 auto; }
.maka-tool-terminal-copy[data-pending="true"],
.maka-agent-preview-copy[data-pending="true"] { cursor: progress; }
.maka-tool-terminal-copy[data-copy-error="true"],
.maka-agent-preview-copy[data-copy-error="true"] { border-color: oklch(from var(--destructive) l c h / 0.35); color: var(--destructive); }
.maka-agent-preview {
font: var(--maka-text-supporting);
display: grid; gap: var(--space-2-5); padding: var(--space-2-5) var(--space-3); border: 1px solid var(--foreground-10); border-radius: var(--radius-surface); background: var(--foreground-3); white-space: normal; }
.maka-agent-preview[data-ok="false"],
.maka-agent-preview[data-status="failed"],
.maka-agent-preview[data-status="cancelled"] { border-color: oklch(from var(--destructive) l c h / 0.22); }
.maka-agent-preview-head { display: grid; gap: var(--space-0-5); padding-bottom: var(--space-1-5); border-bottom: 1px solid var(--foreground-10); }
.maka-agent-preview-head strong {
font: var(--maka-text-heading-4);
min-width: 0; overflow: hidden; color: var(--foreground); text-overflow: ellipsis; white-space: nowrap; }
.maka-agent-preview-head small,
.maka-agent-preview-section small,
.maka-agent-preview-meta dt {
font: var(--maka-text-supporting);
color: var(--muted-foreground); letter-spacing: var(--tracking-wider); text-transform: uppercase; }
.maka-agent-preview-summary-line,
.maka-agent-preview-section-head { display: flex; min-width: 0; align-items: center; justify-content: space-between; gap: var(--space-2); }
.maka-agent-preview-summary-line small { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.maka-agent-preview-actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-1-5); margin-top: var(--space-1); }
.maka-agent-preview-message {
font: var(--maka-text-supporting);
padding: var(--space-2) var(--space-2-5); border-radius: var(--radius-control); background: oklch(from var(--destructive) l c h / 0.07); color: var(--destructive); }
.maka-agent-preview-meta { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); margin: 0; }
.maka-agent-preview-meta > div { display: grid; min-width: 0; gap: var(--space-0-5); }
.maka-agent-preview-meta dd {
font: var(--maka-text-supporting);
min-width: 0; margin: 0; overflow: hidden; color: var(--foreground-secondary); text-overflow: ellipsis; white-space: nowrap; }
.maka-agent-preview-section { display: grid; gap: var(--space-1-5); }
.maka-agent-preview-section > strong,
.maka-agent-preview-section-head > strong {
font: var(--maka-text-heading-5);
min-width: 0; color: var(--foreground); }
.maka-agent-preview-section ul { display: grid; gap: var(--space-1-5); margin: 0; padding: 0; list-style: none; }
.maka-agent-preview-section li { display: grid; min-width: 0; gap: var(--space-0-5); padding-block: var(--space-1-5); border-top: 1px solid var(--foreground-5); }
.maka-agent-preview-section li:first-child { padding-top: 0; border-top: 0; }
.maka-agent-preview-section code {
font: var(--maka-text-supporting);
min-width: 0; overflow: hidden; background: transparent; color: var(--foreground); text-overflow: ellipsis; white-space: nowrap; }
.maka-agent-preview-section p,
.maka-agent-preview-section span {
font: var(--maka-text-supporting);
margin: 0; color: var(--foreground-secondary); white-space: pre-wrap; word-break: break-word; }
.maka-agent-preview-copy[data-copied="true"] { border-color: oklch(from var(--link) l c h / 0.35); color: var(--link); }
.maka-web-search-preview { display: grid; gap: var(--space-2); padding: var(--space-2-5) var(--space-3); border: 1px solid var(--foreground-10); border-radius: var(--radius-surface); background: var(--foreground-3); }
.maka-web-search-preview > header { display: flex; flex-direction: column; gap: var(--space-0-5); padding-bottom: var(--space-1-5); border-bottom: 1px solid var(--foreground-10); }
.maka-web-search-preview > header strong {
font: var(--maka-text-heading-4);
color: var(--foreground); }
.maka-web-search-preview > header small,
.maka-web-search-preview li small {
font: var(--maka-text-supporting);
color: var(--muted-foreground); letter-spacing: var(--tracking-wider); text-transform: uppercase; }
.maka-web-search-preview ul { display: grid; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.maka-web-search-preview li { display: grid; gap: var(--space-0-5); padding-block: var(--space-2); border-top: 1px solid var(--foreground-5); }
.maka-web-search-preview li:first-child { padding-top: 0; border-top: 0; }
.maka-web-search-preview a {
font: var(--maka-text-heading-4);
color: var(--foreground); text-decoration: none; }
.maka-web-search-preview a:hover,
.maka-web-search-preview a:focus-visible { text-decoration: underline; }
.maka-web-search-preview li p {
font: var(--maka-text-supporting);
margin: 0; color: var(--foreground-secondary); white-space: pre-wrap; word-break: break-word; }
.maka-web-search-error { border-color: color-mix(in oklab, var(--destructive-text) 32%, var(--foreground-10)); background: color-mix(in oklab, var(--destructive-text) 8%, var(--foreground-3)); }
.maka-web-search-error-message,
.maka-web-search-error-repair {
font: var(--maka-text-supporting);
margin: 0; white-space: pre-wrap; word-break: break-word; }
.maka-web-search-error-message { color: var(--destructive-text); }
.maka-web-search-error-repair { color: var(--foreground-secondary); }
.maka-load-tool-preview {
font: var(--maka-text-supporting);
display: grid; gap: var(--space-0-5); margin: var(--space-1) 0 0; padding: var(--space-1) var(--space-2); border-radius: var(--radius-control); background: var(--background); box-shadow: var(--ring-soft); }
.maka-load-tool-preview p { margin: 0; }
.maka-load-tool-title {
font: var(--maka-text-heading-5);
}
.maka-load-tool-count,
.maka-load-tool-footer { color: var(--muted-foreground); }
.maka-load-tool-tools {
font: var(--maka-text-supporting);
word-break: break-word; }
.maka-load-tool-footer {
font: var(--maka-text-supporting);
}
/* Turn-footer action rings go inset (T5 geometry probe): the Astryx chat
layout's scrollport clips an outward ring on the first visible row's
actions. Same idiom as the titlebar strip and workbar launcher. The
layout's own scroll-to-bottom control has the same clip inside vendor
DOM — upstream item, not fixable from product CSS. */
.maka-turn-footer-action:focus-visible {
outline: none;
box-shadow: inset 0 0 0 var(--focus-ring-width) var(--focus-ring);
}