/* Block containers — layout + entrance animations */

.stream-block {
    animation: streamBlockIn 120ms ease-out;
    /* Explicit sizing: parents up the chain are flex columns, where a
       block-level child without min-width:0 collapses to its smallest
       intrinsic width — one character per line. The old appendTextMessage
       path didn't have this wrapper so never hit it. */
    width: 100%;
    min-width: 0;
}

/* Defensive min-width:0 up the chain. Flex items default to
   min-width: auto which prevents them from shrinking below their content's
   intrinsic width; in fast-streaming partial states the intrinsic width
   can flip and cause a cascade where .text-message shrinks to ~1ch and
   each character wraps to its own line. Setting min-width:0 lets flex
   ancestors shrink without starving their block children. */
.ai-message .text-message-container,
.ai-message .text-message,
.ai-message .message-content {
    min-width: 0;
}

.block-code        { animation: streamBlockIn  80ms ease-out; }
.block-math        { animation: streamBlockIn 100ms ease-out; }
.block-mermaid     { animation: streamBlockIn 100ms ease-out; }
.block-list-item   {
    animation: streamBlockIn 60ms ease-out;
    animation-delay: calc(var(--i, 0) * 30ms);
    animation-fill-mode: both;
}

@keyframes streamBlockIn {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .stream-block,
    .block-code,
    .block-math,
    .block-mermaid,
    .block-list-item,
    .stream-cursor {
        animation: none;
    }
}

/* Skeleton shimmer while a code block is streaming */

/* While streaming there's nothing to copy yet — keep it in flow but
   invisible, offset to the right. When the block finalizes, is-streaming
   is removed and the button slides in (transition lives on .copy-button). */
.block-code.is-streaming .copy-button {
    opacity: 0;
    transform: translateX(8px);
    pointer-events: none;
}

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

/* Math / mermaid pending placeholder */

.block-math.is-pending,
.block-mermaid.is-pending {
    opacity: 0.6;
    min-height: 1.4em;
}

.pulse-dots {
    display: inline-block;
    animation: streamPulseDots 1.2s ease-in-out infinite;
}

@keyframes streamPulseDots {
    0%, 100% { opacity: 0.3; }
    50%      { opacity: 1; }
}

/* Cursor — ChatGPT-style pulsing dot. */

.stream-cursor {
    display: inline-block;
    width: 0.65em;
    height: 0.65em;
    border-radius: 50%;
    background: var(--text-primary, #e2e8f0);
    margin-left: 0.3em;
    vertical-align: middle;
    animation: streamCursorPulse 1.1s ease-in-out infinite;
}

@keyframes streamCursorPulse {
    0%, 100% { opacity: 1;   transform: scale(1); }
    50%      { opacity: 0.35; transform: scale(0.7); }
}

/* Pre-warm layout variants */

[data-stream-layout="prose"]      { max-width: 65ch; }
[data-stream-layout="structured"] { max-width: 75ch; }
[data-stream-layout="code-heavy"] { max-width: 85ch; }
[data-stream-layout="enumerated"] { max-width: 70ch; }

/* Replay mode — suppress entrance animations across all stream blocks */
.stream-no-anim {
    animation: none !important;
}

/* ---------------------------------------------------------------------------
   Block-wrapper spacing.
   Each markdown block (<p>, <h2>, <ul>, etc.) is now wrapped in a .stream-block
   div. Existing .text-message rules like `p:last-child { margin-bottom: 0 }`
   and adjacent-sibling rules like `h2 + p` no longer work because every inner
   element is :last-child of its wrapper and siblings are separated by divs.
   Move all spacing to the wrapper level; zero out internal margins to avoid
   double spacing.
   --------------------------------------------------------------------------- */

.text-message .stream-block + .stream-block {
    margin-top: 0.75em;
}

/* Post-stream injections (crypto chart wrapper, email composer card, image
   caption, chart-intro-text, etc.) append to .message-content AFTER the
   stream blocks. They aren't wrapped in .stream-block, so the adjacent-
   sibling rule above doesn't match them and they land flush against the
   last paragraph. Restore rhythm for any non-stream-block that directly
   follows a stream-block. Action buttons are excluded — they have their
   own spacing. */
.text-message .stream-block + *:not(.stream-block):not(.action-buttons) {
    margin-top: 0.75em;
}

.text-message .stream-block.block-heading + .stream-block {
    margin-top: 1em;
}

.text-message .stream-block.block-paragraph + .stream-block.block-heading {
    margin-top: 1.25em;
}

.text-message .stream-block > p,
.text-message .stream-block > h1,
.text-message .stream-block > h2,
.text-message .stream-block > h3,
.text-message .stream-block > h4,
.text-message .stream-block > h5,
.text-message .stream-block > h6,
.text-message .stream-block > ul,
.text-message .stream-block > ol,
.text-message .stream-block > blockquote {
    margin-top: 0;
    margin-bottom: 0;
}

/* Lists (when the stream-block itself IS the <ul>/<ol>) — keep bullet indent. */
.text-message ul.stream-block,
.text-message ol.stream-block {
    margin: 0;
    padding-left: 1.5em;
}

/* ---------------------------------------------------------------------------
   Legacy-CSS compatibility shims.
   Fixes 7 critical + 4 important findings from the post-merge CSS audit.
   These rules win over legacy .ai-message / mobile breakpoint rules that
   re-impose margins on elements-that-ARE-the-stream-block (pre, ul, ol).
   Specificity trumps via the full chain .message.ai-message … and mobile
   rules use !important because some breakpoint rules themselves use
   specific compound selectors.
   --------------------------------------------------------------------------- */

/* Critical #1, #2, #5, #6: pre/ul/ol at the stream-block level.
   Zero their OWN margins — all spacing is owned by .stream-block + .stream-block
   above. Higher-specificity variants cover .ai-message and .message.ai-message
   chains that would otherwise re-impose margins (message-styles.css:637-641,
   message-styles.css:1131-1142, message-styles.css:715-720). */
.text-message pre.stream-block,
.text-message ul.stream-block,
.text-message ol.stream-block,
.ai-message .text-message pre.stream-block,
.ai-message .text-message ul.stream-block,
.ai-message .text-message ol.stream-block,
.message.ai-message .text-message pre.stream-block,
.message.ai-message .text-message ul.stream-block,
.message.ai-message .text-message ol.stream-block {
    margin-top: 0;
    margin-bottom: 0;
}

/* Critical #3, #4, #6, #7: mobile breakpoint rules (mobile.css:2822, 2836,
   3484, 3513, 3707, 3739) re-impose margins on pre/ul/ol/p at multiple
   mobile widths. Match their specificity and neutralize. !important is the
   pragmatic choice: some mobile rules use compound chains specific enough
   to tie with us and win on source-order in older Safari. */
@media (max-width: 768px) {
    .message .text-message pre.stream-block,
    .message .text-message ul.stream-block,
    .message .text-message ol.stream-block,
    .message.ai-message .text-message pre.stream-block,
    .message.ai-message .text-message ul.stream-block,
    .message.ai-message .text-message ol.stream-block {
        margin-top: 0 !important;
        margin-bottom: 0 !important;
    }
    /* Paragraphs inside stream-block wrappers — mobile.css:3484 kills
       margin-bottom via p:last-child (every streamed <p> is last-child of
       its wrapper). Our existing `stream-block > p { margin: 0 }` handles
       this at tie-specificity with source-order, but mobile may reorder; be
       explicit. */
    .message.ai-message .text-message .stream-block > p {
        margin-top: 0 !important;
        margin-bottom: 0 !important;
    }
}

/* Important #1: .text-message p:last-child { margin-bottom: 0 } in
   message-styles.css:653 fires on every streamed <p> (each is the only
   child of its wrapper). Our stream-block > p rule at line 126 zeros both
   margins, so the legacy rule is already neutralized on desktop — this is
   documentation only, no extra rule needed. Same for Important #2 (adjacent
   siblings h2+p etc.) — those selectors simply don't match the new DOM,
   their effect is dropped silently; spacing is now governed by the
   .stream-block + .stream-block rule. */

/* Important #3: list own-margin load-order fragility. Already covered by
   the pre/ul/ol zero-out above for AI content; user-message lists still go
   through appendTextMessage and keep their legacy margins. */

/* Minor cosmetic: .text-message has a contentFadeIn animation that stacks
   with per-block streamBlockIn. On replay mode we suppress per-block; the
   container fade is unchanged. Not overridden. */
