/* ═══════════════════════════════════════════════════════════════════════
   DELPHOS · AGENT-ACTIVITY.CSS
   Loaded LAST. Owns the "agent activity" layer of the conversation:

     1.  Tokens + the silver dotted-orbit indicator (replaces spinning arrows)
     2.  Activity group  — steps while working; ONE summary line when done
     3.  Rows            — flat stack, newest 3 while live, fold when done
     4.  Motion          — a quiet fade (no rail, no node dots, no jitter)
     5.  Thinking row    — a first-class step alongside tools
     6.  Tool row        — dotted ring while running, quiet tint when settled
     7.  Shimmer loaders (label sweep, skeleton lineup)
     8.  Sub-agent task rail (parallel work)
     9.  Reply action bar — only after the turn finishes or errors
     10. Reduced motion / light mode / mobile

   Everything here is additive: markup contracts from chat-engine.js are
   preserved, this sheet only re-skins and re-orders them. `!important` is
   used where ui-polish.css / chat-stream.css already shout.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
    /* Silver family — the activity layer is deliberately colourless so the
       accent stays reserved for things the user can act on. */
    --act-silver: #c8cdd7;
    --act-silver-soft: rgba(200, 205, 215, 0.62);
    --act-silver-dim: rgba(196, 202, 213, 0.34);
    --act-text: rgba(163, 169, 181, 0.92);
    --act-text-dim: rgba(151, 157, 169, 0.62);
    --act-line: rgba(255, 255, 255, 0.075);
    --act-surface: rgba(255, 255, 255, 0.026);
    --act-surface-hi: rgba(255, 255, 255, 0.05);
    --act-ok: #6fbf8b;
    --act-err: #e5827a;
    --act-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
    --act-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ─────────────────────────────────────────────────────────────
   1. THE DOTTED ORBIT INDICATOR
   A ring of silver dots that pulse in sequence while the whole ring
   turns slowly. Used for: thinking rows, the activity group glyph, the
   pre-first-token loader, and running sub-tasks.
   ───────────────────────────────────────────────────────────── */

/* Shared silver text-sweep, used by every "this is actively happening" label:
   the live thinking label, a running tool's label, and a running sub-agent
   task title. Defined once, here, so nothing depending on it can go missing
   again without every reference breaking loudly instead of freezing silently. */
@keyframes actTitleSweep {
    from { background-position: 200% center; }
    to   { background-position: -80% center; }
}

.dph-dots {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    flex-shrink: 0;
    color: var(--act-silver);
}

.dph-dots svg {
    width: 100%;
    height: 100%;
    overflow: visible;
    animation: dphOrbit 7s linear infinite;
    transform-origin: 50% 50%;
}

.dph-dots circle {
    fill: currentColor;
    transform-box: fill-box;
    transform-origin: center;
    animation: dphDot 1.45s ease-in-out infinite;
}

/* Sequenced so the pulse chases around the ring. */
.dph-dots circle:nth-child(1) { animation-delay: 0ms; }
.dph-dots circle:nth-child(2) { animation-delay: 110ms; }
.dph-dots circle:nth-child(3) { animation-delay: 220ms; }
.dph-dots circle:nth-child(4) { animation-delay: 330ms; }
.dph-dots circle:nth-child(5) { animation-delay: 440ms; }
.dph-dots circle:nth-child(6) { animation-delay: 550ms; }
.dph-dots circle:nth-child(7) { animation-delay: 660ms; }
.dph-dots circle:nth-child(8) { animation-delay: 770ms; }
.dph-dots circle:nth-child(9) { animation-delay: 880ms; }
.dph-dots circle:nth-child(10) { animation-delay: 990ms; }

@keyframes dphOrbit {
    to { transform: rotate(360deg); }
}

@keyframes dphDot {
    0%, 100% { opacity: 0.16; transform: scale(0.62); }
    42%      { opacity: 1;    transform: scale(1.32); }
}

/* Settled state: the ring stops, dots hold a quiet even weight. */
.dph-dots.is-idle svg { animation: none; }
.dph-dots.is-idle circle { animation: none; opacity: 0.42; }

/* The small sparkle that replaces the bulb on a finished thought. */
.dph-spark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    flex-shrink: 0;
    color: var(--act-silver-soft);
}

.dph-spark svg { width: 100%; height: 100%; }

/* Pre-first-token loader inherits the same language. */
.delphos-mini-loader .dph-dots { width: 15px; height: 15px; }

/* …but it must not double up. Once an activity group is on screen its own glyph
   is the "working" mark; a second identical orbit sitting under the body just
   reads as a rendering bug. The loader comes back if the group seals and the
   answer still hasn't started. */
.chat-list .message.loading:has(.act-group.is-live) .stream-loader {
    display: none !important;
}

/* ─────────────────────────────────────────────────────────────
   2. ACTIVITY GROUP
   Two views, never both at once:
     • WHILE WORKING  → the step rows stream in (newest few visible).
     • WHEN DONE      → the rows fold away behind ONE summary line
                        ("Searched the web · Read page  v") that expands
                        on click. This is the whole point — no header
                        duplicating the rows, no rail, no scatter of dots.
   ───────────────────────────────────────────────────────────── */
.act-group {
    display: block;
    width: 100%;
    margin: 12px 0 14px;
    padding: 0;
    border: none;
    background: none;
}

/* The summary line. Hidden entirely while the group is live; it only becomes
   the group's face once work has finished. */
.act-head {
    display: none;
    align-items: center;
    gap: 6px;
    /* Fitted to its label, like .tool-header/.thinking-header — NOT full
       width. At 100% width the chevron (margin-left:auto) stranded itself at
       the far edge of the message on any short summary, which is the
       "floating arrow" look. */
    width: auto;
    max-width: 100%;
    min-height: 22px;
    margin: 0;
    padding: 3px 0;
    border: none;
    background: none;
    border-radius: 8px;
    cursor: pointer;
    user-select: none;
    transition: opacity 0.15s var(--act-ease);
}

/* Shown only when the group is done — and, for a lone step, not even then
   (a single step is left to speak for itself, no summary needed). */
.act-group.is-done:not(.act-solo) .act-head { display: flex; }

.act-title {
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1.4;
    letter-spacing: 0.012em;
    color: var(--act-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    transition: color 0.15s var(--act-ease);
}

/* No icon, no chevron — matches the reference exactly: the summary is plain
   muted text with nothing decorating it, even on hover. Its own text colour
   lifting slightly is the only signal that it's clickable. */
.act-head:hover .act-title { color: var(--text-color, #e8eaed); }

/* ─────────────────────────────────────────────────────────────
   3. ROWS — a plain vertical stack. No rail, no node dots: the row's
   own icon (tool glyph / thought spark) is its marker, and everything
   lines up on one left edge with the plain-text summary above it.
   ───────────────────────────────────────────────────────────── */
.act-rows {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

/* Done + folded -> the rows step aside for the summary line. Expanding brings
   them back. While live they are always shown (windowed below). */
.act-group.is-done:not(.is-expanded):not(.act-solo) .act-rows { display: none; }

.act-row {
    position: relative;
    min-width: 0;
    margin: 0 !important;
}

/* Windowed out of view while live (an older step once >3 are on screen), or
   folded away when done. Still in the DOM for copy/restore. */
.act-row.act-off { display: none !important; }

/* The "N earlier steps" / "Show fewer" affordance. Appears only while the group
   is live AND more steps exist than the window shows. */
.act-more {
    display: none;
    align-items: center;
    gap: 6px;
    margin: 0 0 3px;
    padding: 2px;
    border: none;
    background: none;
    color: var(--act-text-dim);
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.01em;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: color 0.15s var(--act-ease);
}

.act-group.act-windowable.is-live .act-more { display: inline-flex; }

.act-more:hover { color: var(--text-color, #e8eaed); }

.act-more-ic {
    width: 13px;
    height: 13px;
    display: inline-flex;
    transition: transform 0.24s var(--act-ease);
}

.act-more-ic svg { width: 100%; height: 100%; }

.act-group.is-expanded .act-more-ic { transform: rotate(180deg); }

/* ── 4. MOTION — a quiet fade. No blur, no scale, no exit slide: those were
   what made the list feel like it was jittering. A newly arrived step fades
   up a couple of pixels; parked steps just leave. `!important` matches
   chat-stream.css's own `!important` blockFadeIn on .tool-block. */
.act-row.act-enter {
    animation: actRowIn 200ms var(--act-ease-out) !important;
}

@keyframes actRowIn {
    from { opacity: 0; transform: translateY(3px); }
    to   { opacity: 1; transform: none; }
}

/* ─────────────────────────────────────────────────────────────
   5. THINKING ROW — a step like any other
   ───────────────────────────────────────────────────────────── */
.act-row.thinking-block {
    margin: 0 !important;
    padding: 0 !important;
    background: transparent !important;
    border: none !important;
}

.act-row.thinking-block .thinking-header {
    gap: 7px !important;
    padding: 3px 0 !important;
    margin-left: 0 !important;
    border-radius: 8px !important;
}

.act-row.thinking-block .thinking-icon {
    width: 15px !important;
    height: 15px !important;
    font-size: 0 !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    animation: none !important;
    color: var(--act-silver) !important;
}

/* ui-polish.css §19 spins `.thinking-icon` itself (it used to hold a rotating
   glyph). The indicator is now an SVG that animates internally, so that outer
   rotation has to die everywhere — including any thinking block that somehow
   renders outside a group. Equal specificity, later source: this wins. */
.thinking-block .thinking-icon,
.thinking-block:not(.collapsed) .thinking-icon {
    animation: none !important;
}

.thinking-block .thinking-icon .dph-dots,
.thinking-block .thinking-icon .dph-spark {
    width: 15px;
    height: 15px;
}

.act-row.thinking-block .thinking-label,
.act-row.thinking-block .thinking-time {
    font-size: 12.5px !important;
    color: var(--act-text) !important;
    -webkit-text-fill-color: var(--act-text) !important;
}

.act-row.thinking-block .thinking-label--active {
    background: linear-gradient(100deg,
            rgba(150, 156, 168, 0.72) 28%,
            rgba(228, 233, 242, 0.98) 48%,
            rgba(150, 156, 168, 0.72) 68%) !important;
    background-size: 240% 100% !important;
    -webkit-background-clip: text !important;
    background-clip: text !important;
    -webkit-text-fill-color: transparent !important;
    animation: actTitleSweep 2.4s linear infinite !important;
}

.act-row.thinking-block .thinking-time {
    font-size: 11.5px !important;
    color: var(--act-text-dim) !important;
    -webkit-text-fill-color: var(--act-text-dim) !important;
}

.act-row.thinking-block .thinking-chevron {
    font-size: 15px !important;
    opacity: 0.55;
    transition: transform 260ms var(--act-ease), opacity 160ms var(--act-ease);
}

.act-row.thinking-block .thinking-header:hover .thinking-chevron { opacity: 0.9; }

/* Reasoning body: quiet card, blur-in when opened.

   Scoped to the OPEN states on purpose. ui-polish.css collapses this element
   with `!important` padding/margin/border-colour resets at the same specificity
   as `.act-row.thinking-block .thinking-content`, and this file loads later —
   so an unscoped skin here would win those resets and leave an empty bordered
   box under every collapsed thought. */
.act-row.thinking-block:not(.collapsed):not(.is-live) .thinking-content,
.act-row.thinking-block.is-live.expanded .thinking-content {
    margin-top: 6px !important;
    padding: 10px 12px !important;
    font-size: 12.5px !important;
    background: var(--act-surface) !important;
    border: 1px solid var(--act-line) !important;
    border-radius: 12px !important;
    color: rgba(170, 176, 189, 0.85) !important;
    animation: actRowIn 340ms var(--act-ease-out);
}

/* ─────────────────────────────────────────────────────────────
   6. TOOL ROW
   ───────────────────────────────────────────────────────────── */
/* At rest the row must not carry chat-stream.css's blockFadeIn — the row-level
   fade owns this element's motion. Guarded with :not() so the act-enter fade
   still lands the one time it runs. */
.act-row.tool-block:not(.act-enter) {
    margin: 0 !important;
    animation: none !important;
}

.act-row.tool-block { margin: 0 !important; }

.act-row.tool-block .tool-header {
    gap: 7px !important;
    padding: 3px 0 !important;
    margin-left: 0 !important;
    border-radius: 8px !important;
    transition: color 160ms var(--act-ease), opacity 160ms var(--act-ease);
}

.act-row.tool-block .tool-label {
    font-size: 12.5px !important;
    color: var(--act-text) !important;
}

.act-row.tool-block .tool-icon {
    /* Explicitly positioned so the loading ring's absolute ::before anchors to
       the icon box itself, not to some ancestor — that mis-anchoring is what
       threw the ring off-centre from the glyph. */
    position: relative !important;
    width: 18px !important;
    height: 18px !important;
    font-size: 15px !important;
    box-sizing: border-box !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    opacity: 0.8;
}

/* Running: the glyph stays (a tool you can still recognise) and ONE clean, thin
   ring spins concentrically around it. The previous version layered a loose
   dotted ring AND a soft box-shadow "glow" halo, both offset by inset:-4px on a
   16px box — taller than the 22px row, so the halo bled and the ring read as
   mis-centred. One ring, box-sizing:border-box, a tight inset, no halo. */
.act-row.tool-block.loading .tool-icon,
.tool-block.loading .tool-icon {
    color: var(--act-silver) !important;
    -webkit-text-fill-color: var(--act-silver) !important;
    opacity: 0.9 !important;
}

.act-row.tool-block.loading .tool-icon::before,
.tool-block.loading .tool-icon::before {
    content: "" !important;
    position: absolute !important;
    inset: -3px !important;
    box-sizing: border-box !important;
    border-radius: 50% !important;
    border: 1.5px solid var(--act-silver-dim) !important;
    border-top-color: var(--act-silver) !important;
    animation: toolRingSpin 0.75s linear infinite !important;
}

/* Kill the old second pseudo-element (the glow halo) wherever this stylesheet's
   earlier revision or chat-stream.css might still paint one. */
.act-row.tool-block.loading .tool-icon::after {
    content: none !important;
    display: none !important;
}

/* Settled tints — quiet, never a coloured banner. */
.act-row.tool-block.tool-ok .tool-icon { color: rgba(180, 187, 199, 0.9) !important; }
.act-row.tool-block.tool-err .tool-icon { color: var(--act-err) !important; opacity: 0.9; }
.act-row.tool-block.tool-err .tool-label { color: rgba(224, 150, 143, 0.92) !important; }

/* Result panel inside a row keeps the rail alignment. No fill-mode: a lingering
   `filter: blur(0)` would keep this panel on its own compositing layer, which
   softens text and screenshots for no reason once the reveal is over. */
.act-row.tool-block .tool-results {
    margin: 6px 0 4px !important;
    border-color: var(--act-line) !important;
    border-radius: 12px !important;
    animation: actRowIn 320ms var(--act-ease-out);
}

/* ─────────────────────────────────────────────────────────────
   7. SHIMMER LOADERS
   ───────────────────────────────────────────────────────────── */
.act-row .tool-skeleton,
.act-row .tool-steps {
    margin: 7px 0 4px !important;
    padding: 9px 11px !important;
    border: 1px solid var(--act-line) !important;
    border-radius: 12px !important;
    background: var(--act-surface);
    animation: actRowIn 340ms var(--act-ease-out);
}

.act-row .tool-skeleton .sk-dot,
.act-row .tool-skeleton .sk-line {
    background: linear-gradient(100deg,
            rgba(200, 205, 215, 0.07) 26%,
            rgba(214, 219, 228, 0.2) 46%,
            rgba(200, 205, 215, 0.07) 66%) !important;
    background-size: 260% 100% !important;
    animation: actSkeletonSweep 1.5s linear infinite !important;
    border-radius: 999px !important;
}

.act-row .tool-skeleton .sk-dot {
    border-radius: 50% !important;
}

@keyframes actSkeletonSweep {
    from { background-position: 200% 0; }
    to   { background-position: -60% 0; }
}

/* Label sweep re-tuned to the silver family.
   THE BUG: this rule was missing background-clip:text + transparent fill, so
   the gradient painted as a solid block behind the text instead of being
   clipped TO the text — the "blank grey bar" over a running web search. Every
   other shimmering label in this file (.thinking-label--active,
   .subagent-running .subagent-title) already carries these three lines; this
   one has to match them exactly. */
.act-row .tool-label.shimmer-text {
    background: linear-gradient(100deg,
            rgba(150, 156, 168, 0.75) 28%,
            rgba(228, 233, 242, 0.98) 48%,
            rgba(150, 156, 168, 0.75) 68%) !important;
    background-size: 240% 100% !important;
    -webkit-background-clip: text !important;
    background-clip: text !important;
    -webkit-text-fill-color: transparent !important;
    color: transparent !important;
    animation: actTitleSweep 2.4s linear infinite !important;
}

/* Stepper rungs: silver, calm. */
.act-row .tstep.active { color: rgba(206, 211, 220, 0.95); opacity: 1; }
.act-row .tstep.done { opacity: 0.5; }

/* ─────────────────────────────────────────────────────────────
   8. SUB-AGENT TASK RAIL (parallel work)
   Styled here — not only in dev-mode.css — so delegated tasks look the
   same on every surface.
   ───────────────────────────────────────────────────────────── */
.subagent-head {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 2px 4px 8px;
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--act-text-dim);
}

.subagent-head-ic {
    width: 13px;
    height: 13px;
    display: inline-flex;
    color: var(--act-silver-soft);
}

.subagent-head-ic svg { width: 100%; height: 100%; }

.subagent-rows {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 2px;
}

.subagent-row {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    padding: 8px 10px;
    border: 1px solid var(--act-line);
    border-radius: 11px;
    background: var(--act-surface);
    animation: actRowIn 360ms var(--act-ease-out);
    transition: border-color 200ms var(--act-ease), background 200ms var(--act-ease);
}

.subagent-row:hover { background: var(--act-surface-hi); }

.subagent-ic {
    width: 14px;
    height: 14px;
    margin-top: 1px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--act-silver-soft);
}

.subagent-ic svg { width: 100%; height: 100%; }

/* dev-mode.css spins this element (it used to hold a Material glyph). The dots
   indicator animates from the inside now, so the outer spin has to stop or the
   two rotations fight each other. */
.subagent-row .subagent-ic,
.subagent-running .subagent-ic {
    animation: none !important;
}

.subagent-copy {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.subagent-title {
    font-size: 12.5px;
    font-weight: 500;
    color: var(--act-text);
    line-height: 1.4;
}

.subagent-running .subagent-title {
    background: linear-gradient(100deg,
            rgba(150, 156, 168, 0.75) 28%,
            rgba(228, 233, 242, 0.98) 48%,
            rgba(150, 156, 168, 0.75) 68%);
    background-size: 240% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: actTitleSweep 2.4s linear infinite;
}

.subagent-body {
    font-size: 12px;
    line-height: 1.6;
    color: rgba(163, 169, 181, 0.78);
    white-space: pre-wrap;
    word-break: break-word;
}

.subagent-body-err { color: rgba(224, 150, 143, 0.92); }

.subagent-done { border-color: rgba(255, 255, 255, 0.1); }
.subagent-error { border-color: rgba(229, 130, 122, 0.28); }
.subagent-done .subagent-ic { color: var(--act-ok); }
.subagent-error .subagent-ic { color: var(--act-err); }

/* ─────────────────────────────────────────────────────────────
   9. REPLY ACTION BAR
   Empty (and invisible) until the turn actually settles. On an errored
   turn that produced no prose there is nothing to copy or rate, so the
   bar carries a single Retry affordance instead.
   ───────────────────────────────────────────────────────────── */
.chat-list .message.incoming .message-actions.is-pending {
    display: none !important;
    pointer-events: none !important;
}

/* An errored turn still gets its bar (retry / copy-partial) — this undoes the
   blanket hide in ui-polish.css §21 for bars we deliberately mounted. */
.chat-list .message.incoming.stream-errored .message-actions.is-ready,
.chat-list .message.incoming:has(.ai-error-card) .message-actions.is-ready {
    display: flex !important;
    pointer-events: auto !important;
}

/* Only the reply that just settled animates in. No fill-mode: once the 300ms
   are over the normal cascade takes back control, so an older reply still fades
   out to "faint until hover" instead of being pinned visible by the animation. */
.chat-list .message.incoming.is-last-ai .message-actions.is-ready {
    animation: actBarIn 300ms var(--act-ease-out);
}

@keyframes actBarIn {
    from { opacity: 0; transform: translateY(3px); filter: blur(4px); }
    to   { opacity: 1; transform: none; filter: blur(0); }
}

/* Hover motion, Claude-quiet: background fades in, glyph lifts a hair. */
.ai-act-btn {
    position: relative;
    overflow: hidden;
}

.ai-act-btn .material-symbols-rounded {
    transition: transform 200ms var(--act-ease), opacity 160ms var(--act-ease);
}

.ai-act-btn:hover .material-symbols-rounded { transform: translateY(-1px); }
.ai-act-btn:active .material-symbols-rounded { transform: translateY(0) scale(0.92); }

.ai-act-btn.copy-btn.is-copied { color: var(--act-ok) !important; }

/* Retry: on a turn that produced nothing, it is the only thing to do — so it
   reads as a labelled chip rather than a bare glyph. */
.ai-act-btn.retry-btn {
    width: auto;
    gap: 6px;
    padding: 0 11px 0 9px;
    height: 30px;
    border: 1px solid var(--act-line);
    border-radius: 999px;
    font-size: 12.5px;
    font-weight: 500;
    letter-spacing: 0.01em;
    color: var(--act-text);
}

.ai-act-btn.retry-btn:hover {
    border-color: rgba(255, 255, 255, 0.16);
    color: var(--text-color, #e8eaed);
}

.ai-act-btn.retry-btn .material-symbols-rounded { font-size: 16px; }

.ai-act-btn.retry-btn .retry-lbl {
    line-height: 1;
    white-space: nowrap;
}

.ai-act-btn.retry-btn:hover .material-symbols-rounded {
    transform: rotate(-110deg);
}

.ai-act-btn.retry-btn.spinning .material-symbols-rounded {
    animation: ai-regen-spin 0.7s cubic-bezier(0.4, 0, 0.2, 1);
}

.message-actions.retry-only {
    gap: 6px !important;
    margin-left: 0 !important;
}

/* ─────────────────────────────────────────────────────────────
   10. LIGHT MODE / REDUCED MOTION / MOBILE
   ───────────────────────────────────────────────────────────── */
body.light_mode {
    --act-silver: #6d7480;
    --act-silver-soft: rgba(96, 103, 114, 0.66);
    --act-silver-dim: rgba(96, 103, 114, 0.3);
    --act-text: rgba(75, 82, 94, 0.92);
    --act-text-dim: rgba(96, 103, 114, 0.7);
    --act-line: rgba(0, 0, 0, 0.09);
    --act-surface: rgba(0, 0, 0, 0.025);
    --act-surface-hi: rgba(0, 0, 0, 0.045);
}

body.light_mode .act-row .tool-label.shimmer-text,
body.light_mode .act-row.thinking-block .thinking-label--active,
body.light_mode .subagent-running .subagent-title {
    background: linear-gradient(100deg,
            rgba(96, 103, 114, 0.72) 28%,
            rgba(24, 28, 34, 0.95) 48%,
            rgba(96, 103, 114, 0.72) 68%) !important;
    background-size: 240% 100% !important;
    -webkit-background-clip: text !important;
    background-clip: text !important;
    -webkit-text-fill-color: transparent !important;
}

@media (max-width: 640px) {
    .act-title { font-size: 12px; }
}

@media (prefers-reduced-motion: reduce) {

    .dph-dots svg,
    .dph-dots circle,
    .act-row.tool-block.loading .tool-icon::before,
    .act-row.tool-block.loading .tool-icon::after {
        animation: none !important;
    }

    .act-row.act-enter,
    .act-row .tool-results,
    .act-row .tool-skeleton,
    .subagent-row,
    .chat-list .message.incoming .message-actions.is-ready {
        animation: none !important;
        filter: none !important;
    }

    .act-row .tool-label.shimmer-text,
    .act-row.thinking-block .thinking-label--active,
    .subagent-running .subagent-title {
        animation: none !important;
        -webkit-text-fill-color: var(--act-text) !important;
        background: none !important;
    }
}

body.pref-reduce-motion .dph-dots svg,
body.pref-reduce-motion .dph-dots circle {
    animation: none !important;
}

/* ═══════════════════════════════════════════════════════════════════════
   11. THE LIVE HEADLINE  (.act-live)
   ───────────────────────────────────────────────────────────────────────
   One word above the steps, marked by the four-point spark, saying the
   agent is mid-thought. It is the group's face WHILE LIVE, exactly as
   .act-head is its face once done — the two are never both on screen.

   It replaces three overlapping indicators that used to run at once: the
   typewriter phrase carousel on the thinking row, the `.stream-status`
   line above the reply, and the shimmer on a running tool label. Only the
   last of those survives, because it belongs to a specific step.
   ═══════════════════════════════════════════════════════════════════════ */
:root {
    /* The spark is the one warm mark in an otherwise colourless layer, and
       that is the point: it is the single "alive" signal, so it is allowed
       the only bit of temperature on the rail. */
    --act-spark: #d97757;
    --act-add: #7fc99a;
    --act-del: #e08b83;
    --act-chip-bg: rgba(255, 255, 255, 0.05);
    --act-chip-line: rgba(255, 255, 255, 0.09);
    --act-chip-text: rgba(186, 192, 204, 0.9);
    --act-warn: #d9b26a;
}

.act-live {
    display: none;
    align-items: center;
    gap: 8px;
    width: auto;
    max-width: 100%;
    min-height: 22px;
    margin: 0 0 4px;
    padding: 3px 0;
}

/* Live only. A done group hands the space to .act-head. */
.act-group.is-live .act-live { display: flex; }

.act-live-ic {
    width: 15px;
    height: 15px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.act-live-ic .dph-spark {
    width: 15px;
    height: 15px;
    color: var(--act-spark);
    /* A slow, shallow breath. Not a spin: the headline is the calm marker and
       the per-step orbit rings are the busy ones — two competing rotations on
       one rail is what made the old stack feel frantic. */
    animation: actSparkBreathe 2.6s ease-in-out infinite;
}

@keyframes actSparkBreathe {
    0%, 100% { opacity: 0.62; transform: scale(0.92) rotate(0deg); }
    50%      { opacity: 1;    transform: scale(1.04) rotate(45deg); }
}

.act-live-word {
    font-size: 13px;
    font-weight: 500;
    line-height: 1.4;
    letter-spacing: 0.012em;
    color: var(--act-text);
    white-space: nowrap;
    /* Crossfade between words. The old carousel deleted and retyped a text node
       every 38ms for the whole reasoning pass, which is a lot of layout work to
       say nothing new. */
    transition: opacity 180ms var(--act-ease);
}

.act-live-word.act-word-out { opacity: 0; }

/* ═══════════════════════════════════════════════════════════════════════
   12. ROW DETAIL SLOTS — the file chip and the +N/−M stat
   ───────────────────────────────────────────────────────────────────────
   Both are evidence, not decoration: the chip names the file an action
   actually landed on, the stat is a real line diff of before/after. They
   are rendered only when measured, so their ABSENCE means "unknown" and
   never "nothing happened".
   ═══════════════════════════════════════════════════════════════════════ */
.act-row.tool-block .tool-header .tool-file,
.act-row.tool-block .tool-header .tool-dstat {
    flex-shrink: 0;
}

.tool-file {
    display: inline-flex;
    align-items: center;
    max-width: 42%;
    padding: 1px 7px;
    border: 1px solid var(--act-chip-line);
    border-radius: 6px;
    background: var(--act-chip-bg);
    font-family: var(--font-mono, ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace);
    font-size: 11px;
    line-height: 1.5;
    letter-spacing: 0;
    color: var(--act-chip-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tool-dstat {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-family: var(--font-mono, ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace);
    font-size: 11px;
    font-style: normal;
    line-height: 1.5;
    letter-spacing: 0;
}

.tool-dstat i {
    font-style: normal;
    font-variant-numeric: tabular-nums;
}

.tool-dstat .add { color: var(--act-add); }
.tool-dstat .del { color: var(--act-del); }

/* `.hide` is the app-wide utility; restated here because agent-activity.css
   loads last and re-skins these very selectors, so an empty chip must stay
   gone regardless of what else matched. */
.tool-file.hide,
.tool-dstat.hide { display: none !important; }

/* A failed step keeps its evidence but drains its colour: green "+10" next to
   "Edit did not apply" would contradict the row it sits on. */
.act-row.tool-block.tool-err .tool-dstat .add,
.act-row.tool-block.tool-err .tool-dstat .del {
    color: var(--act-text-dim);
}

/* Partial apply — some hunks matched, some did not. Deliberately its own state
   between ok and err, because it is neither: the file DID change, just not the
   way the model intended, and flattening that into "done" is how a
   half-applied patch gets silently built on by the next turn. */
.act-row.tool-block.tool-warn .tool-icon { color: var(--act-warn) !important; opacity: 0.95; }
.act-row.tool-block.tool-warn .tool-label { color: rgba(214, 183, 124, 0.95) !important; }

/* ═══════════════════════════════════════════════════════════════════════
   13. A STREAMING PANEL PARKED INSIDE A ROW
   ───────────────────────────────────────────────────────────────────────
   Artifact builds and patches used to drop a full-width code box straight
   into the transcript, outside the activity group — the one action that
   escaped the rail, with no step, no summary line and no place in the
   recap of what the turn did. The box now hangs in the row's collapsed
   results panel: the row states what changed and by how much, and the
   diff is one click away for anyone who wants to read it.
   ═══════════════════════════════════════════════════════════════════════ */
.act-row.tool-block .tool-results > .sandbox-codebox {
    margin: 0 !important;
    width: 100% !important;
}

/* The results panel is only a container here — its own frame would double the
   code box's border and inset it for no reason. */
.act-row.tool-block .tool-results:has(> .sandbox-codebox) {
    padding: 0 !important;
    border: none !important;
    background: none !important;
}

/* The row is the outer disclosure, so a parked box opens with it — the collapsed
   class is dropped when the stream finalises (see the artifact_edit_end handler)
   rather than being overridden here, which keeps the box's own header toggle
   working instead of leaving it looking inert. */

/* ── light mode ─────────────────────────────────────────────────────────── */
body.light_mode {
    --act-spark: #c25f3e;
    --act-add: #2f8a52;
    --act-del: #c0453c;
    --act-chip-bg: rgba(0, 0, 0, 0.04);
    --act-chip-line: rgba(0, 0, 0, 0.1);
    --act-chip-text: rgba(70, 77, 88, 0.92);
    --act-warn: #9a7420;
}

/* ── reduced motion ─────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {

    .act-live-ic .dph-spark { animation: none !important; }
    .act-live-word { transition: none !important; }
}

body.pref-reduce-motion .act-live-ic .dph-spark { animation: none !important; }
