Add chat widget to docs site: floating panel for Q&A + live celestial queries

Floating chat panel injected via Starlight Head override:

- ChatWidget.astro wrapper with astro:after-swap for View Transitions
- ~900-line TypeScript widget: SSE streaming, conversation persistence
  (localStorage), markdown rendering (marked + DOMPurify), suggestion
  pills, two-click delete, conversation history, rAF-throttled streaming
- ~680-line CSS: dark/light themes (amber/slate palette), full-screen
  mobile layout with FAB/Ask button overlap fix, reduced-motion support
- Three domain-specific suggestions: "Where is Jupiter right now?",
  "How do I predict satellite passes?", "What functions handle rise/set
  prediction?"
- FAB uses orbit SVG icon (3 rotated ellipses + center circle)
- Wired to /api/chat/stream endpoint (search backend)
This commit is contained in:
Ryan Malloy 2026-03-01 15:42:31 -07:00
parent 317f74b33b
commit a40ae9437d
7 changed files with 2011 additions and 8 deletions

View File

@ -33,6 +33,7 @@ export default defineConfig({
customCss: [
"./src/styles/custom.css",
"./src/styles/katex-fixes.css",
"./src/styles/chat-widget.css",
"katex/dist/katex.min.css",
],
head: [

40
docs/package-lock.json generated
View File

@ -17,7 +17,9 @@
"astro-mermaid": "^1.3.1",
"astro-opengraph-images": "^1.14.3",
"astro-seo-meta": "^5.2.0",
"dompurify": "^3.2.0",
"katex": "^0.16.28",
"marked": "^15.0.0",
"react": "^19.2.4",
"rehype-katex": "^7.0.1",
"remark-math": "^6.0.0",
@ -25,6 +27,7 @@
},
"devDependencies": {
"@tailwindcss/vite": "^4.0.0",
"@types/dompurify": "^3.2.0",
"tailwindcss": "^4.0.0",
"typescript": "^5.7.0"
}
@ -3072,6 +3075,17 @@
"@types/ms": "*"
}
},
"node_modules/@types/dompurify": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.2.0.tgz",
"integrity": "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==",
"deprecated": "This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.",
"dev": true,
"license": "MIT",
"dependencies": {
"dompurify": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@ -3168,8 +3182,7 @@
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT",
"optional": true,
"peer": true
"optional": true
},
"node_modules/@types/unist": {
"version": "3.0.3",
@ -5902,7 +5915,6 @@
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz",
"integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==",
"license": "(MPL-2.0 OR Apache-2.0)",
"peer": true,
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
@ -7771,16 +7783,15 @@
}
},
"node_modules/marked": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",
"integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",
"version": "15.0.12",
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
"integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
"license": "MIT",
"peer": true,
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 20"
"node": ">= 18"
}
},
"node_modules/mdast-util-definitions": {
@ -8170,6 +8181,19 @@
"uuid": "^11.1.0"
}
},
"node_modules/mermaid/node_modules/marked": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",
"integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",
"license": "MIT",
"peer": true,
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 20"
}
},
"node_modules/micromark": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",

View File

@ -22,10 +22,13 @@
"react": "^19.2.4",
"rehype-katex": "^7.0.1",
"remark-math": "^6.0.0",
"dompurify": "^3.2.0",
"marked": "^15.0.0",
"sharp": "^0.33.0"
},
"devDependencies": {
"@tailwindcss/vite": "^4.0.0",
"@types/dompurify": "^3.2.0",
"tailwindcss": "^4.0.0",
"typescript": "^5.7.0"
}

View File

@ -0,0 +1,10 @@
---
// Chat widget — floating panel for docs Q&A + live celestial queries
// Appends to document.body to escape Starlight stacking contexts
---
<script>
import { initChatWidget } from './chat-widget.ts';
initChatWidget();
document.addEventListener('astro:after-swap', initChatWidget);
</script>

View File

@ -9,6 +9,7 @@
*/
import Default from "@astrojs/starlight/components/Head.astro";
import { getImagePath } from "astro-opengraph-images";
import ChatWidget from "./ChatWidget.astro";
const route = Astro.locals.starlightRoute;
const title = route?.entry?.data?.title ?? "pg_orrery";
@ -23,3 +24,4 @@ const ogImageUrl = getImagePath({ url: Astro.url, site: Astro.site });
<meta name="twitter:image" content={ogImageUrl} />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<ChatWidget />

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,889 @@
/* ============================================
pg_orrery Chat Widget
FAB + sliding panel for docs Q&A
============================================ */
/* ---- FAB (floating action button) ---- */
.chat-fab {
position: fixed;
bottom: 1.5rem;
right: 1.5rem;
z-index: 1000;
width: 3.25rem;
height: 3.25rem;
border-radius: 50%;
border: none;
background: var(--sl-color-accent);
color: var(--sl-color-black);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.chat-fab:hover {
transform: scale(1.08);
box-shadow: 0 4px 20px rgba(245, 158, 11, 0.3);
}
.chat-fab:active {
transform: scale(0.96);
}
.chat-fab svg {
width: 1.25rem;
height: 1.25rem;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
/* X icon when panel is open */
.chat-fab[aria-expanded="true"] svg.icon-message { display: none; }
.chat-fab[aria-expanded="true"] svg.icon-close { display: block; }
.chat-fab[aria-expanded="false"] svg.icon-message { display: block; }
.chat-fab[aria-expanded="false"] svg.icon-close { display: none; }
/* ---- Panel ---- */
.chat-panel {
position: fixed;
bottom: 5.5rem;
right: 1.5rem;
z-index: 999;
width: 24rem;
max-height: 70vh;
display: flex;
flex-direction: column;
background: var(--sl-color-bg-nav);
border: 1px solid var(--sl-color-hairline-light);
border-radius: 0.75rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
transform: translateY(0.5rem);
opacity: 0;
pointer-events: none;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.chat-panel[data-open="true"] {
transform: translateY(0);
opacity: 1;
pointer-events: auto;
}
/* ---- Panel header ---- */
.chat-header {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--sl-color-hairline-shade);
font-family: var(--sl-font-headings);
font-size: 0.85rem;
font-weight: 600;
color: var(--sl-color-white);
}
.chat-header-dot {
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
background: var(--sl-color-accent);
flex-shrink: 0;
}
.chat-header-title {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chat-header-actions {
display: flex;
gap: 0.25rem;
margin-left: auto;
flex-shrink: 0;
}
.chat-header-btn {
width: 1.75rem;
height: 1.75rem;
padding: 0;
border: none;
background: transparent;
color: var(--sl-color-gray-3);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.25rem;
transition: color 0.15s ease, background 0.15s ease;
}
.chat-header-btn:hover {
color: var(--sl-color-white);
background: var(--sl-color-gray-5);
}
.chat-header-btn:active {
transform: scale(0.92);
}
.chat-header-btn svg {
width: 1rem;
height: 1rem;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
/* ---- Messages area ---- */
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 0.75rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
min-height: 8rem;
scroll-behavior: smooth;
}
.chat-messages::-webkit-scrollbar {
width: 4px;
}
.chat-messages::-webkit-scrollbar-track {
background: transparent;
}
.chat-messages::-webkit-scrollbar-thumb {
background: var(--sl-color-gray-4);
border-radius: 2px;
}
/* ---- Message bubbles ---- */
.chat-msg {
max-width: 88%;
padding: 0.5rem 0.75rem;
border-radius: 0.5rem;
font-size: 0.85rem;
line-height: 1.5;
color: var(--sl-color-white);
word-wrap: break-word;
overflow-wrap: break-word;
}
.chat-msg a {
color: var(--sl-color-accent);
text-decoration: underline;
text-underline-offset: 2px;
}
.chat-msg a:hover {
color: var(--sl-color-accent-high);
}
.chat-msg-user {
align-self: flex-end;
background: var(--sl-color-accent-low);
border: 1px solid var(--sl-color-hairline-light);
}
.chat-msg-assistant {
align-self: flex-start;
background: var(--sl-color-gray-6);
max-width: 100%;
}
.chat-msg-assistant strong {
color: var(--sl-color-accent-high);
}
.chat-msg-assistant code {
font-family: var(--sl-font-mono);
font-size: 0.82em;
background: var(--sl-color-gray-5);
padding: 0.1em 0.3em;
border-radius: 0.2rem;
}
/* ---- Markdown elements in assistant bubbles ---- */
.chat-msg-assistant p {
margin: 0 0 0.4em;
}
.chat-msg-assistant p:last-child {
margin-bottom: 0;
}
.chat-msg-assistant h1,
.chat-msg-assistant h2,
.chat-msg-assistant h3,
.chat-msg-assistant h4,
.chat-msg-assistant h5,
.chat-msg-assistant h6 {
font-family: var(--sl-font-headings);
margin: 0.6em 0 0.3em;
line-height: 1.3;
color: var(--sl-color-accent-high);
}
.chat-msg-assistant h1 { font-size: 1.1em; }
.chat-msg-assistant h2 { font-size: 1.05em; }
.chat-msg-assistant h3 { font-size: 1em; }
.chat-msg-assistant h4,
.chat-msg-assistant h5,
.chat-msg-assistant h6 { font-size: 0.9em; }
.chat-msg-assistant h1:first-child,
.chat-msg-assistant h2:first-child,
.chat-msg-assistant h3:first-child {
margin-top: 0;
}
.chat-msg-assistant ul,
.chat-msg-assistant ol {
margin: 0.3em 0;
padding-left: 1.4em;
}
.chat-msg-assistant li {
margin: 0.15em 0;
}
.chat-msg-assistant li > ul,
.chat-msg-assistant li > ol {
margin: 0.1em 0;
}
.chat-msg-assistant blockquote {
margin: 0.4em 0;
padding: 0.3em 0.6em;
border-left: 3px solid var(--sl-color-accent);
background: var(--sl-color-gray-5);
border-radius: 0 0.25rem 0.25rem 0;
font-style: italic;
}
.chat-msg-assistant blockquote p {
margin: 0;
}
.chat-msg-assistant pre {
margin: 0.4em 0;
padding: 0.5em 0.65em;
background: var(--sl-color-gray-5);
border-radius: 0.375rem;
overflow-x: auto;
font-size: 0.82em;
line-height: 1.45;
}
.chat-msg-assistant pre code {
background: none;
padding: 0;
border-radius: 0;
font-size: inherit;
}
.chat-msg-assistant table {
width: 100%;
border-collapse: collapse;
margin: 0.4em 0;
font-size: 0.82em;
}
.chat-msg-assistant th,
.chat-msg-assistant td {
padding: 0.3em 0.5em;
border: 1px solid var(--sl-color-hairline-shade);
text-align: left;
}
.chat-msg-assistant th {
background: var(--sl-color-gray-5);
font-weight: 600;
}
.chat-msg-assistant tbody tr:nth-child(even) {
background: rgba(255, 255, 255, 0.03);
}
.chat-msg-assistant hr {
border: none;
border-top: 1px solid var(--sl-color-hairline-shade);
margin: 0.5em 0;
}
.chat-msg-assistant del,
.chat-msg-assistant s {
opacity: 0.65;
}
.chat-msg-assistant input[type="checkbox"] {
pointer-events: none;
margin-right: 0.3em;
}
/* ---- Sources below assistant message ---- */
.chat-sources {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
margin-top: 0.35rem;
padding-top: 0.35rem;
border-top: 1px solid var(--sl-color-hairline-shade);
}
.chat-source-link {
font-size: 0.72rem;
color: var(--sl-color-accent);
text-decoration: none;
padding: 0.15rem 0.4rem;
border-radius: 0.25rem;
background: var(--sl-color-accent-low);
transition: background 0.1s ease;
white-space: nowrap;
}
.chat-source-link:hover {
background: var(--sl-color-hairline-light);
color: var(--sl-color-accent-high);
}
/* ---- Thinking indicator ---- */
.chat-thinking {
align-self: flex-start;
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
}
.chat-thinking-dots {
display: flex;
gap: 0.25rem;
}
.chat-thinking-dots span {
width: 0.4rem;
height: 0.4rem;
border-radius: 50%;
background: var(--sl-color-gray-3);
animation: chat-pulse 1.4s ease-in-out infinite;
}
.chat-thinking-dots span:nth-child(2) {
animation-delay: 0.2s;
}
.chat-thinking-dots span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes chat-pulse {
0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
40% { opacity: 1; transform: scale(1); }
}
.chat-thinking-text {
font-size: 0.75rem;
color: var(--sl-color-gray-3);
transition: opacity 0.15s ease;
max-width: 16rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-style: italic;
}
/* ---- Reasoning (collapsed thinking) ---- */
.chat-reasoning {
align-self: flex-start;
margin: 0.25rem 0;
font-size: 0.75rem;
color: var(--sl-color-gray-3);
}
.chat-reasoning summary {
cursor: pointer;
font-style: italic;
user-select: none;
list-style: none;
display: flex;
align-items: center;
gap: 0.35rem;
}
.chat-reasoning summary::before {
content: '\25B6';
font-size: 0.5rem;
transition: transform 0.15s ease;
}
.chat-reasoning[open] summary::before {
transform: rotate(90deg);
}
.chat-reasoning-body {
margin-top: 0.35rem;
padding: 0.5rem 0.625rem;
background: var(--sl-color-gray-6);
border-radius: 0.375rem;
font-size: 0.7rem;
line-height: 1.45;
max-height: 10rem;
overflow-y: auto;
color: var(--sl-color-gray-2);
font-style: italic;
}
/* ---- Suggestion chips ---- */
.chat-suggestions {
display: flex;
flex-direction: column;
gap: 0.35rem;
padding: 0.25rem 0;
}
.chat-suggestion {
background: none;
border: 1px solid var(--sl-color-hairline-light);
border-radius: 0.5rem;
padding: 0.45rem 0.65rem;
font-size: 0.8rem;
color: var(--sl-color-gray-2);
cursor: pointer;
text-align: left;
font-family: var(--sl-font);
transition: border-color 0.15s ease, color 0.15s ease;
}
.chat-suggestion:hover {
border-color: var(--sl-color-accent);
color: var(--sl-color-accent);
}
/* ---- Input area ---- */
.chat-input-area {
display: flex;
gap: 0.5rem;
padding: 0.65rem 0.75rem;
border-top: 1px solid var(--sl-color-hairline-shade);
}
.chat-input {
flex: 1;
background: var(--sl-color-gray-6);
border: 1px solid var(--sl-color-hairline-light);
border-radius: 0.4rem;
padding: 0.45rem 0.65rem;
font-size: 0.85rem;
font-family: var(--sl-font);
color: var(--sl-color-white);
outline: none;
transition: border-color 0.15s ease;
}
.chat-input::placeholder {
color: var(--sl-color-gray-3);
}
.chat-input:focus {
border-color: var(--sl-color-accent);
}
.chat-send {
background: var(--sl-color-accent);
color: var(--sl-color-black);
border: none;
border-radius: 0.4rem;
padding: 0.45rem 0.65rem;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
font-family: var(--sl-font);
transition: opacity 0.1s ease;
}
.chat-send:hover {
opacity: 0.9;
}
.chat-send:disabled {
opacity: 0.4;
cursor: not-allowed;
}
/* ---- History list ---- */
.chat-history-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.chat-history-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.6rem 0.75rem;
border-radius: 0.4rem;
cursor: pointer;
border-left: 3px solid transparent;
transition: background 0.15s ease, border-color 0.15s ease;
}
.chat-history-item:hover {
background: var(--sl-color-gray-6);
}
.chat-history-item:focus-visible {
outline: 2px solid var(--sl-color-accent);
outline-offset: -2px;
}
.chat-history-item[data-active] {
border-left-color: var(--sl-color-accent);
}
.chat-history-item[data-confirm] {
background: rgba(220, 50, 50, 0.08);
border-left-color: #dc3232;
}
.chat-history-item-content {
flex: 1;
min-width: 0;
}
.chat-history-item-title {
font-size: 0.82rem;
color: var(--sl-color-white);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.4;
}
.chat-history-item-meta {
font-size: 0.72rem;
color: var(--sl-color-gray-3);
margin-top: 0.1rem;
}
.chat-history-delete {
width: 1.5rem;
height: 1.5rem;
padding: 0;
border: none;
background: transparent;
color: var(--sl-color-gray-4);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.25rem;
opacity: 0;
transition: opacity 0.15s ease, color 0.15s ease;
flex-shrink: 0;
}
.chat-history-item:hover .chat-history-delete,
.chat-history-item[data-confirm] .chat-history-delete {
opacity: 1;
}
.chat-history-delete:hover {
color: #dc3232;
}
.chat-history-delete svg {
width: 0.85rem;
height: 0.85rem;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
.chat-history-delete-confirm {
font-size: 0.7rem;
color: #dc3232;
font-weight: 600;
white-space: nowrap;
}
/* ---- Empty state ---- */
.chat-history-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem 1rem;
gap: 0.75rem;
}
.chat-history-empty-icon {
width: 2rem;
height: 2rem;
fill: none;
stroke: var(--sl-color-gray-4);
stroke-width: 1.5;
stroke-linecap: round;
stroke-linejoin: round;
opacity: 0.6;
}
.chat-history-empty-hint {
font-size: 0.82rem;
color: var(--sl-color-gray-3);
}
/* ---- View toggle: hide input in history mode ---- */
.chat-panel[data-view="history"] .chat-input-area {
display: none;
}
/* ---- Mobile: full-screen panel ---- */
@media (max-width: 40rem) {
.chat-fab {
bottom: 1rem;
right: 1rem;
width: 2.75rem;
height: 2.75rem;
}
.chat-fab svg {
width: 1.1rem;
height: 1.1rem;
}
.chat-panel {
inset: 0;
width: 100%;
max-height: 100%;
border-radius: 0;
bottom: 0;
right: 0;
}
.chat-panel[data-open="true"] {
transform: translateY(0);
}
.chat-panel[data-open="true"] ~ .chat-fab {
bottom: 0.45rem;
}
.chat-input-area {
padding-right: 4.25rem;
}
}
/* ---- Light theme overrides ---- */
:root[data-theme='light'] .chat-panel {
background: #fdfcf9;
border-color: rgba(245, 158, 11, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
}
:root[data-theme='light'] .chat-header {
color: #1a1a2e;
border-bottom-color: rgba(245, 158, 11, 0.15);
}
:root[data-theme='light'] .chat-header-btn {
color: #94a3b8;
}
:root[data-theme='light'] .chat-header-btn:hover {
color: #1a1a2e;
background: #f1f5f9;
}
:root[data-theme='light'] .chat-msg {
color: #1a1a2e;
}
:root[data-theme='light'] .chat-msg-user {
background: #fef3c7;
border-color: rgba(245, 158, 11, 0.2);
}
:root[data-theme='light'] .chat-msg-assistant {
background: #f8fafc;
}
:root[data-theme='light'] .chat-msg-assistant strong {
color: #b45309;
}
:root[data-theme='light'] .chat-msg-assistant code {
background: #f1f5f9;
}
:root[data-theme='light'] .chat-msg-assistant h1,
:root[data-theme='light'] .chat-msg-assistant h2,
:root[data-theme='light'] .chat-msg-assistant h3,
:root[data-theme='light'] .chat-msg-assistant h4,
:root[data-theme='light'] .chat-msg-assistant h5,
:root[data-theme='light'] .chat-msg-assistant h6 {
color: #b45309;
}
:root[data-theme='light'] .chat-msg-assistant blockquote {
background: #f1f5f9;
border-left-color: #f59e0b;
}
:root[data-theme='light'] .chat-msg-assistant pre {
background: #f1f5f9;
}
:root[data-theme='light'] .chat-msg-assistant th {
background: #f1f5f9;
}
:root[data-theme='light'] .chat-msg-assistant th,
:root[data-theme='light'] .chat-msg-assistant td {
border-color: rgba(245, 158, 11, 0.2);
}
:root[data-theme='light'] .chat-msg-assistant tbody tr:nth-child(even) {
background: rgba(245, 158, 11, 0.04);
}
:root[data-theme='light'] .chat-msg-assistant hr {
border-top-color: rgba(245, 158, 11, 0.15);
}
:root[data-theme='light'] .chat-msg a {
color: #d97706;
}
:root[data-theme='light'] .chat-msg a:hover {
color: #92400e;
}
:root[data-theme='light'] .chat-sources {
border-top-color: rgba(245, 158, 11, 0.15);
}
:root[data-theme='light'] .chat-source-link {
color: #d97706;
background: #fef3c7;
}
:root[data-theme='light'] .chat-source-link:hover {
background: rgba(245, 158, 11, 0.15);
color: #92400e;
}
:root[data-theme='light'] .chat-input {
background: #f8fafc;
border-color: rgba(245, 158, 11, 0.2);
color: #1a1a2e;
}
:root[data-theme='light'] .chat-input::placeholder {
color: #94a3b8;
}
:root[data-theme='light'] .chat-input-area {
border-top-color: rgba(245, 158, 11, 0.15);
}
:root[data-theme='light'] .chat-send {
background: #f59e0b;
color: #1a1a2e;
}
:root[data-theme='light'] .chat-suggestion {
color: #475569;
border-color: rgba(245, 158, 11, 0.2);
}
:root[data-theme='light'] .chat-suggestion:hover {
border-color: #f59e0b;
color: #d97706;
}
:root[data-theme='light'] .chat-thinking-dots span {
background: #94a3b8;
}
:root[data-theme='light'] .chat-thinking-text {
color: #94a3b8;
}
:root[data-theme='light'] .chat-reasoning {
color: #94a3b8;
}
:root[data-theme='light'] .chat-reasoning-body {
background: #f8fafc;
color: #64748b;
}
:root[data-theme='light'] .chat-messages::-webkit-scrollbar-thumb {
background: #94a3b8;
}
:root[data-theme='light'] .chat-history-item:hover {
background: #f8fafc;
}
:root[data-theme='light'] .chat-history-item[data-active] {
border-left-color: #f59e0b;
}
:root[data-theme='light'] .chat-history-item[data-confirm] {
background: rgba(220, 50, 50, 0.06);
}
:root[data-theme='light'] .chat-history-item-title {
color: #1a1a2e;
}
:root[data-theme='light'] .chat-history-item-meta {
color: #94a3b8;
}
:root[data-theme='light'] .chat-history-delete {
color: #94a3b8;
}
:root[data-theme='light'] .chat-history-empty-icon {
stroke: #94a3b8;
}
:root[data-theme='light'] .chat-history-empty-hint {
color: #94a3b8;
}
/* ---- Reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
.chat-fab,
.chat-panel,
.chat-header-btn,
.chat-history-item,
.chat-history-delete {
transition-duration: 1ms;
}
.chat-thinking-dots span {
animation-duration: 1ms;
}
.chat-messages {
scroll-behavior: auto;
}
}