POSA_Copyrighter/web/operator-gui/styles.css
changukyu 847f7a852d fix: 게시 호스트를 영속화에서 복구하고 콘솔·LLM 에 노출
P1 3번("매칭 호스트 기반 신뢰출처 구분")을 착수하며 전제를 검증했더니 틀렸다.
호스트는 "이미 저장돼 있고 점수만 안 쓰는" 게 아니라 저장 시점에 파괴되고
있었다.

  google_custom_search.py:240,296 이 실호스트(displayLink)를 data["domain"] 에
  넣는데, store_serialization.py:298 이 data["provider"] 를 우선하므로 그 값이
  API 이름으로 덮어써진다. Vision·Naver 는 호스트를 아예 안 넣고 URL 만 준다.
  결과적으로 저장된 모든 행의 domain 이 "google" 이고, LLM 프롬프트 12줄이 전부
  domain=google 이었다 — 저작권 심사에서 가장 결정적인 "어디에 떠 있나"를 모델도
  운영자도 볼 수 없었다. 같은 케이스의 payload 55행 중 41행이 url 을 갖고 있는데도.

domain 필드는 재사용할 수 없다. provider 커버리지 동기화와 CSE 쿼리 시그니처
매칭이 그 값을 키로 쓰고(store_serialization.py:195, 599) 콘솔도 provider
라벨로 렌더한다. 그래서 host 를 별도 키로 신설했다.

  _result_host(): data["domain"] 이 provider 와 다르면 그것을, 아니면 결과/페이지
  URL 에서 호스트를 파싱한다. www 제거·소문자화. 파싱 실패는 "호스트 없음"이지
  영속화 실패가 아니다.

  점수에는 영향이 없다 — RiskScorer 는 domain 도 host 도 읽지 않는다. 증거 id 도
  안 바뀐다: _evidence_id 는 도메인 객체의 data 를 해싱하고 payload 를 해싱하지
  않으므로 payload 키 추가가 id 를 움직이지 않는다.

  PROMPT_DATA_FIELDS 의 domain -> host. 화이트리스트 크기는 그대로.

  콘솔은 증거 카드 제목 줄에 monospace host-chip 을 붙인다. 기존 저장 행에는 host
  키가 없으므로 evidenceHost() 가 저장된 URL 에서 파생한다 — 292행 마이그레이션도,
  bootstrap 마다 행당 파싱도 필요 없다(렌더되는 행만 지나간다).

실렌더 검증(127.0.0.1:9512, 실 DB)에서만 드러난 레이아웃 결함 2건을 고쳤다.
코드 리딩으로는 보이지 않았다.
  1) 긴 CDN 호스트(cloudfront-ap-northeast-1.images.arcpublishing.com)가 제목 줄
     폭을 다 먹어, evidence-title 의 overflow-wrap:anywhere 와 결합해 제목이 한
     글자씩 세로로 깨졌다. 칩을 max-width 20ch 로 묶고 제목에 flex:1 1 auto 를 줘
     남는 폭을 갖게 했다.
  2) 그 수정 후에는 반대로 biz.chosun.com(14자)까지 잘렸다. 칩을 flex:0 0 auto 로
     고정했다 — 제목("구글 full image match found")은 전 행이 동일한 상용구이고
     호스트가 그 행의 실제 내용이므로, 들어갈 수 있는 호스트가 잘리는 쪽이 잘못이다.
  최종: 칩 82개 전부 유효 호스트 형태(TLD com/edu/hk/jp/kr/net/org), 가시 칩 41개 중
  잘리는 것 4개(모두 20자 초과 CDN), 콘솔 에러 0.

PROVENANCE 배점은 보류한다. 두 조건이 다 안 맞는다. (a) detect_screen_capture 의
카테고리가 geometry 와 subtitle_band 둘뿐인데 2개를 요구하므로 라벨의 실제 의미는
"geometry AND 자막"이고, 자막 없는 방송 캡처는 구조적으로 도달 불가다 — 임계를
낮추는 게 아니라 독립적인 세 번째 신호가 필요하고 그건 새 탐지기다. 한계를 실측과
함께 상수 옆에 기록했다. (b) "새 탐지 신호는 0점 라벨로 출시한 뒤 decision_stats
상관을 보고 가중치 부여"가 2026-07-25 결정이고, 판정 0건인 지금 배점하면 그 결정을
위반한다.

신뢰출처 판정 자체는 "어느 도메인이 우리 것/라이선스 계약처인가"라는 운영자
데이터가 있어야 한다. 호스트가 살아났으므로 목록만 정해지면 cleared_reference 와
같은 패턴(점수 0 + 사유 노출)으로 붙는다.

테스트 4건 추가. pytest 574 passed(착수 전 570), 린트 클린.
2026-08-09 08:51:12 +09:00

3178 lines
61 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. */
.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;
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. */
.evidence-title > span:last-child {
flex: 1 1 auto;
}
.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 -------------------------------------------- */
.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; }