/* ========================================================================== 族語翻譯模型測試系統 — 視覺系統取自 design/DESIGN.md(Claude.com 設計語言) 暖色編輯風:奶油畫布 + 珊瑚主色 + 襯線標題 + 人文無襯線內文 ========================================================================== */ :root { /* --- Claude 色彩 tokens(淺色 / 預設) --- */ --color-primary: #cc785c; --color-primary-active: #a9583e; --color-primary-disabled: #e6dfd8; --color-ink: #141413; --color-body: #3d3d3a; --color-body-strong: #252523; --color-muted: #6c6a64; --color-muted-soft: #8e8b82; --color-hairline: #e6dfd8; --color-hairline-soft: #ebe6df; --color-canvas: #faf9f5; --color-surface-soft: #f5f0e8; --color-surface-card: #efe9de; --color-surface-cream-strong: #e8e0d2; --color-surface-dark: #181715; --color-surface-dark-elevated: #252320; --color-surface-dark-soft: #1f1e1b; --color-on-primary: #ffffff; --color-on-dark: #faf9f5; --color-on-dark-soft: #a09d96; --color-accent-teal: #5db8a6; --color-accent-amber: #e8a55a; --color-success: #5db872; --color-warning: #d4a017; --color-error: #c64545; /* --- 語意化角色(跟著主題切換的變數,元件只認這一層) --- */ --bg-canvas: var(--color-canvas); --bg-surface: var(--color-surface-card); --bg-surface-soft: var(--color-surface-soft); --bg-elevated: #ffffff; --text-primary: var(--color-ink); --text-body: var(--color-body); --text-muted: var(--color-muted); --text-muted-soft: var(--color-muted-soft); --border-hairline: var(--color-hairline); --shadow-card: 0 1px 2px rgba(20, 20, 19, 0.04), 0 8px 24px -12px rgba(20, 20, 19, 0.10); /* --- 字體 --- */ --font-display: "Cormorant Garamond", "Tiempos Headline", Georgia, "Noto Serif TC", serif; --font-body: "Inter", "PingFang TC", "Microsoft JhengHei", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; /* 族語(羅馬拼音)文字用的字體:中文字元瀏覽器會自動 fallback 到系統中文 字型顯示,不受影響。 */ --font-lang: "Times New Roman", Times, "Noto Serif TC", serif; /* --- 圓角 / 間距(沿用 DESIGN.md 比例) --- */ --radius-sm: 6px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-pill: 9999px; --space-xxs: 4px; --space-xs: 8px; --space-sm: 12px; --space-md: 16px; --space-lg: 24px; --space-xl: 32px; --space-xxl: 48px; } /* --- 深色模式(同一套品牌系統的暗面 surface,不是通用反色) --- 優先序:使用者手動切換(data-theme 屬性)> 瀏覽器/系統偏好設定 > 預設淺色。 */ :root[data-theme="dark"] { --bg-canvas: var(--color-surface-dark); --bg-surface: var(--color-surface-dark-elevated); --bg-surface-soft: var(--color-surface-dark-soft); --bg-elevated: var(--color-surface-dark-elevated); --text-primary: var(--color-on-dark); --text-body: #cfcbc2; --text-muted: var(--color-on-dark-soft); --text-muted-soft: #7a766e; --border-hairline: #34312b; --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px -12px rgba(0, 0, 0, 0.5); } @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]):not([data-theme="dark"]) { --bg-canvas: var(--color-surface-dark); --bg-surface: var(--color-surface-dark-elevated); --bg-surface-soft: var(--color-surface-dark-soft); --bg-elevated: var(--color-surface-dark-elevated); --text-primary: var(--color-on-dark); --text-body: #cfcbc2; --text-muted: var(--color-on-dark-soft); --text-muted-soft: #7a766e; --border-hairline: #34312b; --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px -12px rgba(0, 0, 0, 0.5); } } * { box-sizing: border-box; } html, body { margin: 0; padding: 0; } body { background: var(--bg-canvas); color: var(--text-body); font-family: var(--font-body); font-size: 16px; line-height: 1.55; -webkit-font-smoothing: antialiased; transition: background-color 0.2s ease, color 0.2s ease; overflow-x: hidden; } h1, h2, h3 { font-family: var(--font-display); font-weight: 500; color: var(--text-primary); margin: 0; } /* ============================== Top Nav ============================== */ .topnav { position: sticky; top: 0; z-index: 20; background: var(--bg-canvas); border-bottom: 1px solid var(--border-hairline); } .topnav-inner { max-width: 1280px; margin: 0 auto; padding: 0 var(--space-lg); height: 64px; display: flex; align-items: center; gap: var(--space-md); } .brand { display: flex; align-items: center; gap: var(--space-xs); font-weight: 600; color: var(--text-primary); font-size: 15px; white-space: nowrap; } .brand-mark { color: var(--color-primary); font-size: 18px; } .engine-status { display: flex; gap: var(--space-xs); margin-left: auto; flex-wrap: wrap; } .status-pill { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: var(--radius-pill); background: var(--bg-surface); font-size: 12px; font-weight: 500; color: var(--text-muted); border: 1px solid var(--border-hairline); } .status-pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-muted-soft); flex-shrink: 0; } .status-pill.is-live .dot { background: var(--color-success); } .status-pill.is-stub .dot { background: var(--color-accent-amber); } .theme-toggle { border: 1px solid var(--border-hairline); background: var(--bg-surface); color: var(--text-primary); width: 36px; height: 36px; border-radius: var(--radius-pill); cursor: pointer; font-size: 15px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: background-color 0.15s ease; } .theme-toggle:hover { background: var(--bg-surface-soft); } /* ============================== Control Bar ============================== */ .control-bar { padding: var(--space-xxl) var(--space-lg) var(--space-xl); } .control-bar-inner { max-width: 880px; margin: 0 auto; text-align: center; } .eyebrow { font-family: var(--font-body); font-size: 12px; font-weight: 600; letter-spacing: 1.5px; color: var(--color-primary); margin: 0 0 var(--space-sm); } .control-bar h1 { font-size: 42px; line-height: 1.15; letter-spacing: -0.5px; margin-bottom: var(--space-md); } .subtitle { color: var(--text-muted); font-size: 16px; max-width: 620px; margin: 0 auto var(--space-xl); } .input-card { background: var(--bg-elevated); border: 1px solid var(--border-hairline); border-radius: var(--radius-lg); padding: var(--space-lg); box-shadow: var(--shadow-card); text-align: left; } .direction-row { display: flex; align-items: center; justify-content: center; gap: var(--space-sm); margin-bottom: var(--space-sm); } .direction-label { font-family: var(--font-mono); font-size: 13px; font-weight: 500; letter-spacing: 0.3px; color: var(--text-muted); padding: 3px 10px; border-radius: var(--radius-pill); background: var(--bg-surface-soft); min-width: 64px; text-align: center; transition: color 0.15s ease, background-color 0.15s ease; } .direction-label.is-active { color: var(--color-on-primary); background: var(--color-primary); } .direction-swap { border: 1px solid var(--border-hairline); background: var(--bg-surface); color: var(--text-primary); width: 28px; height: 28px; border-radius: var(--radius-pill); cursor: pointer; font-size: 14px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: transform 0.2s ease, background-color 0.15s ease; } .direction-swap:hover { background: var(--bg-surface-soft); } .direction-swap.is-flipped { transform: rotate(180deg); } .source-textarea { width: 100%; resize: vertical; border: none; outline: none; background: transparent; color: var(--text-primary); font-family: var(--font-body); font-size: 18px; line-height: 1.5; padding: var(--space-xs) 0 var(--space-md); min-height: 64px; } .source-textarea::placeholder { color: var(--text-muted-soft); } .control-row { display: flex; align-items: flex-end; gap: var(--space-md); padding-top: var(--space-md); border-top: 1px solid var(--border-hairline); flex-wrap: wrap; } .field { display: flex; flex-direction: column; gap: 6px; min-width: 160px; } .field-grow { flex: 1; min-width: 220px; } .field label { font-size: 12px; font-weight: 500; color: var(--text-muted); } select { appearance: none; background: var(--bg-surface); color: var(--text-primary); border: 1px solid var(--border-hairline); border-radius: var(--radius-md); padding: 10px 32px 10px 12px; font-family: var(--font-body); font-size: 14px; height: 40px; cursor: pointer; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%236c6a64' stroke-width='1.5' fill='none' fill-rule='evenodd'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; } select:focus { border-color: var(--color-primary); } .domain-filter { display: inline-flex; gap: 2px; background: var(--bg-surface-soft); border: 1px solid var(--border-hairline); border-radius: var(--radius-pill); padding: 2px; width: fit-content; } .domain-filter-btn { appearance: none; border: none; background: transparent; color: var(--text-muted); font-family: var(--font-body); font-size: 12px; font-weight: 500; border-radius: var(--radius-pill); padding: 4px 12px; cursor: pointer; transition: background-color 0.15s ease, color 0.15s ease; } .domain-filter-btn:hover { color: var(--text-primary); } .domain-filter-btn.is-active { background: var(--color-primary); color: var(--color-on-primary); } #exampleSearch { background: var(--bg-surface); color: var(--text-primary); border: 1px solid var(--border-hairline); border-radius: var(--radius-md); padding: 10px 12px; font-family: var(--font-body); font-size: 14px; height: 40px; width: 100%; } #exampleSearch:focus { border-color: var(--color-primary); outline: none; } #exampleSearch::placeholder { color: var(--text-muted-soft); } /* --- 單句真實 BLEU/chrF(對照 561 句 held-out 語料裡的人工參考答案時才會出現) --- */ .sentence-score { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600; color: #3f7d50; background: rgba(93, 184, 114, 0.12); border-radius: var(--radius-pill); padding: 3px 9px; width: fit-content; } :root[data-theme="dark"] .sentence-score { color: #7fd090; } .btn-primary { background: var(--color-primary); color: var(--color-on-primary); border: none; border-radius: var(--radius-md); padding: 0 20px; height: 40px; font-family: var(--font-body); font-size: 14px; font-weight: 500; cursor: pointer; display: inline-flex; align-items: center; gap: 8px; transition: background-color 0.15s ease, transform 0.1s ease; white-space: nowrap; } .btn-primary:hover { background: var(--color-primary-active); } .btn-primary:active { transform: translateY(1px); } .btn-primary:disabled { background: var(--color-primary-disabled); color: var(--text-muted); cursor: not-allowed; } .btn-arrow { transition: transform 0.15s ease; } .btn-primary:hover .btn-arrow { transform: translateX(2px); } /* ============================== Results ============================== */ .results-section { padding: 0 var(--space-lg) var(--space-xxl); } .results-inner { max-width: 1280px; margin: 0 auto; } .empty-state { text-align: center; padding: var(--space-xxl) var(--space-lg); color: var(--text-muted); } .empty-mark { display: block; font-size: 28px; color: var(--color-primary-disabled); margin-bottom: var(--space-sm); } .error-banner { background: #fbeaea; border: 1px solid var(--color-error); color: var(--color-error); border-radius: var(--radius-md); padding: var(--space-sm) var(--space-md); margin-bottom: var(--space-md); font-size: 14px; } :root[data-theme="dark"] .error-banner { background: rgba(198, 69, 69, 0.12); } .reference-banner { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 6px; background: rgba(42, 120, 214, 0.08); border: 1px solid #2a78d6; border-radius: var(--radius-md); padding: var(--space-md) var(--space-lg); margin-bottom: var(--space-md); } :root[data-theme="dark"] .reference-banner { background: rgba(57, 135, 229, 0.12); border-color: #3987e5; } .reference-banner-label { font-size: 12px; font-weight: 700; letter-spacing: 0.02em; color: #1c5cab; } :root[data-theme="dark"] .reference-banner-label { color: #86b6ef; } .reference-banner-text { font-size: 22px; font-weight: 700; color: #2a78d6; line-height: 1.4; } :root[data-theme="dark"] .reference-banner-text { color: #6da7ec; } .results-grid { display: grid; /* 引擎數量會隨 DEMO_ENGINE_IDS 增減(目前 3 個),用 auto-fit 讓欄數自動 跟著卡片數量走,不寫死固定欄數,之後加減引擎不用回來改這裡。 */ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--space-md); } @media (max-width: 1100px) { .results-grid { grid-template-columns: repeat(2, 1fr); } } @media (max-width: 620px) { .results-grid { grid-template-columns: 1fr; } .control-bar h1 { font-size: 32px; } } .result-card { background: var(--bg-elevated); border: 1px solid var(--border-hairline); border-radius: var(--radius-lg); padding: var(--space-md); display: flex; flex-direction: column; gap: var(--space-sm); box-shadow: var(--shadow-card); animation: card-in 0.35s ease both; } @keyframes card-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } .result-card-header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-xs); } .engine-label { font-size: 13px; font-weight: 600; color: var(--text-primary); } .engine-sub { font-size: 11px; color: var(--text-muted-soft); margin-top: 2px; } .badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px; border-radius: var(--radius-pill); font-size: 11px; font-weight: 600; letter-spacing: 0.3px; white-space: nowrap; } .badge-live { background: rgba(93, 184, 114, 0.15); color: #3f7d50; } :root[data-theme="dark"] .badge-live { color: #7fd090; } .badge-stub { background: rgba(232, 165, 90, 0.18); color: #9a6a24; } :root[data-theme="dark"] .badge-stub { color: var(--color-accent-amber); } .badge-error { background: rgba(198, 69, 69, 0.15); color: var(--color-error); } .result-output { font-family: var(--font-lang); font-size: 18px; line-height: 1.6; color: var(--text-primary); background: var(--bg-surface-soft); border-radius: var(--radius-md); padding: var(--space-sm) var(--space-md); min-height: 64px; word-break: break-word; white-space: pre-wrap; } .result-output.is-empty { color: var(--text-muted-soft); font-family: var(--font-body); font-style: italic; } .result-meta { display: flex; justify-content: space-between; font-size: 11px; color: var(--text-muted-soft); } /* --- 即時句子統計(字數/詞數/長度比例) --- */ .result-stats { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 11px; color: var(--text-muted-soft); } .badge-warn { background: rgba(232, 165, 90, 0.18); color: #9a6a24; } :root[data-theme="dark"] .badge-warn { color: var(--color-accent-amber); } /* --- 測試集 BLEU/chrF 品質徽章(第二層,預設隱藏,由 quality-toggle 開啟) --- */ .results-toolbar { display: flex; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-sm); } .quality-toggle { font-family: var(--font-body); font-size: 12px; font-weight: 600; color: var(--color-primary); background: var(--bg-elevated); border: 1px solid var(--border-hairline); border-radius: var(--radius-pill); padding: 6px 14px; cursor: pointer; transition: background 0.15s ease, border-color 0.15s ease; } .quality-toggle:hover { border-color: var(--color-primary); } .quality-toggle.is-active { background: rgba(199, 106, 74, 0.12); border-color: var(--color-primary); } .quality-toggle:disabled { opacity: 0.6; cursor: default; } .quality-toggle-hint { font-size: 11px; color: var(--text-muted-soft); } .engine-quality { display: none; font-size: 11px; color: var(--text-muted); margin-top: 2px; } body.show-quality-badges .engine-quality { display: block; } .engine-quality .quality-figure { font-weight: 600; color: var(--text-primary); } .rag-examples { border-top: 1px solid var(--border-hairline); padding-top: var(--space-sm); } .rag-examples summary { cursor: pointer; font-size: 12px; font-weight: 500; color: var(--text-muted); list-style: none; display: flex; align-items: center; gap: 4px; } .rag-examples summary::-webkit-details-marker { display: none; } .rag-examples summary::before { content: "▸"; font-size: 10px; transition: transform 0.15s ease; } .rag-examples[open] summary::before { transform: rotate(90deg); } .rag-example-item { margin-top: var(--space-xs); padding: var(--space-xs) var(--space-sm); background: var(--bg-surface-soft); border-radius: var(--radius-sm); font-size: 12px; line-height: 1.6; } .rag-example-item + .rag-example-item { margin-top: 6px; } .rag-example-item .rag-line { display: block; } .rag-example-item .rag-raw { font-family: var(--font-lang); font-size: 13px; color: var(--text-primary); } .rag-example-item .rag-cn { color: var(--text-muted); } /* --- Skeleton loading --- */ .result-card.is-loading .result-output { color: transparent; position: relative; overflow: hidden; background: var(--bg-surface-soft); } .result-card.is-loading .result-output::after { content: ""; position: absolute; inset: 0; background: linear-gradient( 90deg, transparent, rgba(255, 255, 255, 0.35), transparent ); animation: shimmer 1.3s infinite; } :root[data-theme="dark"] .result-card.is-loading .result-output::after { background: linear-gradient( 90deg, transparent, rgba(255, 255, 255, 0.06), transparent ); } @keyframes shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } } /* ============================== Dashboard (品質儀表板) ============================== 顏色用 dataviz skill 的預設驗證categorical palette 前三個 slot(blue/orange/ aqua),這三個 slot 本身就通過 all-pairs CVD 檢查(見 skill 的 references/palette.md),固定順序對應 v1/v2/v7,不隨排序/篩選變動。 */ .viz-root { --series-v1: #2a78d6; /* blue */ --series-v2: #eb6834; /* orange */ --series-v7: #1baf7a; /* aqua */ } :root[data-theme="dark"] .viz-root { --series-v1: #3987e5; --series-v2: #d95926; --series-v7: #199e70; } @media (prefers-color-scheme: dark) { :root:where(:not([data-theme="light"])) .viz-root { --series-v1: #3987e5; --series-v2: #d95926; --series-v7: #199e70; } } .dashboard-section { padding: 0 var(--space-lg); max-width: 1280px; margin: 0 auto var(--space-lg); } .dashboard-inner { background: var(--bg-elevated); border: 1px solid var(--border-hairline); border-radius: var(--radius-lg); box-shadow: var(--shadow-card); overflow: hidden; } .dashboard-toggle { width: 100%; display: flex; align-items: center; gap: var(--space-sm); background: none; border: none; padding: var(--space-md); font-family: var(--font-body); font-size: 14px; font-weight: 600; color: var(--text-primary); cursor: pointer; text-align: left; } .dashboard-toggle-hint { font-size: 12px; font-weight: 400; color: var(--text-muted-soft); flex: 1; } .dashboard-toggle-arrow { transition: transform 0.15s ease; color: var(--text-muted); } .dashboard-toggle[aria-expanded="true"] .dashboard-toggle-arrow { transform: rotate(180deg); } .dashboard-body { padding: 0 var(--space-md) var(--space-md); border-top: 1px solid var(--border-hairline); } .dashboard-toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-sm); flex-wrap: wrap; padding-top: var(--space-md); } .dashboard-table-toggle { font-family: var(--font-body); font-size: 12px; font-weight: 500; color: var(--text-muted); background: none; border: 1px solid var(--border-hairline); border-radius: var(--radius-pill); padding: 5px 12px; cursor: pointer; } .dashboard-table-toggle:hover { color: var(--text-primary); border-color: var(--color-primary); } .dash-legend { display: flex; gap: var(--space-md); flex-wrap: wrap; padding: var(--space-sm) 2px 0; } .dash-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-body); } .dash-legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; } .dashboard-panels { display: flex; gap: var(--space-lg); flex-wrap: wrap; padding-top: var(--space-md); } .dash-panel { flex: 1; min-width: 280px; } .dash-panel-title { font-size: 13px; font-weight: 600; color: var(--text-primary); margin: 0 0 var(--space-sm); } .dash-chart { display: flex; gap: var(--space-lg); align-items: flex-end; height: 200px; padding: 0 4px; border-bottom: 1px solid var(--border-hairline); } .dash-group { display: flex; flex-direction: column; align-items: center; flex: 1; height: 100%; } .dash-bars { display: flex; align-items: flex-end; justify-content: center; gap: 6px; flex: 1; width: 100%; } .dash-bar { width: 26px; border-radius: 4px 4px 0 0; position: relative; min-height: 2px; cursor: default; } .dash-bar-value { position: absolute; top: -18px; left: 50%; transform: translateX(-50%); font-size: 11px; font-weight: 600; color: var(--text-body); white-space: nowrap; font-variant-numeric: tabular-nums; } .dash-bar[data-tooltip]:hover::after { content: attr(data-tooltip); position: absolute; bottom: calc(100% + 22px); left: 50%; transform: translateX(-50%); background: var(--color-ink); color: var(--color-on-dark); font-size: 11px; font-weight: 500; white-space: nowrap; padding: 4px 8px; border-radius: var(--radius-sm); z-index: 5; pointer-events: none; } :root[data-theme="dark"] .dash-bar[data-tooltip]:hover::after { background: var(--color-surface-dark-elevated); border: 1px solid var(--border-hairline); } .dash-group-label { margin-top: var(--space-xs); font-size: 12px; color: var(--text-muted); text-align: center; } .dashboard-table-wrap { padding-top: var(--space-md); display: flex; gap: var(--space-lg); flex-wrap: wrap; overflow-x: auto; } .dashboard-table { flex: 1; min-width: 280px; border-collapse: collapse; font-size: 13px; } .dashboard-table caption { text-align: left; font-size: 13px; font-weight: 700; color: var(--text-primary); padding-bottom: var(--space-sm); } .dashboard-table th, .dashboard-table td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--border-hairline); white-space: nowrap; font-variant-numeric: tabular-nums; } .dashboard-table th { color: var(--text-muted); font-weight: 500; font-size: 12px; } .dashboard-table td.dashboard-table-domain { color: var(--text-muted); font-weight: 600; vertical-align: top; border-right: 1px solid var(--border-hairline); } .dashboard-table-n { display: block; font-size: 11px; font-weight: 400; color: var(--text-muted-soft); } .dashboard-table td.is-best { color: #1c5cab; font-weight: 700; } :root[data-theme="dark"] .dashboard-table td.is-best { color: #6da7ec; } .dashboard-methodology { margin: var(--space-md) 2px 0; font-size: 11px; color: var(--text-muted-soft); line-height: 1.6; } @media (max-width: 700px) { .dashboard-panels { flex-direction: column; } } /* ============================== Footer ============================== */ .footer { text-align: center; padding: var(--space-lg); color: var(--text-muted-soft); font-size: 12px; border-top: 1px solid var(--border-hairline); }