diff --git a/docs/plans/2026-06-11-001-feat-operator-console-clean-review-ui-plan.md b/docs/plans/2026-06-11-001-feat-operator-console-clean-review-ui-plan.md new file mode 100644 index 0000000..a1e5e35 --- /dev/null +++ b/docs/plans/2026-06-11-001-feat-operator-console-clean-review-ui-plan.md @@ -0,0 +1,52 @@ +--- +status: active +created: 2026-06-11 +type: quality +title: Operator console clean review UI + Pretendard font plan +spec: docs/superpowers/specs/2026-06-11-operator-console-clean-review-ui-design.md +--- + +# Operator Console Clean Review UI Plan + +목표: 전체적 UI 점검 및 폰트 변경, 깔끔하게 잘 떨어지는 심사 프로그램의 목적에 맞게 개선. + +## Phase 0 — Baseline (done) + +- [x] 전 뷰 데스크톱/모바일 스크린샷 + 오버플로 감사 (`data/logs/ui-font-baseline-*`) +- [x] 정적 테스트 베이스라인: `test_workbench_shell_exposes_all_internal_operator_views` 실패 + (product-purpose 블록/CSS 삭제됨), index.html BOM 유입 확인 +- [x] Pretendard Variable woff2 v1.3.9 오프라인 번들 확보 + (`web/operator-gui/assets/fonts/PretendardVariable.woff2`) + +## Phase 1 — Font integration + +- [ ] `styles.css` 상단에 `@font-face` (Pretendard Variable, weight 45 920, swap) +- [ ] `--font-ui`, `--font-stamp` 토큰을 Pretendard 스택으로 교체 (mono 유지) +- [ ] `index.html`에 woff2 preload 링크 추가, BOM 제거 +- [ ] 점수/쿼터/시간 표기에 `font-variant-numeric: tabular-nums` + +## Phase 2 — Clean re-skin (token-driven) + +- [ ] `:root` 팔레트를 종이톤 → 중성 쿨톤으로 교체 (spec 3.2) +- [ ] body 노이즈 텍스처 + radial-gradient 제거 (플랫 서피스) +- [ ] :root 밖 하드코딩 색상(37 hex, 36 rgba) 중 종이톤 잔재 일괄 정리 +- [ ] 그림자/포커스 링을 쿨톤으로 정돈 +- [ ] 모든 테스트 계약 문자열 보존 (spec 3.4 목록) + +## Phase 3 — Contract restoration + +- [ ] top-bar에 `product-purpose` 블록 복원 (HTML + CSS) +- [ ] 큐 오버플로 미세 정리: 데스크톱 `strong`, 모바일 `provider-chip` + +## Phase 4 — Verification (사용자 반응성 검토) + +- [ ] `pytest tests/operator_gui` 전체 통과 +- [ ] 라이브 서버 Playwright 감사: 8개 뷰 × 데스크톱/모바일, docW <= vw +- [ ] `data/logs/ui-overhaul-final-results.json` + 계약 스크린샷 8종 재생성 +- [ ] Pretendard 적용 여부 런타임 확인 (document.fonts) +- [ ] 오프라인 검증: GUI 내 외부 fetch 참조 0건 +- [ ] `pytest` 전체 회귀 + +## Phase 5 — Commit + +- [ ] 디자인 문서 + 구현 + 산출물 커밋 (feat/chore 분리 없이 단일 feat 커밋) diff --git a/docs/superpowers/specs/2026-06-11-operator-console-clean-review-ui-design.md b/docs/superpowers/specs/2026-06-11-operator-console-clean-review-ui-design.md new file mode 100644 index 0000000..e584a30 --- /dev/null +++ b/docs/superpowers/specs/2026-06-11-operator-console-clean-review-ui-design.md @@ -0,0 +1,114 @@ +# Operator Console Clean Review UI — Design Spec + +- Date: 2026-06-11 +- Status: approved (autonomous goal session) +- Goal: 전체적 UI 점검 및 폰트 변경, 깔끔하게 잘 떨어지는 심사 프로그램의 목적에 맞게 개선 + +## 1. Problem + +The current working-tree restyle ("Forensic Dossier") gives the operator console a +vintage paper-document look: beige paper background with an SVG noise texture, +ochre/teal dual accents, a "stamp" display font (Bahnschrift), and warm hairlines. +That direction conflicts with the product's purpose — a fast, trustworthy, +**clean review instrument** an operator stares at for hours. + +Additional defects found during the baseline audit (2026-06-11): + +- `tests/operator_gui/test_static_workbench.py::test_workbench_shell_exposes_all_internal_operator_views` + fails: the uncommitted change deleted the `product-purpose` top-bar block and its CSS. +- `index.html` gained a UTF-8 BOM (``). +- Typography relies on Malgun Gothic, which renders Korean UI text loosely and + inconsistently across weights. +- Minor horizontal overflow offenders: `strong` inside desktop queue rows, + `span.provider-chip` on mobile queue (inside the intentionally scrollable table shell). + +Baseline artifacts: `data/logs/ui-font-baseline-*.png`, `data/logs/ui-font-baseline-results.json`. + +## 2. Approaches Considered + +1. **Token re-skin (chosen).** Keep the stylesheet's structure, selectors, and all + test-contract strings; replace the design tokens (`:root`), the body texture, + the font stack, and sweep hardcoded decorative colors. Lowest risk: every + class hook and fixed contract string (queue grid template, audit widths, + floating panel geometry, media queries) survives untouched. +2. Full stylesheet rewrite. Cleanest end state but high risk of dropping one of + the ~30 fixed contract strings the static tests pin; harder to review. +3. Revert to the committed styles and start over. Discards legitimate layout + work already in the working tree (queue grid, floating decision panel). + +## 3. Design Decisions + +### 3.1 Typography (폰트 변경) + +- Bundle **Pretendard Variable** (`web/operator-gui/assets/fonts/PretendardVariable.woff2`, + v1.3.9, 2.06 MB) — downloaded at build time, served locally. **No CDN reference** + at runtime (air-gapped rule). +- `@font-face` with `font-display: swap`, weight range 45–920. +- Token changes: + - `--font-ui: "Pretendard Variable", Pretendard, "Malgun Gothic", "Apple SD Gothic Neo", "Segoe UI", system-ui, sans-serif` + - `--font-stamp` (labels, IDs, numeric stamps): same Pretendard stack — + differentiation now comes from weight (650–750), size, and `letter-spacing`, + not from a second display face. Bahnschrift is dropped. + - `--font-mono` unchanged (Cascadia Mono / Consolas) for hashes, IDs, code. +- `index.html` gets `` + for the woff2. +- Numeric UI (scores, quotas, timestamps) uses `font-variant-numeric: tabular-nums`. + +### 3.2 Surface and color (깔끔하게 잘 떨어지는 톤) + +- Remove the SVG noise texture and both radial gradient washes from `body` — + flat, calm surface. +- Palette shifts from warm paper to neutral cool: + - `--paper` `#f3f5f7` (workspace background), `--paper-2` `#e9edf0` + - `--card` `#ffffff`, `--card-raised` `#ffffff`, `--card-sunk` `#f6f8fa` + - Ink scale: `--ink #1a2128`, `--ink-soft #51606b`, `--ink-faint #8794a1` + - Hairlines: cool grays (`#dde3e8` / `#e7ecf0` / `#b9c4cd`) +- Single primary accent: deep teal-blue (`--teal #1f6f8b` family). The ochre + family stays defined (components reference it) but is re-tuned toward a + restrained amber used only for "hold/attention" semantics, not branding. +- Risk semantics keep their class names and stay high-contrast: + red=high, amber=medium, green=low/approve, plus failed/pending neutrals. +- Shadows get smaller and cooler; radii stay (6/9/14px). No `linear-gradient` + (test contract), and the remaining radial gradients are removed for flatness. +- The dark nav rail stays dark (orientation anchor) but moves to a neutral + slate so the single accent color reads clearly. + +### 3.3 Contract restorations + +- Re-add the `product-purpose` block to the top bar (`class="product-purpose"`, + `aria-label="제품 목적"`, copy: "이미지 저작권 위험 심사" / + "제출 이미지, 외부 검색 근거, 내부 기준 DB를 한 화면에서 검토합니다.") and its + `.product-purpose` CSS, styled to fit the clean top bar. +- Remove the BOM from `index.html`. +- The deleted global-search form and operator chip stay deleted (no test + references them; the queue has its own search field). + +### 3.4 Explicitly preserved test-contract strings in `styles.css` + +`grid-template-columns: 28px 64px minmax(104px, 0.68fr) 72px minmax(126px, 0.58fr) minmax(360px, 1.5fr) 82px 76px 90px`, +`--audit-object-width: 24%`, `.audit-table th:nth-child(4)/(5)` with +`width: var(--audit-object-width)`, `.floating-decision-panel` with +`position: fixed` / `bottom: 24px` / `padding-right: 334px` / +`padding-bottom: 260px`, `data-workbench-panel="evidence"` selector, +`@media (max-width: 980px)`, `@media (max-width: 680px)`, `:focus-visible`, +`.risk-high/.risk-medium/.risk-low/.risk-failed`, +`.source-naver/.source-google/.source-llm/.source-internal`, +`.queue-row td:nth-child(5)/(6)`, `white-space: nowrap`, `flex-wrap: nowrap`, +`.queue-provider-strip`, `.coverage-tabs`/`.coverage-tab`, no `linear-gradient`, +no `orb`, obsolete selectors (`.coverage-main`, `.coverage-badges`, +`.coverage-provider-grid`, `.coverage-mini-line`, `.queue-table th:nth-child`) +stay absent. + +## 4. Verification Plan (사용자 반응성 검토) + +1. `node --check` on touched JS (none expected) and full + `pytest tests/operator_gui` static contracts. +2. Live-server Playwright audit at 1440×900 and 390×844 across all 8 views: + zero horizontal document overflow (`docW <= vw`), regenerate + `data/logs/ui-overhaul-final-results.json` and the 8 contract screenshots. +3. Interaction smoke: existing `test_browser_smoke.py` (suggested-query fill, + real upload flow) must pass against the restyled DOM. +4. Visual review of screenshots: font rendering (Pretendard active), flat + surfaces, risk colors legible, floating decision panel unobstructed. +5. Offline check: grep the GUI for `http://`/`https://` font or CSS fetches — + none allowed. diff --git a/web/operator-gui/app.js b/web/operator-gui/app.js index 676a33f..f4b4fa4 100644 --- a/web/operator-gui/app.js +++ b/web/operator-gui/app.js @@ -3239,36 +3239,6 @@ async function emergencyDisableExternalProviders() { -function handleGlobalSearch(event) { - - event.preventDefault(); - - const query = document.getElementById("global-search-input").value.trim().toLowerCase(); - - if (!query) return; - - const match = submissions.find( - - (submission) => submission.id.toLowerCase().includes(query) || submission.title.toLowerCase().includes(query), - - ); - - if (match) { - - selectCase(match.id); - - return; - - } - - switchView("knowledge"); - - renderAll(); - -} - - - function bindEvents() { document.querySelectorAll(".nav-button").forEach((button) => { @@ -3411,8 +3381,6 @@ function bindEvents() { document.getElementById("knowledge-image").addEventListener("change", updateKnowledgeImageName); - document.getElementById("global-search-form").addEventListener("submit", handleGlobalSearch); - document.getElementById("reload-submissions").addEventListener("click", reloadSubmissions); document.getElementById("submission-image").addEventListener("change", updateSubmissionImageName); document.getElementById("upload-submission-image").addEventListener("click", uploadSubmissionImage); diff --git a/web/operator-gui/assets/fonts/PretendardVariable.woff2 b/web/operator-gui/assets/fonts/PretendardVariable.woff2 new file mode 100644 index 0000000..49c54b5 Binary files /dev/null and b/web/operator-gui/assets/fonts/PretendardVariable.woff2 differ diff --git a/web/operator-gui/index.html b/web/operator-gui/index.html index 68e5500..83c8c25 100644 --- a/web/operator-gui/index.html +++ b/web/operator-gui/index.html @@ -1,9 +1,10 @@ - + 권리 검수 콘솔 + @@ -58,21 +59,13 @@
-
이미지 저작권 위험 심사 제출 이미지, 외부 검색 근거, 내부 기준 DB를 한 화면에서 검토합니다.
-
-
- 운영자 - rights.ops -
+
diff --git a/web/operator-gui/styles.css b/web/operator-gui/styles.css index 5d5afa4..4187804 100644 --- a/web/operator-gui/styles.css +++ b/web/operator-gui/styles.css @@ -1,20 +1,85 @@ +/* ===================================================================== + 권리 검수 콘솔 — clean review instrument + Flat, neutral, single-accent restyle for long review sessions. + Air-gapped safe: Pretendard Variable is bundled locally (no CDN). + CSS-only: every app.js class hook and grid template is preserved. + ===================================================================== */ + +@font-face { + font-family: "Pretendard Variable"; + src: url("assets/fonts/PretendardVariable.woff2") format("woff2-variations"); + font-weight: 45 920; + font-style: normal; + font-display: swap; +} + :root { - --surface: #f4f1ea; - --surface-strong: #ffffff; - --surface-muted: #e8e3d8; - --ink: #172124; - --ink-soft: #4a5558; - --line: #cfc6b7; - --line-strong: #8b806f; - --accent: #2f6272; - --accent-strong: #234a56; - --green: #2f6d50; - --amber: #9a6a13; - --red: #a63f35; - --blue: #355f92; - --violet: #5d5478; - --radius: 8px; - --shadow: 0 14px 32px rgba(35, 42, 39, 0.12); + /* --- Type ---------------------------------------------------------- */ + --font-ui: "Pretendard Variable", Pretendard, "Malgun Gothic", "Apple SD Gothic Neo", "Segoe UI", system-ui, sans-serif; + --font-stamp: "Pretendard Variable", Pretendard, "Malgun Gothic", "Segoe UI Semibold", sans-serif; + --font-mono: "Cascadia Mono", "Consolas", ui-monospace, "DejaVu Sans Mono", monospace; + + /* --- Surfaces (neutral cool) --------------------------------------- */ + --paper: #f3f5f7; + --paper-2: #e9edf0; + --card: #fcfdfe; + --card-raised: #ffffff; + --card-sunk: #f4f6f8; + + --ink: #1a2128; + --ink-soft: #51606b; + --ink-faint: #8794a1; + + /* --- Hairlines ----------------------------------------------------- */ + --hair: #dde3e8; + --hair-soft: #e7ecf0; + --hair-strong: #b9c4cd; + + /* --- Brand / accents ----------------------------------------------- */ + --teal: #1f6f8b; + --teal-deep: #14495c; + --teal-tint: #e7f1f5; + --teal-line: #a9cdda; + + --ochre: #b07d18; + --ochre-deep: #8a5f0d; + --ochre-tint: #faf2dd; + --ochre-line: #e2c887; + + /* --- Dark rail ----------------------------------------------------- */ + --rail-top: #222b33; + --rail-bot: #1a232b; + --rail-edge: #10171d; + + /* --- Risk / signal semantics --------------------------------------- */ + --red: #b03a2e; --red-tint: #fceae7; --red-line: #e4aca3; + --amber: #93620c; --amber-tint: #faf1d8; --amber-line: #e0c581; + --green: #25684a; --green-tint: #e7f3ec; --green-line: #a6d3b9; + --violet: #564d75; --violet-tint: #efebf6;--violet-line: #c7bbd9; + --blue: #2c5a8c; --blue-tint: #e8f0fa; --blue-line: #b1c6e2; + + /* --- Geometry & elevation ------------------------------------------ */ + --r-sm: 6px; + --radius: 9px; + --r-lg: 14px; + --r-pill: 999px; + + --shadow-sm: 0 1px 2px rgba(23, 32, 41, 0.06); + --shadow: 0 1px 3px rgba(23, 32, 41, 0.05), 0 10px 24px -18px rgba(23, 32, 41, 0.25); + --shadow-lg: 0 18px 48px -20px rgba(16, 28, 38, 0.35), 0 3px 8px rgba(23, 32, 41, 0.05); + --press: inset 0 1px 0 rgba(255, 255, 255, 0.55); + + --ease: cubic-bezier(0.22, 0.61, 0.36, 1); + + /* --- Back-compat aliases (legacy variable names still referenced) -- */ + --surface: var(--paper); + --surface-strong: var(--card-raised); + --surface-muted: var(--paper-2); + --line: var(--hair); + --line-strong: var(--hair-strong); + --accent: var(--teal); + --accent-strong: var(--teal-deep); + --audit-object-width: 24%; } @@ -24,16 +89,24 @@ html { min-height: 100%; - background: var(--surface); + background: var(--paper); color: var(--ink); - font-family: "Segoe UI", "Malgun Gothic", "Apple SD Gothic Neo", sans-serif; + font-family: var(--font-ui); letter-spacing: 0; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; } body { min-height: 100vh; margin: 0; - background: var(--surface); + color: var(--ink); + background-color: var(--paper); + font-variant-numeric: tabular-nums; +} + +::selection { + background: rgba(31, 111, 139, 0.22); color: var(--ink); } @@ -54,12 +127,32 @@ input:disabled, select:disabled, textarea:disabled { cursor: not-allowed; - opacity: 0.62; + opacity: 0.55; } :focus-visible { - outline: 3px solid #d6a33a; + outline: 2px solid var(--teal); outline-offset: 2px; + border-radius: 4px; +} + +/* Themed thin scrollbars — air-gap safe */ +* { + scrollbar-width: thin; + scrollbar-color: var(--hair-strong) transparent; +} +*::-webkit-scrollbar { + width: 11px; + height: 11px; +} +*::-webkit-scrollbar-thumb { + border: 3px solid transparent; + border-radius: 999px; + background-clip: padding-box; + background-color: var(--hair-strong); +} +*::-webkit-scrollbar-thumb:hover { + background-color: var(--ink-faint); } .asset-preload { @@ -79,22 +172,26 @@ textarea:disabled { } .app-shell { + position: relative; + z-index: 1; display: grid; grid-template-columns: 248px minmax(0, 1fr); min-height: 100vh; } +/* ====================== Navigation rail ============================== */ .nav-rail { position: sticky; top: 0; display: flex; flex-direction: column; - gap: 8px; + gap: 6px; height: 100vh; - padding: 18px 14px; - background: #26343a; - color: #f8f4ec; - border-right: 1px solid #1a2529; + padding: 20px 14px 16px; + background: var(--rail-top); + color: #e8edf2; + border-right: 1px solid var(--rail-edge); + box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.04); } .brand-block { @@ -102,8 +199,9 @@ textarea:disabled { align-items: center; gap: 12px; min-height: 58px; - margin-bottom: 8px; - padding: 0 6px; + margin-bottom: 14px; + padding: 0 6px 14px; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); } .brand-block strong, @@ -114,44 +212,72 @@ textarea:disabled { .brand-block strong { font-size: 17px; line-height: 1.2; + letter-spacing: -0.01em; } .brand-block div span { - margin-top: 3px; - color: #c6d0ce; - font-size: 12px; + margin-top: 4px; + color: #9fb0bd; + font-family: var(--font-stamp); + font-size: 11px; + letter-spacing: 0.12em; + text-transform: uppercase; } .brand-mark { display: inline-grid; place-items: center; - width: 42px; - height: 42px; - border-radius: 8px; - background: #d6a33a; - color: #1b2326; - font-weight: 800; + width: 44px; + height: 44px; + border-radius: 10px; + background: var(--teal); + color: #fff; + font-family: var(--font-stamp); + font-weight: 750; + font-size: 17px; + letter-spacing: 0.04em; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18); } .nav-button { + position: relative; display: grid; grid-template-columns: 28px minmax(0, 1fr); align-items: center; - gap: 10px; + gap: 11px; width: 100%; - min-height: 42px; - padding: 9px 10px; + min-height: 44px; + padding: 9px 11px; border: 1px solid transparent; - border-radius: 8px; + border-radius: var(--radius); background: transparent; - color: #ecf0ed; + color: #d3dce4; text-align: left; + font-size: 14px; + transition: background 160ms var(--ease), color 160ms var(--ease), transform 120ms var(--ease); +} + +.nav-button:hover { + background: rgba(255, 255, 255, 0.07); + color: #fff; } -.nav-button:hover, .nav-button.active { - background: #f4f1ea; - color: #182225; + background: var(--card-raised); + color: var(--ink); + box-shadow: var(--shadow-sm); +} + +.nav-button.active::before { + content: ""; + position: absolute; + left: -14px; + top: 50%; + width: 4px; + height: 24px; + transform: translateY(-50%); + border-radius: 0 4px 4px 0; + background: var(--teal); } .nav-button span:first-child { @@ -161,8 +287,17 @@ textarea:disabled { height: 28px; border: 1px solid currentColor; border-radius: 7px; + font-family: var(--font-stamp); font-size: 12px; font-weight: 700; + opacity: 0.85; +} + +.nav-button.active span:first-child { + border-color: var(--teal); + background: var(--teal); + color: #fff; + opacity: 1; } .nav-button span:last-child { @@ -175,90 +310,61 @@ textarea:disabled { align-items: center; gap: 8px; margin-top: auto; - padding: 10px; - border: 1px solid rgba(255, 255, 255, 0.16); - border-radius: 8px; - color: #dfe6e3; - font-size: 13px; + padding: 11px 12px; + border: 1px solid rgba(255, 255, 255, 0.14); + border-radius: var(--radius); + color: #d3dce4; + font-family: var(--font-stamp); + font-size: 12px; + letter-spacing: 0.06em; + text-transform: uppercase; } .status-dot { + position: relative; width: 9px; height: 9px; border-radius: 50%; - background: var(--line-strong); + background: var(--hair-strong); } .status-dot.ok { - background: #74ba86; + background: #6fc188; + box-shadow: 0 0 0 3px rgba(111, 193, 136, 0.22); } .status-dot.warn { - background: #d8a842; + background: #e0b24c; + box-shadow: 0 0 0 3px rgba(224, 178, 76, 0.22); } .status-dot.bad { - background: #dc6a5f; + background: #e07060; + box-shadow: 0 0 0 3px rgba(224, 112, 96, 0.22); } .workspace { min-width: 0; } +/* ========================= Top bar ================================== */ .top-bar { position: sticky; top: 0; z-index: 10; display: grid; - grid-template-columns: minmax(240px, 0.9fr) minmax(280px, 1.1fr) auto auto; - align-items: center; + grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr) minmax(0, 0.85fr) minmax(0, 2fr); + align-items: stretch; gap: 12px; - min-height: 68px; - padding: 12px 22px; - background: rgba(244, 241, 234, 0.96); - border-bottom: 1px solid var(--line); + min-height: 56px; + padding: 10px 22px; + background: rgba(255, 255, 255, 0.86); + backdrop-filter: blur(12px) saturate(1.05); + -webkit-backdrop-filter: blur(12px) saturate(1.05); + border-bottom: 1px solid var(--hair); + box-shadow: 0 8px 20px -20px rgba(23, 32, 41, 0.5); } -.global-search { - order: 1; -} - -.queue-health { - order: 3; -} - -.provider-pulse { - order: 4; -} - -.operator-chip { - order: 5; -} - -.product-purpose { - order: 2; - display: grid; - gap: 2px; - min-height: 42px; - padding: 7px 12px; - border: 1px solid #dacdb8; - border-radius: 8px; - background: #fffaf0; - color: var(--ink); -} - -.product-purpose strong { - font-size: 13px; - line-height: 1.2; -} - -.product-purpose span { - color: var(--ink-soft); - font-size: 12px; - line-height: 1.35; -} - -.global-search input, .toolbar input, .toolbar select, .manual-query-form input, @@ -273,49 +379,93 @@ textarea:disabled { .knowledge-form textarea, .memo-field textarea { width: 100%; - border: 1px solid var(--line); - border-radius: 8px; - background: var(--surface-strong); + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-raised); color: var(--ink); + box-shadow: inset 0 1px 2px rgba(20, 30, 28, 0.04); + transition: border-color 140ms var(--ease), box-shadow 140ms var(--ease); } -.global-search input { - min-height: 42px; - padding: 0 14px; +.toolbar input:hover, +.toolbar select:hover, +.memo-field textarea:hover, +.knowledge-form input:hover, +.knowledge-form select:hover, +.knowledge-form textarea:hover, +.manual-query-form input:hover, +.candidate-collection-form input:hover, +.collection-promotion-form input:hover, +.collection-promotion-form textarea:hover { + border-color: var(--hair-strong); +} + +.toolbar input:focus, +.toolbar select:focus, +.manual-query-form input:focus, +.manual-query-form select:focus, +.candidate-collection-form input:focus, +.candidate-collection-form select:focus, +.collection-promotion-form input:focus, +.collection-promotion-form select:focus, +.collection-promotion-form textarea:focus, +.knowledge-form input:focus, +.knowledge-form select:focus, +.knowledge-form textarea:focus, +.memo-field textarea:focus { + outline: none; + border-color: var(--teal); + box-shadow: inset 0 1px 2px rgba(20, 30, 28, 0.04), 0 0 0 3px rgba(34, 96, 114, 0.16); +} + +.product-purpose { + display: grid; + align-content: center; + gap: 2px; + min-width: 0; + min-height: 40px; + padding: 5px 11px 5px 12px; + border-left: 3px solid var(--teal); + border-radius: 4px; + background: var(--teal-tint); +} + +.product-purpose strong { + color: var(--teal-deep); + font-size: 13px; + font-weight: 750; + letter-spacing: -0.01em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.product-purpose span { + color: var(--ink-soft); + font-size: 11.5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .queue-health, .provider-pulse, -.coverage-tabs, -.operator-chip { +.coverage-tabs { display: flex; align-items: center; gap: 8px; - min-height: 36px; - padding: 7px 10px; - border: 1px solid var(--line); - border-radius: 8px; - background: var(--surface-strong); + min-height: 40px; + padding: 7px 11px; + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-raised); color: var(--ink-soft); font-size: 13px; white-space: nowrap; -} - -.operator-chip { - flex-direction: column; - align-items: flex-start; - gap: 0; - line-height: 1.2; -} - -.operator-chip strong { - color: var(--ink); + box-shadow: var(--shadow-sm); } .coverage-tabs { - order: 5; - grid-column: 1 / -1; - display: flex; align-items: stretch; gap: 0; min-width: 0; @@ -330,14 +480,19 @@ textarea:disabled { align-items: center; gap: 8px; min-width: 0; - min-height: 36px; - padding: 6px 10px; + min-height: 38px; + padding: 6px 12px; border: 0; - border-right: 1px solid var(--line); + border-right: 1px solid var(--hair); background: transparent; color: var(--ink-soft); font-size: 12px; text-align: left; + transition: background 140ms var(--ease), color 140ms var(--ease); +} + +.coverage-tab:hover { + background: var(--card-sunk); } .coverage-tab:last-child { @@ -353,16 +508,17 @@ textarea:disabled { .coverage-tab strong { color: var(--ink); + font-family: var(--font-mono); font-size: 13px; } .coverage-tab.active { - background: #26343a; - color: #ffffff; + background: var(--teal-deep); + color: #fff; } .coverage-tab.active strong { - color: #ffffff; + color: #fff; } .coverage-tab.ok strong { @@ -380,11 +536,12 @@ textarea:disabled { .coverage-tab.active.ok strong, .coverage-tab.active.warn strong, .coverage-tab.active.bad strong { - color: #ffffff; + color: #fff; } +/* ========================= Main surface ============================= */ .main-surface { - padding: 22px; + padding: 24px 24px 30px; } .view { @@ -393,6 +550,22 @@ textarea:disabled { .view.active { display: block; + /* fill-mode `backwards` (not `both`): the entrance animation must NOT persist + its end-state transform. A retained identity transform on .view.active would + establish a containing block and break `position: fixed` on the floating + decision panel, dropping it to the bottom of the page. */ + animation: viewRise 320ms var(--ease) backwards; +} + +@keyframes viewRise { + from { + opacity: 0; + transform: translateY(9px); + } + to { + opacity: 1; + transform: none; + } } .section-header { @@ -400,89 +573,83 @@ textarea:disabled { align-items: flex-end; justify-content: space-between; gap: 16px; - margin-bottom: 16px; + margin-bottom: 18px; + padding-bottom: 14px; + border-bottom: 1px solid var(--hair); } .section-header h1 { margin: 0; - font-size: 24px; - line-height: 1.2; + font-size: 25px; + line-height: 1.15; + letter-spacing: -0.02em; + font-weight: 800; } .queue-intent { max-width: 560px; - margin: 6px 0 0; + margin: 7px 0 0; color: var(--ink-soft); font-size: 13px; line-height: 1.55; } -.workbench-tabs { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin: 0 0 14px; - border-bottom: 1px solid var(--line); -} - -.workbench-tabs button { - min-height: 38px; - padding: 0 14px; - border: 1px solid transparent; - border-bottom: 0; - border-radius: 8px 8px 0 0; - background: transparent; - color: var(--ink-soft); - font-weight: 800; -} - -.workbench-tabs button.active { - border-color: var(--line); - background: var(--surface-strong); - color: var(--ink); -} - -.workbench-panel { - display: none; -} - -.workbench-panel.active { - display: block; -} - +/* ===================== Tabs (workbench / knowledge) ================= */ +.workbench-tabs, .knowledge-tabs { display: flex; flex-wrap: wrap; - gap: 6px; - margin: 0 0 14px; - border-bottom: 1px solid var(--line); + gap: 4px; + margin: 0 0 16px; + border-bottom: 1px solid var(--hair); } +.workbench-tabs button, .knowledge-tabs button { - min-height: 38px; - padding: 0 14px; - border: 1px solid transparent; - border-bottom: 0; - border-radius: 8px 8px 0 0; + position: relative; + min-height: 40px; + padding: 0 16px; + border: 0; background: transparent; color: var(--ink-soft); - font-weight: 800; + font-weight: 700; + font-size: 14px; + transition: color 140ms var(--ease); } -.knowledge-tabs button.active { - border-color: var(--line); - background: var(--surface-strong); +.workbench-tabs button:hover, +.knowledge-tabs button:hover { color: var(--ink); } +.workbench-tabs button.active, +.knowledge-tabs button.active { + color: var(--teal-deep); +} + +.workbench-tabs button.active::after, +.knowledge-tabs button.active::after { + content: ""; + position: absolute; + left: 12px; + right: 12px; + bottom: -1px; + height: 2.5px; + border-radius: 3px; + background: var(--teal); +} + +.workbench-panel, .knowledge-panel { display: none; } +.workbench-panel.active, .knowledge-panel.active { display: block; } +/* ===================== Queue actions ================================ */ .queue-actions { display: flex; flex-wrap: wrap; @@ -492,31 +659,35 @@ textarea:disabled { .queue-folder-input { display: grid; - gap: 4px; + gap: 5px; min-width: 260px; } .queue-file-input { display: grid; - gap: 4px; + gap: 5px; min-width: 190px; cursor: pointer; } .queue-folder-input span, .queue-file-input span:first-child { - color: var(--ink-soft); - font-size: 12px; - font-weight: 700; + color: var(--ink-faint); + font-family: var(--font-stamp); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.07em; + text-transform: uppercase; } .queue-folder-input input { min-height: 38px; - padding: 0 10px; - border: 1px solid var(--line); - border-radius: 8px; - background: var(--surface-strong); + padding: 0 11px; + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-raised); color: var(--ink); + box-shadow: inset 0 1px 2px rgba(20, 30, 28, 0.04); } .queue-file-input span:last-of-type { @@ -524,11 +695,11 @@ textarea:disabled { align-items: center; min-height: 38px; max-width: 220px; - padding: 0 10px; + padding: 0 11px; overflow: hidden; - border: 1px solid var(--line); - border-radius: 8px; - background: var(--surface-strong); + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-raised); color: var(--ink-soft); font-size: 13px; text-overflow: ellipsis; @@ -554,49 +725,65 @@ textarea:disabled { align-items: center; gap: 8px; min-height: 38px; - margin-bottom: 12px; - padding: 8px 10px; - border: 1px solid var(--line); - border-radius: 8px; - background: #fbfaf6; + margin-bottom: 14px; + padding: 8px 12px; + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card); color: var(--ink-soft); font-size: 13px; } +/* ===================== Operator workflow strip ===================== */ .operator-workflow { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 10px; - margin: 0 0 14px; + gap: 12px; + margin: 0 0 18px; } .operator-workflow article { + position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr); - gap: 3px 10px; - min-height: 82px; - padding: 13px; - border: 1px solid #dacdb8; - border-radius: 12px; - background: #fffaf0; + gap: 4px 12px; + min-height: 84px; + padding: 15px 16px; + border: 1px solid var(--hair); + border-radius: var(--r-lg); + background: var(--card-raised); + box-shadow: var(--shadow-sm); + overflow: hidden; +} + +.operator-workflow article::before { + content: ""; + position: absolute; + inset: 0 auto 0 0; + width: 3px; + background: var(--teal); + opacity: 0.55; } .operator-workflow article span { display: inline-grid; place-items: center; - width: 26px; - height: 26px; - border-radius: 999px; - background: #26343a; - color: #fffaf0; - font-size: 12px; - font-weight: 900; + width: 28px; + height: 28px; + border-radius: var(--r-pill); + background: var(--teal); + color: #fff; + font-family: var(--font-stamp); + font-size: 13px; + font-weight: 700; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18); } .operator-workflow strong { align-self: center; color: var(--ink); font-size: 14px; + letter-spacing: -0.01em; } .operator-workflow p { @@ -604,23 +791,31 @@ textarea:disabled { margin: 0; color: var(--ink-soft); font-size: 12px; - line-height: 1.45; + line-height: 1.5; } .eyebrow { - margin: 0 0 5px; - color: var(--ink-soft); - font-size: 12px; - font-weight: 700; + margin: 0 0 6px; + color: var(--teal); + font-family: var(--font-stamp); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.16em; text-transform: uppercase; } +/* ========================= Toolbar / filters ======================= */ .toolbar { display: grid; grid-template-columns: minmax(340px, 1.6fr) minmax(150px, 0.8fr) minmax(150px, 0.8fr) minmax(140px, 0.7fr) minmax(210px, 1fr); - gap: 10px; + gap: 12px; align-items: end; - margin-bottom: 14px; + margin-bottom: 16px; + padding: 14px; + border: 1px solid var(--hair); + border-radius: var(--r-lg); + background: var(--card); + box-shadow: var(--shadow-sm); } .toolbar label, @@ -630,7 +825,7 @@ textarea:disabled { .knowledge-form label, .memo-field { display: grid; - gap: 5px; + gap: 6px; min-width: 0; } @@ -640,9 +835,12 @@ textarea:disabled { .collection-promotion-form label span, .knowledge-form label span, .memo-field span { - color: var(--ink-soft); - font-size: 12px; - font-weight: 700; + color: var(--ink-faint); + font-family: var(--font-stamp); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.07em; + text-transform: uppercase; } .toolbar input, @@ -656,15 +854,16 @@ textarea:disabled { .knowledge-form input, .knowledge-form select { min-height: 38px; - padding: 0 10px; + padding: 0 11px; } .memo-field textarea, .collection-promotion-form textarea, .knowledge-form textarea { min-height: 114px; - padding: 10px; + padding: 11px; resize: vertical; + line-height: 1.5; } .file-picker { @@ -678,9 +877,9 @@ textarea:disabled { align-items: center; min-height: 38px; padding: 5px; - border: 1px solid var(--line); - border-radius: 8px; - background: var(--surface-strong); + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-raised); } .file-picker-button { @@ -688,16 +887,17 @@ textarea:disabled { align-items: center; justify-content: center; min-height: 28px; - padding: 0 10px; - border-radius: 6px; - background: #2f6f7e; - color: #ffffff; + padding: 0 12px; + border-radius: 5px; + background: var(--teal); + color: #fff; font-size: 13px; - font-weight: 800; + font-weight: 700; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18); } .knowledge-form .file-picker-button { - color: #ffffff; + color: #fff; } #knowledge-image-name { @@ -723,24 +923,33 @@ textarea:disabled { white-space: nowrap; } +/* ========================= Segmented control ======================= */ .segmented { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); min-height: 38px; - border: 1px solid var(--line); - border-radius: 8px; - background: var(--surface-strong); + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-raised); overflow: hidden; + box-shadow: inset 0 1px 2px rgba(20, 30, 28, 0.04); } .segmented button { min-width: 0; border: 0; - border-right: 1px solid var(--line); + border-right: 1px solid var(--hair); background: transparent; color: var(--ink-soft); font-size: 13px; + font-weight: 600; overflow-wrap: anywhere; + transition: background 130ms var(--ease), color 130ms var(--ease); +} + +.segmented button:hover { + background: var(--card-sunk); + color: var(--ink); } .segmented button:last-child { @@ -748,10 +957,11 @@ textarea:disabled { } .segmented button.active { - background: #26343a; - color: #ffffff; + background: var(--teal-deep); + color: #fff; } +/* ============================ Buttons =============================== */ .secondary-action, .primary-action, .danger-outline, @@ -761,8 +971,10 @@ textarea:disabled { .provider-actions button, .row-action { min-height: 36px; - border-radius: 8px; + border-radius: var(--r-sm); font-weight: 700; + transition: transform 110ms var(--ease), box-shadow 140ms var(--ease), + background 140ms var(--ease), border-color 140ms var(--ease), color 140ms var(--ease); } .secondary-action, @@ -770,44 +982,82 @@ textarea:disabled { .quick-actions button, .icon-actions button, .provider-actions button { - border: 1px solid var(--line); - background: var(--surface-strong); + border: 1px solid var(--hair); + background: var(--card-raised); color: var(--ink); + box-shadow: var(--shadow-sm); +} + +.secondary-action:hover, +.row-action:hover, +.quick-actions button:hover, +.icon-actions button:hover, +.provider-actions button:hover { + border-color: var(--hair-strong); + background: var(--card); + transform: translateY(-1px); +} + +.secondary-action:active, +.row-action:active, +.quick-actions button:active, +.icon-actions button:active, +.provider-actions button:active, +.primary-action:active, +.danger-outline:active, +.decision-actions button:active { + transform: translateY(0); + box-shadow: var(--press); } .row-action.active { - border-color: var(--accent); - background: #e7f0f2; - color: var(--accent-strong); + border-color: var(--teal); + background: var(--teal-tint); + color: var(--teal-deep); } .row-action.danger { - border-color: #e0aaa4; - background: #fff0ed; + border-color: var(--red-line); + background: var(--red-tint); color: var(--red); } .primary-action { - border: 1px solid var(--accent-strong); - background: var(--accent); - color: #ffffff; + padding: 0 16px; + border: 1px solid var(--teal-deep); + background: var(--teal); + color: #fff; + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.16) inset, 0 6px 14px -10px rgba(20, 73, 92, 0.7); +} + +.primary-action:hover { + background: var(--teal-deep); + transform: translateY(-1px); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 10px 18px -10px rgba(20, 73, 92, 0.75); } .danger-outline { - border: 1px solid var(--red); - background: #fff8f6; + padding: 0 16px; + border: 1px solid var(--red-line); + background: var(--red-tint); color: var(--red); } +.danger-outline:hover { + border-color: var(--red); + transform: translateY(-1px); +} + +/* ====================== Cards / table shells ======================= */ .table-shell, .pane, .provider-row, .correction-row, .knowledge-row, .audit-row { - border: 1px solid var(--line); - border-radius: 8px; - background: var(--surface-strong); + border: 1px solid var(--hair); + border-radius: var(--r-lg); + background: var(--card-raised); } .table-shell { @@ -849,7 +1099,12 @@ table { .queue-row { min-height: 78px; - border-bottom: 1px solid var(--line); + border-bottom: 1px solid var(--hair-soft); + transition: background 140ms var(--ease), box-shadow 140ms var(--ease); +} + +.queue-grid tbody .queue-row:nth-child(even) { + background: rgba(240, 244, 247, 0.55); } .queue-row td, @@ -899,7 +1154,7 @@ table { width: auto; max-width: 100%; min-height: 32px; - padding: 4px 8px; + padding: 4px 9px; overflow: hidden; white-space: nowrap; } @@ -920,6 +1175,12 @@ table { white-space: nowrap; } +.queue-time-cell { + font-family: var(--font-mono); + font-size: 12px; + color: var(--ink-soft); +} + .queue-row td:nth-child(5), .queue-row td:nth-child(6) { align-self: stretch; @@ -933,9 +1194,6 @@ table { .queue-row td:nth-child(6) { padding-left: 6px; -} - -.queue-row td:nth-child(6) { align-content: center; flex-wrap: nowrap; gap: 3px; @@ -954,7 +1212,7 @@ table { flex: 0 0 auto; min-width: 0; margin: 0; - padding-inline: 5px; + padding-inline: 6px; font-size: 11px; white-space: nowrap; overflow: hidden; @@ -964,7 +1222,7 @@ table { th, td { padding: 10px; - border-bottom: 1px solid var(--line); + border-bottom: 1px solid var(--hair-soft); text-align: left; vertical-align: middle; } @@ -973,10 +1231,14 @@ th { position: sticky; top: 0; z-index: 1; - background: #efe9dd; - color: #3f4b4d; - font-size: 12px; - font-weight: 800; + background: var(--card-sunk); + color: var(--ink-faint); + font-family: var(--font-stamp); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + border-bottom: 1px solid var(--hair); } td { @@ -985,31 +1247,40 @@ td { } tbody tr { - transition: background 140ms ease, box-shadow 140ms ease; + transition: background 140ms var(--ease), box-shadow 140ms var(--ease); } tbody tr:hover { - background: #fbfaf6; + background: var(--teal-tint); +} + +.queue-grid tbody .queue-row:hover { + background: var(--teal-tint); } tbody tr.selected-row { - box-shadow: inset 4px 0 0 var(--accent); + box-shadow: inset 4px 0 0 var(--teal), var(--shadow-sm); + background: #eef6f9; } .thumb { width: 62px; height: 48px; - border: 1px solid var(--line); - border-radius: 6px; - background: #f7f4ee; + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-sunk); object-fit: cover; + box-shadow: var(--shadow-sm); } .submission-id { display: block; - font-weight: 800; min-width: 0; overflow: hidden; + font-family: var(--font-mono); + font-weight: 700; + font-size: 12.5px; + letter-spacing: -0.01em; text-overflow: ellipsis; white-space: nowrap; } @@ -1027,6 +1298,7 @@ tbody tr.selected-row { gap: 4px; } +/* =========================== Pills & chips ========================= */ .score-pill, .risk-badge, .source-chip, @@ -1041,18 +1313,25 @@ tbody tr.selected-row { gap: 5px; max-width: 100%; min-height: 24px; - padding: 3px 8px; - border-radius: 999px; + padding: 3px 9px; + border-radius: var(--r-pill); border: 1px solid transparent; font-size: 12px; - font-weight: 800; + font-weight: 700; line-height: 1.2; overflow-wrap: anywhere; } +.score-pill, +.risk-badge { + font-family: var(--font-mono); + font-weight: 700; + letter-spacing: -0.01em; +} + .risk-high { - border-color: #e0aaa4; - background: #fff0ed; + border-color: var(--red-line); + background: var(--red-tint); color: var(--red); } @@ -1063,14 +1342,14 @@ tbody tr.selected-row { .evidence-status-chip, .watchlist-chip.watchlist { - border-color: #e3c886; - background: #fff7df; + border-color: var(--amber-line); + background: var(--amber-tint); color: var(--amber); } .watchlist-chip.confirmed { - border-color: #afd6c1; - background: #ecf8f0; + border-color: var(--green-line); + background: var(--green-tint); color: var(--green); } @@ -1081,80 +1360,84 @@ tbody tr.selected-row { } .risk-medium { - border-color: #e3c886; - background: #fff7df; + border-color: var(--amber-line); + background: var(--amber-tint); color: var(--amber); } .risk-low { - border-color: #afd6c1; - background: #ecf8f0; + border-color: var(--green-line); + background: var(--green-tint); color: var(--green); } .risk-failed { - border-color: #c9b4cb; - background: #f4eef5; + border-color: var(--violet-line); + background: var(--violet-tint); color: var(--violet); } .risk-pending { - border-color: #b8c6d7; - background: #eef5fc; + border-color: var(--blue-line); + background: var(--blue-tint); color: var(--blue); } .source-naver { - border-color: #b7d8be; - background: #eef9f0; - color: #245c38; + border-color: #b1d5b9; + background: #ecf7ef; + color: #215633; } .source-google { - border-color: #b6c9e4; - background: #eef5ff; - color: #315f92; + border-color: var(--blue-line); + background: var(--blue-tint); + color: var(--blue); } .source-llm { - border-color: #c9bdd9; - background: #f5f0fb; - color: #5a4f77; + border-color: var(--violet-line); + background: var(--violet-tint); + color: var(--violet); } .source-internal { - border-color: #c7bca7; - background: #f7f2e8; - color: #695b45; + border-color: #c2cdd6; + background: #eef2f5; + color: #4c5d6b; } .source-failure { - border-color: #e0aaa4; - background: #fff2f0; + border-color: var(--red-line); + background: var(--red-tint); color: var(--red); } .provider-chip { margin: 1px 2px 1px 0; - border-color: var(--line); - background: #f7f4ee; + border-color: var(--hair); + background: var(--card-sunk); color: var(--ink-soft); + font-family: var(--font-stamp); + letter-spacing: 0.02em; } .provider-chip.ok { - border-color: #afd6c1; + border-color: var(--green-line); + background: var(--green-tint); color: var(--green); } .provider-chip.failed { - border-color: #e0aaa4; + border-color: var(--red-line); + background: var(--red-tint); color: var(--red); } .provider-chip.disabled, .provider-chip.skipped { - border-color: #d4c9b8; - color: #776a5a; + border-color: var(--hair-strong); + color: #66747f; } .reason-cell { @@ -1172,10 +1455,11 @@ tbody tr.selected-row { white-space: nowrap; } +/* ========================= Case layout ============================= */ .case-layout { display: grid; grid-template-columns: minmax(270px, 0.9fr) minmax(360px, 1.3fr) minmax(288px, 0.82fr); - gap: 14px; + gap: 16px; align-items: start; } @@ -1190,7 +1474,8 @@ tbody tr.selected-row { .pane { min-width: 0; - padding: 14px; + padding: 16px; + box-shadow: var(--shadow-sm); } .pane-heading { @@ -1198,7 +1483,9 @@ tbody tr.selected-row { align-items: center; justify-content: space-between; gap: 12px; - margin-bottom: 12px; + margin-bottom: 14px; + padding-bottom: 11px; + border-bottom: 1px solid var(--hair-soft); } .pane-heading.vertical { @@ -1209,6 +1496,7 @@ tbody tr.selected-row { margin: 0; font-size: 16px; line-height: 1.25; + letter-spacing: -0.01em; } .icon-actions { @@ -1217,27 +1505,30 @@ tbody tr.selected-row { } .icon-actions button { - width: 40px; + width: 44px; padding: 0 6px; + font-size: 12px; } .review-image-frame { margin: 0; + position: relative; } .review-image-frame img { display: block; width: 100%; aspect-ratio: 4 / 3; - border: 1px solid var(--line); - border-radius: 8px; - background: #f7f4ee; + border: 1px solid var(--hair); + border-radius: var(--radius); + background: var(--card-sunk); object-fit: cover; + box-shadow: var(--shadow-sm); } .review-image-frame figcaption { min-height: 18px; - margin-top: 6px; + margin-top: 7px; color: var(--ink-soft); font-size: 12px; } @@ -1246,27 +1537,32 @@ tbody tr.selected-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; - margin-top: 12px; + margin-top: 14px; } .fact-item { min-height: 54px; - padding: 8px; - border: 1px solid var(--line); - border-radius: 8px; - background: #fbfaf6; + padding: 9px 10px; + border: 1px solid var(--hair-soft); + border-radius: var(--r-sm); + background: var(--card); } .fact-item span { display: block; - color: var(--ink-soft); - font-size: 11px; - font-weight: 800; + color: var(--ink-faint); + font-family: var(--font-stamp); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.07em; + text-transform: uppercase; } .fact-item strong { display: block; margin-top: 5px; + font-family: var(--font-mono); + font-size: 13px; overflow-wrap: anywhere; } @@ -1274,7 +1570,7 @@ tbody tr.selected-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; - margin-top: 12px; + margin-top: 14px; } .similar-item { @@ -1286,8 +1582,8 @@ tbody tr.selected-row { .similar-item img { width: 100%; aspect-ratio: 4 / 3; - border: 1px solid var(--line); - border-radius: 8px; + border: 1px solid var(--hair); + border-radius: var(--r-sm); object-fit: cover; } @@ -1306,10 +1602,10 @@ tbody tr.selected-row { .evidence-summary-board { display: grid; gap: 12px; - padding: 12px; - border: 1px solid var(--line); - border-radius: 8px; - background: #fbfaf6; + padding: 14px; + border: 1px solid var(--hair); + border-radius: var(--radius); + background: var(--card); } .summary-lead { @@ -1322,6 +1618,7 @@ tbody tr.selected-row { .summary-lead strong { display: block; font-size: 17px; + letter-spacing: -0.01em; } .summary-lead span:not(.risk-badge) { @@ -1334,33 +1631,38 @@ tbody tr.selected-row { .summary-stat-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); - gap: 7px; + gap: 8px; } .summary-stat { min-width: 0; - padding: 8px; - border: 1px solid var(--line); - border-radius: 8px; - background: #ffffff; + padding: 9px 10px; + border: 1px solid var(--hair-soft); + border-radius: var(--r-sm); + background: var(--card-raised); } .summary-stat.has-signal { - border-color: #d7b65d; - background: #fff8df; + border-color: var(--ochre-line); + background: var(--ochre-tint); } .summary-stat span { display: block; - color: var(--ink-soft); - font-size: 11px; - font-weight: 800; + color: var(--ink-faint); + font-family: var(--font-stamp); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; } .summary-stat strong { display: block; - margin-top: 2px; - font-size: 18px; + margin-top: 3px; + font-family: var(--font-mono); + font-size: 19px; + letter-spacing: -0.02em; } .evidence-groups { @@ -1375,10 +1677,11 @@ tbody tr.selected-row { .evidence-next-action-panel { display: grid; gap: 10px; - padding: 12px; - border: 1px solid #e3c886; - border-radius: 8px; - background: #fffaf0; + padding: 14px; + border: 1px solid var(--ochre-line); + border-left: 3px solid var(--ochre); + border-radius: var(--radius); + background: var(--ochre-tint); } .evidence-next-action-panel strong, @@ -1386,6 +1689,10 @@ tbody tr.selected-row { display: block; } +.evidence-next-action-panel strong { + letter-spacing: -0.01em; +} + .evidence-next-action-panel span { margin-top: 3px; color: var(--ink-soft); @@ -1411,19 +1718,20 @@ tbody tr.selected-row { display: grid; gap: 10px; min-width: 0; - padding: 10px; - border: 1px solid var(--line); - border-radius: 8px; - background: #ffffff; + padding: 12px; + border: 1px solid var(--hair); + border-radius: var(--radius); + background: var(--card-raised); } .evidence-group-strong { - border-color: #d9b85b; - background: #fffdf5; + border-color: var(--ochre-line); + background: #fdf9ee; + box-shadow: 0 0 0 1px rgba(176, 125, 24, 0.08); } .evidence-group-weak { - background: #fbfaf6; + background: var(--card); } .evidence-group-head { @@ -1437,6 +1745,7 @@ tbody tr.selected-row { margin: 0; color: var(--ink); font-size: 15px; + letter-spacing: -0.01em; } .evidence-group-head span { @@ -1449,10 +1758,11 @@ tbody tr.selected-row { .evidence-group-head strong { flex: 0 0 auto; min-width: 42px; - padding: 4px 8px; - border: 1px solid var(--line); - border-radius: 999px; - background: #fbfaf6; + padding: 4px 9px; + border: 1px solid var(--hair); + border-radius: var(--r-pill); + background: var(--card); + font-family: var(--font-mono); font-size: 12px; text-align: center; } @@ -1472,13 +1782,19 @@ tbody tr.selected-row { cursor: pointer; width: max-content; max-width: 100%; - padding: 7px 10px; - border: 1px solid var(--line); - border-radius: 8px; - background: #f7f2e8; + padding: 7px 12px; + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-sunk); color: var(--ink); font-size: 13px; - font-weight: 800; + font-weight: 700; + transition: background 130ms var(--ease), border-color 130ms var(--ease); +} + +.evidence-details summary:hover { + border-color: var(--hair-strong); + background: var(--card); } .evidence-details[open] summary { @@ -1498,16 +1814,16 @@ tbody tr.selected-row { display: grid; gap: 8px; min-width: 0; - padding: 11px; - border: 1px solid var(--line); - border-radius: 8px; - background: #fbfaf6; + padding: 12px; + border: 1px solid var(--hair); + border-radius: var(--radius); + background: var(--card); } .evidence-row { grid-template-columns: 118px minmax(0, 1fr) auto; align-items: start; - background: #ffffff; + background: var(--card-raised); } .evidence-row.no-preview { @@ -1533,9 +1849,9 @@ tbody tr.selected-row { display: block; width: 118px; aspect-ratio: 1 / 1; - border: 1px solid var(--line); - border-radius: 8px; - background: #ffffff; + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-raised); object-fit: cover; } @@ -1543,10 +1859,17 @@ tbody tr.selected-row { display: inline-block; max-width: 100%; margin-top: 7px; - color: #315f92; + color: var(--blue); + font-family: var(--font-mono); font-size: 12px; - font-weight: 800; + font-weight: 700; overflow-wrap: anywhere; + text-decoration-color: rgba(44, 87, 138, 0.35); + text-underline-offset: 2px; +} + +.evidence-link:hover { + text-decoration-color: currentColor; } .evidence-title, @@ -1555,7 +1878,8 @@ tbody tr.selected-row { align-items: center; gap: 7px; min-width: 0; - font-weight: 800; + font-weight: 700; + letter-spacing: -0.01em; } .evidence-title span:last-child, @@ -1582,22 +1906,24 @@ tbody tr.selected-row { } .knowledge-row.watchlist { - border-color: #e3c886; - background: #fffaf0; + border-color: var(--ochre-line); + background: #fdf9ee; } +/* ===================== Decision pane / floating ==================== */ .recommendation-box { display: grid; gap: 8px; - margin-bottom: 12px; - padding: 12px; - border: 1px solid var(--line); - border-radius: 8px; - background: #fbfaf6; + margin-bottom: 14px; + padding: 13px; + border: 1px solid var(--hair); + border-radius: var(--radius); + background: var(--card); } .recommendation-box strong { font-size: 18px; + letter-spacing: -0.01em; } .floating-decision-panel { @@ -1608,7 +1934,21 @@ tbody tr.selected-row { width: min(310px, calc(100vw - 48px)); max-height: calc(100vh - 132px); overflow: auto; - box-shadow: 0 18px 48px rgba(23, 33, 36, 0.18); + border-radius: var(--r-lg); + background: var(--card-raised); + box-shadow: var(--shadow-lg); + animation: panelIn 360ms var(--ease) both; +} + +@keyframes panelIn { + from { + opacity: 0; + transform: translateY(14px) scale(0.985); + } + to { + opacity: 1; + transform: none; + } } .floating-decision-head { @@ -1617,7 +1957,7 @@ tbody tr.selected-row { .floating-decision-panel .recommendation-box { margin-bottom: 10px; - padding: 10px; + padding: 11px; } .floating-decision-panel .recommendation-box strong { @@ -1649,36 +1989,54 @@ tbody tr.selected-row { .decision-secondary summary { cursor: pointer; color: var(--ink-soft); - font-size: 12px; - font-weight: 800; + font-family: var(--font-stamp); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.07em; + text-transform: uppercase; } .approve-action { - border: 1px solid #2f6d50; - background: #e9f8ee; - color: #245c38; + border: 1px solid var(--green-line); + background: var(--green-tint); + color: var(--green); +} + +.approve-action:hover { + border-color: var(--green); + transform: translateY(-1px); } .hold-action { - border: 1px solid #c89b2c; - background: #fff7df; - color: #80570c; + border: 1px solid var(--amber-line); + background: var(--amber-tint); + color: var(--amber); +} + +.hold-action:hover { + border-color: var(--amber); + transform: translateY(-1px); } .reject-action { - border: 1px solid #bf5a50; - background: #fff0ed; + border: 1px solid var(--red-line); + background: var(--red-tint); color: var(--red); } +.reject-action:hover { + border-color: var(--red); + transform: translateY(-1px); +} + .derived-preview { display: grid; gap: 6px; margin-top: 12px; - padding: 10px; - border: 1px solid var(--line); - border-radius: 8px; - background: #f7f2e8; + padding: 11px; + border: 1px solid var(--hair-soft); + border-radius: var(--r-sm); + background: var(--card-sunk); font-size: 13px; } @@ -1693,10 +2051,11 @@ tbody tr.selected-row { width: 100%; } +/* ===================== Split workspace / forms ===================== */ .split-workspace { display: grid; grid-template-columns: minmax(280px, 0.85fr) minmax(420px, 1.15fr); - gap: 14px; + gap: 16px; align-items: start; } @@ -1705,8 +2064,8 @@ tbody tr.selected-row { .collection-promotion-form, .knowledge-form { display: grid; - gap: 10px; - margin-bottom: 14px; + gap: 12px; + margin-bottom: 16px; } .manual-query-form { @@ -1751,10 +2110,16 @@ tbody tr.selected-row { grid-template-columns: auto 74px minmax(0, 1fr) auto; gap: 10px; align-items: center; - padding: 9px; - border: 1px solid var(--line); - border-radius: 8px; - background: #fbfaf6; + padding: 10px; + border: 1px solid var(--hair); + border-radius: var(--radius); + background: var(--card); + transition: border-color 140ms var(--ease), box-shadow 140ms var(--ease); +} + +.candidate-card:hover { + border-color: var(--hair-strong); + box-shadow: var(--shadow-sm); } .candidate-actions { @@ -1765,7 +2130,7 @@ tbody tr.selected-row { } .candidate-card.promoted { - background: #f1eee8; + background: var(--card-sunk); } .candidate-select { @@ -1778,20 +2143,20 @@ tbody tr.selected-row { .candidate-select input { width: 18px; height: 18px; - accent-color: var(--accent); + accent-color: var(--teal); } .candidate-thumb { width: 74px; aspect-ratio: 1; - border: 1px solid var(--line); - border-radius: 8px; - background: #eee7dc; + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-sunk); object-fit: cover; } .candidate-thumb.empty { - background: #eee7dc; + background: var(--card-sunk); } .candidate-main { @@ -1805,8 +2170,8 @@ tbody tr.selected-row { .section-divider { height: 1px; - margin: 14px 0; - background: var(--line); + margin: 16px 0; + background: var(--hair); } .inline-status { @@ -1821,15 +2186,17 @@ tbody tr.selected-row { gap: 10px; } +/* ========================= Knowledge rows ========================== */ .knowledge-row { grid-template-columns: 72px minmax(0, 1fr) minmax(128px, auto); grid-template-areas: "thumb main actions"; align-items: start; + background: var(--card-raised); } .knowledge-row.inactive, .correction-row.inactive { - background: #f1eee8; + background: var(--card-sunk); color: var(--ink-soft); } @@ -1837,14 +2204,14 @@ tbody tr.selected-row { grid-area: thumb; width: 72px; aspect-ratio: 1; - border: 1px solid var(--line); - border-radius: 8px; - background: #f5f0e7; + border: 1px solid var(--hair); + border-radius: var(--r-sm); + background: var(--card-sunk); object-fit: cover; } .knowledge-thumb.empty { - background: #eee7dc; + background: var(--card-sunk); } .knowledge-main { @@ -1856,6 +2223,7 @@ tbody tr.selected-row { .knowledge-title { font-size: 15px; + letter-spacing: -0.01em; } .knowledge-detail-line { @@ -1914,14 +2282,14 @@ tbody tr.selected-row { } .provenance-chip.manual { - border-color: #a9c8d1; - background: #edf7f9; - color: var(--accent); + border-color: var(--teal-line); + background: var(--teal-tint); + color: var(--teal); } .provenance-chip.automatic { - border-color: #e3c886; - background: #fff7df; + border-color: var(--ochre-line); + background: var(--ochre-tint); color: var(--amber); } @@ -1929,6 +2297,7 @@ tbody tr.selected-row { grid-template-columns: minmax(0, 1fr) auto; } +/* ========================= Providers =============================== */ .provider-list { display: grid; gap: 12px; @@ -1937,6 +2306,7 @@ tbody tr.selected-row { .provider-row { grid-template-columns: minmax(0, 1fr) minmax(240px, 0.45fr) auto; align-items: center; + background: var(--card-raised); } .provider-summary { @@ -1946,15 +2316,18 @@ tbody tr.selected-row { .quota-meter { height: 9px; - border-radius: 999px; - background: #ded5c8; + border-radius: var(--r-pill); + background: var(--paper-2); overflow: hidden; + box-shadow: inset 0 1px 2px rgba(20, 30, 28, 0.12); } .quota-meter span { display: block; height: 100%; - background: var(--accent); + border-radius: inherit; + background: var(--teal); + transition: width 600ms var(--ease); } .provider-actions { @@ -1962,11 +2335,18 @@ tbody tr.selected-row { gap: 8px; } +/* ========================= Audit table ============================= */ .audit-table th:nth-child(1), .audit-table td:nth-child(1) { width: 168px; } +.audit-table td:nth-child(1) { + font-family: var(--font-mono); + font-size: 12px; + color: var(--ink-soft); +} + .audit-table th:nth-child(2), .audit-table td:nth-child(2) { width: 120px; @@ -1988,14 +2368,59 @@ tbody tr.selected-row { } .empty-state { + display: grid; + place-items: center; min-height: 94px; - padding: 18px; - border: 1px dashed var(--line-strong); - border-radius: 8px; - background: #fbfaf6; + padding: 20px; + border: 1px dashed var(--hair-strong); + border-radius: var(--radius); + background: var(--card-sunk); color: var(--ink-soft); + text-align: center; } +/* ===================== Page-load choreography ====================== */ +@media (prefers-reduced-motion: no-preference) { + .nav-button { + animation: railIn 460ms var(--ease) both; + } + .nav-button:nth-of-type(1) { animation-delay: 60ms; } + .nav-button:nth-of-type(2) { animation-delay: 110ms; } + .nav-button:nth-of-type(3) { animation-delay: 160ms; } + .nav-button:nth-of-type(4) { animation-delay: 210ms; } + .nav-button:nth-of-type(5) { animation-delay: 260ms; } + + .operator-workflow article { + animation: railIn 500ms var(--ease) both; + } + .operator-workflow article:nth-child(1) { animation-delay: 120ms; } + .operator-workflow article:nth-child(2) { animation-delay: 190ms; } + .operator-workflow article:nth-child(3) { animation-delay: 260ms; } +} + +@keyframes railIn { + from { + opacity: 0; + transform: translateX(-7px); + } + to { + opacity: 1; + transform: none; + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.001ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.001ms !important; + scroll-behavior: auto !important; + } +} + +/* ============================ Responsive =========================== */ @media (min-width: 1181px) { .workbench-panel[data-workbench-panel="evidence"] { padding-right: 334px; @@ -2042,22 +2467,40 @@ tbody tr.selected-row { grid-column: 1 / -1; } + .brand-block { + margin-bottom: 6px; + padding-bottom: 10px; + } + + .nav-button.active::before { + left: 0; + top: auto; + bottom: -2px; + width: 100%; + height: 3px; + transform: none; + border-radius: 3px 3px 0 0; + } + .top-bar { grid-template-columns: 1fr; align-items: stretch; } - .global-search, .product-purpose, .queue-health, .provider-pulse, - .coverage-tabs, - .operator-chip { + .coverage-tabs { min-width: 0; width: 100%; white-space: normal; } + .product-purpose strong, + .product-purpose span { + white-space: normal; + } + .coverage-tabs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); @@ -2199,9 +2642,11 @@ tbody tr.selected-row { grid-template-columns: 24px 72px minmax(0, 1fr); gap: 8px 10px; align-items: start; - padding: 10px; - border-bottom: 1px solid var(--line); - background: var(--surface-strong); + padding: 12px; + border: 1px solid var(--hair); + border-radius: var(--radius); + background: var(--card-raised); + box-shadow: var(--shadow-sm); } .queue-table td { @@ -2251,17 +2696,17 @@ tbody tr.selected-row { .queue-table td:nth-child(7)::before { content: "지원자"; - font-weight: 800; + font-weight: 700; } .queue-table td:nth-child(8)::before { content: "판정"; - font-weight: 800; + font-weight: 700; } .queue-table td:nth-child(9)::before { content: "시간"; - font-weight: 800; + font-weight: 700; } .queue-table .thumb { @@ -2302,10 +2747,11 @@ tbody tr.selected-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 10px; - padding: 10px; - border: 1px solid var(--line); - border-radius: 8px; - background: var(--surface-strong); + padding: 12px; + border: 1px solid var(--hair); + border-radius: var(--radius); + background: var(--card-raised); + box-shadow: var(--shadow-sm); } .audit-table td {