/* Chat Bubble — Markdown Prose */ .chat-md { color: hsl(var(--foreground)); font-size: 0.875rem; line-height: 1.7; } .chat-md > *:first-child { margin-top: 0; } .chat-md > *:last-child { margin-bottom: 0; } .chat-md h1 { font-size: 1.4em; font-weight: 700; margin: 1.2em 0 0.4em; padding-bottom: 0.25em; border-bottom: 1px solid hsl(var(--border)); letter-spacing: -0.02em; } .chat-md h2 { font-size: 1.2em; font-weight: 600; margin: 1.1em 0 0.35em; padding-bottom: 0.2em; border-bottom: 1px solid hsl(var(--border)); } .chat-md h3 { font-size: 1.1em; font-weight: 600; margin: 1em 0 0.3em; } .chat-md h4, .chat-md h5, .chat-md h6 { font-size: 1em; font-weight: 600; margin: 0.8em 0 0.25em; } .chat-md p { margin: 0.6em 0; } .chat-md a { color: var(--duck-teal); text-decoration: underline; text-underline-offset: 2px; } .chat-md a:hover { opacity: 0.8; } .chat-md strong { font-weight: 600; } .chat-md blockquote { margin: 0.75em 0; padding: 0.4em 0.8em; border-left: 3px solid var(--duck-teal); background: color-mix(in srgb, var(--duck-teal) 5%, transparent); border-radius: 0 0.375rem 0.375rem 0; color: hsl(var(--muted-foreground)); } /* Tailwind's preflight resets `list-style: none` on every ul/ol, so a list rendered here had the indent but no bullets and — more confusingly — no NUMBERS: an ordered list read as an unordered pile of lines, and copying the same text elsewhere showed it numbered correctly, which makes it look like the model emitted bad markdown rather than the stylesheet eating the markers. The `li::marker` rule below was colouring a marker that was never drawn. */ .chat-md ul, .chat-md ol { margin: 0.5em 0; padding-left: 1.5em; list-style-position: outside; } .chat-md ul { list-style-type: disc; } .chat-md ol { list-style-type: decimal; } /* Nested levels, following the usual typographic convention so depth stays readable. */ .chat-md ul ul { list-style-type: circle; } .chat-md ul ul ul { list-style-type: square; } .chat-md ol ol { list-style-type: lower-alpha; } .chat-md ol ol ol { list-style-type: lower-roman; } /* A task list carries its own checkbox; a bullet next to it is noise. */ .chat-md li:has(> input[type='checkbox']) { list-style-type: none; } .chat-md li { margin: 0.2em 0; } .chat-md li::marker { color: var(--duck-teal); } .chat-md code { padding: 0.15em 0.4em; border-radius: 0.25rem; background: color-mix(in srgb, var(--duck-teal) 10%, transparent); color: var(--duck-teal); font-size: 0.85em; font-family: ui-monospace, monospace; } .chat-md pre { margin: 0.75em 0; padding: 0.75em 1em; border-radius: 0.5rem; background: #0d1117; overflow-x: auto; font-size: 0.8125rem; line-height: 1.6; } .chat-md pre code { padding: 0; border-radius: 0; background: none; color: #e6edf3; font-size: inherit; } .chat-md hr { border: none; height: 1px; background: hsl(var(--border)); margin: 1.5em 0; } /* ── The shared code surface (`CodeSurface`) ── Deliberately NOT scoped under `.chat-md`: a tool call renders code too, and it lives outside the prose container. These rules come after the `.chat-md pre` block above so they win the specificity tie when a surface does sit inside a chat reply. */ /* Vertical rhythm belongs to the prose that surrounds a block, not to the block. Inside a tool panel the surface is already boxed and spaced by its container, and a leaked 0.75em reads as a gap nobody chose. */ .chat-md .code-surface { margin: 0.75em 0; } .code-surface pre { margin: 0; padding: 0.75em 1em; /* Room for the copy controls, which sit absolutely at the bottom-right of the block. Horizontal so a long line scrolls past them rather than under them; vertical so the LAST line — the one they now overlap — keeps its own space. */ padding-right: 5.5em; padding-bottom: 2em; border-radius: 0.5rem; background: #0d1117; /* Scroll, never wrap: wrapping folds `ls -la`, `git log --oneline` and any other columnar output into mush, and it breaks the one-line-per-number contract the gutter makes. */ overflow-x: auto; white-space: pre; font-size: 0.8125rem; line-height: 1.6; } /* A block that named a language also shows the name, which needs its own room. */ .code-surface[data-lang] pre { padding-right: 10em; } /* The positioning context for those controls. Without it they escape to the nearest positioned ancestor, which is how a copy button ends up floating below the block it belongs to. */ .code-surface { position: relative; } .code-surface pre code { padding: 0; border-radius: 0; background: none; color: #e6edf3; font-size: inherit; font-family: ui-monospace, monospace; } /* Line numbers come from a counter rather than from markup, because generated content is not part of `textContent` — so the copy buttons, and a hand-made selection, still yield the code and nothing else. Both branches of the block emit `.line` spans, so the numbers survive the swap to highlighted output. `--line-offset` seeds the counter: a `Read` of lines 400-460 has to say 400, not 1. They scroll with a long line rather than sticking to the left edge; a sticky gutter would need an opaque background, and that means hardcoding a colour the theme is entitled to change. */ .code-surface[data-numbered] code { counter-reset: line var(--line-offset, 0); } .code-surface[data-numbered] .line::before { counter-increment: line; content: counter(line); display: inline-block; width: calc(var(--line-digits, 2) * 1ch); margin-right: 1.25em; text-align: right; color: #6e7681; user-select: none; -webkit-user-select: none; } .chat-md table { width: 100%; border-collapse: collapse; margin: 0.75em 0; font-size: 0.85em; } .chat-md th { color: hsl(var(--muted-foreground)); background: color-mix(in srgb, var(--duck-teal) 8%, transparent); font-weight: 600; text-align: left; padding: 0.4em 0.6em; border: 1px solid hsl(var(--border)); } .chat-md td { padding: 0.4em 0.6em; border: 1px solid hsl(var(--border)); } .chat-md tr:nth-child(even) { background: hsl(var(--muted) / 0.3); } .chat-md img { max-width: 100%; border-radius: 0.375rem; margin: 0.75em 0; } .chat-md input[type='checkbox'] { accent-color: var(--duck-teal); margin-right: 0.4em; } /* File Viewer — Markdown Prose */ .file-viewer-md { color: hsl(var(--foreground)); font-size: 0.95rem; line-height: 1.75; } .file-viewer-md h1 { color: hsl(var(--foreground)); font-size: 2em; font-weight: 700; margin: 1.5em 0 0.5em; padding-bottom: 0.3em; border-bottom: 2px solid hsl(var(--border)); letter-spacing: -0.02em; } .file-viewer-md h2 { color: hsl(var(--foreground)); font-size: 1.5em; font-weight: 600; margin: 1.4em 0 0.4em; padding-bottom: 0.25em; border-bottom: 1px solid hsl(var(--border)); } .file-viewer-md h3 { color: hsl(var(--foreground)); font-size: 1.25em; font-weight: 600; margin: 1.2em 0 0.4em; } .file-viewer-md h4, .file-viewer-md h5, .file-viewer-md h6 { color: hsl(var(--foreground)); font-size: 1.05em; font-weight: 600; margin: 1em 0 0.3em; } .file-viewer-md p { margin: 0.75em 0; } .file-viewer-md a { color: var(--duck-teal); text-decoration: underline; text-underline-offset: 2px; } .file-viewer-md a:hover { opacity: 0.8; } .file-viewer-md strong { font-weight: 600; } .file-viewer-md blockquote { margin: 1em 0; padding: 0.5em 1em; border-left: 3px solid var(--duck-teal); background: color-mix(in srgb, var(--duck-teal) 5%, transparent); border-radius: 0 0.5rem 0.5rem 0; color: hsl(var(--muted-foreground)); } .file-viewer-md ul, .file-viewer-md ol { margin: 0.75em 0; padding-left: 1.75em; list-style-position: outside; } .file-viewer-md ul { list-style-type: disc; } .file-viewer-md ol { list-style-type: decimal; } .file-viewer-md ul ul { list-style-type: circle; } .file-viewer-md ol ol { list-style-type: lower-alpha; } .file-viewer-md li:has(> input[type='checkbox']) { list-style-type: none; } .file-viewer-md li { margin: 0.25em 0; } .file-viewer-md li::marker { color: var(--duck-teal); } .file-viewer-md hr { border: none; height: 1px; background: hsl(var(--border)); margin: 2em 0; } .file-viewer-md table { width: 100%; border-collapse: collapse; margin: 1em 0; font-size: 0.9em; } .file-viewer-md th { color: hsl(var(--muted-foreground)); background: color-mix(in srgb, var(--duck-teal) 8%, transparent); font-weight: 600; text-align: left; padding: 0.5em 0.75em; border: 1px solid hsl(var(--border)); } .file-viewer-md td { padding: 0.5em 0.75em; border: 1px solid hsl(var(--border)); } .file-viewer-md tr:nth-child(even) { background: hsl(var(--muted) / 0.3); } .file-viewer-md img { max-width: 100%; border-radius: 0.5rem; margin: 1em 0; } .file-viewer-md input[type='checkbox'] { accent-color: var(--duck-teal); margin-right: 0.5em; } /* Skills — Markdown Prose */ .skill-md { color: hsl(var(--foreground)); font-size: 0.95rem; line-height: 1.75; } .skill-md h1 { color: hsl(var(--foreground)); font-size: 2em; font-weight: 700; margin: 1.5em 0 0.5em; padding-bottom: 0.3em; border-bottom: 2px solid hsl(var(--border)); letter-spacing: -0.02em; } .skill-md h2 { color: hsl(var(--foreground)); font-size: 1.5em; font-weight: 600; margin: 1.4em 0 0.4em; padding-bottom: 0.25em; border-bottom: 1px solid hsl(var(--border)); } .skill-md h3 { color: hsl(var(--foreground)); font-size: 1.25em; font-weight: 600; margin: 1.2em 0 0.4em; } .skill-md h4, .skill-md h5, .skill-md h6 { color: hsl(var(--foreground)); font-size: 1.05em; font-weight: 600; margin: 1em 0 0.3em; } .skill-md p { margin: 0.75em 0; } .skill-md a { color: var(--duck-teal); text-decoration: underline; text-underline-offset: 2px; } .skill-md a:hover { opacity: 0.8; } .skill-md strong { font-weight: 600; } .skill-md blockquote { margin: 1em 0; padding: 0.5em 1em; border-left: 3px solid var(--duck-teal); background: color-mix(in srgb, var(--duck-teal) 5%, transparent); border-radius: 0 0.5rem 0.5rem 0; color: hsl(var(--muted-foreground)); } .skill-md ul, .skill-md ol { margin: 0.75em 0; padding-left: 1.75em; list-style-position: outside; } .skill-md ul { list-style-type: disc; } .skill-md ol { list-style-type: decimal; } .skill-md ul ul { list-style-type: circle; } .skill-md ol ol { list-style-type: lower-alpha; } .skill-md li:has(> input[type='checkbox']) { list-style-type: none; } .skill-md li { margin: 0.25em 0; } .skill-md li::marker { color: var(--duck-teal); } .skill-md code { padding: 0.15em 0.4em; border-radius: 0.25rem; background: color-mix(in srgb, var(--duck-teal) 10%, transparent); color: var(--duck-teal); font-size: 0.85em; font-family: ui-monospace, monospace; } .skill-md pre { margin: 1em 0; padding: 1em; border-radius: 0.5rem; background: #0d1117; overflow-x: auto; font-size: 0.875rem; line-height: 1.6; } .skill-md pre code { padding: 0; border-radius: 0; background: none; color: #e6edf3; font-size: inherit; } .skill-md hr { border: none; height: 1px; background: hsl(var(--border)); margin: 2em 0; } .skill-md table { width: 100%; border-collapse: collapse; margin: 1em 0; font-size: 0.9em; } .skill-md th { color: hsl(var(--muted-foreground)); background: color-mix(in srgb, var(--duck-teal) 8%, transparent); font-weight: 600; text-align: left; padding: 0.5em 0.75em; border: 1px solid hsl(var(--border)); } .skill-md td { padding: 0.5em 0.75em; border: 1px solid hsl(var(--border)); } .skill-md tr:nth-child(even) { background: hsl(var(--muted) / 0.3); } .skill-md img { max-width: 100%; border-radius: 0.5rem; margin: 1em 0; } .skill-md input[type='checkbox'] { accent-color: var(--duck-teal); margin-right: 0.5em; }