diff --git a/CHANGELOG.md b/CHANGELOG.md index 29466dc0..648d8588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ the frozen-backend fallback mirror it for their toolchains. **Highlights** - A Reduce motion switch in Settings, for calm without changing your whole system (#1857) +- A light theme, and System Auto now follows a light-mode OS instead of staying dark (#1973) — thanks @CoDe-ReDz! - Generating from a one-character input now says the input was too short, instead of quoting a convolution error (#1826) - First run asks about text size before the install, not after it (#1849) - Cloning without a reference clip now says so, instead of naming library parameters you cannot set (#1879) diff --git a/frontend/src/components/settings/AppearancePanel.jsx b/frontend/src/components/settings/AppearancePanel.jsx index 6d3389f7..0787c941 100644 --- a/frontend/src/components/settings/AppearancePanel.jsx +++ b/frontend/src/components/settings/AppearancePanel.jsx @@ -13,12 +13,19 @@ import { useAppStore, FONT_OPTIONS, FONT_STACKS } from '../../store'; import { SettingsSection, SettingRow, InfoHint, SettingsToggle } from './primitives'; const THEMES = [ + { + id: 'auto', + labelKey: 'settings.theme_auto', + defaultLabel: 'System Auto', + dot: 'linear-gradient(135deg, #fdf6e3 50%, #1d2021 50%)', + }, { id: 'gruvbox', label: 'Gruvbox', dot: '#d3869b' }, { id: 'midnight', label: 'Midnight', dot: '#8b5cf6' }, { id: 'nord', label: 'Nord', dot: '#88c0d0' }, { id: 'solarized', label: 'Solarized', dot: '#268bd2' }, { id: 'rose-pine', label: 'Rosé Pine', dot: '#ebbcba' }, { id: 'catppuccin', label: 'Catppuccin', dot: '#cba6f7' }, + { id: 'light', labelKey: 'settings.theme_light', defaultLabel: 'Light', dot: '#1d6b9f' }, ]; /** @@ -204,8 +211,10 @@ export default function AppearancePanel() { style={{ '--dot-color': th.dot }} onClick={() => setTheme(th.id)} onKeyDown={(e) => radioGroupKeyDown(e, themeIds, theme, setTheme)} - title={th.label} - aria-label={th.label} + title={th.labelKey ? t(th.labelKey, { defaultValue: th.defaultLabel }) : th.label} + aria-label={ + th.labelKey ? t(th.labelKey, { defaultValue: th.defaultLabel }) : th.label + } aria-checked={theme === th.id} role="radio" tabIndex={radioTabIndex(themeIds, theme, th.id)} diff --git a/frontend/src/components/settings/AppearancePanel.test.jsx b/frontend/src/components/settings/AppearancePanel.test.jsx index 459ffc77..02807f4e 100644 --- a/frontend/src/components/settings/AppearancePanel.test.jsx +++ b/frontend/src/components/settings/AppearancePanel.test.jsx @@ -149,3 +149,21 @@ describe('AppearancePanel — navigation style picker', () => { useAppStore.getState().setNavStyle('rail'); }); }); + +describe('AppearancePanel — theme selection', () => { + it('renders localized labels for the Auto and Light themes', () => { + render(); + + // The test runner uses the `defaultValue` from our t() hooks. + // This verifies the labels are correctly exposed to screen readers via aria-label. + const autoTheme = screen.getByRole('radio', { name: 'System Auto' }); + const lightTheme = screen.getByRole('radio', { name: 'Light' }); + + expect(autoTheme).toBeInTheDocument(); + expect(lightTheme).toBeInTheDocument(); + + // Verify it generates the correct data attribute for the OS-sync theme + expect(autoTheme).toHaveAttribute('data-radio-value', 'auto'); + expect(lightTheme).toHaveAttribute('data-radio-value', 'light'); + }); +}); diff --git a/frontend/src/i18n/locales/ar.json b/frontend/src/i18n/locales/ar.json index e1bbd310..3058edae 100644 --- a/frontend/src/i18n/locales/ar.json +++ b/frontend/src/i18n/locales/ar.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (مشفّر، موصى به)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "متغير البيئة", + "theme_auto": "تلقائي حسب النظام", + "theme_light": "فاتح", "reduce_motion": "تقليل الحركة", "reduce_motion_desc": "يوقف الرسوم المتحركة في الخلفية والانتقالات. إعداد النظام يظل ساريًا بذاته؛ وهذا يضاف إليه." }, diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json index 9f59b87c..28fe191a 100644 --- a/frontend/src/i18n/locales/de.json +++ b/frontend/src/i18n/locales/de.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (verschlüsselt, empfohlen)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Umgebungsvariable", + "theme_auto": "System-Standard", + "theme_light": "Hell", "reduce_motion": "Bewegung reduzieren", "reduce_motion_desc": "Stoppt Hintergrundanimationen und Übergänge. Die Systemeinstellung gilt weiterhin für sich; dies kommt hinzu." }, diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index 24a61319..068472a3 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -695,6 +695,8 @@ "device": "Device & compute", "device_desc": "Live hardware and backend readouts.", "network_desc": "Proxy for downloads and model fetches.", + "theme_auto": "System Auto", + "theme_light": "Light", "translation_desc": "How dubbing translates dialogue, and which engine does it.", "translate_quality": "Translation quality", "translate_quality_desc": "Fast is literal and quick; Cinematic uses the LLM for natural phrasing.", diff --git a/frontend/src/i18n/locales/es.json b/frontend/src/i18n/locales/es.json index f92fac35..e43b6591 100644 --- a/frontend/src/i18n/locales/es.json +++ b/frontend/src/i18n/locales/es.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (cifrado, recomendado)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Variable de entorno", + "theme_auto": "Automático del sistema", + "theme_light": "Claro", "reduce_motion": "Reducir movimiento", "reduce_motion_desc": "Detiene las animaciones de fondo y las transiciones. Tu ajuste del sistema se respeta por sí solo; esto se suma." }, diff --git a/frontend/src/i18n/locales/fr.json b/frontend/src/i18n/locales/fr.json index 5606994c..24d36df4 100644 --- a/frontend/src/i18n/locales/fr.json +++ b/frontend/src/i18n/locales/fr.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (chiffré, recommandé)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Variable d’environnement", + "theme_auto": "Auto (Système)", + "theme_light": "Clair", "reduce_motion": "Réduire les animations", "reduce_motion_desc": "Arrête les animations d’arrière-plan et les transitions. Le réglage système reste actif de son côté ; ceci s’y ajoute." }, diff --git a/frontend/src/i18n/locales/hi.json b/frontend/src/i18n/locales/hi.json index 4c4883ba..897b2e85 100644 --- a/frontend/src/i18n/locales/hi.json +++ b/frontend/src/i18n/locales/hi.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (एन्क्रिप्ट किया गया, अनुशंसित)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "परिवेश चर", + "theme_auto": "सिस्टम डिफ़ॉल्ट", + "theme_light": "लाइट", "reduce_motion": "गति कम करें", "reduce_motion_desc": "पृष्ठभूमि एनिमेशन और ट्रांज़िशन रोकता है। आपकी सिस्टम सेटिंग स्वयं लागू रहती है; यह उसमें जुड़ता है।" }, diff --git a/frontend/src/i18n/locales/id.json b/frontend/src/i18n/locales/id.json index b50664d5..0601f5f6 100644 --- a/frontend/src/i18n/locales/id.json +++ b/frontend/src/i18n/locales/id.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (terenkripsi, disarankan)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Variabel lingkungan", + "theme_auto": "Otomatis Sistem", + "theme_light": "Terang", "reduce_motion": "Kurangi gerakan", "reduce_motion_desc": "Menghentikan animasi latar dan transisi. Pengaturan sistem Anda tetap berlaku sendiri; ini menambahnya." }, diff --git a/frontend/src/i18n/locales/it.json b/frontend/src/i18n/locales/it.json index 5ec31951..54d1baa0 100644 --- a/frontend/src/i18n/locales/it.json +++ b/frontend/src/i18n/locales/it.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (crittografato, consigliato)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Variabile d’ambiente", + "theme_auto": "Automatico di sistema", + "theme_light": "Chiaro", "reduce_motion": "Riduci le animazioni", "reduce_motion_desc": "Ferma le animazioni di sfondo e le transizioni. L’impostazione di sistema resta valida da sola; questa si aggiunge." }, diff --git a/frontend/src/i18n/locales/ja.json b/frontend/src/i18n/locales/ja.json index b6605a67..e0a214f3 100644 --- a/frontend/src/i18n/locales/ja.json +++ b/frontend/src/i18n/locales/ja.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio(暗号化済み、推奨)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "環境変数", + "theme_auto": "システム自動", + "theme_light": "ライト", "reduce_motion": "視差効果を減らす", "reduce_motion_desc": "背景アニメーションとトランジションを停止します。システム設定は単独で有効なままで、これはそれに加わります。" }, diff --git a/frontend/src/i18n/locales/ko.json b/frontend/src/i18n/locales/ko.json index 158bcfda..53b880af 100644 --- a/frontend/src/i18n/locales/ko.json +++ b/frontend/src/i18n/locales/ko.json @@ -576,6 +576,8 @@ "generate_timeout_shadowed_note": "VoiceStudio 외부의 환경 변수(셸, .env 파일 또는 컨테이너)가 현재 이 값을 설정하고 있습니다 — 해당 변수가 제거될 때까지 여기에 저장한 값은 무시됩니다.", "generate_timeout_shadowed_toast": "저장되었지만, VoiceStudio 외부의 환경 변수가 현재 이 값을 설정하고 있습니다 — 해당 변수가 제거될 때까지 계속 사용됩니다.", "hf_token_not_checked": "테스트 안 함", + "theme_auto": "시스템 자동", + "theme_light": "라이트", "reduce_motion": "모션 줄이기", "reduce_motion_desc": "배경 애니메이션과 전환을 중지합니다. 시스템 설정은 그대로 적용되며, 이 옵션은 거기에 더해집니다." }, diff --git a/frontend/src/i18n/locales/nl.json b/frontend/src/i18n/locales/nl.json index 6c892469..b359d356 100644 --- a/frontend/src/i18n/locales/nl.json +++ b/frontend/src/i18n/locales/nl.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (versleuteld, aanbevolen)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Omgevingsvariabele", + "theme_auto": "Systeem auto", + "theme_light": "Licht", "reduce_motion": "Beweging verminderen", "reduce_motion_desc": "Stopt achtergrondanimaties en overgangen. Je systeeminstelling blijft op zichzelf gelden; dit komt daarbij." }, diff --git a/frontend/src/i18n/locales/pl.json b/frontend/src/i18n/locales/pl.json index 208e455e..a71118eb 100644 --- a/frontend/src/i18n/locales/pl.json +++ b/frontend/src/i18n/locales/pl.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (zaszyfrowane, zalecane)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Zmienna środowiskowa", + "theme_auto": "Automatyczny systemowy", + "theme_light": "Jasny", "reduce_motion": "Ogranicz ruch", "reduce_motion_desc": "Zatrzymuje animacje tła i przejścia. Ustawienie systemowe działa samo z siebie; to je uzupełnia." }, diff --git a/frontend/src/i18n/locales/pt.json b/frontend/src/i18n/locales/pt.json index d556c6d2..25517f8e 100644 --- a/frontend/src/i18n/locales/pt.json +++ b/frontend/src/i18n/locales/pt.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (criptografado, recomendado)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Variável de ambiente", + "theme_auto": "Automático do Sistema", + "theme_light": "Claro", "reduce_motion": "Reduzir movimento", "reduce_motion_desc": "Para as animações de fundo e as transições. A definição do sistema continua a ser respeitada; esta acrescenta-se." }, diff --git a/frontend/src/i18n/locales/ru.json b/frontend/src/i18n/locales/ru.json index 4c5128b9..fe0e049d 100644 --- a/frontend/src/i18n/locales/ru.json +++ b/frontend/src/i18n/locales/ru.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (зашифровано, рекомендуется)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Переменная окружения", + "theme_auto": "Системный", + "theme_light": "Светлая", "reduce_motion": "Уменьшить движение", "reduce_motion_desc": "Останавливает фоновые анимации и переходы. Системная настройка действует сама по себе; эта добавляется к ней." }, diff --git a/frontend/src/i18n/locales/sv.json b/frontend/src/i18n/locales/sv.json index 6952cb97..33ddc7ca 100644 --- a/frontend/src/i18n/locales/sv.json +++ b/frontend/src/i18n/locales/sv.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (krypterad, rekommenderas)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Miljövariabel", + "theme_auto": "Systemval", + "theme_light": "Ljust", "reduce_motion": "Minska rörelse", "reduce_motion_desc": "Stoppar bakgrundsanimationer och övergångar. Systeminställningen gäller ändå; det här läggs till." }, diff --git a/frontend/src/i18n/locales/th.json b/frontend/src/i18n/locales/th.json index ba75362a..067b6024 100644 --- a/frontend/src/i18n/locales/th.json +++ b/frontend/src/i18n/locales/th.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (เข้ารหัส แนะนำ)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "ตัวแปรสภาพแวดล้อม", + "theme_auto": "อัตโนมัติตามระบบ", + "theme_light": "สว่าง", "reduce_motion": "ลดการเคลื่อนไหว", "reduce_motion_desc": "หยุดแอนิเมชันพื้นหลังและทรานซิชัน การตั้งค่าระบบยังมีผลด้วยตัวเอง ส่วนนี้เพิ่มเข้าไป" }, diff --git a/frontend/src/i18n/locales/tr.json b/frontend/src/i18n/locales/tr.json index fac8a388..b169f8c1 100644 --- a/frontend/src/i18n/locales/tr.json +++ b/frontend/src/i18n/locales/tr.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (şifrelenmiş, önerilen)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Ortam değişkeni", + "theme_auto": "Sistem Otomatik", + "theme_light": "Açık", "reduce_motion": "Hareketi azalt", "reduce_motion_desc": "Arka plan animasyonlarını ve geçişleri durdurur. Sistem ayarınız kendi başına geçerlidir; bu ona eklenir." }, diff --git a/frontend/src/i18n/locales/uk.json b/frontend/src/i18n/locales/uk.json index f3ea6f20..30cf01f6 100644 --- a/frontend/src/i18n/locales/uk.json +++ b/frontend/src/i18n/locales/uk.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (зашифровано, рекомендовано)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Змінна середовища", + "theme_auto": "Системний", + "theme_light": "Світла", "reduce_motion": "Зменшити рух", "reduce_motion_desc": "Зупиняє фонові анімації та переходи. Системне налаштування діє саме по собі; це доповнює його." }, diff --git a/frontend/src/i18n/locales/vi.json b/frontend/src/i18n/locales/vi.json index 970e4190..84871928 100644 --- a/frontend/src/i18n/locales/vi.json +++ b/frontend/src/i18n/locales/vi.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio (được mã hóa, khuyên dùng)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "Biến môi trường", + "theme_auto": "Tự động theo hệ thống", + "theme_light": "Sáng", "reduce_motion": "Giảm chuyển động", "reduce_motion_desc": "Dừng hoạt ảnh nền và hiệu ứng chuyển tiếp. Cài đặt hệ thống vẫn có hiệu lực riêng; mục này bổ sung thêm." }, diff --git a/frontend/src/i18n/locales/zh-CN.json b/frontend/src/i18n/locales/zh-CN.json index 62f6226c..de559dd0 100644 --- a/frontend/src/i18n/locales/zh-CN.json +++ b/frontend/src/i18n/locales/zh-CN.json @@ -841,6 +841,8 @@ "generate_timeout_shadowed_note": "VoiceStudio 之外的一个环境变量(终端、.env 文件或容器)当前正在设置此值——在移除该变量之前,此处保存的值会被忽略。", "generate_timeout_shadowed_toast": "已保存,但 VoiceStudio 之外的一个环境变量当前正在设置此值——在移除该变量之前,将继续使用该值。", "hf_token_not_checked": "未测试", + "theme_auto": "系统自动", + "theme_light": "浅色", "reduce_motion": "减少动效", "reduce_motion_desc": "停止背景动画和过渡效果。系统设置本身仍然生效,此项在其基础上叠加。" }, diff --git a/frontend/src/i18n/locales/zh-TW.json b/frontend/src/i18n/locales/zh-TW.json index 6a37354f..489b3ca6 100644 --- a/frontend/src/i18n/locales/zh-TW.json +++ b/frontend/src/i18n/locales/zh-TW.json @@ -354,6 +354,8 @@ "hf_source_app_label": "VoiceStudio(已加密,建議)", "hf_source_cli_label": "HuggingFace CLI", "hf_source_env_label": "環境變數", + "theme_auto": "系統自動", + "theme_light": "淺色", "reduce_motion": "減少動態效果", "reduce_motion_desc": "停止背景動畫與轉場效果。系統設定本身仍然生效,此項在其基礎上疊加。" }, diff --git a/frontend/src/index.css b/frontend/src/index.css index 0d3fc7c1..8e160494 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -563,18 +563,78 @@ --select-caret: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2210%22%20height%3D%226%22%3E%3Cpath%20d%3D%22M1%201l4%204%204-4%22%20fill%3D%22none%22%20stroke%3D%22%23a6adc8%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E"); } +/* ── Light (Solarized Light) ───────────────────────────────────────── */ +[data-theme="light"] { + --color-fg: #073642; + --color-fg-muted: #586e75; + --color-fg-subtle: #4a5a60; + --color-fg-inverse: #fdf6e3; + + --color-bg: #fdf6e3; + --color-bg-elev-1: rgba(238, 232, 213, 0.85); + --color-bg-elev-2: rgba(238, 232, 213, 0.40); + --color-bg-elev-3: rgba(238, 232, 213, 0.25); + + --color-border: rgba(7, 54, 66, 0.12); + --color-border-strong: rgba(7, 54, 66, 0.20); + --color-border-warm: rgba(181, 137, 0, 0.15); + + --color-brand: #1d6b9f; + --color-brand-hover: #144b70; + --color-brand-glow: rgba(29, 107, 159, 0.25); + + --color-accent: #8c6b00; + --color-success: #667500; + --color-warn: #9c3a11; + --color-danger: #a82624; + --color-info: #515594; + + --chrome-bg: #eee8d5; + --chrome-fg: #073642; + --chrome-fg-muted: #4d5f66; + --chrome-fg-dim: #4a5a60; + --chrome-border: #d5ccb7; + + color-scheme: light; + --select-caret: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2210%22%20height%3D%226%22%3E%3Cpath%20d%3D%22M1%201l4%204%204-4%22%20fill%3D%22none%22%20stroke%3D%22%23586e75%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E"); +} + /* ── System-preference sync ────────────────────────────────────────── */ -/* When theme is "auto", the light theme activates on light-mode OS. - Currently we don't ship a light theme; this is scaffolding for - community contributions. */ +/* When theme is "auto", the light theme activates on light-mode OS. */ @media (prefers-color-scheme: light) { [data-theme="auto"] { - /* Future light theme tokens go here. NOTE: no light theme ships yet, so - "auto" still renders the dark default tokens even on a light-mode OS — - which is why `color-scheme` is intentionally left at the :root `dark` - here (a `light` value would make native form chrome mismatch the still - -dark surface). When a real light theme lands, set `color-scheme: light` - and its own --select-caret in this block. */ + --color-fg: #073642; + --color-fg-muted: #586e75; + --color-fg-subtle: #4a5a60; + --color-fg-inverse: #fdf6e3; + + --color-bg: #fdf6e3; + --color-bg-elev-1: rgba(238, 232, 213, 0.85); + --color-bg-elev-2: rgba(238, 232, 213, 0.40); + --color-bg-elev-3: rgba(238, 232, 213, 0.25); + + --color-border: rgba(7, 54, 66, 0.12); + --color-border-strong: rgba(7, 54, 66, 0.20); + --color-border-warm: rgba(181, 137, 0, 0.15); + + --color-brand: #1d6b9f; + --color-brand-hover: #144b70; + --color-brand-glow: rgba(29, 107, 159, 0.25); + + --color-accent: #8c6b00; + --color-success: #667500; + --color-warn: #9c3a11; + --color-danger: #a82624; + --color-info: #515594; + + --chrome-bg: #eee8d5; + --chrome-fg: #073642; + --chrome-fg-muted: #4d5f66; + --chrome-fg-dim: #4a5a60; + --chrome-border: #d5ccb7; + + color-scheme: light; + --select-caret: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2210%22%20height%3D%226%22%3E%3Cpath%20d%3D%22M1%201l4%204%204-4%22%20fill%3D%22none%22%20stroke%3D%22%23586e75%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E"); } } @@ -7231,6 +7291,20 @@ button.dub-stepper__action:focus-visible { max-width: 240px; } +/* Override header gradient for light modes */ +[data-theme="light"] .header-area h1 { + background: linear-gradient(135deg, var(--chrome-fg) 0%, var(--chrome-fg-muted) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} +@media (prefers-color-scheme: light) { + [data-theme="auto"] .header-area h1 { + background: linear-gradient(135deg, var(--chrome-fg) 0%, var(--chrome-fg-muted) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } +} + /* ═══════════════════════════════════════════════════════════════════════ APP-WIDE BORDER / DIVIDER REMOVAL (owner-approved) Zero every decorative border/hairline token. Kept LAST in source order so