fix: resolve code-review findings from the clean-review restyle
Correctness: - Make the local-artifact audit test skip on fresh clones (data/ is gitignored), so the suite passes outside this workstation - Drop the transform from the viewRise entrance animation: an animated transform made .view.active a containing block for 320ms and threw the fixed decision panel off-screen on every workbench entry - Collapse the queue toolbar at 1380px instead of 1180px; 1280x800 laptops no longer get a horizontal scrollbar (verified live) - Serve .woff2 as font/woff2 with an immutable cache header so the 2MB bundled font is fetched once, not per page load (with test) - Clip overflow on top-bar status chips (long apiError strings spilled over neighbors at 981-1180px) - Give queue-row selection a selector that outranks the even-row zebra stripe (selection background was parity-dependent) Cleanup: - Replace the stale old-palette focus ring and ::selection literals with color-mix over var(--teal) - Delete dead tokens: unused back-compat aliases (the comment claiming they were referenced was false), --rail-bot, --ochre-deep, and --font-stamp (identical to --font-ui since the Pretendard switch) - Tokenize scattered raw colors: rail ink scale, soft tint levels, inset-well and bevel shadows, naver/internal source-chip triplets - Remove the asset-preload div and three orphan SVGs nothing renders; tests now reject reintroducing them Verified: 359 tests pass; Playwright audit at 1440/1280/390 shows zero horizontal overflow on all views, Pretendard active, decision panel fixed at the viewport corner mid-animation.
This commit is contained in:
parent
ed701bd436
commit
7cac0b3835
8 changed files with 102 additions and 114 deletions
|
|
@ -12,6 +12,16 @@ from urllib.parse import unquote, urlparse
|
||||||
from rights_filter.server.image_store import LocalSubmissionImageStore, SUPPORTED_IMAGE_SUFFIXES
|
from rights_filter.server.image_store import LocalSubmissionImageStore, SUPPORTED_IMAGE_SUFFIXES
|
||||||
from rights_filter.server.sqlite_store import CopyrighterStore
|
from rights_filter.server.sqlite_store import CopyrighterStore
|
||||||
|
|
||||||
|
# Windows Python builds may not know .woff2, which would serve the bundled
|
||||||
|
# 2MB font as application/octet-stream.
|
||||||
|
mimetypes.add_type("font/woff2", ".woff2")
|
||||||
|
mimetypes.add_type("font/woff", ".woff")
|
||||||
|
|
||||||
|
# Bundled font binaries never change between releases; without any cache
|
||||||
|
# validator the browser re-downloads the full file on every page load.
|
||||||
|
_IMMUTABLE_SUFFIXES = {".woff2", ".woff"}
|
||||||
|
_IMMUTABLE_CACHE_CONTROL = "public, max-age=31536000, immutable"
|
||||||
|
|
||||||
|
|
||||||
def build_server(
|
def build_server(
|
||||||
host: str,
|
host: str,
|
||||||
|
|
@ -214,6 +224,8 @@ def build_server(
|
||||||
self.send_response(HTTPStatus.OK)
|
self.send_response(HTTPStatus.OK)
|
||||||
self.send_header("Content-Type", content_type)
|
self.send_header("Content-Type", content_type)
|
||||||
self.send_header("Content-Length", str(len(data)))
|
self.send_header("Content-Length", str(len(data)))
|
||||||
|
if path.suffix.lower() in _IMMUTABLE_SUFFIXES:
|
||||||
|
self.send_header("Cache-Control", _IMMUTABLE_CACHE_CONTROL)
|
||||||
if untrusted:
|
if untrusted:
|
||||||
# Neutralize stored XSS from operator-uploaded / externally
|
# Neutralize stored XSS from operator-uploaded / externally
|
||||||
# collected media (an SVG can carry an inline <script>). `sandbox`
|
# collected media (an SVG can carry an inline <script>). `sandbox`
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
APP_DIR = ROOT / "web" / "operator-gui"
|
APP_DIR = ROOT / "web" / "operator-gui"
|
||||||
|
|
@ -270,6 +272,11 @@ def test_design_contract_has_accessibility_risk_states_and_responsive_layouts():
|
||||||
assert class_name in styles
|
assert class_name in styles
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
not UI_OVERHAUL_FINAL_RESULTS.exists(),
|
||||||
|
reason="local-only audit artifacts: data/ is gitignored, so fresh clones lack them; "
|
||||||
|
"regenerate with the live-server Playwright audit before release sign-off",
|
||||||
|
)
|
||||||
def test_ui_overhaul_final_audit_has_no_overflow_and_required_screenshots():
|
def test_ui_overhaul_final_audit_has_no_overflow_and_required_screenshots():
|
||||||
results = json.loads(_read(UI_OVERHAUL_FINAL_RESULTS))
|
results = json.loads(_read(UI_OVERHAUL_FINAL_RESULTS))
|
||||||
|
|
||||||
|
|
@ -772,11 +779,12 @@ def test_visual_assets_are_referenced_for_review_images():
|
||||||
html = _read(INDEX)
|
html = _read(INDEX)
|
||||||
assets_dir = APP_DIR / "assets"
|
assets_dir = APP_DIR / "assets"
|
||||||
|
|
||||||
for asset_name in [
|
assert (assets_dir / "case-portrait.svg").exists()
|
||||||
"case-portrait.svg",
|
assert "assets/case-portrait.svg" in html or "assets/case-portrait.svg" in _read(APP_JS)
|
||||||
|
|
||||||
|
for orphan_asset in [
|
||||||
"case-character.svg",
|
"case-character.svg",
|
||||||
"case-emblem.svg",
|
"case-emblem.svg",
|
||||||
"match-web.svg",
|
"match-web.svg",
|
||||||
]:
|
]:
|
||||||
assert (assets_dir / asset_name).exists()
|
assert not (assets_dir / orphan_asset).exists(), f"{orphan_asset} is unused; do not reintroduce it"
|
||||||
assert f"assets/{asset_name}" in html or f"assets/{asset_name}" in _read(APP_JS)
|
|
||||||
|
|
|
||||||
|
|
@ -1130,3 +1130,33 @@ def test_operator_gui_uses_api_bootstrap():
|
||||||
|
|
||||||
assert "/api/bootstrap" in app_js
|
assert "/api/bootstrap" in app_js
|
||||||
assert "refreshFromApi" in app_js
|
assert "refreshFromApi" in app_js
|
||||||
|
|
||||||
|
|
||||||
|
def test_http_server_serves_bundled_font_with_mime_and_immutable_cache(tmp_path: Path):
|
||||||
|
static_dir, image_store, store = _fixtures(tmp_path)
|
||||||
|
fonts_dir = static_dir / "assets" / "fonts"
|
||||||
|
fonts_dir.mkdir(parents=True)
|
||||||
|
(fonts_dir / "PretendardVariable.woff2").write_bytes(b"wOF2fake")
|
||||||
|
server = build_server(
|
||||||
|
host="127.0.0.1",
|
||||||
|
port=0,
|
||||||
|
store=store,
|
||||||
|
image_store=image_store,
|
||||||
|
static_dir=static_dir,
|
||||||
|
)
|
||||||
|
_start(server)
|
||||||
|
base = f"http://127.0.0.1:{server.server_port}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
with urlopen(base + "/assets/fonts/PretendardVariable.woff2", timeout=5) as response:
|
||||||
|
font_headers = dict(response.headers)
|
||||||
|
font_body = response.read()
|
||||||
|
with urlopen(base + "/index.html", timeout=5) as response:
|
||||||
|
html_headers = dict(response.headers)
|
||||||
|
finally:
|
||||||
|
server.shutdown()
|
||||||
|
|
||||||
|
assert font_body == b"wOF2fake"
|
||||||
|
assert font_headers["Content-Type"] == "font/woff2"
|
||||||
|
assert font_headers["Cache-Control"] == "public, max-age=31536000, immutable"
|
||||||
|
assert "Cache-Control" not in html_headers
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480" role="img" aria-labelledby="title desc">
|
|
||||||
<title id="title">Generic character submission</title>
|
|
||||||
<desc id="desc">A stylized character-style image used as a rights review sample.</desc>
|
|
||||||
<rect width="640" height="480" fill="#dfe8e4"/>
|
|
||||||
<path d="M104 410c28-121 103-188 216-188s188 67 216 188z" fill="#435b50"/>
|
|
||||||
<circle cx="320" cy="198" r="104" fill="#f0c7a8" stroke="#26302d" stroke-width="7"/>
|
|
||||||
<path d="M218 182c18-93 67-139 149-139 61 13 91 55 90 127-60-39-135-40-239 12z" fill="#5c352c"/>
|
|
||||||
<circle cx="284" cy="192" r="14" fill="#172124"/>
|
|
||||||
<circle cx="362" cy="192" r="14" fill="#172124"/>
|
|
||||||
<circle cx="280" cy="187" r="5" fill="#fffaf2"/>
|
|
||||||
<circle cx="358" cy="187" r="5" fill="#fffaf2"/>
|
|
||||||
<path d="M281 244c33 23 61 23 88 0" fill="none" stroke="#9a4c41" stroke-width="10" stroke-linecap="round"/>
|
|
||||||
<path d="M184 110l58-50 26 62M454 112l-58-50-26 62" fill="#435b50" stroke="#26302d" stroke-width="7" stroke-linejoin="round"/>
|
|
||||||
<rect x="72" y="66" width="118" height="28" fill="#d1a339"/>
|
|
||||||
<rect x="72" y="108" width="88" height="20" fill="#5c7f99"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
|
@ -1,11 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480" role="img" aria-labelledby="title desc">
|
|
||||||
<title id="title">Generic emblem submission</title>
|
|
||||||
<desc id="desc">A geometric emblem image used as a review sample.</desc>
|
|
||||||
<rect width="640" height="480" fill="#ebe7dc"/>
|
|
||||||
<rect x="74" y="54" width="492" height="372" rx="8" fill="#f7f4ee" stroke="#26343a" stroke-width="6"/>
|
|
||||||
<circle cx="320" cy="238" r="132" fill="#2f6272"/>
|
|
||||||
<path d="M320 113l40 83 91 14-66 64 16 90-81-43-81 43 16-90-66-64 91-14z" fill="#d6a33a" stroke="#26343a" stroke-width="8" stroke-linejoin="round"/>
|
|
||||||
<circle cx="320" cy="238" r="52" fill="#f4efe6" stroke="#26343a" stroke-width="7"/>
|
|
||||||
<path d="M252 86h136M222 392h196" stroke="#8d7961" stroke-width="12" stroke-linecap="round"/>
|
|
||||||
<rect x="460" y="92" width="58" height="24" fill="#c44b3f"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 843 B |
|
|
@ -1,14 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480" role="img" aria-labelledby="title desc">
|
|
||||||
<title id="title">Matched web result</title>
|
|
||||||
<desc id="desc">A generic web result thumbnail for evidence comparison.</desc>
|
|
||||||
<rect width="640" height="480" fill="#f1eee5"/>
|
|
||||||
<rect x="60" y="62" width="520" height="356" rx="8" fill="#ffffff" stroke="#29363a" stroke-width="6"/>
|
|
||||||
<rect x="92" y="94" width="168" height="132" fill="#dfe8e4" stroke="#6f7d7a" stroke-width="4"/>
|
|
||||||
<circle cx="176" cy="144" r="42" fill="#d39a72"/>
|
|
||||||
<path d="M127 210c14-44 45-68 87-68s73 25 87 68z" fill="#315f72"/>
|
|
||||||
<path d="M292 106h188M292 144h238M292 182h160" stroke="#465458" stroke-width="12" stroke-linecap="round"/>
|
|
||||||
<rect x="92" y="260" width="416" height="18" fill="#c7bca7"/>
|
|
||||||
<rect x="92" y="300" width="356" height="18" fill="#c7bca7"/>
|
|
||||||
<rect x="92" y="340" width="198" height="18" fill="#c7bca7"/>
|
|
||||||
<rect x="470" y="332" width="52" height="26" fill="#c44b3f"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 977 B |
|
|
@ -13,13 +13,6 @@
|
||||||
<script src="app.js" defer></script>
|
<script src="app.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body data-internal-only="true">
|
<body data-internal-only="true">
|
||||||
<div class="asset-preload" aria-hidden="true">
|
|
||||||
<img src="assets/case-portrait.svg" alt="">
|
|
||||||
<img src="assets/case-character.svg" alt="">
|
|
||||||
<img src="assets/case-emblem.svg" alt="">
|
|
||||||
<img src="assets/match-web.svg" alt="">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="app-shell">
|
<div class="app-shell">
|
||||||
<nav class="nav-rail" aria-label="내부 운영 콘솔">
|
<nav class="nav-rail" aria-label="내부 운영 콘솔">
|
||||||
<div class="brand-block">
|
<div class="brand-block">
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
:root {
|
:root {
|
||||||
/* --- Type ---------------------------------------------------------- */
|
/* --- Type ---------------------------------------------------------- */
|
||||||
--font-ui: "Pretendard Variable", Pretendard, "Malgun Gothic", "Apple SD Gothic Neo", "Segoe UI", system-ui, sans-serif;
|
--font-ui: "Pretendard Variable", Pretendard, "Malgun Gothic", "Apple SD Gothic Neo", "Segoe UI", system-ui, sans-serif;
|
||||||
--font-stamp: "Pretendard Variable", Pretendard, "Malgun Gothic", "Segoe UI Semibold", sans-serif;
|
|
||||||
--font-mono: "Cascadia Mono", "Consolas", ui-monospace, "DejaVu Sans Mono", monospace;
|
--font-mono: "Cascadia Mono", "Consolas", ui-monospace, "DejaVu Sans Mono", monospace;
|
||||||
|
|
||||||
/* --- Surfaces (neutral cool) --------------------------------------- */
|
/* --- Surfaces (neutral cool) --------------------------------------- */
|
||||||
|
|
@ -39,17 +38,20 @@
|
||||||
--teal: #1f6f8b;
|
--teal: #1f6f8b;
|
||||||
--teal-deep: #14495c;
|
--teal-deep: #14495c;
|
||||||
--teal-tint: #e7f1f5;
|
--teal-tint: #e7f1f5;
|
||||||
|
--teal-tint-soft: #eef6f9;
|
||||||
--teal-line: #a9cdda;
|
--teal-line: #a9cdda;
|
||||||
|
|
||||||
--ochre: #b07d18;
|
--ochre: #b07d18;
|
||||||
--ochre-deep: #8a5f0d;
|
|
||||||
--ochre-tint: #faf2dd;
|
--ochre-tint: #faf2dd;
|
||||||
|
--ochre-tint-soft: #fdf9ee;
|
||||||
--ochre-line: #e2c887;
|
--ochre-line: #e2c887;
|
||||||
|
|
||||||
/* --- Dark rail ----------------------------------------------------- */
|
/* --- Dark rail ----------------------------------------------------- */
|
||||||
--rail-top: #222b33;
|
--rail: #222b33;
|
||||||
--rail-bot: #1a232b;
|
|
||||||
--rail-edge: #10171d;
|
--rail-edge: #10171d;
|
||||||
|
--rail-ink: #d3dce4;
|
||||||
|
--rail-ink-soft: #9fb0bd;
|
||||||
|
--rail-ink-strong: #e8edf2;
|
||||||
|
|
||||||
/* --- Risk / signal semantics --------------------------------------- */
|
/* --- Risk / signal semantics --------------------------------------- */
|
||||||
--red: #b03a2e; --red-tint: #fceae7; --red-line: #e4aca3;
|
--red: #b03a2e; --red-tint: #fceae7; --red-line: #e4aca3;
|
||||||
|
|
@ -57,6 +59,8 @@
|
||||||
--green: #25684a; --green-tint: #e7f3ec; --green-line: #a6d3b9;
|
--green: #25684a; --green-tint: #e7f3ec; --green-line: #a6d3b9;
|
||||||
--violet: #564d75; --violet-tint: #efebf6;--violet-line: #c7bbd9;
|
--violet: #564d75; --violet-tint: #efebf6;--violet-line: #c7bbd9;
|
||||||
--blue: #2c5a8c; --blue-tint: #e8f0fa; --blue-line: #b1c6e2;
|
--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 ------------------------------------------ */
|
/* --- Geometry & elevation ------------------------------------------ */
|
||||||
--r-sm: 6px;
|
--r-sm: 6px;
|
||||||
|
|
@ -68,18 +72,11 @@
|
||||||
--shadow: 0 1px 3px rgba(23, 32, 41, 0.05), 0 10px 24px -18px rgba(23, 32, 41, 0.25);
|
--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);
|
--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);
|
--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);
|
--ease: cubic-bezier(0.22, 0.61, 0.36, 1);
|
||||||
|
|
||||||
/* --- Back-compat aliases (legacy variable names still referenced) -- */
|
|
||||||
--surface: var(--paper);
|
|
||||||
--surface-strong: var(--card-raised);
|
|
||||||
--surface-muted: var(--paper-2);
|
|
||||||
--line: var(--hair);
|
|
||||||
--line-strong: var(--hair-strong);
|
|
||||||
--accent: var(--teal);
|
|
||||||
--accent-strong: var(--teal-deep);
|
|
||||||
|
|
||||||
--audit-object-width: 24%;
|
--audit-object-width: 24%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +103,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
background: rgba(31, 111, 139, 0.22);
|
background: color-mix(in srgb, var(--teal) 22%, transparent);
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,10 +152,6 @@ textarea:disabled {
|
||||||
background-color: var(--ink-faint);
|
background-color: var(--ink-faint);
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-preload {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sr-only {
|
.sr-only {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
|
|
@ -188,8 +181,8 @@ textarea:disabled {
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
padding: 20px 14px 16px;
|
padding: 20px 14px 16px;
|
||||||
background: var(--rail-top);
|
background: var(--rail);
|
||||||
color: #e8edf2;
|
color: var(--rail-ink-strong);
|
||||||
border-right: 1px solid var(--rail-edge);
|
border-right: 1px solid var(--rail-edge);
|
||||||
box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.04);
|
box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.04);
|
||||||
}
|
}
|
||||||
|
|
@ -217,8 +210,7 @@ textarea:disabled {
|
||||||
|
|
||||||
.brand-block div span {
|
.brand-block div span {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
color: #9fb0bd;
|
color: var(--rail-ink-soft);
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
letter-spacing: 0.12em;
|
letter-spacing: 0.12em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
@ -232,11 +224,10 @@ textarea:disabled {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background: var(--teal);
|
background: var(--teal);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-weight: 750;
|
font-weight: 750;
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
|
box-shadow: var(--bevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-button {
|
.nav-button {
|
||||||
|
|
@ -251,7 +242,7 @@ textarea:disabled {
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: #d3dce4;
|
color: var(--rail-ink);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
transition: background 160ms var(--ease), color 160ms var(--ease), transform 120ms var(--ease);
|
transition: background 160ms var(--ease), color 160ms var(--ease), transform 120ms var(--ease);
|
||||||
|
|
@ -287,7 +278,6 @@ textarea:disabled {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
border: 1px solid currentColor;
|
border: 1px solid currentColor;
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
|
|
@ -313,8 +303,7 @@ textarea:disabled {
|
||||||
padding: 11px 12px;
|
padding: 11px 12px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
color: #d3dce4;
|
color: var(--rail-ink);
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.06em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
@ -383,7 +372,7 @@ textarea:disabled {
|
||||||
border-radius: var(--r-sm);
|
border-radius: var(--r-sm);
|
||||||
background: var(--card-raised);
|
background: var(--card-raised);
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
box-shadow: inset 0 1px 2px rgba(20, 30, 28, 0.04);
|
box-shadow: var(--inset-well);
|
||||||
transition: border-color 140ms var(--ease), box-shadow 140ms var(--ease);
|
transition: border-color 140ms var(--ease), box-shadow 140ms var(--ease);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -415,7 +404,7 @@ textarea:disabled {
|
||||||
.memo-field textarea:focus {
|
.memo-field textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--teal);
|
border-color: var(--teal);
|
||||||
box-shadow: inset 0 1px 2px rgba(20, 30, 28, 0.04), 0 0 0 3px rgba(34, 96, 114, 0.16);
|
box-shadow: var(--inset-well), 0 0 0 3px color-mix(in srgb, var(--teal) 16%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-purpose {
|
.product-purpose {
|
||||||
|
|
@ -456,12 +445,14 @@ textarea:disabled {
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
padding: 7px 11px;
|
padding: 7px 11px;
|
||||||
|
overflow: hidden;
|
||||||
border: 1px solid var(--hair);
|
border: 1px solid var(--hair);
|
||||||
border-radius: var(--r-sm);
|
border-radius: var(--r-sm);
|
||||||
background: var(--card-raised);
|
background: var(--card-raised);
|
||||||
color: var(--ink-soft);
|
color: var(--ink-soft);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -550,21 +541,18 @@ textarea:disabled {
|
||||||
|
|
||||||
.view.active {
|
.view.active {
|
||||||
display: block;
|
display: block;
|
||||||
/* fill-mode `backwards` (not `both`): the entrance animation must NOT persist
|
/* Opacity-only on purpose: any transform here — even one animating toward
|
||||||
its end-state transform. A retained identity transform on .view.active would
|
`none` — establishes a containing block for the whole animation and breaks
|
||||||
establish a containing block and break `position: fixed` on the floating
|
`position: fixed` on the floating decision panel inside this view. */
|
||||||
decision panel, dropping it to the bottom of the page. */
|
|
||||||
animation: viewRise 320ms var(--ease) backwards;
|
animation: viewRise 320ms var(--ease) backwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes viewRise {
|
@keyframes viewRise {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(9px);
|
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: none;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -673,7 +661,6 @@ textarea:disabled {
|
||||||
.queue-folder-input span,
|
.queue-folder-input span,
|
||||||
.queue-file-input span:first-child {
|
.queue-file-input span:first-child {
|
||||||
color: var(--ink-faint);
|
color: var(--ink-faint);
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.07em;
|
letter-spacing: 0.07em;
|
||||||
|
|
@ -687,7 +674,7 @@ textarea:disabled {
|
||||||
border-radius: var(--r-sm);
|
border-radius: var(--r-sm);
|
||||||
background: var(--card-raised);
|
background: var(--card-raised);
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
box-shadow: inset 0 1px 2px rgba(20, 30, 28, 0.04);
|
box-shadow: var(--inset-well);
|
||||||
}
|
}
|
||||||
|
|
||||||
.queue-file-input span:last-of-type {
|
.queue-file-input span:last-of-type {
|
||||||
|
|
@ -773,10 +760,9 @@ textarea:disabled {
|
||||||
border-radius: var(--r-pill);
|
border-radius: var(--r-pill);
|
||||||
background: var(--teal);
|
background: var(--teal);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
|
box-shadow: var(--bevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
.operator-workflow strong {
|
.operator-workflow strong {
|
||||||
|
|
@ -797,7 +783,6 @@ textarea:disabled {
|
||||||
.eyebrow {
|
.eyebrow {
|
||||||
margin: 0 0 6px;
|
margin: 0 0 6px;
|
||||||
color: var(--teal);
|
color: var(--teal);
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.16em;
|
letter-spacing: 0.16em;
|
||||||
|
|
@ -836,7 +821,6 @@ textarea:disabled {
|
||||||
.knowledge-form label span,
|
.knowledge-form label span,
|
||||||
.memo-field span {
|
.memo-field span {
|
||||||
color: var(--ink-faint);
|
color: var(--ink-faint);
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.07em;
|
letter-spacing: 0.07em;
|
||||||
|
|
@ -893,7 +877,7 @@ textarea:disabled {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
|
box-shadow: var(--bevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
.knowledge-form .file-picker-button {
|
.knowledge-form .file-picker-button {
|
||||||
|
|
@ -932,7 +916,7 @@ textarea:disabled {
|
||||||
border-radius: var(--r-sm);
|
border-radius: var(--r-sm);
|
||||||
background: var(--card-raised);
|
background: var(--card-raised);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: inset 0 1px 2px rgba(20, 30, 28, 0.04);
|
box-shadow: var(--inset-well);
|
||||||
}
|
}
|
||||||
|
|
||||||
.segmented button {
|
.segmented button {
|
||||||
|
|
@ -1233,7 +1217,6 @@ th {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
background: var(--card-sunk);
|
background: var(--card-sunk);
|
||||||
color: var(--ink-faint);
|
color: var(--ink-faint);
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
|
|
@ -1258,9 +1241,12 @@ tbody tr:hover {
|
||||||
background: var(--teal-tint);
|
background: var(--teal-tint);
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr.selected-row {
|
/* 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);
|
box-shadow: inset 4px 0 0 var(--teal), var(--shadow-sm);
|
||||||
background: #eef6f9;
|
background: var(--teal-tint-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumb {
|
.thumb {
|
||||||
|
|
@ -1384,9 +1370,9 @@ tbody tr.selected-row {
|
||||||
}
|
}
|
||||||
|
|
||||||
.source-naver {
|
.source-naver {
|
||||||
border-color: #b1d5b9;
|
border-color: var(--forest-line);
|
||||||
background: #ecf7ef;
|
background: var(--forest-tint);
|
||||||
color: #215633;
|
color: var(--forest);
|
||||||
}
|
}
|
||||||
|
|
||||||
.source-google {
|
.source-google {
|
||||||
|
|
@ -1402,9 +1388,9 @@ tbody tr.selected-row {
|
||||||
}
|
}
|
||||||
|
|
||||||
.source-internal {
|
.source-internal {
|
||||||
border-color: #c2cdd6;
|
border-color: var(--slate-line);
|
||||||
background: #eef2f5;
|
background: var(--slate-tint);
|
||||||
color: #4c5d6b;
|
color: var(--slate);
|
||||||
}
|
}
|
||||||
|
|
||||||
.source-failure {
|
.source-failure {
|
||||||
|
|
@ -1418,7 +1404,6 @@ tbody tr.selected-row {
|
||||||
border-color: var(--hair);
|
border-color: var(--hair);
|
||||||
background: var(--card-sunk);
|
background: var(--card-sunk);
|
||||||
color: var(--ink-soft);
|
color: var(--ink-soft);
|
||||||
font-family: var(--font-stamp);
|
|
||||||
letter-spacing: 0.02em;
|
letter-spacing: 0.02em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1437,7 +1422,7 @@ tbody tr.selected-row {
|
||||||
.provider-chip.disabled,
|
.provider-chip.disabled,
|
||||||
.provider-chip.skipped {
|
.provider-chip.skipped {
|
||||||
border-color: var(--hair-strong);
|
border-color: var(--hair-strong);
|
||||||
color: #66747f;
|
color: var(--ink-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reason-cell {
|
.reason-cell {
|
||||||
|
|
@ -1551,7 +1536,6 @@ tbody tr.selected-row {
|
||||||
.fact-item span {
|
.fact-item span {
|
||||||
display: block;
|
display: block;
|
||||||
color: var(--ink-faint);
|
color: var(--ink-faint);
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.07em;
|
letter-spacing: 0.07em;
|
||||||
|
|
@ -1650,7 +1634,6 @@ tbody tr.selected-row {
|
||||||
.summary-stat span {
|
.summary-stat span {
|
||||||
display: block;
|
display: block;
|
||||||
color: var(--ink-faint);
|
color: var(--ink-faint);
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.06em;
|
||||||
|
|
@ -1726,7 +1709,7 @@ tbody tr.selected-row {
|
||||||
|
|
||||||
.evidence-group-strong {
|
.evidence-group-strong {
|
||||||
border-color: var(--ochre-line);
|
border-color: var(--ochre-line);
|
||||||
background: #fdf9ee;
|
background: var(--ochre-tint-soft);
|
||||||
box-shadow: 0 0 0 1px rgba(176, 125, 24, 0.08);
|
box-shadow: 0 0 0 1px rgba(176, 125, 24, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1907,7 +1890,7 @@ tbody tr.selected-row {
|
||||||
|
|
||||||
.knowledge-row.watchlist {
|
.knowledge-row.watchlist {
|
||||||
border-color: var(--ochre-line);
|
border-color: var(--ochre-line);
|
||||||
background: #fdf9ee;
|
background: var(--ochre-tint-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===================== Decision pane / floating ==================== */
|
/* ===================== Decision pane / floating ==================== */
|
||||||
|
|
@ -1989,7 +1972,6 @@ tbody tr.selected-row {
|
||||||
.decision-secondary summary {
|
.decision-secondary summary {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--ink-soft);
|
color: var(--ink-soft);
|
||||||
font-family: var(--font-stamp);
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.07em;
|
letter-spacing: 0.07em;
|
||||||
|
|
@ -2319,7 +2301,7 @@ tbody tr.selected-row {
|
||||||
border-radius: var(--r-pill);
|
border-radius: var(--r-pill);
|
||||||
background: var(--paper-2);
|
background: var(--paper-2);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: inset 0 1px 2px rgba(20, 30, 28, 0.12);
|
box-shadow: inset 0 1px 2px rgba(23, 32, 41, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.quota-meter span {
|
.quota-meter span {
|
||||||
|
|
@ -2427,7 +2409,9 @@ tbody tr.selected-row {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1180px) {
|
@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 {
|
.toolbar {
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
@ -2435,7 +2419,9 @@ tbody tr.selected-row {
|
||||||
.filter-search {
|
.filter-search {
|
||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1180px) {
|
||||||
.case-layout {
|
.case-layout {
|
||||||
grid-template-columns: minmax(280px, 0.9fr) minmax(360px, 1.1fr);
|
grid-template-columns: minmax(280px, 0.9fr) minmax(360px, 1.1fr);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue