POSA_Copyrighter/web/operator-gui/styles.css
changukyu 975c252201 feat: 증거 일괄 표시 — 51번 클릭하던 되먹임을 1번으로
8/8 실효성 평가가 "운영자 되먹임이 순증 노동"이라고 적은 항목이다. 스코어러는
점수를 만든 행이 표시돼야 점수를 내려놓는데, 단건 API(/api/evidence/{id}/status)
하나뿐이라 실 DB 제출물 하나를 100 에서 35 로 내리려면 google 증거 51행을
개별로 눌러야 했다. 되먹임이 노동보다 비싸면 아무도 되먹이지 않는다.

증거 그룹 헤더의 개수 옆에 [전부 무관] 을 붙이고, POST /api/evidence/status-batch
를 신설했다.

서버는 한 트랜잭션, 리스코어 1회, 감사 이벤트 1건으로 묶는다. 감사 부분은 그
자체로 중요하다 — 이건 한 번의 운영자 동작이고, 51건을 쓰면 동작을 잘못
표현할 뿐 아니라 500행 감사 캡을 클릭 한 번에 먹는다. 행별 who/when/note 는
각 증거 payload 에 그대로 남으므로 개별 행이 설명 불가가 되지는 않는다.

원자성: 남의 제출물 증거 id 가 섞이면 아무것도 쓰지 않고 전체가 실패한다.
절반만 표시된 상태로 끝나면 운영자는 무엇이 반영됐는지 알 수 없다.

상한은 500 이다. 일괄 판정 상한(200)보다 큰 이유는 단위가 다르기 때문이다 —
그쪽은 케이스 수고 이쪽은 한 케이스 안의 증거 행 수다.

그룹 액션은 irrelevant(무관)로 보낸다. 행별 "미사용" 버튼은 false_positive
(오탐)에 매핑돼 있는데, 한 제공자의 결과 전체를 오탐이라 단정하는 것은 과한
주장이다. 점수 효과는 같고(스코어러는 둘 다 건너뛴다) 주장만 정직해진다.

확인 대화는 두지 않았다. 이 동작은 행마다 되돌릴 수 있고, 여기서 줄이려는
것이 바로 그 클릭이다. 되돌릴 수 없는 느낌이 드는 것은 판정이고 확인 대화는
거기에만 있다.

실렌더(Playwright 1440x900, 실제 서버): 51행 그룹에서 1타로
{"status":"irrelevant","evidence_ids":[51개]} 전송, 콘솔 에러 0.

검증: ruff clean, pytest 636 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BStHFrjQj2VypecSG9ex1V
2026-09-21 14:34:05 +09:00

3236 lines
63 KiB
CSS

/* =====================================================================
권리 검수 콘솔 — 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 {
/* --- Type ---------------------------------------------------------- */
--font-ui: "Pretendard Variable", Pretendard, "Malgun Gothic", "Apple SD Gothic Neo", "Segoe UI", system-ui, 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-tint-soft: #eef6f9;
--teal-line: #a9cdda;
--ochre: #b07d18;
--ochre-tint: #faf2dd;
--ochre-tint-soft: #fdf9ee;
--ochre-line: #e2c887;
/* --- Dark rail ----------------------------------------------------- */
--rail: #222b33;
--rail-edge: #10171d;
--rail-ink: #d3dce4;
--rail-ink-soft: #9fb0bd;
--rail-ink-strong: #e8edf2;
/* --- 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;
--forest: #215633; --forest-tint: #ecf7ef;--forest-line: #b1d5b9;
--slate: #4c5d6b; --slate-tint: #eef2f5; --slate-line: #c2cdd6;
/* --- 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);
--inset-well: inset 0 1px 2px rgba(23, 32, 41, 0.04);
--bevel: var(--bevel);
--ease: cubic-bezier(0.22, 0.61, 0.36, 1);
--audit-object-width: 24%;
}
* {
box-sizing: border-box;
}
html {
min-height: 100%;
background: var(--paper);
color: var(--ink);
font-family: var(--font-ui);
letter-spacing: 0;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
body {
min-height: 100vh;
margin: 0;
color: var(--ink);
background-color: var(--paper);
font-variant-numeric: tabular-nums;
}
::selection {
background: color-mix(in srgb, var(--teal) 22%, transparent);
color: var(--ink);
}
button,
input,
select,
textarea {
font: inherit;
letter-spacing: 0;
}
button {
cursor: pointer;
}
button:disabled,
input:disabled,
select:disabled,
textarea:disabled {
cursor: not-allowed;
opacity: 0.55;
}
:focus-visible {
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);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.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: 6px;
height: 100vh;
padding: 20px 14px 16px;
background: var(--rail);
color: var(--rail-ink-strong);
border-right: 1px solid var(--rail-edge);
box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.04);
}
.brand-block {
display: flex;
align-items: center;
gap: 12px;
min-height: 58px;
margin-bottom: 14px;
padding: 0 6px 14px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.brand-block strong,
.brand-block span {
display: block;
}
.brand-block strong {
font-size: 17px;
line-height: 1.2;
letter-spacing: -0.01em;
}
.brand-block div span {
margin-top: 4px;
color: var(--rail-ink-soft);
font-size: 11px;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.brand-mark {
display: inline-grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 10px;
background: var(--teal);
color: #fff;
font-weight: 750;
font-size: 17px;
letter-spacing: 0.04em;
box-shadow: var(--bevel);
}
.nav-button {
position: relative;
display: grid;
grid-template-columns: 28px minmax(0, 1fr);
align-items: center;
gap: 11px;
width: 100%;
min-height: 44px;
padding: 9px 11px;
border: 1px solid transparent;
border-radius: var(--radius);
background: transparent;
color: var(--rail-ink);
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.active {
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 {
display: inline-grid;
place-items: center;
width: 28px;
height: 28px;
border: 1px solid currentColor;
border-radius: 7px;
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 {
min-width: 0;
overflow-wrap: anywhere;
}
.nav-footer {
display: flex;
align-items: center;
gap: 8px;
margin-top: auto;
padding: 11px 12px;
border: 1px solid rgba(255, 255, 255, 0.14);
border-radius: var(--radius);
color: var(--rail-ink);
font-size: 12px;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.status-dot {
position: relative;
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--hair-strong);
}
.status-dot.ok {
background: #6fc188;
box-shadow: 0 0 0 3px rgba(111, 193, 136, 0.22);
}
.status-dot.warn {
background: #e0b24c;
box-shadow: 0 0 0 3px rgba(224, 178, 76, 0.22);
}
.status-dot.bad {
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(0, 1.15fr) minmax(0, 0.85fr) minmax(0, 0.85fr) minmax(0, 2fr);
align-items: stretch;
gap: 12px;
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);
}
.toolbar input,
.toolbar select,
.manual-query-form input,
.manual-query-form select,
.candidate-collection-form input,
.candidate-collection-form select,
.collection-promotion-form input,
.collection-promotion-form select,
.collection-promotion-form textarea,
.knowledge-form input,
.knowledge-form select,
.knowledge-form textarea,
.memo-field textarea {
width: 100%;
border: 1px solid var(--hair);
border-radius: var(--r-sm);
background: var(--card-raised);
color: var(--ink);
box-shadow: var(--inset-well);
transition: border-color 140ms var(--ease), box-shadow 140ms var(--ease);
}
.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: var(--inset-well), 0 0 0 3px color-mix(in srgb, var(--teal) 16%, transparent);
}
.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 {
display: flex;
align-items: center;
gap: 8px;
min-height: 40px;
padding: 7px 11px;
overflow: hidden;
border: 1px solid var(--hair);
border-radius: var(--r-sm);
background: var(--card-raised);
color: var(--ink-soft);
font-size: 13px;
white-space: nowrap;
text-overflow: ellipsis;
box-shadow: var(--shadow-sm);
}
.coverage-tabs {
align-items: stretch;
gap: 0;
min-width: 0;
max-width: 100%;
padding: 0;
overflow: hidden;
}
.coverage-tab {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 8px;
min-width: 0;
min-height: 38px;
padding: 6px 12px;
border: 0;
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 {
border-right: 0;
}
.coverage-tab span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.coverage-tab strong {
color: var(--ink);
font-family: var(--font-mono);
font-size: 13px;
}
.coverage-tab.active {
background: var(--teal-deep);
color: #fff;
}
.coverage-tab.active strong {
color: #fff;
}
.coverage-tab.ok strong {
color: var(--green);
}
.coverage-tab.warn strong {
color: var(--amber);
}
.coverage-tab.bad strong {
color: var(--red);
}
.coverage-tab.active.ok strong,
.coverage-tab.active.warn strong,
.coverage-tab.active.bad strong {
color: #fff;
}
/* ========================= Main surface ============================= */
.main-surface {
padding: 24px 24px 30px;
}
.view {
display: none;
}
.view.active {
display: block;
/* Opacity-only on purpose: any transform here — even one animating toward
`none` — establishes a containing block for the whole animation and breaks
`position: fixed` on the floating decision panel inside this view. */
animation: viewRise 320ms var(--ease) backwards;
}
@keyframes viewRise {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.section-header {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 16px;
margin-bottom: 18px;
padding-bottom: 14px;
border-bottom: 1px solid var(--hair);
}
.section-header h1 {
margin: 0;
font-size: 25px;
line-height: 1.15;
letter-spacing: -0.02em;
font-weight: 800;
}
.queue-intent {
max-width: 560px;
margin: 7px 0 0;
color: var(--ink-soft);
font-size: 13px;
line-height: 1.55;
}
/* ===================== Tabs (workbench / knowledge) ================= */
.workbench-tabs,
.knowledge-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin: 0 0 16px;
border-bottom: 1px solid var(--hair);
}
.workbench-tabs button,
.knowledge-tabs button {
position: relative;
min-height: 40px;
padding: 0 16px;
border: 0;
background: transparent;
color: var(--ink-soft);
font-weight: 700;
font-size: 14px;
transition: color 140ms var(--ease);
}
.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;
gap: 8px;
justify-content: flex-end;
}
.queue-folder-input {
display: grid;
gap: 5px;
min-width: 260px;
}
.queue-file-input {
display: grid;
gap: 5px;
min-width: 190px;
cursor: pointer;
}
.queue-folder-input span,
.queue-file-input span:first-child {
color: var(--ink-faint);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.07em;
text-transform: uppercase;
}
.queue-folder-input input {
min-height: 38px;
padding: 0 11px;
border: 1px solid var(--hair);
border-radius: var(--r-sm);
background: var(--card-raised);
color: var(--ink);
box-shadow: var(--inset-well);
}
.queue-file-input span:last-of-type {
display: flex;
align-items: center;
min-height: 38px;
max-width: 220px;
padding: 0 11px;
overflow: hidden;
border: 1px solid var(--hair);
border-radius: var(--r-sm);
background: var(--card-raised);
color: var(--ink-soft);
font-size: 13px;
text-overflow: ellipsis;
white-space: nowrap;
}
.queue-file-input input {
position: absolute;
width: 1px;
height: 1px;
max-width: 1px;
max-height: 1px;
overflow: hidden;
opacity: 0;
pointer-events: none;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
}
.import-status {
display: flex;
align-items: center;
gap: 8px;
min-height: 38px;
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: 12px;
margin: 0 0 18px;
}
.operator-workflow article {
position: relative;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
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: 28px;
height: 28px;
border-radius: var(--r-pill);
background: var(--teal);
color: #fff;
font-size: 13px;
font-weight: 700;
box-shadow: var(--bevel);
}
.operator-workflow strong {
align-self: center;
color: var(--ink);
font-size: 14px;
letter-spacing: -0.01em;
}
.operator-workflow p {
grid-column: 2;
margin: 0;
color: var(--ink-soft);
font-size: 12px;
line-height: 1.5;
}
.eyebrow {
margin: 0 0 6px;
color: var(--teal);
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: 12px;
align-items: end;
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,
.manual-query-form label,
.candidate-collection-form label,
.collection-promotion-form label,
.knowledge-form label,
.memo-field {
display: grid;
gap: 6px;
min-width: 0;
}
.toolbar label span,
.manual-query-form label span,
.candidate-collection-form label span,
.collection-promotion-form label span,
.knowledge-form label span,
.memo-field span {
color: var(--ink-faint);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.07em;
text-transform: uppercase;
}
.toolbar input,
.toolbar select,
.manual-query-form input,
.manual-query-form select,
.candidate-collection-form input,
.candidate-collection-form select,
.collection-promotion-form input,
.collection-promotion-form select,
.knowledge-form input,
.knowledge-form select {
min-height: 38px;
padding: 0 11px;
}
.memo-field textarea,
.collection-promotion-form textarea,
.knowledge-form textarea {
min-height: 114px;
padding: 11px;
resize: vertical;
line-height: 1.5;
}
.file-picker {
cursor: pointer;
}
.file-picker-control {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 8px;
align-items: center;
min-height: 38px;
padding: 5px;
border: 1px solid var(--hair);
border-radius: var(--r-sm);
background: var(--card-raised);
}
.file-picker-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 28px;
padding: 0 12px;
border-radius: 5px;
background: var(--teal);
color: #fff;
font-size: 13px;
font-weight: 700;
box-shadow: var(--bevel);
}
.knowledge-form .file-picker-button {
color: #fff;
}
#knowledge-image-name {
min-width: 0;
overflow: hidden;
color: var(--ink-soft);
font-size: 13px;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-picker-input {
position: absolute;
width: 1px;
height: 1px;
max-width: 1px;
max-height: 1px;
overflow: hidden;
opacity: 0;
pointer-events: none;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
}
/* ========================= Segmented control ======================= */
.segmented {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
min-height: 38px;
border: 1px solid var(--hair);
border-radius: var(--r-sm);
background: var(--card-raised);
overflow: hidden;
box-shadow: var(--inset-well);
}
.segmented button {
min-width: 0;
border: 0;
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 {
border-right: 0;
}
.segmented button.active {
background: var(--teal-deep);
color: #fff;
}
/* ============================ Buttons =============================== */
.secondary-action,
.primary-action,
.danger-outline,
.decision-actions button,
.quick-actions button,
.icon-actions button,
.provider-actions button,
.row-action {
min-height: 36px;
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,
.row-action,
.quick-actions button,
.icon-actions button,
.provider-actions button {
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(--teal);
background: var(--teal-tint);
color: var(--teal-deep);
}
.row-action.danger {
border-color: var(--red-line);
background: var(--red-tint);
color: var(--red);
}
.primary-action {
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 {
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(--hair);
border-radius: var(--r-lg);
background: var(--card-raised);
}
.table-shell {
overflow: visible;
box-shadow: var(--shadow);
}
table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.queue-grid {
display: block;
min-width: 0;
}
.queue-grid thead {
display: block;
}
.queue-grid tbody {
display: grid;
}
.queue-grid thead tr,
.queue-row {
display: grid;
grid-template-columns: 28px 64px minmax(104px, 0.68fr) 72px minmax(126px, 0.58fr) minmax(360px, 1.5fr) 82px 76px 90px;
column-gap: 0;
align-items: center;
}
.queue-grid thead tr,
.queue-row {
width: 100%;
}
.queue-row {
min-height: 78px;
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,
.queue-grid th {
min-width: 0;
width: auto !important;
padding: 8px;
}
.queue-row td {
border-bottom: 0;
}
.queue-grid th {
position: static;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.queue-grid td {
width: auto !important;
}
.queue-row .reason-cell {
max-width: 100%;
}
.queue-submission-cell,
.queue-risk-cell,
.queue-time-cell {
min-width: 0;
}
.queue-submission-cell {
display: flex;
align-items: center;
gap: 8px;
overflow: hidden;
white-space: nowrap;
}
.queue-case-button {
display: inline-flex;
justify-content: flex-start;
justify-self: start;
width: auto;
max-width: 100%;
min-height: 32px;
padding: 4px 9px;
overflow: hidden;
white-space: nowrap;
}
.score-stack.queue-risk-cell {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: nowrap;
}
.queue-risk-cell span,
.queue-time-cell,
.queue-row td:nth-child(7),
.queue-row td:nth-child(8) {
overflow: hidden;
text-overflow: ellipsis;
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;
display: flex;
align-items: center;
}
.queue-row td:nth-child(5) {
padding-right: 6px;
}
.queue-row td:nth-child(6) {
padding-left: 6px;
align-content: center;
flex-wrap: nowrap;
gap: 3px;
}
.queue-provider-strip {
display: flex;
flex-wrap: nowrap;
gap: 3px;
min-width: 0;
max-width: 100%;
overflow: hidden;
}
.queue-provider-chip {
flex: 0 0 auto;
min-width: 0;
margin: 0;
padding-inline: 6px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
th,
td {
padding: 10px;
border-bottom: 1px solid var(--hair-soft);
text-align: left;
vertical-align: middle;
}
th {
position: sticky;
top: 0;
z-index: 1;
background: var(--card-sunk);
color: var(--ink-faint);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
border-bottom: 1px solid var(--hair);
}
td {
color: var(--ink);
font-size: 13px;
}
tbody tr {
transition: background 140ms var(--ease), box-shadow 140ms var(--ease);
}
tbody tr:hover {
background: var(--teal-tint);
}
.queue-grid tbody .queue-row:hover {
background: var(--teal-tint);
}
/* Selection must outrank the zebra stripe (.queue-grid tbody
.queue-row:nth-child(even), specificity 0,3,1) on even rows. */
tbody tr.selected-row,
.queue-grid tbody .queue-row.selected-row {
box-shadow: inset 4px 0 0 var(--teal), var(--shadow-sm);
background: var(--teal-tint-soft);
}
.thumb {
width: 62px;
height: 48px;
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;
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;
}
.muted {
color: var(--ink-soft);
}
.small {
font-size: 12px;
}
.score-stack {
display: grid;
gap: 4px;
}
/* =========================== Pills & chips ========================= */
.score-pill,
.risk-badge,
.source-chip,
.decision-badge,
.provider-chip,
.provenance-chip,
.evidence-status-chip,
.watchlist-chip {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 5px;
max-width: 100%;
min-height: 24px;
padding: 3px 9px;
border-radius: var(--r-pill);
border: 1px solid transparent;
font-size: 12px;
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: var(--red-line);
background: var(--red-tint);
color: var(--red);
}
.source-chip {
flex: 0 0 auto;
white-space: nowrap;
}
/* Publishing host. Monospace so hostnames align down a long evidence list and
near-identical domains (cdn.example.com vs cdn-example.com) separate at a
glance; quieter than the source chip because it sits next to it on every row
and two competing chips would flatten the hierarchy. */
/* A retained case whose source file is gone. The image cannot be served, so the
frame says so instead of leaving a silently broken thumbnail: the row is still
the record of a real decision and has to stay legible as one. */
.thumb-missing,
.case-image-missing {
outline: 2px dashed var(--amber-line);
outline-offset: 2px;
opacity: 0.55;
}
.host-chip {
/* Bounded, then rigid. Bounded because CDN hostnames run past 45 characters
and an unbounded chip ate the whole title line, leaving the headline to
wrap one character per row. Rigid within that bound because the title beside
it is boilerplate ("구글 full image match found" on every row) while this is
the row's actual content — a host short enough to fit must never be the
thing that gets truncated. */
flex: 0 0 auto;
max-width: 20ch;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: var(--font-mono);
font-size: 12px;
font-weight: 600;
letter-spacing: 0;
color: var(--ink-soft);
}
.evidence-status-chip,
.watchlist-chip.watchlist {
border-color: var(--amber-line);
background: var(--amber-tint);
color: var(--amber);
}
.watchlist-chip.confirmed {
border-color: var(--green-line);
background: var(--green-tint);
color: var(--green);
}
.watchlist-chip.excluded {
border-color: #d6cbd2;
background: #f2eef1;
color: var(--ink-soft);
}
.risk-medium {
border-color: var(--amber-line);
background: var(--amber-tint);
color: var(--amber);
}
.risk-low {
border-color: var(--green-line);
background: var(--green-tint);
color: var(--green);
}
.risk-failed {
border-color: var(--violet-line);
background: var(--violet-tint);
color: var(--violet);
}
.risk-pending {
border-color: var(--blue-line);
background: var(--blue-tint);
color: var(--blue);
}
.source-naver {
border-color: var(--forest-line);
background: var(--forest-tint);
color: var(--forest);
}
.source-google {
border-color: var(--blue-line);
background: var(--blue-tint);
color: var(--blue);
}
.source-llm {
border-color: var(--violet-line);
background: var(--violet-tint);
color: var(--violet);
}
.source-internal {
border-color: var(--slate-line);
background: var(--slate-tint);
color: var(--slate);
}
.source-failure {
border-color: var(--red-line);
background: var(--red-tint);
color: var(--red);
}
.provider-chip {
margin: 1px 2px 1px 0;
border-color: var(--hair);
background: var(--card-sunk);
color: var(--ink-soft);
letter-spacing: 0.02em;
}
.provider-chip.ok {
border-color: var(--green-line);
background: var(--green-tint);
color: var(--green);
}
.provider-chip.failed {
border-color: var(--red-line);
background: var(--red-tint);
color: var(--red);
}
.provider-chip.disabled,
.provider-chip.skipped {
border-color: var(--hair-strong);
color: var(--ink-soft);
}
.reason-cell {
display: flex;
flex-wrap: nowrap;
gap: 4px;
min-width: 0;
overflow: hidden;
}
.reason-cell span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
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: 16px;
align-items: start;
}
.evidence-layout {
grid-template-columns: minmax(280px, 0.72fr) minmax(0, 1fr);
padding-bottom: 260px;
}
.evidence-layout .decision-pane {
grid-column: auto;
}
.pane {
min-width: 0;
padding: 16px;
box-shadow: var(--shadow-sm);
}
.pane-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 14px;
padding-bottom: 11px;
border-bottom: 1px solid var(--hair-soft);
}
.pane-heading.vertical {
display: block;
}
.pane-heading h2 {
margin: 0;
font-size: 16px;
line-height: 1.25;
letter-spacing: -0.01em;
}
.icon-actions {
display: flex;
gap: 6px;
}
.icon-actions button {
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(--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: 7px;
color: var(--ink-soft);
font-size: 12px;
}
.fact-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
margin-top: 14px;
}
.fact-item {
min-height: 54px;
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-faint);
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;
}
.similar-strip {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
margin-top: 14px;
}
.similar-item {
display: grid;
gap: 5px;
min-width: 0;
}
.similar-item img {
width: 100%;
aspect-ratio: 4 / 3;
border: 1px solid var(--hair);
border-radius: var(--r-sm);
object-fit: cover;
}
.similar-item span {
color: var(--ink-soft);
font-size: 12px;
overflow-wrap: anywhere;
}
.reason-list {
display: grid;
gap: 12px;
margin-bottom: 12px;
}
.evidence-summary-board {
display: grid;
gap: 12px;
padding: 14px;
border: 1px solid var(--hair);
border-radius: var(--radius);
background: var(--card);
}
.summary-lead {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 10px;
align-items: center;
}
.summary-lead strong {
display: block;
font-size: 17px;
letter-spacing: -0.01em;
}
.summary-lead span:not(.risk-badge) {
display: block;
margin-top: 3px;
color: var(--ink-soft);
font-size: 12px;
}
.summary-stat-grid {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 8px;
}
.summary-stat {
min-width: 0;
padding: 9px 10px;
border: 1px solid var(--hair-soft);
border-radius: var(--r-sm);
background: var(--card-raised);
}
.summary-stat.has-signal {
border-color: var(--ochre-line);
background: var(--ochre-tint);
}
.summary-stat span {
display: block;
color: var(--ink-faint);
font-size: 10px;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.summary-stat strong {
display: block;
margin-top: 3px;
font-family: var(--font-mono);
font-size: 19px;
letter-spacing: -0.02em;
}
.evidence-groups {
display: grid;
gap: 14px;
}
.evidence-next-actions {
margin-bottom: 12px;
}
.evidence-next-action-panel {
display: grid;
gap: 10px;
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,
.evidence-next-action-panel span {
display: block;
}
.evidence-next-action-panel strong {
letter-spacing: -0.01em;
}
.evidence-next-action-panel span {
margin-top: 3px;
color: var(--ink-soft);
font-size: 12px;
}
.evidence-followup-reasons {
display: grid;
gap: 4px;
margin: 0;
padding-left: 18px;
color: var(--ink-soft);
font-size: 13px;
}
.suggested-query-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.evidence-group {
display: grid;
gap: 10px;
min-width: 0;
padding: 12px;
border: 1px solid var(--hair);
border-radius: var(--radius);
background: var(--card-raised);
}
.evidence-group-strong {
border-color: var(--ochre-line);
background: var(--ochre-tint-soft);
box-shadow: 0 0 0 1px rgba(176, 125, 24, 0.08);
}
.evidence-group-weak {
background: var(--card);
}
.evidence-group-head {
display: flex;
align-items: start;
justify-content: space-between;
gap: 10px;
}
.evidence-group h3 {
margin: 0;
color: var(--ink);
font-size: 15px;
letter-spacing: -0.01em;
}
.evidence-group-head span {
display: block;
margin-top: 3px;
color: var(--ink-soft);
font-size: 12px;
}
.evidence-group-head strong {
flex: 0 0 auto;
min-width: 42px;
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;
}
.evidence-card-grid {
display: grid;
grid-template-columns: 1fr;
gap: 9px;
}
.evidence-details {
display: grid;
gap: 9px;
}
.evidence-details summary {
cursor: pointer;
width: max-content;
max-width: 100%;
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: 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 {
margin-bottom: 9px;
}
.evidence-card-grid-detail {
padding-top: 0;
}
.evidence-row,
.query-row,
.result-row,
.knowledge-row,
.correction-row,
.provider-row {
display: grid;
gap: 8px;
min-width: 0;
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: var(--card-raised);
}
.evidence-row.no-preview {
grid-template-columns: minmax(0, 1fr) auto;
}
.evidence-main {
min-width: 0;
}
.evidence-preview {
display: block;
width: 118px;
min-width: 118px;
align-self: start;
}
.evidence-preview-empty {
display: none;
}
.evidence-preview img {
display: block;
width: 118px;
aspect-ratio: 1 / 1;
border: 1px solid var(--hair);
border-radius: var(--r-sm);
background: var(--card-raised);
object-fit: cover;
}
.evidence-link {
display: inline-block;
max-width: 100%;
margin-top: 7px;
color: var(--blue);
font-family: var(--font-mono);
font-size: 12px;
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,
.row-title {
display: flex;
/* Wraps because the chips beside the title do not. source-chip and host-chip
are both rigid, and at phone width they leave the headline a few dozen
pixels - which `overflow-wrap: anywhere` below then honours by breaking it
one character per row. With wrapping the title drops to its own line
intact, and desktop layout is unchanged because nothing overflows there. */
flex-wrap: wrap;
align-items: center;
gap: 7px;
min-width: 0;
font-weight: 700;
letter-spacing: -0.01em;
}
.evidence-title span:last-child,
.row-title span:last-child {
min-width: 0;
overflow-wrap: anywhere;
}
/* The title claims the space the chips beside it do not, so `overflow-wrap:
anywhere` above stays a wrapping rule and never becomes a one-character
column. The floor is what makes the container's `flex-wrap` fire: without it
the title shrinks to the residue left by the chips instead of moving to the
next line. */
.evidence-title > span:last-child {
flex: 1 1 auto;
min-width: 14ch;
}
.evidence-meta,
.row-meta {
display: flex;
flex-wrap: wrap;
gap: 6px 10px;
margin-top: 5px;
color: var(--ink-soft);
font-size: 12px;
}
.evidence-actions {
display: flex;
flex-direction: column;
gap: 6px;
align-items: flex-end;
}
.knowledge-row.watchlist {
border-color: var(--ochre-line);
background: var(--ochre-tint-soft);
}
/* ===================== Decision pane / floating ==================== */
.recommendation-box {
display: grid;
gap: 8px;
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 {
position: fixed;
right: 24px;
bottom: 24px;
z-index: 20;
width: min(310px, calc(100vw - 48px));
max-height: calc(100vh - 132px);
overflow: auto;
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 {
margin-bottom: 10px;
}
.floating-decision-panel .recommendation-box {
margin-bottom: 10px;
padding: 11px;
}
.floating-decision-panel .recommendation-box strong {
font-size: 15px;
}
.floating-decision-panel .memo-field textarea {
min-height: 76px;
}
.form-error {
min-height: 18px;
margin: 6px 0 8px;
color: var(--red);
font-size: 12px;
font-weight: 700;
}
.decision-actions {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
}
.decision-secondary {
margin-top: 10px;
}
.decision-secondary summary {
cursor: pointer;
color: var(--ink-soft);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.07em;
text-transform: uppercase;
}
.approve-action {
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 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 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: 11px;
border: 1px solid var(--hair-soft);
border-radius: var(--r-sm);
background: var(--card-sunk);
font-size: 13px;
}
.quick-actions {
display: grid;
grid-template-columns: 1fr;
gap: 8px;
margin-top: 12px;
}
.quick-actions button {
width: 100%;
}
/* ===================== Split workspace / forms ===================== */
.split-workspace {
display: grid;
grid-template-columns: minmax(280px, 0.85fr) minmax(420px, 1.15fr);
gap: 16px;
align-items: start;
}
.manual-query-form,
.candidate-collection-form,
.collection-promotion-form,
.knowledge-form {
display: grid;
gap: 12px;
margin-bottom: 16px;
}
.manual-query-form {
grid-template-columns: minmax(160px, 0.4fr) minmax(240px, 1fr) auto;
align-items: end;
}
.candidate-collection-form {
grid-template-columns: minmax(120px, 0.35fr) minmax(220px, 1fr) auto;
align-items: end;
}
.collection-promotion-form {
grid-template-columns: minmax(160px, 1fr) minmax(140px, 0.7fr);
align-items: end;
padding-top: 2px;
}
.collection-promotion-form .wide-field {
grid-column: 1 / -1;
}
.collection-promotion-form button {
justify-self: start;
}
.candidate-selection-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 2px 0 10px;
}
.collection-candidates {
display: grid;
gap: 8px;
margin-bottom: 14px;
}
.candidate-card {
display: grid;
grid-template-columns: auto 74px minmax(0, 1fr) auto;
gap: 10px;
align-items: center;
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 {
display: grid;
gap: 6px;
justify-items: stretch;
align-self: center;
}
.candidate-card.promoted {
background: var(--card-sunk);
}
.candidate-select {
display: grid;
place-items: center;
width: 26px;
min-height: 38px;
}
.candidate-select input {
width: 18px;
height: 18px;
accent-color: var(--teal);
}
.candidate-thumb {
width: 74px;
aspect-ratio: 1;
border: 1px solid var(--hair);
border-radius: var(--r-sm);
background: var(--card-sunk);
object-fit: cover;
}
.candidate-thumb.empty {
background: var(--card-sunk);
}
.candidate-main {
min-width: 0;
}
.compact-heading {
margin-top: 4px;
margin-bottom: 8px;
}
.section-divider {
height: 1px;
margin: 16px 0;
background: var(--hair);
}
.inline-status {
min-height: 20px;
margin-bottom: 10px;
color: var(--ink-soft);
font-size: 13px;
}
.stack-list {
display: grid;
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: var(--card-sunk);
color: var(--ink-soft);
}
.knowledge-thumb {
grid-area: thumb;
width: 72px;
aspect-ratio: 1;
border: 1px solid var(--hair);
border-radius: var(--r-sm);
background: var(--card-sunk);
object-fit: cover;
}
.knowledge-thumb.empty {
background: var(--card-sunk);
}
.knowledge-main {
grid-area: main;
display: grid;
gap: 7px;
min-width: 0;
}
.knowledge-title {
font-size: 15px;
letter-spacing: -0.01em;
}
.knowledge-detail-line {
display: flex;
align-items: center;
gap: 8px 12px;
min-width: 0;
white-space: nowrap;
overflow: hidden;
}
.knowledge-chip-row {
display: flex;
flex: 0 0 auto;
flex-wrap: nowrap;
gap: 6px;
align-items: center;
}
.knowledge-chip-row .provenance-chip,
.knowledge-chip-row .watchlist-chip {
flex: 0 0 auto;
white-space: nowrap;
}
.knowledge-meta {
flex: 1 1 auto;
align-items: center;
gap: 5px 12px;
min-width: 0;
margin-top: 0;
overflow: hidden;
white-space: nowrap;
}
.knowledge-meta span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.knowledge-actions {
grid-area: actions;
display: flex;
flex-direction: column;
gap: 6px;
align-items: stretch;
min-width: 128px;
}
.chip-row {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.provenance-chip.manual {
border-color: var(--teal-line);
background: var(--teal-tint);
color: var(--teal);
}
.provenance-chip.automatic {
border-color: var(--ochre-line);
background: var(--ochre-tint);
color: var(--amber);
}
.correction-row {
grid-template-columns: minmax(0, 1fr) auto;
}
/* ========================= Providers =============================== */
.provider-list {
display: grid;
gap: 12px;
}
.provider-row {
grid-template-columns: minmax(0, 1fr) minmax(240px, 0.45fr) auto;
align-items: center;
background: var(--card-raised);
}
.provider-summary {
display: grid;
gap: 6px;
}
.quota-meter {
height: 9px;
border-radius: var(--r-pill);
background: var(--paper-2);
overflow: hidden;
box-shadow: inset 0 1px 2px rgba(23, 32, 41, 0.12);
}
.quota-meter span {
display: block;
height: 100%;
border-radius: inherit;
background: var(--teal);
transition: width 600ms var(--ease);
}
.provider-actions {
display: flex;
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;
}
.audit-table th:nth-child(3),
.audit-table td:nth-child(3) {
width: 172px;
}
.audit-table th:nth-child(4),
.audit-table td:nth-child(4) {
width: var(--audit-object-width);
}
.audit-table th:nth-child(5),
.audit-table td:nth-child(5) {
width: var(--audit-object-width);
}
.empty-state {
display: grid;
place-items: center;
min-height: 94px;
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;
}
}
@media (max-width: 1380px) {
/* The 5-track toolbar needs ~1364px of viewport (track minimums + rail +
paddings); collapse it before that point, not at the generic 1180px step. */
.toolbar {
grid-template-columns: 1fr 1fr 1fr;
}
.filter-search {
grid-column: span 2;
}
}
@media (max-width: 1180px) {
.case-layout {
grid-template-columns: minmax(280px, 0.9fr) minmax(360px, 1.1fr);
}
.decision-pane {
grid-column: 1 / -1;
}
.floating-decision-panel {
grid-column: auto;
}
}
@media (max-width: 980px) {
.app-shell {
grid-template-columns: 1fr;
}
.nav-rail {
position: static;
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
height: auto;
padding: 12px;
}
.brand-block,
.nav-footer {
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;
}
.product-purpose,
.queue-health,
.provider-pulse,
.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));
overflow: hidden;
}
.case-layout,
.operator-workflow,
.split-workspace,
.provider-row {
grid-template-columns: 1fr;
}
.evidence-layout {
padding-bottom: 300px;
}
.summary-stat-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
@media (max-width: 680px) {
.main-surface {
padding: 14px;
}
.section-header {
display: grid;
align-items: start;
}
.toolbar,
.manual-query-form,
.candidate-collection-form,
.collection-promotion-form {
grid-template-columns: 1fr;
}
.segmented {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.nav-rail {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.decision-actions,
.fact-grid,
.similar-strip,
.summary-stat-grid {
grid-template-columns: 1fr;
}
.floating-decision-panel {
right: 10px;
bottom: 10px;
width: calc(100vw - 20px);
max-height: 46vh;
padding: 10px;
}
.floating-decision-panel .decision-actions {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.floating-decision-panel .memo-field textarea {
min-height: 58px;
}
.evidence-row,
.evidence-row.no-preview,
.candidate-card {
grid-template-columns: 1fr;
}
.knowledge-detail-line,
.knowledge-meta {
flex-wrap: wrap;
white-space: normal;
}
.knowledge-row {
grid-template-columns: 56px minmax(0, 1fr);
grid-template-areas:
"thumb main"
"actions actions";
}
.knowledge-thumb {
width: 56px;
}
.knowledge-actions {
min-width: 0;
}
.evidence-preview,
.evidence-preview img,
.candidate-thumb {
width: 100%;
min-width: 0;
}
.evidence-actions {
flex-direction: row;
align-items: stretch;
}
th,
td {
padding: 8px;
}
.table-shell {
overflow: visible;
box-shadow: none;
}
.queue-table,
.queue-table tbody,
.queue-table tr,
.queue-table td {
display: block;
width: 100%;
}
.queue-table thead {
display: none;
}
.queue-table tbody {
display: grid;
gap: 10px;
}
.queue-table tr {
display: grid;
grid-template-columns: 24px 72px minmax(0, 1fr);
gap: 8px 10px;
align-items: start;
padding: 12px;
border: 1px solid var(--hair);
border-radius: var(--radius);
background: var(--card-raised);
box-shadow: var(--shadow-sm);
}
.queue-table td {
min-width: 0;
padding: 0;
border-bottom: 0;
}
.queue-table td:nth-child(1) {
grid-column: 1;
grid-row: 1;
align-self: center;
}
.queue-table td:nth-child(2) {
grid-column: 2;
grid-row: 1 / span 2;
}
.queue-table td:nth-child(3) {
grid-column: 3;
grid-row: 1;
}
.queue-table td:nth-child(4) {
grid-column: 3;
grid-row: 2;
}
.queue-table td:nth-child(5),
.queue-table td:nth-child(6),
.queue-table td:nth-child(7),
.queue-table td:nth-child(8),
.queue-table td:nth-child(9) {
grid-column: 1 / -1;
}
.queue-table td:nth-child(7),
.queue-table td:nth-child(8),
.queue-table td:nth-child(9) {
display: grid;
grid-template-columns: 58px minmax(0, 1fr);
gap: 6px;
color: var(--ink-soft);
font-size: 12px;
}
.queue-table td:nth-child(7)::before {
content: "지원자";
font-weight: 700;
}
.queue-table td:nth-child(8)::before {
content: "판정";
font-weight: 700;
}
.queue-table td:nth-child(9)::before {
content: "시간";
font-weight: 700;
}
.queue-table .thumb {
width: 72px;
height: 56px;
}
.queue-table .row-action {
max-width: 100%;
height: auto;
min-height: 34px;
overflow-wrap: anywhere;
white-space: normal;
}
.queue-table .submission-id {
overflow-wrap: anywhere;
}
.audit-table,
.audit-table tbody,
.audit-table tr,
.audit-table td {
display: block;
width: 100% !important;
}
.audit-table thead {
display: none;
}
.audit-table tbody {
display: grid;
gap: 10px;
}
.audit-table tr {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 6px 10px;
padding: 12px;
border: 1px solid var(--hair);
border-radius: var(--radius);
background: var(--card-raised);
box-shadow: var(--shadow-sm);
}
.audit-table td {
min-width: 0;
padding: 0;
border-bottom: 0;
overflow-wrap: anywhere;
}
.audit-table td:nth-child(1),
.audit-table td:nth-child(2),
.audit-table td:nth-child(3) {
grid-column: 1 / -1;
}
.audit-table td:nth-child(4),
.audit-table td:nth-child(5) {
grid-column: auto;
}
}
.suggested-query-item {
display: inline-flex;
gap: 4px;
align-items: center;
}
.suggested-query-actions {
margin-top: 8px;
}
.knowledge-filter-bar {
display: flex;
flex-wrap: wrap;
gap: 8px 12px;
margin-bottom: 12px;
}
.knowledge-edit-form {
display: grid;
gap: 8px;
margin-top: 8px;
padding: 10px;
border: 1px solid var(--hair);
border-radius: 8px;
}
.knowledge-edit-actions {
display: flex;
gap: 8px;
}
.face-crop-strip .face-crop-item img {
border: 1px solid var(--hair);
border-radius: 6px;
}
.rerun-diff-panel {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
align-items: center;
padding: 10px 12px;
border: 1px solid var(--hair);
border-radius: 8px;
margin-bottom: 12px;
}
.evidence-new-chip {
border: 1px solid var(--teal);
border-radius: 999px;
padding: 0 8px;
font-size: 12px;
color: var(--teal);
}
.evidence-row-new {
box-shadow: inset 0 0 0 2px var(--teal);
}
/* --- Auth gate ------------------------------------------------------- */
/* Shown only when the server runs with COPYRIGHTER_AUTH_TOKEN and a data
route answered 401. Covers the shell so no half-loaded queue is visible
behind it. */
.auth-gate {
position: fixed;
inset: 0;
z-index: 100;
display: grid;
place-items: center;
padding: 24px;
background: rgba(16, 23, 29, 0.55);
backdrop-filter: blur(2px);
}
.auth-gate[hidden] {
display: none;
}
.auth-gate-card {
width: min(420px, 100%);
display: flex;
flex-direction: column;
gap: 10px;
padding: 26px 26px 22px;
background: var(--card-raised);
border: 1px solid var(--hair);
border-radius: var(--r-lg);
box-shadow: var(--shadow-lg);
}
.auth-gate-card h1 {
margin: 0;
font-size: 1.05rem;
letter-spacing: -0.01em;
color: var(--ink);
}
.auth-gate-hint {
margin: 0;
font-size: 0.82rem;
line-height: 1.55;
color: var(--ink-soft);
}
.auth-gate-hint code {
font-family: var(--font-mono);
font-size: 0.78rem;
padding: 1px 5px;
border-radius: var(--r-sm);
background: var(--card-sunk);
border: 1px solid var(--hair-soft);
}
.auth-gate-label {
margin-top: 4px;
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--ink-soft);
}
.auth-gate-input {
width: 100%;
padding: 9px 11px;
font-family: var(--font-mono);
font-size: 0.86rem;
color: var(--ink);
background: var(--card);
border: 1px solid var(--hair-strong);
border-radius: var(--radius);
}
.auth-gate-input:focus {
outline: 2px solid var(--teal-line);
outline-offset: 1px;
border-color: var(--teal);
}
.auth-gate-error {
margin: 0;
font-size: 0.78rem;
color: var(--red);
}
.auth-gate-error[hidden] {
display: none;
}
.auth-gate-card .primary-action {
margin-top: 6px;
justify-content: center;
}
/* --- Batch decisions / review throughput ------------------------------ */
.queue-actions-secondary {
margin-top: 8px;
padding-top: 8px;
border-top: 1px dashed var(--hair);
align-items: center;
}
.bulk-decision-label {
font-size: 0.78rem;
font-weight: 600;
color: var(--ink-soft);
font-variant-numeric: tabular-nums;
}
.review-progress {
margin-left: auto;
font-size: 0.76rem;
color: var(--ink-faint);
font-variant-numeric: tabular-nums;
}
.keyboard-help {
margin: 8px 0 0;
padding: 12px 14px;
font-family: var(--font-mono);
font-size: 0.78rem;
line-height: 1.7;
color: var(--ink-soft);
background: var(--card-sunk);
border: 1px solid var(--hair);
border-radius: var(--radius);
white-space: pre;
overflow-x: auto;
}
.keyboard-help[hidden] {
display: none;
}
/* --- Decision memo presets -------------------------------------------- */
.memo-presets {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-top: 6px;
}
.memo-preset-chip {
padding: 3px 9px;
font-size: 0.74rem;
color: var(--ink-soft);
background: var(--card-sunk);
border: 1px solid var(--hair);
border-radius: var(--r-pill);
cursor: pointer;
}
.memo-preset-chip:hover {
color: var(--teal-deep);
border-color: var(--teal-line);
background: var(--teal-tint-soft);
}
.memo-preset-add {
border-style: dashed;
color: var(--ink-faint);
}
/* --- Duplicate group badge -------------------------------------------- */
/* 동일 이미지 선례. 판정 버튼 바로 위에 서서 "이건 이미 판정한 이미지"를
먼저 읽게 한다 — 이 정보는 원래 큐 배지의 title 에만 있어 마우스를 올린
사람만 볼 수 있었다. */
.prior-decision {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin-top: 8px;
padding: 8px 10px;
border: 1px solid var(--green-line);
border-radius: 8px;
background: var(--green-tint);
font-size: 0.82rem;
}
.prior-decision strong {
color: var(--green);
}
/* 판정 불일치는 승계를 제안하지 않는 상태다. 같은 초록으로 두면 "정리된 것"으로
읽히므로 색을 갈라 운영자가 직접 봐야 하는 신호임을 드러낸다. */
.prior-decision:has(.prior-decision-conflict) {
border-color: var(--amber-line);
background: var(--amber-tint);
}
.prior-decision-conflict {
color: var(--amber);
}
/* 그룹 헤더의 개수와 일괄 액션. 개수 옆에 두어 "이 51건을 한 번에"가 한 시선에
읽히게 한다 — 행마다 누르던 노동이 여기서 없어진다. */
.evidence-group-actions {
display: flex;
align-items: center;
gap: 8px;
}
.duplicate-badge {
display: inline-block;
margin-top: 3px;
padding: 2px 8px;
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.01em;
color: var(--blue);
background: var(--blue-tint);
border: 1px solid var(--blue-line);
border-radius: var(--r-pill);
cursor: pointer;
}
.duplicate-badge:hover {
filter: brightness(0.97);
}
.duplicate-badge.conflicting {
color: var(--red);
background: var(--red-tint);
border-color: var(--red-line);
}
/* --- LLM draft (grounded summary + pull into memo) -------------------- */
.llm-draft {
margin: 8px 0;
padding: 10px 12px;
background: var(--violet-tint);
border: 1px solid var(--violet-line);
border-radius: var(--radius);
}
.llm-draft[hidden] { display: none; }
.llm-draft-head {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.llm-draft-tag {
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.02em;
color: var(--violet);
text-transform: uppercase;
}
.llm-draft-body {
margin: 0 0 8px;
font-size: 0.84rem;
line-height: 1.6;
color: var(--ink);
white-space: pre-wrap;
}
.llm-draft-use {
padding: 4px 10px;
font-size: 0.75rem;
color: var(--violet);
background: var(--card);
border: 1px solid var(--violet-line);
border-radius: var(--r-pill);
cursor: pointer;
}
/* --- Stats / calibration view ---------------------------------------- */
.section-sub {
margin: 4px 0 0;
font-size: 0.82rem;
color: var(--ink-soft);
}
.stats-body { display: flex; flex-direction: column; gap: 18px; }
.stat-tiles {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px;
}
.stat-tile {
display: flex;
flex-direction: column;
gap: 3px;
padding: 14px 16px;
background: var(--card-raised);
border: 1px solid var(--hair);
border-left: 3px solid var(--hair-strong);
border-radius: var(--radius);
box-shadow: var(--shadow-sm);
}
.stat-tile-label { font-size: 0.76rem; font-weight: 600; color: var(--ink-soft); }
.stat-tile-value { font-size: 1.7rem; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.stat-tile-sub { font-size: 0.72rem; color: var(--ink-faint); }
.stat-tile.tone-good { border-left-color: var(--green); }
.stat-tile.tone-good .stat-tile-value { color: var(--green); }
.stat-tile.tone-warn { border-left-color: var(--amber); }
.stat-tile.tone-warn .stat-tile-value { color: var(--amber); }
.stat-tile.tone-bad { border-left-color: var(--red); }
.stat-tile.tone-bad .stat-tile-value { color: var(--red); }
.stat-tile.tone-neutral { border-left-color: var(--slate-line); }
.stats-columns {
display: grid;
grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
gap: 16px;
}
@media (max-width: 900px) {
.stats-columns { grid-template-columns: 1fr; }
}
.stats-card {
padding: 16px;
background: var(--card);
border: 1px solid var(--hair);
border-radius: var(--r-lg);
}
.stats-card h2 { margin: 0 0 2px; font-size: 0.95rem; color: var(--ink); }
.stats-matrix { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 0.82rem; }
.stats-matrix th, .stats-matrix td { padding: 7px 10px; text-align: center; border-bottom: 1px solid var(--hair-soft); }
.stats-matrix thead th { color: var(--ink-soft); font-weight: 600; font-size: 0.76rem; }
.stats-matrix th[scope="row"] { text-align: left; }
.matrix-cell { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.matrix-cell.has-value { color: var(--ink); font-weight: 600; }
.matrix-total { color: var(--ink-soft); font-weight: 700; font-variant-numeric: tabular-nums; }
.throughput-chart {
display: flex;
align-items: flex-end;
gap: 6px;
height: 160px;
margin-top: 12px;
padding-top: 8px;
}
.throughput-bar {
flex: 1 1 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
gap: 2px;
height: 100%;
min-width: 0;
}
.throughput-bar-fill {
width: 100%;
max-width: 22px;
min-height: 2px;
background: var(--teal);
border-radius: 4px 4px 0 0;
}
.throughput-bar-count { font-size: 0.68rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.throughput-bar-day { font-size: 0.62rem; color: var(--ink-faint); white-space: nowrap; }