API 연결 실패 또는 제출 이미지 없음 상태입니다.
`;
document.getElementById("recommendation-box").innerHTML = `
@@ -204,6 +229,7 @@
결정 메모
+
diff --git a/web/operator-gui/styles.css b/web/operator-gui/styles.css
index 2858953..f44342b 100644
--- a/web/operator-gui/styles.css
+++ b/web/operator-gui/styles.css
@@ -2817,3 +2817,191 @@ tbody tr.selected-row,
.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);
+}