ux-crime-scene / styles.css
kasbsquall's picture
feat: THE PROSECUTION β€” NVIDIA Nemotron argues the case in The Trial (Nemotron prize); README tags + cold-start note
4e33211 verified
Raw
History Blame Contribute Delete
162 kB
/* UX Crime Scene β€” noir / forensic styling. Off-Brand badge bait. */
/* Fonts are loaded via <link> tags injected in app.py's Blocks(head=...) β€”
Gradio 6 forbids @import inside its constructed stylesheet.
Asset URLs (--asset-*) are injected at runtime by app.py if the PNGs exist
in assets/; every one falls back gracefully to a pure-CSS version. */
:root {
--paper: #ede4cf;
--paper-dark: #d8caa8;
--ink: #1a1a1a;
--ink-soft: #2b2b2b;
--blood: #c0392b;
--blood-bright: #e74c3c;
--evidence: #f1c40f;
--redacted: #111;
/* Asset hooks (--asset-*) are defined ONLY in the head <style> injected by
app.py when files exist. We deliberately do NOT default them to `none`
here: the css= bundle loads AFTER the head <style>, so a `none` default
would override the real data-URIs. When an asset is absent, `var(--asset-x)`
is simply invalid and the property falls back to its initial value. */
}
/* ---------- Global ---------- */
body, .gradio-container {
background:
radial-gradient(ellipse at 50% -10%, #343434 0%, #1a1a1a 55%, #0b0b0b 100%) !important;
font-family: 'Courier Prime', 'Courier New', monospace !important;
color: #e9e4d4 !important;
}
.gradio-container { max-width: 1240px !important; margin: 0 auto !important; }
html, body { overflow-x: hidden; max-width: 100%; } /* kill stray horizontal scroll on mobile */
footer { display: none !important; }
/* Global film-grain + vignette over everything (subtle) */
.gradio-container::after {
content: "";
position: fixed; inset: 0;
pointer-events: none;
z-index: 9999;
background:
radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,0.55) 100%);
mix-blend-mode: multiply;
}
/* ---------- Header (left-aligned to the body grid β€” one spine) ---------- */
#ux-header {
padding: 14px 4px 12px; /* small top padding β€” recover the space above */
border-bottom: 3px double var(--blood);
margin-bottom: 16px;
position: relative;
}
#ux-header .ux-head-row {
display: flex; align-items: center; gap: 22px;
}
#ux-header .ux-head-text { text-align: left; }
#ux-header .emblem {
flex: 0 0 auto;
width: 118px; height: 118px;
background-color: #efe7d2; /* cream coin behind */
background-image: var(--asset-emblem); /* detective on top of the coin */
background-size: 96%;
background-repeat: no-repeat;
background-position: center;
border-radius: 50%;
box-shadow: 0 0 0 2px rgba(0,0,0,0.35), inset 0 0 0 4px rgba(0,0,0,0.06);
filter: drop-shadow(0 0 16px rgba(192,57,43,0.45)) drop-shadow(0 5px 10px rgba(0,0,0,0.7));
display: var(--emblem-display, none);
animation: flickerIn 1.3s ease-out both, plPulse 3s ease-in-out 1.3s infinite;
}
#ux-header .badge {
display: inline-block;
background: var(--blood);
color: #fff;
font-family: 'Oswald', sans-serif;
letter-spacing: 0.35em;
padding: 4px 14px;
font-size: 12px;
transform: rotate(-1.5deg);
margin-bottom: 8px;
box-shadow: 2px 2px 0 #000;
}
#ux-header h1 {
font-family: 'Anton', 'Oswald', sans-serif;
font-size: clamp(34px, 5vw, 58px);
letter-spacing: 0.06em;
color: var(--paper);
margin: 0;
/* cinematic chiaroscuro, not a flat 3D sticker */
text-shadow: 2px 2px 0 #000, 0 0 1px #000, 0 16px 28px rgba(0,0,0,0.7);
animation: flickerIn 1.1s ease-out both;
}
#ux-header p {
font-family: 'Special Elite', monospace;
color: var(--paper-dark);
margin: 10px 0 0;
font-size: 13.5px;
line-height: 1.5;
letter-spacing: 0.04em;
max-width: 62ch;
}
/* ---------- Buttons ---------- */
button.primary, button#investigate-btn {
background: var(--blood) !important;
color: #fff !important;
font-family: 'Oswald', sans-serif !important;
letter-spacing: 0.25em !important;
font-weight: 700 !important;
border: 2px solid #000 !important;
border-radius: 0 !important;
box-shadow: 4px 4px 0 #000 !important;
transition: transform 0.08s ease, box-shadow 0.08s ease !important;
text-transform: uppercase !important;
}
button.primary:hover, button#investigate-btn:hover {
transform: translate(-2px, -2px);
box-shadow: 6px 6px 0 #000 !important;
}
button.primary:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 #000 !important; }
/* ---------- Upload ---------- */
.gr-image, .gr-file, [data-testid="image"] {
background: #111 !important;
border: 2px dashed var(--blood) !important;
border-radius: 0 !important;
}
label, .gr-block label, span[data-testid] {
font-family: 'Oswald', sans-serif !important;
letter-spacing: 0.18em !important;
color: var(--paper) !important;
text-transform: uppercase !important;
font-size: 12px !important;
}
/* =======================================================================
VIEWER β€” loading scan + interactive evidence board
======================================================================= */
.viewer {
position: relative;
width: 100%;
min-height: 380px;
background: #0c0c0c;
border: 2px solid #000;
box-shadow: inset 0 0 80px rgba(0,0,0,0.8), 0 12px 30px rgba(0,0,0,0.5);
overflow: hidden;
}
.viewer-empty {
display: flex; align-items: center; justify-content: center;
height: 380px;
font-family: 'Special Elite', monospace;
color: #5a5a5a;
letter-spacing: 0.15em;
text-align: center;
padding: 20px;
}
/* ---- Scanning loader ---- */
.scan-stage { position: relative; width: 100%; line-height: 0; aspect-ratio: 16 / 9; overflow: hidden; }
.scan-photo {
width: 100%; height: 100%; display: block; object-fit: cover;
filter: grayscale(0.45) brightness(0.66) contrast(1.06);
}
/* FULL-SCREEN cinematic intro: covers the ENTIRE viewport while it plays.
Sits above everything else; when it ends it fades out, revealing the laptop +
live scan composed below. The .has-intro modifier on .sweep-viewer hides the
sweep contents until the video is done. */
.intro-fullscreen {
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
width: 100vw; height: 100vh;
z-index: 10000;
background: #000;
display: flex; align-items: center; justify-content: center;
transition: opacity 0.9s ease;
}
.intro-fullscreen.ended { opacity: 0; pointer-events: none; }
.intro-video {
width: 100vw; height: 100vh; object-fit: cover; display: block;
}
/* While the intro is playing, hide everything in the sweep step except the
inspector character (he lives outside .sweep-viewer). */
.sweep-viewer.has-intro { visibility: hidden; }
.sweep-viewer.has-intro ~ .inspector-fig { visibility: hidden; }
/* Hide ALL chrome while the intro plays β€” no sound bubble peeking through. */
body:has(.intro-fullscreen:not(.ended)) #sound-bubble { display: none !important; }
/* The .viewer rectangle (dark plate w/ white inner glow) was made for the OLD
monitor-bezel layout. With the laptop frame doing the framing, that plate
shows around the laptop as a grey rectangle. Kill it for the sweep step. */
.sweep-viewer {
background: transparent !important;
border: none !important;
box-shadow: none !important;
overflow: visible !important;
min-height: 0 !important;
padding: 0 !important;
}
/* Gradio wraps gr.HTML in several padded grey blocks that show through as a
grey rectangle around the laptop. Force every ancestor of the sweep view
transparent and padless, so the laptop floats on the page bg directly. */
.screen-sweep,
.screen-sweep > div,
.screen-sweep .gr-block,
.screen-sweep .gr-padded,
.screen-sweep .block,
.screen-sweep .form,
.screen-sweep .wrap,
.screen-sweep .gradio-html,
.screen-sweep .prose,
.screen-sweep > .gr-group,
.screen-sweep .gr-group > div {
background: transparent !important;
border: none !important;
box-shadow: none !important;
padding: 0 !important;
margin: 0 !important;
}
/* Sweep step is FULL WIDTH (room for the big laptop). The verdict keeps the
normal 1240px container β€” driven by body classes, NOT a :has() leak that
also fired on the verdict step. */
body.uxc-sweep .gradio-container { max-width: none !important; }
.scan-stage::after { /* moving scan line */
content: "";
position: absolute; left: 0; right: 0; top: 0; height: 3px;
background: linear-gradient(90deg, transparent, var(--evidence), transparent);
box-shadow: 0 0 18px 4px rgba(241,196,15,0.5);
animation: scanLine 2.6s ease-in-out infinite;
}
.magnifier {
position: absolute;
width: 176px; height: 176px;
margin: -88px 0 0 -88px; /* center on the left/top anchor point */
left: 14%; top: 18%;
animation: sweep 9s ease-in-out infinite;
z-index: 6;
filter: drop-shadow(0 0 24px rgba(241,196,15,0.5)); /* only the glow, no black trail */
}
.magnifier::before { /* bright spotlight under the lens β€” gives it presence */
content: ""; position: absolute; left: 50%; top: 47%;
width: 168px; height: 168px; transform: translate(-50%, -50%); border-radius: 50%;
background: radial-gradient(circle, rgba(255,246,205,0.30), rgba(241,196,15,0.12) 45%, transparent 70%);
mix-blend-mode: screen; pointer-events: none;
}
.magnifier .glass {
width: 130px; height: 130px; border-radius: 50%;
border: 11px solid #2b2b2b;
background:
radial-gradient(circle at 38% 32%, rgba(255,255,255,0.22), transparent 45%),
radial-gradient(circle, rgba(241,196,15,0.16), rgba(241,196,15,0.04) 60%, transparent 70%);
box-shadow: 0 0 0 3px #000, inset 0 0 22px rgba(241,196,15,0.25), 0 8px 18px rgba(0,0,0,0.6);
}
.magnifier.has-asset { background-image: var(--asset-magnifier); background-size: contain; background-repeat: no-repeat; }
/* When the asset is present, kill BOTH the CSS glass AND the CSS handle AND
the dark spotlight ::before β€” otherwise dark shapes trail behind the asset. */
.magnifier.has-asset .glass,
.magnifier.has-asset .handle { display: none !important; }
.magnifier.has-asset::before { display: none !important; }
.magnifier .handle {
position: absolute; bottom: -34px; right: 10px;
width: 16px; height: 54px;
background: linear-gradient(#3a3a3a, #1d1d1d);
border-radius: 8px; transform: rotate(42deg);
box-shadow: 0 4px 8px rgba(0,0,0,0.6);
}
/* ---- Monitor-style frame: REC + status live on the bezel, OFF the image ---- */
.scan-frame {
border: 1px solid #2c2c2c;
border-radius: 10px;
background: linear-gradient(#16161a, #0c0c0e);
box-shadow: 0 18px 50px rgba(0,0,0,0.6), inset 0 0 0 1px rgba(255,255,255,0.03),
0 0 0 1px #000;
padding: 0; overflow: hidden;
}
.scan-bezel {
display: flex; align-items: center; gap: 12px;
padding: 9px 14px; min-height: 22px;
background: linear-gradient(#1b1b1f, #131316);
font-family: 'Oswald', sans-serif; letter-spacing: 0.16em; text-transform: uppercase;
font-size: 12px; color: #8a857a;
}
.scan-bezel-top { border-bottom: 1px solid #000; }
.scan-bezel-bot { border-top: 1px solid #000; }
.scan-bezel .rec { display: inline-flex; align-items: center; gap: 6px; color: var(--blood-bright); font-family: 'Special Elite', monospace; }
.scan-bezel .rec-dot { color: var(--blood-bright); animation: blink 1.1s step-start infinite; }
.scan-bezel .scan-cap { color: #b9b2a3; flex: 0 1 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.scan-bezel .scan-ch { margin-left: auto; color: #6a665d; font-family: 'Special Elite', monospace; letter-spacing: 0.1em; }
.scan-grain { /* faint scanline texture INSIDE the screen only */
position: absolute; inset: 0; z-index: 4; pointer-events: none;
background: repeating-linear-gradient(0deg, rgba(0,0,0,0.10) 0 1px, transparent 1px 3px);
}
.scan-bezel-bot { justify-content: center; position: relative; }
.scan-dots { display: inline-flex; gap: 5px; margin-left: 12px; }
.scan-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--evidence); opacity: 0.3; animation: dotPulse 1.2s ease-in-out infinite; }
.scan-dots i:nth-child(2){ animation-delay: 0.2s; }
.scan-dots i:nth-child(3){ animation-delay: 0.4s; }
@keyframes dotPulse { 0%,100%{ opacity:0.25; transform:scale(0.8);} 50%{ opacity:1; transform:scale(1);} }
.scan-status {
position: relative; height: 18px; min-width: 280px; text-align: center;
}
.scan-status span {
position: absolute; left: 0; right: 0;
font-family: 'Oswald', sans-serif;
letter-spacing: 0.16em; text-transform: uppercase;
color: var(--evidence);
font-size: 12px; white-space: nowrap;
text-shadow: 0 0 10px rgba(241,196,15,0.5), 1px 1px 0 #000;
opacity: 0;
animation: statusCycle 10s linear infinite; /* 5 messages Γ— 2s each */
}
.scan-status span:nth-child(1){ animation-delay: 0s; }
.scan-status span:nth-child(2){ animation-delay: 2s; }
.scan-status span:nth-child(3){ animation-delay: 4s; }
.scan-status span:nth-child(4){ animation-delay: 6s; }
.scan-status span:nth-child(5){ animation-delay: 8s; }
/* ---- Interactive evidence board ---- */
.board-stage { position: relative; width: 100%; line-height: 0; }
.board-photo { width: 100%; display: block; }
.board-stage::before { /* faint vignette on the photo */
content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
box-shadow: inset 0 0 120px rgba(0,0,0,0.55);
}
.ev-rect {
position: absolute; z-index: 3;
border: 3px solid var(--c);
background: color-mix(in srgb, var(--c) 12%, transparent);
box-shadow: 0 0 0 1px rgba(0,0,0,0.6), 0 0 16px color-mix(in srgb, var(--c) 50%, transparent);
animation: stampIn 0.5s cubic-bezier(.2,1.5,.4,1) both;
animation-delay: calc(var(--i) * 0.35s + 0.2s);
}
.ev-pin {
position: absolute; z-index: 6;
transform: translate(-50%, -50%);
animation: stampIn 0.5s cubic-bezier(.2,1.5,.4,1) both;
animation-delay: calc(var(--i) * 0.35s + 0.35s);
}
.ev-pin .ring {
position: absolute; left: 50%; top: 50%;
width: 52px; height: 52px; margin: -26px 0 0 -26px;
border: 3px solid var(--c); border-radius: 50%;
animation: pulse 2s ease-out infinite;
}
.ev-pin .num {
position: relative;
width: 34px; height: 34px; border-radius: 50%;
background: var(--c); color: #fff;
display: flex; align-items: center; justify-content: center;
font-family: 'Anton', sans-serif; font-size: 18px;
border: 2px solid #111;
box-shadow: 2px 2px 0 #000, 0 0 14px color-mix(in srgb, var(--c) 60%, transparent);
cursor: pointer;
}
.ev-pin .pop {
position: absolute; left: 44px; top: -8px;
width: 240px;
background: var(--paper); color: var(--ink);
border: 1px solid #000; border-left: 5px solid var(--c);
padding: 10px 12px;
font-family: 'Special Elite', monospace; font-size: 12.5px; line-height: 1.45;
box-shadow: 4px 4px 0 rgba(0,0,0,0.5);
opacity: 0; transform: translateX(-6px) scale(0.96);
transition: opacity 0.15s ease, transform 0.15s ease;
pointer-events: none;
z-index: 8;
}
.ev-pin .pop b {
display: block; font-family: 'Oswald', sans-serif; text-transform: uppercase;
letter-spacing: 0.06em; color: var(--blood); margin-bottom: 3px; font-size: 12px;
}
.ev-pin:hover { z-index: 20; }
.ev-pin:hover .pop { opacity: 1; transform: translateX(0) scale(1); }
.ev-pin:hover .ring { animation-duration: 1s; }
/* flip popup to the left when near right edge */
.ev-pin.flip .pop { left: auto; right: 44px; border-left: none; border-right: 5px solid var(--c); }
/* =======================================================================
CASE FILE
======================================================================= */
.case-file {
background-color: var(--paper);
background-image: var(--asset-paper); /* defaults to `none` in :root */
background-size: cover; background-position: center;
background-blend-mode: multiply;
color: var(--ink);
padding: 42px 48px 48px;
/* No hard border: the torn-paper mask IS the edge. The drop-shadow filter
follows the mask outline (unlike box-shadow which would render a rectangle). */
border: none;
filter: drop-shadow(0 14px 28px rgba(0,0,0,0.55)) drop-shadow(0 2px 0 rgba(0,0,0,0.35));
font-family: 'Courier Prime', monospace;
position: relative;
overflow: visible;
margin-top: 28px;
animation: fileDrop 0.5s ease-out both;
/* Irregular torn-paper edges via SVG turbulence mask. Smaller frequency =
gentler waves; the outer 18px is the "torn" zone. */
-webkit-mask:
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25'><filter id='r'><feTurbulence type='fractalNoise' baseFrequency='0.018' numOctaves='2' seed='4'/><feDisplacementMap in='SourceGraphic' scale='14'/></filter><rect x='14' y='14' width='calc(100%25 - 28px)' height='calc(100%25 - 28px)' fill='white' filter='url(%23r)'/></svg>") center / 100% 100% no-repeat;
mask:
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25'><filter id='r'><feTurbulence type='fractalNoise' baseFrequency='0.018' numOctaves='2' seed='4'/><feDisplacementMap in='SourceGraphic' scale='14'/></filter><rect x='14' y='14' width='calc(100%25 - 28px)' height='calc(100%25 - 28px)' fill='white' filter='url(%23r)'/></svg>") center / 100% 100% no-repeat;
}
.case-file::before { /* paper grain + soft inner shadow drawn INSIDE the mask */
content: ""; position: absolute; inset: 0; pointer-events: none;
background:
radial-gradient(ellipse at 50% 50%, transparent 60%, rgba(60,40,15,0.18) 95%, rgba(40,25,10,0.30) 100%),
repeating-linear-gradient(0deg, rgba(0,0,0,0.018) 0 2px, transparent 2px 4px),
radial-gradient(circle at 18% 26%, rgba(120,80,40,0.10) 0%, transparent 55%),
radial-gradient(circle at 82% 74%, rgba(80,60,20,0.10) 0%, transparent 55%);
opacity: 0.7;
}
/* CONFIDENTIAL stamp β€” user removed: the paper reads cleaner without it. */
.case-file .stamp-confidential { display: none !important; }
.case-file.has-stamp-asset .stamp-confidential {
background: none; border: none; color: transparent; padding: 0;
width: 280px; height: 110px; top: 14px; right: -10px;
background-image: var(--asset-stamp-confidential);
background-size: contain; background-repeat: no-repeat;
transform: rotate(12deg);
/* Make the stamp INK soak into the paper instead of sitting on top of it. */
mix-blend-mode: multiply;
opacity: 0.78;
filter: contrast(1.05);
}
.case-file .cf-meta {
display: flex; justify-content: space-between; align-items: flex-end;
border-bottom: 2px solid var(--ink); padding-bottom: 8px; margin-bottom: 18px;
position: relative; z-index: 2;
}
.case-file .cf-num { font-family: 'Oswald', sans-serif; letter-spacing: 0.2em; font-size: 12px; }
.case-file .cf-title {
font-family: 'Anton', 'Oswald', sans-serif; font-size: 32px; letter-spacing: 0.03em;
margin: 6px 0 14px; line-height: 1.05; text-transform: uppercase; position: relative; z-index: 2;
}
.case-file .cf-summary {
font-family: 'Special Elite', monospace; font-size: 15px; margin-bottom: 22px;
line-height: 1.55; position: relative; z-index: 2;
}
.case-file h3 {
font-family: 'Oswald', sans-serif; letter-spacing: 0.25em;
border-bottom: 1px dashed var(--ink); margin: 26px 0 14px; padding-bottom: 4px;
font-size: 14px; text-transform: uppercase; position: relative; z-index: 2;
}
.evidence-item {
display: grid; grid-template-columns: 44px 1fr auto; gap: 14px; align-items: start;
padding: 12px 0; border-bottom: 1px dotted rgba(0,0,0,0.3);
position: relative; z-index: 2;
animation: rowIn 0.4s ease-out both;
animation-delay: calc(var(--i, 0) * 0.12s + 0.4s);
}
.evidence-item .ev-num {
width: 38px; height: 38px; background: var(--blood); color: #fff; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-family: 'Anton', sans-serif; font-size: 20px; box-shadow: 2px 2px 0 #000;
}
.evidence-item .ev-crime {
font-family: 'Oswald', sans-serif; font-weight: 700; letter-spacing: 0.05em;
font-size: 16px; text-transform: uppercase;
}
.evidence-item .ev-test { font-family: 'Special Elite', monospace; font-size: 14px; margin-top: 4px; line-height: 1.45; }
.evidence-item .ev-sev {
font-family: 'Oswald', sans-serif; font-size: 11px; letter-spacing: 0.2em;
padding: 4px 10px; border: 1.5px solid var(--ink); text-transform: uppercase;
white-space: nowrap; align-self: center;
}
.ev-sev.capital { background: var(--blood); color: #fff; border-color: #000; }
.ev-sev.high { background: var(--blood-bright); color: #fff; border-color: #000; }
.ev-sev.medium { background: #e67e22; color: #fff; }
.ev-sev.low { background: var(--evidence); color: var(--ink); }
.case-file .cf-footer {
display: grid; grid-template-columns: 1fr auto; gap: 24px;
margin-top: 30px; padding-top: 18px; border-top: 3px double var(--ink);
align-items: center; position: relative; z-index: 2;
}
.case-file .cf-verdict { font-family: 'Anton', sans-serif; font-size: 26px; letter-spacing: 0.1em; text-transform: uppercase; }
.case-file .cf-closing { font-family: 'Special Elite', monospace; font-style: italic; margin-top: 8px; color: var(--ink-soft); font-size: 14px; }
.case-file .cf-grade {
width: 116px; height: 116px; border: 6px solid var(--blood); border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-family: 'Anton', sans-serif; font-size: 66px; color: var(--blood);
transform: rotate(-8deg) scale(0); box-shadow: 0 0 0 3px var(--paper), 4px 4px 0 #000;
background: rgba(255,255,255,0.35);
animation: gradeStamp 0.55s cubic-bezier(.2,1.7,.4,1) 0.9s both;
}
.case-file.has-grade-asset .cf-grade {
border: none; box-shadow: none;
background-color: transparent;
background-image: var(--asset-grade);
background-size: contain; background-repeat: no-repeat; background-position: center;
color: var(--blood);
}
.redacted { background: var(--redacted); color: var(--redacted); padding: 0 6px; user-select: none; }
/* status / footer text */
.status-line {
font-family: 'Special Elite', monospace; color: var(--paper-dark);
letter-spacing: 0.08em; font-size: 13px; text-align: center; margin-top: 8px;
}
.tab-nav button { color: var(--paper) !important; }
/* =======================================================================
KEYFRAMES
======================================================================= */
@keyframes sweep {
0% { left: 16%; top: 20%; }
16% { left: 80%; top: 13%; }
32% { left: 44%; top: 50%; }
48% { left: 86%; top: 68%; }
64% { left: 17%; top: 74%; }
80% { left: 62%; top: 38%; }
100% { left: 16%; top: 20%; }
}
@keyframes scanLine {
0% { top: 2%; opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { top: 98%; opacity: 0; }
}
@keyframes statusCycle {
0% { opacity: 0; transform: translateY(6px); }
2% { opacity: 1; transform: translateY(0); }
18% { opacity: 1; transform: translateY(0); }
20% { opacity: 0; transform: translateY(-6px); }
100% { opacity: 0; }
}
@keyframes pulse {
0% { transform: scale(0.7); opacity: 0.9; }
70% { transform: scale(1.8); opacity: 0; }
100% { transform: scale(1.8); opacity: 0; }
}
@keyframes stampIn {
0% { opacity: 0; transform: translate(-50%, -50%) scale(2.4) rotate(-12deg); }
60% { opacity: 1; }
100% { opacity: 1; transform: translate(-50%, -50%) scale(1) rotate(0); }
}
/* ev-rect uses top-left origin, not centered */
.ev-rect { transform-origin: top left; }
@keyframes fileDrop {
0% { opacity: 0; transform: translateY(-18px) rotate(-1deg); }
100% { opacity: 1; transform: translateY(0) rotate(0); }
}
@keyframes stampSlam {
0% { opacity: 0; transform: rotate(34deg) scale(2.2); }
70% { opacity: 1; }
100% { opacity: 1; transform: rotate(34deg) scale(1); }
}
@keyframes gradeStamp {
0% { opacity: 0; transform: rotate(-8deg) scale(2.6); }
65% { opacity: 1; }
100% { opacity: 1; transform: rotate(-8deg) scale(1); }
}
@keyframes rowIn {
0% { opacity: 0; transform: translateX(-12px); }
100% { opacity: 1; transform: translateX(0); }
}
@keyframes flickerIn {
0%, 18%, 24%, 38% { opacity: 0.3; }
20%, 26%, 100% { opacity: 1; }
}
@keyframes blink { 50% { opacity: 0.25; } }
/* =======================================================================
RESPONSIVE (desktop-first, but don't break narrow)
======================================================================= */
@media (max-width: 760px) {
.case-file { padding: 26px 20px; }
.case-file .cf-footer { grid-template-columns: 1fr; }
.case-file .cf-grade { margin: 0 auto; }
.ev-pin .pop { width: 180px; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation: none !important; }
}
/* =======================================================================
PAGE LOADER β€” noir intro overlay (pure CSS, self-dismisses)
======================================================================= */
#page-loader {
position: fixed; inset: 0; z-index: 100000;
display: flex; flex-direction: column; align-items: center; justify-content: center;
background: radial-gradient(ellipse at center, #1c1c1c 0%, #0c0c0c 70%, #000 100%);
animation: loaderOut 0.9s ease 2.8s forwards;
}
#page-loader::after { /* film grain flicker */
content: ""; position: absolute; inset: 0; pointer-events: none;
background: repeating-linear-gradient(0deg, rgba(255,255,255,0.02) 0 1px, transparent 1px 3px);
animation: plFlicker 0.12s steps(2) infinite;
}
#page-loader .pl-emblem {
width: 150px; height: 150px;
background: var(--asset-emblem) center / contain no-repeat;
filter: drop-shadow(0 0 30px rgba(192,57,43,0.5));
animation: plEmblemIn 1s cubic-bezier(.2,1.4,.4,1) both, plPulse 2.2s ease-in-out 1s infinite;
}
#page-loader .pl-title {
font-family: 'Anton', sans-serif; letter-spacing: 0.2em; font-size: 22px;
color: var(--paper); margin: 22px auto 0; white-space: nowrap;
width: fit-content; max-width: 92vw; overflow: hidden;
animation: plReveal 1s ease 0.5s both; /* left-to-right wipe, any length */
}
#page-loader .pl-sub {
font-family: 'Special Elite', monospace; color: var(--blood-bright);
letter-spacing: 0.25em; font-size: 12px; margin-top: 14px;
animation: blink 1s step-start 1.6s infinite;
}
#page-loader .pl-scan {
position: absolute; left: 0; right: 0; height: 2px;
background: linear-gradient(90deg, transparent, var(--evidence), transparent);
box-shadow: 0 0 16px 3px rgba(241,196,15,0.4);
animation: plScanMove 1.6s ease-in-out infinite;
}
@keyframes loaderOut { to { opacity: 0; visibility: hidden; pointer-events: none; } }
@keyframes plEmblemIn { 0% { opacity: 0; transform: scale(2) rotate(-12deg); } 100% { opacity: 1; transform: scale(1) rotate(0); } }
@keyframes plPulse { 0%,100% { filter: drop-shadow(0 0 22px rgba(192,57,43,0.4)); } 50% { filter: drop-shadow(0 0 40px rgba(231,76,60,0.7)); } }
@keyframes plReveal { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes plCaret { 50% { border-color: transparent; } }
@keyframes plFlicker { 50% { opacity: 0.4; } }
@keyframes plScanMove { 0% { top: 12%; } 100% { top: 88%; } }
/* =======================================================================
STEP WIZARD BAR
======================================================================= */
.steps {
display: flex; justify-content: center; align-items: center; gap: 0;
margin: 6px auto 22px; flex-wrap: wrap;
}
.steps .step {
display: flex; align-items: center; gap: 10px;
padding: 8px 18px; position: relative;
}
.steps .step .dot {
width: 30px; height: 30px; border-radius: 50%;
border: 2px solid #555; color: #777; background: #161616;
display: flex; align-items: center; justify-content: center;
font-family: 'Anton', sans-serif; font-size: 15px; transition: all 0.3s ease;
}
.steps .step .lbl {
font-family: 'Oswald', sans-serif; letter-spacing: 0.18em; font-size: 12px;
color: #777; text-transform: uppercase; transition: all 0.3s ease;
}
.steps .sep { width: 46px; height: 2px; background: #444; }
.steps .step.active .dot {
border-color: var(--blood); color: #fff; background: var(--blood);
box-shadow: 0 0 16px rgba(192,57,43,0.6); transform: scale(1.1);
}
.steps .step.active .lbl { color: var(--paper); }
.steps .step.done .dot { border-color: var(--evidence); color: var(--evidence); }
.steps .step.done .lbl { color: var(--paper-dark); }
/* =======================================================================
INVESTIGATE BUTTON β€” make it the hero CTA
======================================================================= */
button#investigate-btn {
width: 100% !important;
margin-top: 14px !important;
padding: 18px 20px !important;
font-size: 18px !important;
letter-spacing: 0.28em !important;
position: relative; overflow: hidden;
background: linear-gradient(180deg, #d83a2a, #a5301f) !important;
border: 2px solid #000 !important;
box-shadow: 5px 5px 0 #000, inset 0 1px 0 rgba(255,255,255,0.2) !important;
}
button#investigate-btn::after { /* moving sheen */
content: ""; position: absolute; top: 0; left: -60%; width: 40%; height: 100%;
background: linear-gradient(100deg, transparent, rgba(255,255,255,0.28), transparent);
transform: skewX(-20deg);
animation: btnSheen 3.2s ease-in-out infinite;
}
@keyframes btnSheen { 0% { left: -60%; } 55%,100% { left: 130%; } }
button#investigate-btn:hover { background: linear-gradient(180deg, #e74c3c, #c0392b) !important; }
/* Typography refinement */
#ux-header h1 { line-height: 0.95; }
.gradio-container { padding-bottom: 40px !important; padding-top: 0 !important; }
/* Kill any top padding Gradio's main/wrap add above the header. */
.gradio-container > .main, .gradio-container main.fillable,
.gradio-container .wrap.svelte-zxu34v { padding-top: 0 !important; margin-top: 0 !important; }
/* =======================================================================
P0 FIXES β€” surgical review batch
======================================================================= */
/* P0-1: force English noir copy on Gradio's (locale-dependent) dropzone.
font-size:0 collapses ALL descendant text (any nesting/locale); the SVG icon
is hidden; our pseudo-elements re-add English copy at their own font sizes. */
/* OLD pseudo-elements that added "DROP THE EVIDENCE HERE" were lingering here
and showing up under our new Polaroid. Forcibly clear them. */
#evidence-drop .wrap::before, #evidence-drop .wrap::after { content: none !important; display: none !important; }
/* Collapse the Spanish placeholder text nodes that Gradio bakes directly into
the .wrap. font-size:0 + color:transparent kills any locale's text. */
#evidence-drop .wrap:not(.default):not(.center):not(.full) { font-size: 0 !important; color: transparent !important; }
#evidence-drop .wrap svg, #evidence-drop .wrap .icon-wrap { display: none !important; }
/* P0-3: idle viewer β€” legible, framed, intentional (not a black void) */
.viewer-empty {
display: flex; align-items: center; justify-content: center;
min-height: 360px; padding: 22px;
}
.viewer-empty .ve-frame {
width: 100%; max-width: 460px; text-align: center;
border: 2px dashed rgba(192,57,43,0.5);
padding: 34px 26px;
background:
var(--asset-magnifier) center 26px / 60px no-repeat,
repeating-linear-gradient(0deg, rgba(255,255,255,0.015) 0 2px, transparent 2px 4px);
background-blend-mode: normal;
}
.viewer-empty .ve-title {
margin-top: 78px;
font-family: 'Anton', sans-serif; letter-spacing: 0.14em; font-size: 22px;
color: #b9b0a0;
}
.viewer-empty .ve-sub {
font-family: 'Special Elite', monospace; font-size: 13px; line-height: 1.6;
color: #8c8473; /* ~4.4:1 on the dark viewer β€” passes AA */
margin-top: 10px;
}
/* P0-5/6: board popups are TEASERS; pins are links; rows flash on jump */
.viewer .board-hint {
font-family: 'Special Elite', monospace; color: var(--paper-dark);
letter-spacing: 0.08em; font-size: 12px; text-align: center;
padding: 8px 0 2px; line-height: 1;
}
.ev-pin { text-decoration: none; }
.ev-pin .pop { width: auto; min-width: 150px; max-width: 240px; }
.ev-pin .pop b { display: block; margin-bottom: 4px; }
.ev-pin .pop .pop-sev {
display: inline-block; font-family: 'Oswald', sans-serif; font-size: 10px;
letter-spacing: 0.18em; text-transform: uppercase; padding: 2px 8px; color: #fff;
}
.pop-sev.capital { background: #8e1f14; }
.pop-sev.high { background: var(--blood); }
.pop-sev.medium { background: #e67e22; }
.pop-sev.low { background: var(--evidence); color: var(--ink); }
.ev-pin:focus-within { z-index: 20; }
.ev-pin:focus-within .pop { opacity: 1; transform: translateX(0) scale(1); }
.evidence-item { scroll-margin-top: 80px; }
.evidence-item:target { animation: flashRow 1.4s ease; }
@keyframes flashRow {
0%, 100% { background: transparent; }
18% { background: rgba(241,196,15,0.45); }
}
/* P0-8: shareable artifact β€” inline, prominent */
.share-block { margin: 26px 0 6px; }
.share-head {
font-family: 'Oswald', sans-serif; letter-spacing: 0.2em; text-transform: uppercase;
color: var(--paper); font-size: 14px; border-bottom: 1px dashed rgba(192,57,43,0.5);
padding-bottom: 8px; margin-bottom: 10px;
}
.share-caption {
font-family: 'Special Elite', monospace; color: var(--paper-dark);
font-size: 13px; line-height: 1.5; margin-bottom: 12px;
background: rgba(0,0,0,0.3); border-left: 3px solid var(--blood); padding: 10px 12px;
}
#share-img { border: 2px solid #000 !important; }
/* P1: severity contrast bump + capital separation */
.ev-sev.capital { background: #8e1f14 !important; color: #fff; }
.ev-sev.medium { background: #c85a12 !important; }
/* P1: wizard separators connect into one rail */
.steps .sep { flex: 1 1 auto; min-width: 24px; max-width: 60px; }
.steps .step.error .dot { border-color: var(--blood); color: var(--blood); background: #2a1110; }
.steps .step.error .lbl { color: var(--blood-bright); }
.steps .step.done .dot { border-color: var(--evidence); }
/* P1: CTA must not wrap */
button#investigate-btn { white-space: nowrap; letter-spacing: 0.18em !important; font-size: 16px !important; }
/* =======================================================================
P0-4 (CRITICAL): reduced-motion must NOT freeze the page-loader on-screen
nor leave animate-in elements stuck invisible.
======================================================================= */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation: none !important; transition: none !important; }
#page-loader { display: none !important; } /* loaderOut never runs β†’ hide it */
#ux-header .emblem { opacity: 1 !important; }
#ux-header h1 { opacity: 1 !important; }
.case-file { opacity: 1 !important; transform: none !important; }
.case-file .cf-grade { transform: rotate(-8deg) scale(1) !important; opacity: 1 !important; }
.case-file .stamp-confidential { opacity: 1 !important; transform: rotate(34deg) scale(1) !important; }
.evidence-item { opacity: 1 !important; transform: none !important; }
.ev-pin, .ev-rect { opacity: 1 !important; transform: translate(-50%,-50%) !important; }
.ev-rect { transform: none !important; }
}
/* =======================================================================
Mobile tap targets β€” hover is dead on touch, so pins must be tappable
and big enough; popups shrink.
======================================================================= */
@media (max-width: 760px) {
#ux-header .ux-head-row { flex-direction: column; text-align: center; gap: 10px; }
#ux-header .ux-head-text { text-align: center; }
#ux-header p { margin-left: auto; margin-right: auto; }
.ev-pin .num { width: 44px; height: 44px; font-size: 22px; }
.ev-pin .ring { width: 64px; height: 64px; margin: -32px 0 0 -32px; }
.ev-pin .pop { display: none; } /* on touch, tap the pin to scroll to the charge */
.steps .step { padding: 6px 8px; }
.steps .lbl { display: none; } /* compact dots-only rail on phones */
}
/* =======================================================================
WIZARD v2 β€” real 3-screen flow, fixes for paper/text/board/share
======================================================================= */
/* Gradio Group wrappers must be invisible containers */
.screen { background: transparent !important; border: none !important; box-shadow: none !important; padding: 0 !important; }
/* ---- Cinematic screen transitions -----------------------------------------
Gradio toggles display:none on the .screen root when (in)visible. An element
that goes display:none -> shown restarts any CSS animation defined on it, so
each screen plays its entrance every time the wizard advances. */
/* fill-mode is `backwards` (NOT both/forwards): the entrance plays on mount but
leaves NO lingering transform/filter at rest β€” a held transform/filter would
turn .screen into the containing block for the position:fixed Inspector and
trap him inside the (very tall) screen instead of the viewport corner. */
.screen { animation: screenIn 0.55s cubic-bezier(.22,.85,.3,1) backwards; transform-origin: top center; }
.screen-upload { animation: screenInUp 0.5s ease backwards; }
.screen-sweep { animation: screenFade 0.45s ease backwards; }
.screen-verdict { animation: screenSlam 0.6s cubic-bezier(.2,.9,.25,1) backwards; }
@keyframes screenIn { from { opacity:0; transform:translateY(14px); } to { opacity:1; transform:none; } }
@keyframes screenInUp { from { opacity:0; transform:translateY(22px) scale(.985); } to { opacity:1; transform:none; } }
@keyframes screenFade { from { opacity:0; } to { opacity:1; } }
/* the verdict "case file slams onto the desk" */
@keyframes screenSlam {
0% { opacity:0; transform:translateY(-26px) scale(1.05) rotate(-.6deg); filter:blur(3px); }
60% { opacity:1; transform:translateY(0) scale(.995) rotate(.15deg); filter:blur(0); }
80% { transform:translateY(0) scale(1.004) rotate(0); }
100% { transform:none; }
}
@media (prefers-reduced-motion: reduce) {
.screen, .screen-upload, .screen-sweep, .screen-verdict { animation: screenFade 0.25s ease both; }
}
/* Big viewer for the sweep + verdict screens (full width of the container) */
.viewer.big { min-height: 440px; }
.viewer.big .board-photo { width: 100%; display: block; }
/* P0: the case-file text was being overridden by the global light color
(body{color:#e9e4d4 !important}). Force dark ink on the cream paper. */
.case-file,
.case-file .cf-title,
.case-file .cf-summary,
.case-file h3,
.case-file .ev-crime,
.case-file .ev-test,
.case-file .cf-num,
.case-file .cf-verdict { color: var(--ink) !important; }
.case-file .cf-closing { color: var(--ink-soft) !important; }
.case-file .ev-sev,
.case-file .ev-num,
.case-file .stamp-confidential { color: #fff !important; } /* badges keep white */
.case-file .ev-sev.low { color: var(--ink) !important; }
/* Paper fills cleanly (new texture has no dark edges) */
.case-file {
background-size: cover !important;
background-position: center !important;
background-repeat: no-repeat !important;
}
/* Share section */
.share-block { margin: 6px 0 4px; }
.share-row { margin-top: 12px; }
.share-btn {
display: inline-flex; align-items: center; gap: 9px;
font-family: 'Oswald', sans-serif; letter-spacing: 0.12em; text-transform: uppercase;
font-size: 14px; padding: 12px 22px; text-decoration: none;
border: 2px solid #000; box-shadow: 3px 3px 0 #000;
transition: transform 0.08s ease, box-shadow 0.08s ease;
}
.share-btn svg { width: 18px; height: 18px; flex: 0 0 auto; display: block; }
/* Kill Gradio's default blue underline / link color leaking onto our anchors */
.gradio-container a,
.share-block a, .share-btn, .share-btn:visited, .share-btn:hover,
.dl-btn, .dl-btn:visited, .ev-pin, .ev-pin:visited,
#dossier a, .hero a {
text-decoration: none !important;
}
.share-btn, .share-btn:visited { color: #fff !important; }
.share-block a:hover { text-decoration: none !important; }
.share-btn.x { background: #111; color: #fff; }
.share-btn.x:hover { transform: translate(-2px,-2px); box-shadow: 5px 5px 0 #000; background: #000; }
#share-img { border: 2px solid #000 !important; margin-bottom: 6px; }
/* "Investigate another" β€” secondary noir button */
#newcase-btn {
margin-top: 18px !important;
background: #1c1c1c !important; color: var(--paper) !important;
font-family: 'Oswald', sans-serif !important; letter-spacing: 0.2em !important;
border: 2px solid var(--blood) !important; border-radius: 0 !important;
box-shadow: 4px 4px 0 #000 !important; text-transform: uppercase !important;
}
#newcase-btn:hover { background: #241413 !important; transform: translate(-2px,-2px); box-shadow: 6px 6px 0 #000 !important; }
/* Copyable share link textbox */
#share-link { margin-top: 8px; }
#share-link textarea, #share-link input {
background: #111 !important; color: var(--evidence) !important;
font-family: 'Courier Prime', monospace !important; border: 1px solid var(--blood) !important;
}
/* On the verdict screen the board image is large β€” bump marker size a touch */
.screen-verdict .ev-pin .num { width: 38px; height: 38px; font-size: 20px; }
.screen-verdict .ev-pin .ring { width: 58px; height: 58px; margin: -29px 0 0 -29px; }
/* Center & constrain the upload screen */
.screen-upload { max-width: 640px; margin: 0 auto; }
/* =======================================================================
Floating sound bubble
======================================================================= */
#sound-bubble {
position: fixed; right: 22px; bottom: 22px; z-index: 100001;
display: flex; align-items: center; gap: 8px;
padding: 11px 16px; cursor: pointer;
background: #1a1a1a; color: var(--paper-dark);
border: 2px solid var(--blood); border-radius: 40px;
font-family: 'Oswald', sans-serif; letter-spacing: 0.18em; font-size: 11px; text-transform: uppercase;
box-shadow: 0 6px 18px rgba(0,0,0,0.6), 0 0 0 4px rgba(0,0,0,0.25);
transition: transform 0.12s ease, box-shadow 0.12s ease, color 0.12s ease;
}
#sound-bubble:hover { transform: translateY(-2px); box-shadow: 0 10px 22px rgba(0,0,0,0.7); }
#sound-bubble .sb-ico { font-size: 17px; line-height: 1; }
#sound-bubble.on {
color: #fff; border-color: var(--evidence);
box-shadow: 0 0 18px rgba(241,196,15,0.4), 0 6px 18px rgba(0,0,0,0.6);
}
#sound-bubble.on .sb-ico { color: var(--evidence); animation: plPulse 1.6s ease-in-out infinite; }
@media (max-width: 760px){ #sound-bubble .sb-txt{ display:none; } #sound-bubble{ padding:12px; border-radius:50%; } }
/* =======================================================================
Background video (fixed, dimmed, behind everything)
======================================================================= */
#bg-video-wrap {
position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none;
background: #0b0b0b;
}
#bg-video {
position: absolute; inset: 0;
width: 100%; height: 100%;
object-fit: cover; object-position: center;
opacity: 0.85;
filter: contrast(1.08) saturate(1.05);
}
#bg-video-wrap::after { /* light readability scrim only at the edges */
content: ""; position: absolute; inset: 0;
background:
radial-gradient(ellipse at 50% 32%, rgba(10,10,10,0.04), rgba(8,8,8,0.45) 100%),
linear-gradient(180deg, rgba(8,8,8,0.20), rgba(8,8,8,0.40));
}
/* Let the fixed video (z-index:-1, living inside <gradio-app>) show through:
EVERY ancestor up to the root must be transparent β€” especially <gradio-app>,
whose opaque theme background was covering the video entirely. */
html { background: #0b0b0b !important; }
gradio-app,
body, .gradio-container,
.gradio-container .main,
.gradio-container .wrap,
.gradio-container .contain,
.main.fillable { background: transparent !important; background-color: transparent !important; }
@media (prefers-reduced-motion: reduce) { #bg-video { display: none; } }
/* =======================================================================
v3 fixes: hero image, share buttons, confidential stamp, magnifier asset
======================================================================= */
/* The annotated hero image (verdict) β€” large, framed */
#hero-img { border: 2px solid #000 !important; box-shadow: 0 14px 34px rgba(0,0,0,0.6) !important; margin-bottom: 18px; }
#hero-img img { image-rendering: auto; }
/* Make the magnifier ASSET fill the bigger element */
.magnifier.has-asset { background-position: center; background-size: 100% 100%; }
/* Share buttons β€” platform colors */
.share-row { display: flex; flex-wrap: wrap; gap: 10px; }
.share-btn.wa { background: #1faf54; color: #fff; }
.share-btn.wa:hover { background: #169c49; transform: translate(-2px,-2px); box-shadow: 5px 5px 0 #000; }
.share-btn.fb { background: #1877f2; color: #fff; }
.share-btn.fb:hover { background: #0f63d6; transform: translate(-2px,-2px); box-shadow: 5px 5px 0 #000; }
.share-btn.li { background: #0a66c2; color: #fff; }
.share-btn.li:hover { background: #084e95; transform: translate(-2px,-2px); box-shadow: 5px 5px 0 #000; }
/* Keep the CONFIDENTIAL stamp in the top-right CORNER, clear of the meta text */
.case-file.has-stamp-asset .stamp-confidential {
top: 6px !important; right: 18px !important;
width: 220px !important; height: 88px !important;
transform: rotate(13deg) !important;
}
.case-file .stamp-confidential { top: 8px; right: -58px; }
/* =======================================================================
Verdict hero: PIL-annotated image + interactive hotspots (hover = charge)
======================================================================= */
.hero-head {
font-family: 'Special Elite', monospace; color: var(--paper-dark);
letter-spacing: 0.06em; font-size: 12px; margin: 2px 0 8px; text-align: center;
}
.hero-wrap {
position: relative; line-height: 0; width: 100%;
border: 2px solid #000; box-shadow: 0 14px 34px rgba(0,0,0,0.6);
}
.hero-photo { width: 100%; display: block; }
.hotspot {
position: absolute; display: block; text-decoration: none; cursor: pointer;
border: 2px solid transparent; border-radius: 4px;
transition: border-color 0.12s ease, background 0.12s ease;
}
.hotspot:hover, .hotspot:focus {
border-color: var(--evidence);
background: rgba(241,196,15,0.12);
z-index: 20;
}
.hotspot .hs-pop {
position: absolute; left: 100%; top: 0; margin-left: 8px;
min-width: 190px; max-width: 280px;
background: var(--paper); color: var(--ink);
border: 1px solid #000; border-left: 5px solid var(--blood);
padding: 10px 12px; box-shadow: 4px 4px 0 rgba(0,0,0,0.5);
font-family: 'Special Elite', monospace; font-size: 12.5px; line-height: 1.45;
opacity: 0; transform: translateX(-6px); transition: opacity 0.15s ease, transform 0.15s ease;
pointer-events: none; z-index: 30; white-space: normal;
}
.hotspot .hs-pop b {
display: block; font-family: 'Oswald', sans-serif; text-transform: uppercase;
letter-spacing: 0.04em; color: var(--blood); margin-bottom: 4px; font-size: 12px;
}
.hotspot:hover .hs-pop, .hotspot:focus .hs-pop { opacity: 1; transform: translateX(0); }
.hotspot.flip .hs-pop { left: auto; right: 100%; margin: 0 8px 0 0; border-left: none; border-right: 5px solid var(--blood); }
#hero-img { margin-bottom: 16px; }
/* =======================================================================
v4: CONFIDENTIAL stamp cleanup (no white text duplicate, no text collision)
======================================================================= */
/* My dark-text fix had forced the stamp TEXT white, revealing it on top of the
red stamp image. Hide the text entirely when the asset is present. */
.case-file.has-stamp-asset .stamp-confidential { color: transparent !important; }
/* Keep the red stamp in the very top-right corner, clear of the (now left-aligned) meta. */
.case-file.has-stamp-asset .stamp-confidential {
top: 2px !important; right: 14px !important;
width: 188px !important; height: 76px !important;
transform: rotate(9deg) !important; opacity: 0.95;
}
.case-file .cf-meta { display: block; } /* meta is single left column now */
.cf-sub {
display: block; margin-top: 3px;
font-family: 'Oswald', sans-serif; letter-spacing: 0.16em;
font-size: 11px; color: var(--ink-soft) !important; opacity: 0.8;
}
/* Download-the-photo button (replaces the gr.Image native download we lost) */
.dl-btn, .dl-btn:link, .dl-btn:visited, .dl-btn:hover, .dl-btn:active {
display: inline-flex; align-items: center; gap: 9px; margin-top: 12px;
font-family: 'Oswald', sans-serif; letter-spacing: 0.16em; text-transform: uppercase;
font-size: 13px; padding: 11px 22px; text-decoration: none !important;
background: #161616; color: var(--paper) !important;
border: 2px solid var(--evidence); box-shadow: 3px 3px 0 #000;
transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.08s ease, color 0.08s ease;
}
.dl-btn:hover { background: #241c0a; color: #fff !important; border-color: #ffd633;
transform: translate(-2px,-2px); box-shadow: 5px 5px 0 #000; }
/* =======================================================================
Hide Gradio's "processing | Xs" timer β€” it's a confusing CUMULATIVE session
counter, and our noir scan animation already says "working".
======================================================================= */
.progress-text, .eta-bar, .progress-level, .progress-bar,
.meta-text, .meta-text-center,
[class*="progress-text"], [class*="eta"] { display: none !important; }
/* The wrap Gradio injects over a generating component */
.screen .wrap.default.full.translucent,
.screen .wrap.generating { display: none !important; }
/* =======================================================================
Animated evidence circles β€” the Inspector draws them on, one by one
======================================================================= */
.hero-svg {
position: absolute; inset: 0; width: 100%; height: 100%;
pointer-events: none; overflow: visible; z-index: 3;
}
.draw-ring {
fill: none;
stroke-width: 5.5;
vector-effect: non-scaling-stroke; /* constant on-screen thickness at any image size */
stroke-linecap: round;
stroke-dasharray: 1; stroke-dashoffset: 1; /* pathLength=1 => draw-on */
filter: drop-shadow(0 1px 2px rgba(0,0,0,0.55));
opacity: 0.97;
animation: drawRing 0.6s ease forwards;
animation-delay: calc(var(--i) * 0.5s + 0.25s);
}
@keyframes drawRing { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
/* numbered placard stamps in right after its circle finishes drawing */
.hero-wrap .ev-pin {
position: absolute; z-index: 6; transform: translate(-50%, -50%);
text-decoration: none;
animation: stampIn 0.45s cubic-bezier(.2,1.5,.4,1) both;
animation-delay: calc(var(--i) * 0.5s + 0.7s);
}
.hero-wrap .ev-pin .num {
display: flex; align-items: center; justify-content: center;
width: 34px; height: 34px; border-radius: 50%;
background: var(--c); color: #fff; border: 2px solid #111;
font-family: 'Anton', sans-serif; font-size: 18px;
box-shadow: 2px 2px 0 #000, 0 0 14px color-mix(in srgb, var(--c) 60%, transparent);
cursor: pointer;
}
.hero-wrap .ev-pin .pop {
position: absolute; left: 42px; top: -6px; width: 250px;
background: var(--paper); color: var(--ink);
border: 1px solid #000; border-left: 5px solid var(--c);
padding: 10px 12px; box-shadow: 4px 4px 0 rgba(0,0,0,0.5);
font-family: 'Special Elite', monospace; font-size: 12.5px; line-height: 1.45;
opacity: 0; transform: translateX(-6px); transition: opacity .15s, transform .15s;
pointer-events: none; z-index: 30;
}
.hero-wrap .ev-pin .pop b { display: block; font-family: 'Oswald', sans-serif;
text-transform: uppercase; color: var(--blood); margin-bottom: 4px; font-size: 12px; }
.hero-wrap .ev-pin:hover, .hero-wrap .ev-pin:focus { z-index: 40; }
.hero-wrap .ev-pin:hover .pop, .hero-wrap .ev-pin:focus .pop { opacity: 1; transform: translateX(0); }
.hero-wrap .ev-pin.flip .pop { left: auto; right: 42px; border-left: none; border-right: 5px solid var(--c); }
.hero-wrap .ev-pin .num:hover { transform: scale(1.12); }
@media (prefers-reduced-motion: reduce) {
.draw-ring { animation: none; stroke-dashoffset: 0; }
.hero-wrap .ev-pin { animation: none; }
}
/* =======================================================================
The Inspector character β€” slides into the corner and works the scene.
He lives inside a .screen's HTML (so he comes/goes with that screen) but is
position:fixed to anchor to the viewport corner. The entrance is DELAYED past
the screen's own transition so position:fixed resolves against the viewport
(a transformed ancestor would otherwise capture it).
======================================================================= */
.inspector-fig {
position: fixed; left: clamp(24px, calc(50vw - 720px), 90px); bottom: 0;
z-index: 45; pointer-events: none; will-change: transform, opacity;
}
.inspector-fig img {
display: block; width: auto; height: clamp(210px, 30vh, 340px);
filter: drop-shadow(6px 8px 10px rgba(0,0,0,0.55));
}
/* VERDICT: stride in from the left, hold the accusing point, then exit. */
.insp-point { animation: inspPoint 5.2s cubic-bezier(.2,.8,.25,1) 0.7s both; }
@keyframes inspPoint {
0% { transform: translateX(-135%) translateY(8%); opacity: 0; }
14% { transform: translateX(0) translateY(0); opacity: 1; }
19% { transform: translateX(2.5%) rotate(-1.2deg); } /* a little jab at the evidence */
24% { transform: translateX(0) rotate(0); }
72% { transform: translateX(0); opacity: 1; } /* holds, pointing */
100% { transform: translateX(-135%); opacity: 0; } /* slips back into the shadows */
}
/* SWEEP: he stays, examining through the glass, with a slow idle bob. */
.insp-lean { animation: inspIn 0.6s ease 0.55s both, inspBob 3.6s ease-in-out 1.15s infinite; }
@keyframes inspIn { from { transform: translateX(-120%); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes inspBob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }
/* Mid-size laptops: not enough side margin, so make him smaller and let him
straddle the left edge (mostly off-screen, arm reaching in) β€” keeps the
"appears & points" beat without burying the case file. */
@media (min-width: 1181px) and (max-width: 1600px) {
.inspector-fig { left: -22px; }
.inspector-fig img { height: clamp(170px, 23vh, 250px); }
}
/* No room for him beside a narrow layout β€” fold him away. */
@media (max-width: 1180px) { .inspector-fig { display: none; } }
/* =======================================================================
Inspector ROTATOR β€” sweep step. The detective cycles through the 4 corners
OF THE LAPTOP (not the viewport), a different pose each time, hugging the
laptop edges. ~4s on-screen each, 16s loop.
======================================================================= */
/* lives INSIDE .laptop-stage now, so it anchors to the laptop, not the page */
.laptop-stage .inspector-rotator {
position: absolute; inset: 0; z-index: 4; pointer-events: none;
}
.laptop-stage .ir-slide {
position: absolute; z-index: 4; pointer-events: none;
height: 78%; /* proportional to the (cropped) laptop */
/* JS-driven rotation (NOT CSS animation) so EXACTLY one figure is ever
visible β€” no edge case where all four flash at once. */
opacity: 0; transform: translateY(22px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.laptop-stage .ir-slide.ir-on { opacity: 1; transform: translateY(0); }
.laptop-stage .ir-slide img {
display: block; height: 100%; width: auto;
filter: drop-shadow(6px 8px 12px rgba(0,0,0,0.65));
}
/* The cropped laptop fills its stage, so the stage corners ARE the laptop
corners β€” figures hug them, peeking just outside. */
.laptop-stage .ir-tl { top: 8%; left: -12%; }
.laptop-stage .ir-tr { top: 8%; right: -12%; }
.laptop-stage .ir-bl { bottom: 0%; left: -12%; }
.laptop-stage .ir-br { bottom: 0%; right: -12%; }
/* The bottom-right figure (lean) faces right by default β€” flip it so it looks
toward the laptop in the centre. (transform on the IMG, independent of the
slide's own transform.) */
.laptop-stage .ir-br img { transform: scaleX(-1); }
/* Hide the rotator while the intro full-screen video plays. */
.sweep-viewer.has-intro .inspector-rotator { display: none; }
@media (max-width: 1180px) { .inspector-rotator { display: none; } }
@media (prefers-reduced-motion: reduce) {
.insp-point { animation: inspIn 0.4s ease 0.5s both; }
.insp-lean { animation: inspIn 0.4s ease 0.5s both; }
}
/* =======================================================================
SWEEP step only β€” the detective's laptop frames the live scan. The PNG has
BOTH the surround AND the screen punched out to transparent (chroma keyed in
process_laptop.py). The scan goes INSIDE the screen rect computed from the
asset; the laptop image stays in front so the bezel reads as a real device.
======================================================================= */
.sweep-viewer { background: transparent !important; border: none !important; box-shadow: none !important; overflow: visible !important; min-height: 0 !important; }
/* SWEEP step: the laptop asset is cropped (fills its stage), so NO negative
margins are needed. Just keep the rail tight and let the figures overflow. */
body.uxc-sweep .steps { margin: 4px auto 2px !important; }
body.uxc-sweep .sweep-viewer,
body.uxc-sweep .screen-sweep,
body.uxc-sweep .screen-sweep > div,
body.uxc-sweep .screen-sweep .gr-group,
body.uxc-sweep .screen-sweep .styler,
body.uxc-sweep .screen-sweep .block,
body.uxc-sweep .screen-sweep .form,
body.uxc-sweep .column,
body.uxc-sweep .contain,
body.uxc-sweep main.fillable { overflow: visible !important; }
/* Status bar UNDER the laptop. REC + CAMΒ·timer share the top line; the cycling
status message wraps to its OWN full-width line so a long phrase can never
overlap the timer. */
.scan-status-bar {
display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
gap: 4px 22px;
margin: 14px auto 0; padding: 0 14px; max-width: 900px;
font-family: 'Oswald', sans-serif; letter-spacing: 0.16em; text-transform: uppercase;
font-size: 12px; color: #8a857a;
}
.scan-status-bar .rec { display: inline-flex; align-items: center; gap: 6px; color: var(--blood-bright); font-family: 'Special Elite', monospace; }
.scan-status-bar .rec-dot { color: var(--blood-bright); animation: blink 1.1s step-start infinite; }
.scan-status-bar .scan-ch { color: #6a665d; font-family: 'Special Elite', monospace; letter-spacing: 0.1em; white-space: nowrap; }
.scan-status-bar .scan-status {
flex: 1 1 100%; /* its OWN line */
position: relative; height: 18px; min-width: 0; text-align: center;
}
.laptop-stage {
position: relative;
/* Cropped laptop asset (890x594) β€” it now FILLS its stage, no dead margins. */
width: min(1120px, 80vw);
aspect-ratio: 890 / 594;
margin: 8px auto 0;
display: block;
overflow: visible;
}
.laptop-frame-img {
position: absolute; inset: 0; width: 100%; height: 100%;
z-index: 3; pointer-events: none; /* the bezel sits in front, framing the screen */
filter: drop-shadow(0 28px 50px rgba(0,0,0,0.7));
}
.laptop-screen {
position: absolute; z-index: 2;
background: #000; overflow: hidden;
box-shadow: inset 0 0 28px rgba(0,0,0,0.7);
border-radius: 2px;
}
.laptop-stage .scan-stage { aspect-ratio: auto; width: 100%; height: 100%; }
.laptop-stage .scan-stage .scan-photo { width: 100%; height: 100%; object-fit: cover; display: block; }
/* On tablet/phone the laptop reads as a tiny postcard β€” show the scan flat */
@media (max-width: 900px) {
.laptop-stage { aspect-ratio: auto; }
.laptop-frame-img { display: none; }
.laptop-screen { position: static; width: 100%; height: auto; aspect-ratio: 16/9; }
}
/* =======================================================================
STEP 1 β€” The Detective's Desk (top-down, full viewport background)
======================================================================= */
/* THE KEY: while step 1 is visible, the body itself becomes the desk. We
override the noir gradient bg from `body` !important, set the desk as the
body background-image, and add a fixed vignette on top. Way more reliable
than fighting Gradio's wrapper stack with ::before/::after on .screen-upload. */
body.uxc-upload {
background-image: var(--asset-desk) !important;
background-color: #1a120c !important;
background-size: cover !important;
background-position: center !important;
background-attachment: fixed !important;
}
body.uxc-upload .gradio-container,
body.uxc-upload .gradio-container::after { background: transparent !important; }
/* vignette to focus the Polaroid */
body.uxc-upload::after {
content: "";
position: fixed; inset: 0; z-index: -1; pointer-events: none;
background: radial-gradient(ellipse at center, transparent 25%, rgba(0,0,0,0.55) 100%);
}
body.uxc-upload #ux-header,
body.uxc-upload .steps { display: none !important; }
body.uxc-upload .gradio-container { max-width: none !important; padding: 0 !important; }
/* Step 1 is a single fixed viewport β€” no scroll possible. */
body.uxc-upload { overflow: hidden !important; }
/* Keep the footer credits, but fix them to the viewport bottom so they don't
add scroll height. Restyled to read over the wood. */
body.uxc-upload .status-line {
position: fixed !important; left: 0; right: 0; bottom: 0;
margin: 0 !important; z-index: 7; padding: 8px 12px;
text-align: center; pointer-events: none;
color: #e0d3ae !important;
background: rgba(0,0,0,0.7);
box-shadow: 0 -6px 16px rgba(0,0,0,0.45);
text-shadow: 0 1px 4px rgba(0,0,0,0.9);
}
/* CRITICAL: the fixed/full-screen treatment ONLY applies while step 1 is the
ACTIVE step (body.uxc-upload). When it's NOT active, Gradio collapses the
hidden upload to height 0 β€” we hide it hard so it can't cover the verdict. */
body:not(.uxc-upload) .screen-upload { display: none !important; }
/* Gradio renders the upload group as TWO nested .screen-upload divs. Make the
OUTER one the fixed scroll viewport and the INNER one the centered flex column,
so the desk centers when it fits and scrolls when it doesn't (shorter laptops
once the cold-cases + MOST WANTED sign are in play). */
body.uxc-upload .screen-upload:has(.screen-upload) {
position: fixed !important; inset: 0 !important;
height: 100vh !important; max-height: 100vh !important;
width: 100vw !important; max-width: 100vw !important;
box-sizing: border-box !important;
display: block !important; padding: 0 !important; margin: 0 !important; gap: 0 !important;
overflow-y: auto !important; overflow-x: hidden !important;
scrollbar-width: none;
background: transparent !important; z-index: 1;
animation: deskRise 1.1s ease-out both;
}
body.uxc-upload .screen-upload:has(.screen-upload)::-webkit-scrollbar { width: 0; height: 0; }
body.uxc-upload .screen-upload:not(:has(.screen-upload)) {
position: static !important;
min-height: 100% !important; height: auto !important; max-height: none !important;
width: 100% !important; max-width: 100% !important;
display: flex !important; flex-direction: column !important;
align-items: center !important; justify-content: flex-start !important;
gap: 18px !important; padding: 18px 0 24px !important; margin: 0 !important;
background: transparent !important; overflow: visible !important;
}
/* Center the content block when it fits; collapse to top + scroll when it doesn't.
The ::after is a real flex item (NOT padding, so it survives the scroll range) that
both anchors the bottom centering AND leaves room below MOST WANTED to clear the
fixed footer at scroll-end. */
body.uxc-upload .screen-upload:not(:has(.screen-upload)) > :first-child { margin-top: auto !important; }
body.uxc-upload .screen-upload:not(:has(.screen-upload))::after {
content: ""; flex: 0 0 88px; width: 1px; margin-bottom: auto; pointer-events: none;
}
/* Center the inner wrappers β€” only while active. */
body.uxc-upload .screen-upload > div, body.uxc-upload .screen-upload .gr-group,
body.uxc-upload .screen-upload .styler, body.uxc-upload .screen-upload .form,
body.uxc-upload .screen-upload .column {
width: 100% !important; max-width: 100% !important;
align-self: stretch !important;
display: flex !important; flex-direction: column !important;
align-items: center !important; justify-content: center !important;
}
/* The 'block' wrapper directly around #evidence-drop is display:block and won't
auto-center the polaroid. Force margin auto to centre it horizontally. */
.screen-upload .block.auto-margin,
.screen-upload .block {
margin-left: auto !important; margin-right: auto !important;
width: auto !important; max-width: 100% !important;
}
@keyframes deskRise {
from { opacity: 0; filter: blur(8px) brightness(0.55); }
to { opacity: 1; filter: none; }
}
/* ---- The Polaroid (wraps Gradio's gr.Image) ---- */
#evidence-drop {
position: relative;
width: min(520px, 46vw) !important;
height: auto !important;
flex: 0 0 auto !important;
background: #f6efdb !important;
padding: 18px 18px 60px !important;
border: 1px solid #b29a6c !important;
box-shadow:
0 28px 50px rgba(0,0,0,0.7),
inset 0 0 0 1px rgba(0,0,0,0.05) !important;
transition: transform 0.25s ease, box-shadow 0.25s ease;
border-radius: 2px;
overflow: visible !important;
/* no rotation β€” straight on the desk */
}
#evidence-drop:hover {
transform: translateY(-4px);
box-shadow: 0 36px 60px rgba(0,0,0,0.8), inset 0 0 0 1px rgba(0,0,0,0.05) !important;
}
/* Yellow crime-scene tape strapping the polaroid: TWO diagonal strips top+bottom. */
#evidence-drop .tape-top, #evidence-drop .tape-bot {
position: absolute !important;
left: -38px !important; right: -38px !important;
height: 30px !important; width: auto !important;
background-image: repeating-linear-gradient(135deg,
#f1c40f 0 24px,
#1a1a1a 24px 32px) !important;
background-color: transparent !important;
background-size: auto !important;
opacity: 0.92 !important;
box-shadow: 0 4px 8px rgba(0,0,0,0.55) !important;
pointer-events: none;
z-index: 10 !important;
border: none !important;
padding: 0 !important;
}
#evidence-drop .tape-top { top: 18px !important; transform: rotate(-7deg) !important; }
#evidence-drop .tape-bot { bottom: 50px !important; transform: rotate(8deg) !important; }
/* The "EVIDENCE PHOTO" field label floats over the image β€” hide it entirely
(the desk header + the drop guide already explain everything). */
#evidence-drop .label-wrap, #evidence-drop > label, #evidence-drop label.float,
#evidence-drop label[data-testid="block-label"] { display: none !important; }
/* Strip Gradio's grey block backgrounds INSIDE the polaroid β€” but DON'T touch
the tape strips or the guide (they need their own background/padding). */
#evidence-drop > div:not(.tape-top):not(.tape-bot):not(.poladrop-guide),
#evidence-drop .gr-block, #evidence-drop .block,
#evidence-drop .preview, #evidence-drop .image-container,
#evidence-drop .image-frame, #evidence-drop .form {
background: transparent !important;
border: none !important;
padding: 0 !important;
box-shadow: none !important;
}
/* the .wrap (the drop slot) needs its dark color; the 4/3 frame is set below */
#evidence-drop .wrap:not(.default):not(.center):not(.full) {
background: #2b2b2b !important;
border: 2px dashed rgba(255,255,255,0.18) !important;
padding: 0 !important;
box-shadow: none !important;
height: auto !important; min-height: 0 !important;
}
/* When an image is uploaded, Gradio shows a preview. The slot keeps the 4/3
polaroid frame and the preview fits INSIDE it (contain) β€” the user's evidence
is never cropped; the dark slot reads as the photo's matte. */
#evidence-drop .image-container,
#evidence-drop .image-frame,
#evidence-drop [data-testid="image"] {
aspect-ratio: 4 / 3 !important;
width: 100% !important; height: auto !important;
overflow: hidden !important; border-radius: 2px;
}
#evidence-drop .image-container img,
#evidence-drop .image-frame img,
#evidence-drop [data-testid="image"] img,
#evidence-drop img.svelte-1btp92j, #evidence-drop img {
width: 100% !important; height: 100% !important;
object-fit: contain !important; object-position: center !important;
display: block !important;
background: #2b2b2b;
}
/* The clickable/droppable area inside the polaroid: aspect-square photo slot. */
#evidence-drop .wrap:not(.default):not(.center):not(.full) {
position: relative !important;
width: 100% !important;
aspect-ratio: 4 / 3 !important;
height: auto !important;
min-height: 0 !important;
cursor: pointer;
background: #2b2b2b !important; /* dark photo slot (reads like an empty frame) */
border: 2px dashed rgba(255,255,255,0.18) !important;
}
/* Gradio's default upload placeholder: hide everything inside .wrap except
the file input itself. Our .poladrop-guide takes over the visual. */
#evidence-drop .wrap > * { display: none !important; }
#evidence-drop .wrap > input[type=file] { display: block !important;
position: absolute !important; inset: 0 !important; opacity: 0 !important;
width: 100% !important; height: 100% !important; cursor: pointer; }
/* Gradio sometimes renders a button/source toolbar below the wrap β€” kill it. */
#evidence-drop .source-selection, #evidence-drop .controls,
#evidence-drop button[aria-label*="lear"], #evidence-drop .icon-button-wrapper,
#evidence-drop > .source-selection { display: none !important; }
/* Our own dropzone guide, perfectly centered over the dark slot. */
.poladrop-guide {
position: absolute !important;
left: 18px !important; right: 18px !important; top: 18px !important;
bottom: 60px !important; /* matches polaroid padding-bottom */
z-index: 5 !important; pointer-events: none;
display: flex !important; align-items: center !important; justify-content: center !important;
flex-direction: column; gap: 8px;
text-align: center; padding: 0;
background: transparent !important;
}
.poladrop-guide .pg-title {
font-family: 'Anton', sans-serif !important; text-transform: uppercase !important;
font-size: 22px !important; letter-spacing: 0.05em !important; color: #ede4cf !important;
text-shadow: 0 2px 6px rgba(0,0,0,0.8) !important;
}
.poladrop-guide .pg-sub {
font-family: 'Special Elite', monospace !important; font-size: 13px !important;
letter-spacing: 0.08em !important; color: #b9b2a3 !important;
text-shadow: 0 1px 4px rgba(0,0,0,0.7) !important;
}
/* When Gradio has rendered the preview image, hide the guide (it's now a CHILD
of #evidence-drop after JS moves it in, so use descendant not sibling). */
#evidence-drop:has(img[src]) .poladrop-guide { display: none !important; }
/* The Investigate button β€” big red rubber stamp underneath the polaroid.
Width hugs its content (NOT full-width). Gradio stretches buttons by default,
so force the button AND its wrapper to shrink-to-fit. */
#investigate-btn {
position: relative;
margin: 0 auto !important;
width: auto !important; min-width: 0 !important; max-width: max-content !important;
display: inline-flex !important; align-items: center; justify-content: center;
background: var(--blood) !important; color: #fff !important;
font-family: 'Anton', 'Oswald', sans-serif !important;
font-size: 18px !important; letter-spacing: 0.3em !important;
padding: 16px 34px !important;
border: 3px solid #000 !important; border-radius: 0 !important;
box-shadow: 5px 5px 0 #000, 0 0 24px rgba(192,57,43,0.5) !important;
z-index: 6;
align-self: center !important;
}
#investigate-btn:hover { transform: translateY(-2px); }
#investigate-btn:disabled, #investigate-btn[disabled] {
filter: grayscale(0.7) brightness(0.7); opacity: 0.55;
}
/* The Gradio block wrapper around the button must also shrink to content and
center, otherwise the button's max-content has a full-width parent to sit in. */
.screen-upload .block:has(#investigate-btn) {
width: auto !important; max-width: max-content !important;
margin: 0 auto !important; align-self: center !important;
}
/* Make the wrapper Gradio groups invisible AND collapse their height. */
.screen-upload > div, .screen-upload .gr-group, .screen-upload .gr-group > div,
.screen-upload .form, .screen-upload .gradio-html, .screen-upload .block,
.screen-upload .gr-block, .screen-upload .gr-padded {
background: transparent !important;
border: none !important;
box-shadow: none !important;
padding: 0 !important;
margin: 0 !important;
height: auto !important; min-height: 0 !important;
flex: 0 0 auto !important;
}
/* Body and gradio-container shouldn't add their own height while step 1 shows */
body.uxc-upload .gradio-container, body.uxc-upload main {
min-height: 100vh !important;
padding: 0 !important; margin: 0 !important;
}
body.uxc-upload footer { display: none !important; } /* Gradio's own footer only */
/* =======================================================================
INTRO VIDEO β€” fullscreen "alley walk" that plays ONCE on first load.
======================================================================= */
#intro-alley {
position: fixed; inset: 0; z-index: 99999;
background: #000;
display: flex; align-items: center; justify-content: center;
opacity: 1; transition: opacity 1s ease;
}
#intro-alley.ended { opacity: 0; pointer-events: none; }
#intro-alley video { width: 100vw; height: 100vh; object-fit: cover; }
#intro-alley .skip-intro {
position: absolute; right: 22px; bottom: 22px; z-index: 2;
background: rgba(0,0,0,0.6); color: #d6cfb8;
border: 1px solid #444; border-radius: 0;
padding: 8px 16px; font-family: 'Oswald', sans-serif;
letter-spacing: 0.2em; text-transform: uppercase;
font-size: 11px; cursor: pointer;
transition: background 0.2s, color 0.2s;
}
#intro-alley .skip-intro:hover { background: rgba(192,57,43,0.8); color: #fff; }
#intro-alley { cursor: pointer; }
/* Hint that floats at the bottom-center of the intro: "Click anywhere for sound" */
#intro-alley .intro-hint {
position: absolute; left: 50%; bottom: 60px;
transform: translateX(-50%);
z-index: 3; pointer-events: none;
display: inline-flex; align-items: center; gap: 10px;
background: rgba(0,0,0,0.65);
color: #fff; padding: 12px 22px;
border: 1px solid rgba(255,255,255,0.25);
font-family: 'Oswald', sans-serif;
letter-spacing: 0.22em; text-transform: uppercase; font-size: 13px;
box-shadow: 0 8px 24px rgba(0,0,0,0.6), 0 0 0 2px rgba(192,57,43,0.4);
animation: hintPulse 1.8s ease-in-out infinite;
}
#intro-alley .intro-hint .ih-icon { font-size: 18px; }
@keyframes hintPulse {
0%,100% { transform: translateX(-50%) translateY(0); opacity: 0.92; }
50% { transform: translateX(-50%) translateY(-4px); opacity: 1; }
}
#intro-alley .intro-vignette {
position: absolute; inset: 0; pointer-events: none;
background: radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,0.7) 100%);
}
/* =======================================================================
STEP 1 onboarding header β€” sits above the Polaroid on the desk, tells a
first-time visitor what the app does and what to drop.
======================================================================= */
.desk-intro {
text-align: center;
max-width: 640px; margin: 0 auto 22px; /* extra breathing room above the polaroid */
display: flex; flex-direction: column; align-items: center; gap: 6px;
filter: drop-shadow(0 2px 8px rgba(0,0,0,0.8));
}
.desk-emblem {
width: 76px; height: 76px; margin-bottom: 2px;
background-color: #efe7d2;
background-image: var(--asset-emblem);
background-size: 92%; background-repeat: no-repeat; background-position: center;
border-radius: 50%;
box-shadow: 0 0 0 2px rgba(0,0,0,0.4), 0 8px 20px rgba(0,0,0,0.6);
}
.desk-kicker {
font-family: 'Oswald', sans-serif; letter-spacing: 0.34em; font-size: 11px;
color: #f1c40f; text-transform: uppercase;
background: rgba(0,0,0,0.35); padding: 3px 12px;
}
.desk-title {
font-family: 'Anton', sans-serif; font-size: clamp(30px, 4vw, 48px);
letter-spacing: 0.05em; color: #f6efdb; margin: 2px 0 0;
text-shadow: 2px 2px 0 #000, 0 12px 26px rgba(0,0,0,0.8);
}
.desk-sub {
font-family: 'Special Elite', monospace; font-size: 14px; line-height: 1.5;
color: #ece3cb; margin: 4px 0 0; max-width: 560px;
text-shadow: 0 1px 4px rgba(0,0,0,0.9);
}
.desk-sub b { color: #f1c40f; }
@media (max-height: 760px) {
.desk-sub { display: none; } /* tight screens: keep just the title */
.desk-emblem { width: 56px; height: 56px; }
}
/* =======================================================================
SOUND CONSENT GATE β€” the very first thing the visitor sees.
======================================================================= */
#sound-gate {
position: fixed; inset: 0; z-index: 100002;
display: flex; align-items: center; justify-content: center;
background: #0a0a0a;
overflow: hidden;
transition: opacity 0.6s ease;
}
#sound-gate.gone { opacity: 0; pointer-events: none; }
#sound-gate .sg-smoke {
position: absolute; inset: 0; width: 100%; height: 100%;
object-fit: cover; opacity: 0.65; filter: grayscale(0.25) brightness(0.78);
}
#sound-gate .sg-scrim {
position: absolute; inset: 0;
background: radial-gradient(ellipse at center, rgba(0,0,0,0.42) 35%, rgba(0,0,0,0.84) 100%);
}
#sound-gate .sg-inner {
position: relative; z-index: 2; text-align: center;
max-width: 600px; padding: 40px 28px;
display: flex; flex-direction: column; align-items: center; gap: 14px;
animation: sgRise 0.7s ease-out both;
}
@keyframes sgRise { from { opacity:0; transform: translateY(18px); } to { opacity:1; transform:none; } }
#sound-gate .sg-emblem {
width: 92px; height: 92px;
background-color: #efe7d2;
background-image: var(--asset-emblem);
background-size: 92%; background-repeat: no-repeat; background-position: center;
border-radius: 50%;
box-shadow: 0 0 0 2px rgba(0,0,0,0.5), 0 0 40px rgba(192,57,43,0.4);
margin-bottom: 4px;
}
#sound-gate .sg-kicker {
font-family: 'Oswald', sans-serif; letter-spacing: 0.36em; font-size: 12px;
color: #f1c40f; text-transform: uppercase;
}
#sound-gate h2 {
font-family: 'Anton', sans-serif; font-size: clamp(28px, 4vw, 44px);
letter-spacing: 0.03em; color: #f6efdb; margin: 4px 0 0;
text-shadow: 2px 2px 0 #000;
}
#sound-gate p {
font-family: 'Special Elite', monospace; font-size: 15px; line-height: 1.6;
color: #d6cfb8; margin: 6px 0 10px;
}
#sound-gate .sg-btns { display: flex; gap: 16px; margin-top: 6px; flex-wrap: wrap; justify-content: center; align-items: stretch; }
/* Both buttons: identical box model so they're exactly the same height. */
#sound-gate .sg-yes, #sound-gate .sg-no {
box-sizing: border-box; height: 56px;
display: inline-flex; align-items: center; justify-content: center;
padding: 0 30px; border-radius: 0; cursor: pointer;
}
#sound-gate .sg-yes {
background: var(--blood); color: #fff;
font-family: 'Anton', sans-serif; font-size: 16px; letter-spacing: 0.16em;
border: 3px solid #000;
box-shadow: 5px 5px 0 #000, 0 0 26px rgba(192,57,43,0.55);
transition: transform 0.12s ease;
}
#sound-gate .sg-yes:hover { transform: translateY(-2px); }
#sound-gate .sg-no {
background: #1a1a1a; color: #cfc7b2;
font-family: 'Oswald', sans-serif; font-size: 14px; letter-spacing: 0.18em; text-transform: uppercase;
border: 3px solid #000;
box-shadow: 5px 5px 0 #000; /* same drop shadow as the YES button */
transition: transform 0.12s ease, background 0.15s, color 0.15s;
}
#sound-gate .sg-no:hover { transform: translateY(-2px); background: #262626; color: #fff; }
#sound-gate .sg-no:hover { background: rgba(255,255,255,0.06); color: #fff; border-color: #888; }
#sound-gate .sg-foot {
font-family: 'Courier Prime', monospace; font-size: 11.5px; color: #8c856f;
letter-spacing: 0.05em; margin-top: 10px;
}
/* Black cover dropped the instant FILE THE CASE is clicked, so the button can't
be seen flashing while Gradio swaps to the sweep step + the intro mounts. */
#boot-cover {
position: fixed; inset: 0; z-index: 9998;
background: #000; opacity: 0; pointer-events: none;
transition: opacity 0.18s ease;
}
#boot-cover.show { opacity: 1; pointer-events: all; }
/* Overlay HTML blocks (sound gate, intro, bg video, page loader) hold only
position:fixed content β€” collapse their Gradio wrappers to 0 height so they
don't reserve empty vertical space above the header. */
.uxc-overlay, .uxc-overlay.html-container, .uxc-overlay .html-container {
height: 0 !important; min-height: 0 !important;
padding: 0 !important; margin: 0 !important;
overflow: visible !important;
}
/* =======================================================================
MOBILE β€” keep it FUNCTIONAL + a "best on desktop" notice. The heavy
cinematic chrome (laptop frame, inspectors) is already hidden below ~1000px.
======================================================================= */
#desk-notice {
position: fixed; left: 0; right: 0; top: 0; z-index: 100003;
display: flex; align-items: center; gap: 10px;
padding: 10px 12px;
background: linear-gradient(#1a1a1a, #111);
border-bottom: 2px solid var(--blood);
box-shadow: 0 6px 18px rgba(0,0,0,0.6);
font-family: 'Special Elite', monospace; font-size: 12px; line-height: 1.35;
color: #e9e0c8;
transform: translateY(0); transition: transform 0.35s ease, opacity 0.35s ease;
}
#desk-notice.gone { transform: translateY(-110%); opacity: 0; }
#desk-notice .dn-ico { font-size: 18px; flex: 0 0 auto; }
#desk-notice .dn-txt { flex: 1 1 auto; }
#desk-notice .dn-txt b { color: var(--evidence); }
#desk-notice .dn-x {
flex: 0 0 auto; background: transparent; border: 1px solid #555; color: #cfc7b2;
width: 26px; height: 26px; border-radius: 50%; cursor: pointer; font-size: 12px;
}
#desk-notice .dn-x:hover { background: var(--blood); color: #fff; border-color: var(--blood); }
@media (min-width: 1025px) { #desk-notice { display: none !important; } }
/* The sound-gate smoke video is heavy on phones (and the renderer can hang) β€”
drop it; the dark scrim alone reads fine. */
@media (max-width: 900px) {
#sound-gate .sg-smoke { display: none !important; }
#sound-gate .sg-scrim { background: radial-gradient(ellipse at center, #1a1a1a 0%, #0a0a0a 100%); }
}
/* STEP 1 on phones: the diagonal crime-scene tape overlaps the dropzone text.
Hide the tape and tighten the Polaroid so the guide is readable. */
@media (max-width: 760px) {
#evidence-drop { width: min(360px, 86vw) !important; padding: 14px 14px 46px !important; }
#evidence-drop .tape-top, #evidence-drop .tape-bot { display: none !important; }
.poladrop-guide { inset: 14px 14px 46px !important; }
.poladrop-guide .pg-title { font-size: 17px !important; }
.desk-intro { margin-bottom: 14px; }
.desk-title { font-size: clamp(24px, 8vw, 34px); }
.desk-sub { font-size: 12.5px; }
body.uxc-upload .screen-upload { padding: 0 0 4vh !important; gap: 16px; }
}
/* =======================================================================
Shared-link case loader β€” a clean OPAQUE "opening the case file" screen
shown while a ?case=ID verdict streams in (replaces the old dim cover).
======================================================================= */
#case-loading {
position: fixed; inset: 0; z-index: 100000;
background: radial-gradient(ellipse at 50% 35%, #232323 0%, #141414 55%, #0a0a0a 100%);
display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 14px; text-align: center;
transition: opacity 0.6s ease;
}
#case-loading.gone { opacity: 0; pointer-events: none; }
#case-loading .cl-emblem {
width: 96px; height: 96px;
background-color: #efe7d2; background-image: var(--asset-emblem);
background-size: 92%; background-repeat: no-repeat; background-position: center;
border-radius: 50%;
box-shadow: 0 0 0 2px rgba(0,0,0,0.5), 0 0 44px rgba(192,57,43,0.45);
animation: plPulse 2.4s ease-in-out infinite;
}
#case-loading .cl-kicker {
font-family: 'Oswald', sans-serif; letter-spacing: 0.36em; font-size: 12px;
color: #f1c40f; text-transform: uppercase;
}
#case-loading .cl-title {
font-family: 'Anton', sans-serif; font-size: clamp(24px, 3.4vw, 40px);
letter-spacing: 0.04em; color: #f6efdb; text-shadow: 2px 2px 0 #000;
}
#case-loading .cl-dots { display: inline-flex; gap: 7px; margin-top: 4px; }
#case-loading .cl-dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--blood-bright); opacity: 0.3; animation: dotPulse 1.2s ease-in-out infinite; }
#case-loading .cl-dots i:nth-child(2){ animation-delay: 0.2s; }
#case-loading .cl-dots i:nth-child(3){ animation-delay: 0.4s; }
/* ============================================================
UX/UI polish pass β€” legend Β· marker pulse Β· progress Β· cleared Β· a11y
============================================================ */
/* #1 β€” Severity legend (decodes the marker colours; only present ones shown) */
.hero-legend {
display: flex; flex-wrap: wrap; gap: 18px; justify-content: center;
margin: 0 0 12px; font-family: 'Oswald', sans-serif; font-size: 12px;
letter-spacing: 0.14em; text-transform: uppercase; color: #c2bcae;
}
.hero-legend .lg-item { display: inline-flex; align-items: center; gap: 7px; }
.hero-legend .lg-dot {
width: 13px; height: 13px; border-radius: 50%;
border: 2px solid rgba(0,0,0,.55); box-shadow: 0 0 0 1px rgba(255,255,255,.18);
}
/* #5 β€” Markers invite the hover (soft pulse ring) + keyboard focus */
.ev-pin .num::after {
content: ""; position: absolute; inset: -2px; border-radius: 50%;
pointer-events: none; box-shadow: 0 0 0 0 rgba(255,255,255,.5);
animation: uxcPinPulse 2.8s ease-out infinite;
}
@keyframes uxcPinPulse {
0% { box-shadow: 0 0 0 0 rgba(255,255,255,.5); }
70%, 100% { box-shadow: 0 0 0 11px rgba(255,255,255,0); }
}
.ev-pin:focus-visible { outline: 3px solid #fff; outline-offset: 3px; border-radius: 50%; }
.ev-pin:focus-visible .pop { opacity: 1; transform: none; }
/* #2 β€” Indeterminate investigation progress bar (honest: shows activity, no fake ETA) */
.scan-progress {
position: relative; height: 4px; margin: 10px 14px 2px; border-radius: 3px;
background: rgba(255,255,255,.08); overflow: hidden;
}
.scan-progress .sp-bar {
position: absolute; top: 0; height: 100%; width: 38%; left: -38%;
background: linear-gradient(90deg, transparent, var(--blood), #e67e22, transparent);
animation: uxcScanSlide 1.7s cubic-bezier(.4,0,.2,1) infinite;
}
@keyframes uxcScanSlide { 0% { left: -38%; } 100% { left: 100%; } }
/* #3 β€” Cleared / "case dismissed": a genuinely clean (A/B) scene goes green, not blood */
.case-file.cleared .cf-verdict { color: #2e9e5b; }
.case-file.cleared .cf-grade {
background-image: none !important; border: 6px solid #2e9e5b !important;
color: #2e9e5b !important; box-shadow: 0 0 0 3px var(--paper), 4px 4px 0 #000 !important;
}
/* #4 β€” Accessibility: respect reduced-motion for the new decorative animations */
@media (prefers-reduced-motion: reduce) {
.ev-pin .num::after { animation: none; }
.scan-progress .sp-bar { animation: none; left: 0; width: 100%; opacity: .5; }
}
/* Verdict masthead β€” the payoff (grade + title + verdict) leads the screen */
.verdict-strip {
display: flex; align-items: center; gap: 18px;
margin: 2px 0 14px; padding: 14px 18px;
background: #161514; border: 1px solid #2c2a27; border-left: 4px solid var(--blood);
}
.verdict-strip .vs-grade {
flex: 0 0 auto; width: 64px; height: 64px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-family: 'Anton', sans-serif; font-size: 38px;
color: var(--blood); border: 4px solid var(--blood);
background: rgba(192,57,43,0.08);
transform: rotate(-8deg) scale(0);
animation: gradeStamp 0.55s cubic-bezier(.2,1.7,.4,1) 0.35s both;
}
.verdict-strip .vs-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.verdict-strip .vs-title {
font-family: 'Anton', sans-serif; font-size: clamp(18px, 2.6vw, 28px);
letter-spacing: 0.04em; text-transform: uppercase; color: var(--paper); line-height: 1.1;
}
.verdict-strip .vs-verdict {
font-family: 'Oswald', sans-serif; font-size: 13px; letter-spacing: 0.22em;
text-transform: uppercase; color: var(--blood-bright, #e74c3c);
}
.verdict-strip.cleared { border-left-color: #2e9e5b; }
.verdict-strip.cleared .vs-grade { color: #2e9e5b; border-color: #2e9e5b; background: rgba(46,158,91,0.08); }
.verdict-strip.cleared .vs-verdict { color: #2e9e5b; }
/* Honest wait expectation under the progress bar */
.scan-eta {
margin: 8px 14px 2px; font-family: 'Special Elite', monospace;
font-size: 12px; color: #8d887c; text-align: center;
}
/* Touch devices: the hero-board popups are hover affordances β€” suppress them on
small screens (the dossier below carries the same info), preventing 250px paper
tooltips from overflowing the viewport edge on tap. */
@media (max-width: 760px) {
.hero-wrap .ev-pin .pop { display: none !important; }
}
/* Step indicator on the desk β€” step 1 of the same 3-step language */
.desk-steps {
display: flex; align-items: center; justify-content: center; gap: 10px;
margin: 10px 0 2px; font-family: 'Oswald', sans-serif; font-size: 11px;
letter-spacing: 0.18em; color: #8d887c;
}
.desk-steps .ds-step { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.desk-steps .ds-step b {
width: 20px; height: 20px; border-radius: 50%; display: inline-flex;
align-items: center; justify-content: center;
font-family: 'Anton', sans-serif; font-size: 11px; font-weight: 400;
border: 2px solid #5a564d; color: #8d887c;
}
.desk-steps .ds-step.on { color: var(--evidence); }
.desk-steps .ds-step.on b { border-color: var(--evidence); color: var(--evidence); }
.desk-steps .ds-line { width: 34px; height: 1px; background: #4a463e; }
/* Cold-case drawer β€” one-click example evidence */
.coldcase-head {
margin: 20px auto 0; text-align: center; display: block; padding: 0;
font-family: 'Special Elite', monospace; font-size: 12px; color: #f3ead2;
background: none; border-radius: 0; backdrop-filter: none;
text-shadow: 0 1px 2px #000, 0 0 7px rgba(0,0,0,0.92), 1px 1px 0 rgba(0,0,0,0.7);
}
#coldcase-row { display: flex; gap: 10px; justify-content: center; margin-top: 5px; flex-wrap: wrap; }
#coldcase-row .coldcase-btn {
flex: 0 1 auto !important; min-width: 0 !important;
background: rgba(20,19,18,0.85) !important; color: var(--paper) !important;
font-family: 'Oswald', sans-serif !important; font-size: 12px !important;
letter-spacing: 0.16em !important; text-transform: uppercase;
border: 1px dashed rgba(241,196,15,0.55) !important; border-radius: 0 !important;
box-shadow: 2px 2px 0 #000 !important; padding: 8px 16px !important;
}
#coldcase-row .coldcase-btn:hover {
background: #241c0a !important; border-style: solid !important;
transform: translate(-1px,-1px); box-shadow: 3px 3px 0 #000 !important;
}
/* Remove / change the loaded evidence β€” appears only once a photo is in the slot */
#clear-evidence-btn {
margin: 10px auto 0 !important;
max-width: 260px !important; min-width: 0 !important;
background: rgba(20,19,18,0.85) !important; color: var(--paper) !important;
font-family: 'Oswald', sans-serif !important; font-size: 12px !important;
letter-spacing: 0.18em !important; text-transform: uppercase !important;
border: 1px solid rgba(192,57,43,0.6) !important; border-radius: 0 !important;
box-shadow: 2px 2px 0 #000 !important; padding: 9px 18px !important;
}
#clear-evidence-btn:hover {
background: #2a1413 !important; color: #fff !important;
border-color: var(--blood) !important;
transform: translate(-1px,-1px); box-shadow: 3px 3px 0 #000 !important;
}
/* ============================================================
THE PRECINCT ARCHIVE β€” records-room drawer of famous booked cases
============================================================ */
.archive-details { margin-top: 16px; display: block !important; }
.archive-summary {
display: block !important; cursor: pointer; text-align: center;
font-family: 'Special Elite', monospace !important; font-size: 12px !important;
color: #c9a24a !important; padding: 6px 0; transition: color .12s ease;
user-select: none; letter-spacing: 0.04em;
text-shadow: 0 1px 3px rgba(0,0,0,.6);
}
.archive-summary:hover { color: var(--evidence) !important; }
.archive-details.open .archive-summary { color: var(--evidence) !important; }
/* drawer hidden until opened via the JS toggle */
.archive-details .arch-drawer { display: none; }
.archive-details.open .arch-drawer { display: block; }
.arch-drawer {
position: relative; margin-top: 14px; padding: 26px 18px 16px;
background: #1d1c1a; border: 2px solid #34322d; border-radius: 6px;
box-shadow: 0 18px 40px rgba(0,0,0,.7), inset 0 12px 26px rgba(0,0,0,.55);
}
.arch-drawer-tag {
position: absolute; top: -12px; left: 26px; background: #0e0d0b;
border: 1px solid #3a382f; color: #d9cfae; font-family: 'Special Elite', monospace;
font-size: 10px; padding: 3px 12px; letter-spacing: 0.12em;
}
.arch-rail {
display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
gap: 18px;
}
.arch-folder {
position: relative; display: block; margin-top: 14px; text-decoration: none !important;
background: #cdb98a;
background-image: linear-gradient(180deg, rgba(0,0,0,.04), rgba(0,0,0,.12));
border-radius: 3px 3px 4px 4px; box-shadow: 0 7px 14px rgba(0,0,0,.5);
padding: 10px 10px 9px;
transition: transform .12s ease, box-shadow .12s ease;
}
.arch-folder:hover { transform: translateY(-4px) rotate(-0.6deg); box-shadow: 0 12px 22px rgba(0,0,0,.6); }
.arch-tab {
position: absolute; top: -14px; left: 10px; background: #cdb98a;
padding: 2px 12px 1px; border-radius: 5px 5px 0 0;
font-family: 'Courier Prime', monospace; font-weight: 700; font-size: 11px; color: #3a2f1c;
box-shadow: 0 -2px 4px rgba(0,0,0,.18);
}
.arch-clip {
position: absolute; top: -8px; right: 14px; width: 12px; height: 26px;
border: 3px solid #777268; border-radius: 6px; border-bottom: none;
}
.arch-shot { display: block; border: 1px solid #8a7a52; height: 96px; overflow: hidden; background: #222; }
.arch-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.arch-stamp {
position: absolute; top: 30px; left: 50%; transform: translateX(-50%) rotate(-14deg);
border: 3px solid rgba(178,34,28,.75); color: rgba(178,34,28,.8);
font-family: 'Anton', sans-serif; font-size: 14px; letter-spacing: 0.2em;
padding: 1px 9px; pointer-events: none;
}
.arch-meta { display: flex; justify-content: space-between; align-items: flex-end; margin-top: 8px; gap: 6px; }
.arch-line {
font-family: 'Special Elite', monospace; font-size: 10px; color: #4a3c24; line-height: 1.5;
overflow: hidden;
}
.arch-line b { font-size: 11.5px; color: #2c2310; }
.arch-seal {
flex: 0 0 auto; width: 36px; height: 36px; border: 3px solid #b22; border-radius: 50%;
color: #b22; font-family: 'Anton', sans-serif; font-size: 21px;
display: flex; align-items: center; justify-content: center;
transform: rotate(-9deg); background: rgba(255,255,255,.4);
}
.arch-seal.good { border-color: #2e7a4e; color: #2e7a4e; }
.arch-hint {
text-align: center; margin-top: 14px;
font-family: 'Special Elite', monospace; font-size: 11px; color: #8d887c;
}
@media (max-width: 760px) {
.arch-rail { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 14px; }
}
/* Archive open inside the fixed desk: allow scrolling and top-align so the tall
drawer isn't clipped by the desk's overflow:hidden + centered flex. */
body.uxc-upload .screen-upload:has(.archive-details.open) {
overflow-y: auto !important;
justify-content: flex-start !important;
padding-top: 4vh !important;
}
/* Keep the drawer a readable width on large screens. */
.arch-drawer { max-width: 1020px; width: min(94vw, 1020px); margin-left: auto; margin-right: auto; }
.archive-details { width: 100%; }
/* HARD CAP the polaroid's inner content: Gradio's transitional upload/preview
states (and the post-clear placeholder) can momentarily render unconstrained
and balloon the card. Nothing inside may exceed the photo slot height. */
#evidence-drop > div:not(.tape-top):not(.tape-bot):not(.poladrop-extras),
#evidence-drop .wrap,
#evidence-drop .upload-container,
#evidence-drop .image-container,
#evidence-drop .image-frame,
#evidence-drop [data-testid="image"] {
max-height: 380px !important;
overflow: hidden !important;
}
#evidence-drop img { max-height: 380px !important; }
/* The Inspector's remedy β€” suggested correction per charge. Dark green ink on a
faint green note so it reads clearly on the paper yet stands apart from the
testimony. !important: the case-file paper styles override plain color. */
.ev-fix {
margin-top: 8px; font-family: 'Courier Prime', monospace !important;
font-size: 13.5px !important; font-weight: 700;
color: #1f4d2a !important; line-height: 1.55;
background: rgba(46, 107, 63, 0.10);
border-left: 4px solid #2e6b3f; border-radius: 0 3px 3px 0;
padding: 6px 10px 6px 12px; display: block;
}
.ev-fix .ev-fix-tag {
font-family: 'Oswald', sans-serif !important; font-size: 10px !important;
font-weight: 500; letter-spacing: 0.2em;
color: #f3efe2 !important; background: #2e6b3f; padding: 2px 8px;
margin-right: 8px; vertical-align: 1px; border-radius: 2px;
}
/* Gradio's status-tracker also carries class "wrap" β€” without this exclusion our
photo-slot styling turned it into an invisible 362px block that stacked under
the preview during upload/remove (the balloon flash). Kill it entirely. */
#evidence-drop .wrap.default, #evidence-drop .wrap.center, #evidence-drop .wrap.full,
#evidence-drop [data-testid="status-tracker"] {
display: none !important; height: 0 !important; min-height: 0 !important;
aspect-ratio: auto !important; border: none !important; background: none !important;
padding: 0 !important; margin: 0 !important;
}
/* ===========================================================================
VERDICT POWER-UPS β€” Reconstruction (FLUX), Voice (Kokoro), Interrogation.
Each is a dark dossier panel clipped under the case file, with a blood top
rule and gold labels so they read as official extensions of the report.
=========================================================================== */
.case-action {
position: relative; margin: 22px auto 0; max-width: 860px;
background: linear-gradient(180deg, #211d1a 0%, #1a1614 100%);
border: 1px solid #3a322b; border-radius: 4px;
box-shadow: 0 18px 40px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,255,255,0.03);
padding: 20px 22px 22px; overflow: hidden;
}
.case-action::before {
content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px;
background: linear-gradient(90deg, #7a1f17, #c0392b 40%, #7a1f17);
}
.ca-head {
font-family: 'Anton', sans-serif; letter-spacing: 0.04em; font-size: 19px;
color: #ede4cf; text-transform: uppercase; line-height: 1.2; margin-bottom: 14px;
}
.ca-head .ca-ico { color: #c9a24a; margin-right: 8px; }
.ca-head .ca-sub {
display: block; font-family: 'Courier Prime', monospace; font-weight: 400;
text-transform: none; letter-spacing: 0; font-size: 12.5px;
color: #9c8f7d; margin-top: 5px; line-height: 1.5;
}
/* Shared noir action button */
.ca-btn, button.ca-btn {
font-family: 'Oswald', sans-serif !important; letter-spacing: 0.14em !important;
font-weight: 600 !important; text-transform: uppercase; font-size: 13.5px !important;
color: #f3efe2 !important; background: #2a2420 !important;
border: 1px solid #5a4b3c !important; border-radius: 3px !important;
padding: 11px 18px !important; transition: all .18s ease !important;
}
.ca-btn:hover, button.ca-btn:hover {
background: #c0392b !important; border-color: #c0392b !important;
color: #fff !important; transform: translateY(-1px);
box-shadow: 0 8px 20px rgba(192,57,43,0.35) !important;
}
/* ---- THE RECONSTRUCTION (before / after) ---- */
.reco-panel { margin-top: 16px; animation: recoFade .5s ease both; }
.reco-panel.pending .reco-note { color: #b9a98f; }
@keyframes recoFade { from { opacity: 0; transform: translateY(8px);} to { opacity:1; transform:none;} }
.reco-head {
font-family: 'Anton', sans-serif; font-size: 16px; letter-spacing: 0.05em;
color: #c9a24a; text-transform: uppercase; margin-bottom: 4px;
}
.reco-sub { font-family: 'Courier Prime', monospace; font-size: 12.5px; color: #b9a98f; margin-bottom: 14px; line-height: 1.5; }
.reco-grid {
display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px;
}
.reco-col { margin: 0; }
.reco-col img {
width: 100%; border-radius: 3px; border: 1px solid #4a3f33;
background: #0d0b0a; box-shadow: 0 10px 24px rgba(0,0,0,0.5);
}
.reco-col figcaption {
margin-top: 8px; font-family: 'Courier Prime', monospace; font-size: 11.5px;
color: #a89a85; line-height: 1.45;
}
.reco-tag {
display: inline-block; font-family: 'Oswald', sans-serif; font-size: 9.5px;
letter-spacing: 0.16em; font-weight: 600; padding: 2px 7px; border-radius: 2px;
margin-right: 6px; vertical-align: 1px;
}
.reco-tag.found { background: #5a3a36; color: #f0d6d2; }
.reco-tag.fixed { background: #2e6b3f; color: #eafff0; }
.reco-arrow { font-size: 26px; color: #c0392b; text-align: center; }
.reco-note {
margin-top: 14px; font-family: 'Courier Prime', monospace; font-size: 11.5px;
color: #8c7f6e; line-height: 1.5; border-top: 1px dashed #3a322b; padding-top: 12px;
}
@media (max-width: 640px) {
.reco-grid { grid-template-columns: 1fr; }
.reco-arrow { transform: rotate(90deg); }
}
/* ---- THE INSPECTOR'S VOICE ---- */
#verdict-voice { margin-top: 14px; background: transparent !important; border: none !important; }
#verdict-voice audio { width: 100%; }
/* ---- INTERROGATE THE INSPECTOR (chat) ---- */
#grill-chat {
background: #141110 !important; border: 1px solid #3a322b !important;
border-radius: 4px !important; margin-top: 4px;
}
#grill-chat .message.user, #grill-chat [data-testid="user"] {
background: #2a2420 !important; color: #ede4cf !important;
border: 1px solid #4a3f33 !important; font-family: 'Courier Prime', monospace !important;
}
#grill-chat .message.bot, #grill-chat [data-testid="bot"] {
background: #1c2a20 !important; color: #d9e8dd !important;
border: 1px solid #2e4a38 !important; font-family: 'Special Elite', 'Courier Prime', monospace !important;
}
#grill-row { margin-top: 12px; gap: 8px; align-items: stretch; }
#grill-input textarea, #grill-input input {
background: #0f0d0c !important; color: #ede4cf !important;
border: 1px solid #4a3f33 !important; border-radius: 3px !important;
font-family: 'Courier Prime', monospace !important; font-size: 14px !important;
}
#grill-input textarea::placeholder { color: #6e6253 !important; }
#grill-send, button#grill-send {
font-family: 'Oswald', sans-serif !important; letter-spacing: 0.12em !important;
font-weight: 700 !important; background: #c0392b !important; color: #fff !important;
border: none !important; border-radius: 3px !important; min-width: 92px;
}
#grill-send:hover { background: #d8443491 !important; filter: brightness(1.1); }
/* ===========================================================================
VERDICT BROADCAST DESK (P1-A final): masthead | feed + rail | bottom strip.
Gradio Rows/Columns provide the skeleton; these styles dress each region.
=========================================================================== */
/* ---- masthead ---- */
.va-top{align-items:stretch !important;gap:10px !important;margin-bottom:2px}
.va-topbar{flex:1}
.vt-bar{display:flex;align-items:center;gap:13px;background:linear-gradient(90deg,#1a1512,#241d18);
border:1px solid #3a322b;border-top:3px solid #c0392b;padding:9px 16px;min-height:58px}
.vt-logo{width:44px;height:44px;border-radius:50%;flex:none;display:grid;place-items:center;
background:radial-gradient(circle at 38% 30%,#f3ead2,#d9c08c 70%);
box-shadow:0 0 0 2px #c9a24a,0 3px 8px #000}
.vt-logo img{width:82%;height:82%;object-fit:contain}
.vt-brand{font-family:'Oswald',sans-serif;font-size:9px;letter-spacing:.24em;color:#8c7f6e;line-height:1.4}
.vt-brand b{display:block;font-family:'Anton',sans-serif;font-size:13px;letter-spacing:.08em;color:#ede4cf}
.vt-sep{width:1px;align-self:stretch;background:#3a322b;margin:0 2px}
.vt-grade{font-family:'Anton',sans-serif;font-size:22px;color:#fff;background:#c0392b;width:40px;height:40px;
flex:none;border-radius:50%;display:grid;place-items:center;
box-shadow:0 0 0 3px #00000055,0 0 0 5px #c9a24a}
.vt-grade.cleared{background:#2e9e5b}
.vt-text{min-width:0}
.vt-title{display:block;font-family:'Anton',sans-serif;font-size:clamp(15px,1.5vw,20px);letter-spacing:.04em;
text-transform:uppercase;color:#ede4cf;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.vt-sub{display:block;font-family:'Oswald',sans-serif;font-size:10px;letter-spacing:.16em;color:#c9a24a;margin-top:2px}
#newcase-btn{align-self:center;font-family:'Oswald',sans-serif !important;letter-spacing:.14em !important;
font-weight:600 !important;font-size:12px !important;color:#ede4cf !important;background:#2a2420 !important;
border:1px solid #5a4b3c !important;border-radius:3px !important;padding:14px 18px !important;
min-width:0 !important;flex:none !important;width:auto !important;transition:all .15s ease !important}
#newcase-btn:hover{background:#c0392b !important;border-color:#c0392b !important}
/* ---- the evidence feed ---- */
.va-feed{position:relative;background:#000;border:1px solid #3a322b;padding:10px 12px 14px !important;border-radius:2px}
.feed-tag{font-family:'Oswald',sans-serif;font-size:10px;letter-spacing:.2em;color:#ede4cf;
background:#171310;border-left:3px solid #c0392b;padding:6px 10px;margin-bottom:8px;display:inline-block}
.feed-insp{position:absolute;bottom:0;right:-6px;width:clamp(120px,11vw,190px);
filter:drop-shadow(0 8px 20px #000);pointer-events:none;z-index:5}
/* ---- right rail: file + interrogation ---- */
.va-rail{gap:10px !important}
.file-panel{background:#1d1916;border:1px solid #3a322b;display:flex;flex-direction:column;min-height:0}
.fp-head{font-family:'Oswald',sans-serif;font-size:11px;letter-spacing:.22em;color:#c9a24a;
padding:10px 13px;border-bottom:1px solid #3a322b;background:#171310;display:flex;justify-content:space-between;align-items:center}
.fp-stamp{font-family:'Anton',sans-serif;color:#e74c3c;border:2px solid #e74c3c99;padding:0 7px;font-size:11px;transform:rotate(-6deg)}
.file-panel.cleared .fp-stamp{color:#2e9e5b;border-color:#2e9e5b99}
.fp-scroll{overflow:auto;max-height:300px;padding:10px 14px;font-size:12.5px;line-height:1.5}
.fp-summary{color:#bdae97;font-family:'Special Elite',monospace;font-size:12px;padding-bottom:6px;border-bottom:1px dashed #3a322b}
.fp-charge{display:flex;gap:9px;padding:9px 0;border-bottom:1px dashed #3a322b;animation:fpIn .4s ease both;animation-delay:calc(var(--i)*.12s)}
@keyframes fpIn{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}
.fp-num{font-family:'Anton',sans-serif;color:#fff;background:#c0392b;min-width:22px;height:22px;
border-radius:50%;display:grid;place-items:center;font-size:12px;flex:none}
.fp-body{min-width:0}
.fp-crime{font-weight:700;color:#ede4cf}
.fp-sev{font-style:normal;font-family:'Oswald',sans-serif;font-size:9px;letter-spacing:.14em;text-transform:uppercase;padding:1px 6px;border-radius:2px;vertical-align:1px}
.fp-sev.capital{background:#c0392b;color:#fff}.fp-sev.high{background:#e74c3c33;color:#ff9d92}
.fp-sev.medium{background:#e67e2233;color:#f3b97e}.fp-sev.low{background:#f1c40f22;color:#e8d27a}
.fp-test{color:#a89a85;font-size:11.5px;font-family:'Special Elite',monospace}
.fp-fix{color:#7fb88f;font-size:11px;margin-top:3px;border-left:3px solid #2e6b3f;padding-left:7px}
.fp-closing{font-family:'Special Elite',monospace;font-size:11.5px;color:#bdae97;padding-top:8px}
.rail-ph{font-family:'Oswald',sans-serif;font-size:11px;letter-spacing:.22em;color:#c9a24a;
background:#171310;border:1px solid #3a322b;border-bottom:0;padding:9px 13px;margin-bottom:-10px}
.rail-ph b{color:#c0392b;margin-right:5px}
.rail-ph span{display:block;font-family:'Courier Prime',monospace;font-size:10px;letter-spacing:0;color:#8c7f6e;text-transform:none;margin-top:2px}
.tw-on::after{content:"\258C";color:#c9a24a;animation:twb 1s infinite}
@keyframes twb{50%{opacity:0}}
/* ---- bottom strip cells ---- */
.va-bottom{align-items:stretch !important;gap:10px !important}
.va-cell{background:#1d1916 !important;border:1px solid #3a322b !important;border-radius:2px;
padding:10px 13px 13px !important;gap:9px !important}
.cell-ph{font-family:'Oswald',sans-serif;font-size:11px;letter-spacing:.22em;color:#c9a24a}
.cell-ph b{color:#c0392b;margin-right:5px}
.cell-ph span{display:block;font-family:'Courier Prime',monospace;font-size:10px;letter-spacing:0;color:#8c7f6e;text-transform:none;margin-top:2px}
/* reconstruction thumbnails */
.rk-strip{display:flex;gap:10px;flex-wrap:wrap}
.rk{position:relative;width:170px;border:1px solid #4a3f33;overflow:hidden;cursor:pointer;
transition:all .15s ease;text-decoration:none;display:block}
.rk:hover{border-color:#c9a24a;transform:translateY(-2px)}
.rk img{width:100%;height:92px;object-fit:cover;display:block}
.rk-num{position:absolute;top:5px;left:5px;font-family:'Anton',sans-serif;font-size:11px;background:#c0392b;
color:#fff;width:18px;height:18px;border-radius:50%;display:grid;place-items:center;z-index:2}
.rk-zoom{position:absolute;inset:0;display:grid;place-items:center;background:rgba(10,8,6,.5);opacity:0;transition:.15s}
.rk:hover .rk-zoom{opacity:1}
.rk-zoom span{font-family:'Oswald',sans-serif;font-size:9px;letter-spacing:.14em;background:#000d;
border:1px solid #c9a24a;color:#c9a24a;padding:4px 8px}
.rk-badge{position:absolute;bottom:0;left:0;right:0;font-family:'Oswald',sans-serif;font-size:8.5px;
letter-spacing:.14em;text-align:center;background:#000c;color:#7fb88f;padding:3px}
.reco-note{font-family:'Courier Prime',monospace;font-size:11px;color:#8c7f6e;line-height:1.5}
/* the comparator modal (:target) */
.cmp-modal{position:fixed;inset:0;display:none;place-items:center;z-index:300}
.cmp-modal:target{display:grid}
.cmp-bg{position:absolute;inset:0;background:rgba(8,6,4,.9)}
.cmp-box{position:relative;width:min(760px,92vw);background:#1d1916;border:1px solid #c9a24a;
padding:18px;box-shadow:0 40px 90px #000;z-index:2}
.cmp-close{position:absolute;top:8px;right:12px;color:#8c7f6e;font-size:18px;text-decoration:none}
.cmp-close:hover{color:#ede4cf}
.cmp-h{font-family:'Anton',sans-serif;font-size:16px;letter-spacing:.05em;color:#c9a24a;margin-bottom:4px}
.cmp-s{font-family:'Courier Prime',monospace;font-size:11px;color:#9c8f7d;margin-bottom:12px}
.cmp{position:relative;width:100%;aspect-ratio:1.6;overflow:hidden;border:1px solid #4a3f33;user-select:none}
.cmp img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.cmp .cmp-after{clip-path:inset(0 0 0 50%)}
.cmp-handle{position:absolute;top:0;bottom:0;left:50%;width:3px;background:#c9a24a;box-shadow:0 0 12px #c9a24a88;pointer-events:none}
.cmp-handle::after{content:"\25C2 \25B8";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
background:#c9a24a;color:#1d1916;font-size:11px;padding:6px 8px;border-radius:18px;white-space:nowrap;font-family:'Oswald',sans-serif}
.cmp-lb{position:absolute;top:10px;font-family:'Oswald',sans-serif;font-size:9.5px;letter-spacing:.16em;padding:4px 9px;background:#000c}
.cmp-lb.l{left:10px;color:#f0a9a0}.cmp-lb.r{right:10px;color:#9fe7b4}
.cmp input.cmp-rng{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:ew-resize;margin:0}
/* share panel */
.share-panel{display:flex;flex-direction:column;gap:8px}
.sp-row1{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.sp-dl{display:flex;align-items:center;gap:7px;font-family:'Oswald',sans-serif;font-size:10px;letter-spacing:.12em;
background:#2a2420;border:1px solid #c9a24a;color:#c9a24a !important;padding:9px 12px;cursor:pointer;
text-decoration:none !important;transition:.15s}
.sp-dl:hover{background:#c9a24a;color:#1d1916 !important}
.sp-btn{display:grid;place-items:center;width:34px;height:34px;border-radius:3px;color:#fff !important;text-decoration:none !important}
.sp-btn svg{width:16px;height:16px;fill:currentColor}
.sp-btn.x{background:#000;border:1px solid #444}.sp-btn.wa{background:#1faa53}
.sp-btn.fb{background:#1668d8}.sp-btn.li{background:#0a66c2}
.sp-row2{display:flex;gap:6px}
.sp-link{flex:1;font-family:'Courier Prime',monospace;font-size:11.5px;color:#cbbfa8;background:#0f0d0c;
border:1px solid #4a3f33;padding:9px 10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sp-copy{font-family:'Oswald',sans-serif;font-size:10px;letter-spacing:.12em;background:#c0392b;color:#fff;
border:0;padding:0 14px;cursor:pointer;transition:.15s}
.sp-copy.ok{background:#2e9e5b}
/* the broadcast desk needs the full width */
body.uxc-verdict .screen-verdict{max-width:1560px;margin:0 auto}
/* mobile: stack naturally; cap feed inspector */
@media (max-width:900px){
.vt-title{white-space:normal}
.feed-insp{width:96px}
.fp-scroll{max-height:240px}
}
/* the broadcast masthead replaces the page header + wizard on the verdict */
body.uxc-verdict #ux-header, body.uxc-verdict .steps { display: none !important; }
/* FULL TAKEOVER: the broadcast desk replaces the noir stage entirely */
body.uxc-verdict{background:#0d0a08 !important}
body.uxc-verdict #bg-video-wrap,
body.uxc-verdict #laptop-overlay,
body.uxc-verdict .status-line,
body.uxc-verdict .inspector-fig{display:none !important}
body.uxc-verdict gradio-app,body.uxc-verdict .gradio-container{background:#0d0a08 !important;max-width:100% !important}
body.uxc-verdict .screen-verdict{max-width:1560px;margin:0 auto;padding:12px 16px 20px}
/* compact the native audio player into the voice cell */
#verdict-voice{background:transparent !important;border:none !important;padding:0 !important;max-height:96px;overflow:hidden}
#verdict-voice .waveform-container,#verdict-voice canvas{max-height:40px}
#verdict-voice audio{width:100%;height:36px}
/* reco developing note */
.reco-note.dev{color:#c9a24a;font-family:Oswald,sans-serif;letter-spacing:.12em;font-size:11px}
.reco-note.dev::after{content:"\2026";animation:twb 1.2s infinite}
/* ===========================================================================
POLISH ROUND β€” themed scrollbars, unified interrogation panel, single-row
reconstruction carousel, tidy voice cell. Match the approved p1a mockup.
=========================================================================== */
/* ---- noir scrollbars everywhere inside the verdict desk ---- */
.screen-verdict *::-webkit-scrollbar{width:8px;height:8px}
.screen-verdict *::-webkit-scrollbar-track{background:#171310}
.screen-verdict *::-webkit-scrollbar-thumb{background:#4a3f33;border-radius:4px}
.screen-verdict *::-webkit-scrollbar-thumb:hover{background:#c9a24a}
.screen-verdict *{scrollbar-width:thin;scrollbar-color:#4a3f33 #171310}
/* ---- interrogation: chatbot + input row read as ONE panel ---- */
#grill-chat{background:#141110 !important;border:1px solid #3a322b !important;
border-bottom:0 !important;border-radius:0 !important}
#grill-row{background:#141110;border:1px solid #3a322b;border-top:1px dashed #3a322b;
padding:8px !important;margin-top:-12px;gap:6px !important;flex-wrap:nowrap !important;
align-items:stretch !important}
#grill-input{min-width:0}
#grill-input textarea{min-height:38px !important;max-height:38px !important;resize:none !important}
#grill-send,button#grill-send{min-width:74px !important;max-width:96px;height:38px !important;
align-self:center;flex:none !important;padding:0 12px !important}
/* compact noir bubbles; kill Gradio's per-message chrome */
#grill-chat .message-buttons-right,#grill-chat .message-buttons-left,
#grill-chat button[title="Copy message"],#grill-chat .copy-button,
#grill-chat .icon-button-wrapper{display:none !important}
#grill-chat .message{padding:7px 10px !important;font-size:12.5px !important;
border-radius:3px !important}
#grill-chat .bot, #grill-chat .message.bot{font-family:'Special Elite','Courier Prime',monospace !important}
#grill-chat .avatar-container{width:34px !important;height:34px !important;
border:2px solid #c9a24a !important;border-radius:50% !important;overflow:hidden;
background:#11100e;flex:none}
#grill-chat .avatar-container img{object-fit:cover}
#grill-chat .placeholder-content,#grill-chat .placeholder{
font-family:'Special Elite',monospace;color:#8c7f6e}
/* ---- reconstruction: ONE row, horizontal carousel when crowded ---- */
.rk-strip{flex-wrap:nowrap !important;overflow-x:auto;overflow-y:hidden;
padding-bottom:6px;scroll-snap-type:x proximity}
.rk{flex:0 0 168px;scroll-snap-align:start}
/* ---- voice cell: clean compact player, no stray bars ---- */
.va-voice-cell{justify-content:flex-start}
#verdict-voice{max-height:none;overflow:visible}
#verdict-voice .controls,#verdict-voice .settings-wrapper,
#verdict-voice .control-wrapper > .settings-wrapper{display:none !important}
#verdict-voice .component-wrapper{padding:0 !important}
#verdict-voice .waveform-container{background:#171310 !important;border:1px solid #3a322b;
border-radius:3px;padding:4px 8px}
#verdict-voice ::part(cursor){border-color:#c9a24a}
#verdict-voice .source-selection,#verdict-voice .icon-buttons,
#verdict-voice label,#verdict-voice .download-button{display:none !important}
#verdict-voice .play-pause-wrapper button{background:#c0392b !important;border-radius:50% !important;
color:#fff !important;border:none !important}
#verdict-voice .timestamps,#verdict-voice .time,#verdict-voice .duration{
font-family:'Oswald',sans-serif !important;font-size:9px !important;color:#8c7f6e !important}
/* ---- bottom cells: identical height rhythm ---- */
.va-bottom > div{min-height:178px}
.va-cell{justify-content:flex-start !important}
/* ---- file panel scroll area a touch taller, matches chat column ---- */
.fp-scroll{max-height:320px}
/* ===== AUDIT FIXES ===== */
/* HIGH: cap the evidence feed to the viewport so the bottom strip stays above
the fold on 1366x768. The wrapper shrink-wraps the image so the % -positioned
pins and the SVG circles stay perfectly aligned. */
.va-feed{display:flex;flex-direction:column;align-items:center}
.va-feed .feed-tag,.va-feed .hero-legend{align-self:flex-start}
.hero-wrap{width:fit-content;max-width:100%;margin:0 auto}
.hero-wrap img.hero-photo{max-height:56vh;width:auto;max-width:100%;display:block}
/* MED: never crop the rebuilt element in the comparator */
.cmp{background:#0d0b0a}
.cmp img{object-fit:contain !important}
/* MED: the inspector stays decorative β€” behind the evidence, never over a marker */
.hero-wrap{position:relative;z-index:2}
.feed-insp{z-index:1}
/* LOW: one continuous masthead bar (button included) */
.va-top{background:linear-gradient(90deg,#1a1512,#241d18);border:1px solid #3a322b;
border-top:3px solid #c0392b;padding:0 10px 0 0 !important}
.vt-bar{background:transparent;border:none;border-top:none}
/* LOW: bring back the noir vignette behind the desk */
body.uxc-verdict::before{content:"";position:fixed;inset:0;pointer-events:none;z-index:0;
background:radial-gradient(ellipse at 30% 0%,rgba(201,162,74,.07),transparent 55%),
radial-gradient(ellipse at 85% 100%,rgba(192,57,43,.05),transparent 50%)}
/* HIGH fix part 2: the right rail also scales with the viewport, so the
bottom strip stays near the fold on 1366x768 laptops. */
.fp-scroll{max-height:clamp(150px,26vh,320px)}
#grill-chat{height:auto !important;min-height:130px !important;
max-height:clamp(140px,24vh,260px) !important}
/* HIGH fix part 3: kill the dead air above the masthead (empty overlay
wrappers + container padding) so the desk starts at the top edge. */
body.uxc-verdict .gradio-container{padding-top:4px !important}
body.uxc-verdict .uxc-overlay{display:none !important}
body.uxc-verdict .block:has(> #ux-header),
body.uxc-verdict .block:has(.steps){display:none !important}
/* ===== MOCKUP-FAITHFUL CHAT + VOICE (custom HTML, no Gradio widgets) ===== */
#grill-chat-wrap{margin-bottom:-10px}
.grill-box{background:#141110;border:1px solid #3a322b;border-bottom:0;
height:clamp(140px,24vh,260px);overflow-y:auto;padding:10px 12px;
display:flex;flex-direction:column;gap:9px;font-size:12.5px;
font-family:'Courier Prime',monospace;color:#ede4cf}
.grill-empty{font-family:'Special Elite',monospace;color:#8c7f6e;font-size:12px;
margin:auto;text-align:center;padding:0 14px;line-height:1.6}
.grill-box .mu{align-self:flex-end;background:#2a2420;border:1px solid #4a3f33;
padding:7px 10px;border-radius:3px;max-width:86%;line-height:1.45}
.grill-box .brow{display:flex;gap:8px;align-items:flex-start;max-width:92%}
.grill-box .avatar{flex:none;width:34px;height:34px;border-radius:50%;
border:2px solid #c9a24a;background-color:#11100e;background-size:cover;
background-position:center;box-shadow:0 3px 8px #000}
.grill-box .mb{background:#1c2a20;border:1px solid #2e4a38;
font-family:'Special Elite','Courier Prime',monospace;padding:7px 10px;
border-radius:3px;line-height:1.5;color:#d9e8dd;min-width:0}
/* the custom voice player β€” mockup look */
.vp{display:flex;align-items:center;gap:12px;padding:4px 0}
.vp-btn{width:46px;height:46px;border-radius:50%;background:#c0392b;border:none;
color:#fff;font-size:16px;cursor:pointer;flex:none;display:grid;place-items:center;
box-shadow:0 0 0 4px #c0392b22;transition:.15s}
.vp-btn:hover{transform:scale(1.06);box-shadow:0 0 0 6px #c0392b33}
.vp-wave{flex:1;display:flex;align-items:center;gap:3px;height:32px;min-width:40px}
.vp-wave i{width:4px;height:8px;background:#c9a24a;border-radius:2px}
.vp.playing .vp-wave i{animation:wv 1s infinite ease-in-out}
.vp audio{display:none}
/* ===== FAITHFUL ROUND 2: warm panels, typing dots, tape player, type scale ===== */
/* warm the chat surface to the mockup brown (no more black-on-black) */
.grill-box{background:#1d1916;border-color:#3a322b}
/* typing dots while the Inspector thinks */
.typing{display:inline-flex;gap:4px;align-items:center;padding:3px 2px}
.typing i{width:6px;height:6px;border-radius:50%;background:#9fc7ab;animation:tpb 1.2s infinite ease-in-out}
.typing i:nth-child(2){animation-delay:.18s}
.typing i:nth-child(3){animation-delay:.36s}
@keyframes tpb{0%,60%,100%{opacity:.25;transform:translateY(0)}30%{opacity:1;transform:translateY(-3px)}}
/* ---- the tape player v2 ---- */
.vp{display:flex;align-items:center;gap:13px;padding:6px 2px;background:transparent}
.vp-btn{width:50px;height:50px;border-radius:50%;background:radial-gradient(circle at 36% 30%,#d8584a,#a32619 70%);
border:1px solid #e0796d55;color:#fff;font-size:17px;cursor:pointer;flex:none;
display:grid;place-items:center;box-shadow:0 4px 14px #00000080,0 0 0 4px #c0392b22;transition:.16s}
.vp-btn:hover{transform:scale(1.07);box-shadow:0 6px 18px #000000a0,0 0 0 7px #c0392b33}
.vp.playing .vp-btn{box-shadow:0 4px 14px #00000080,0 0 0 5px #c0392b44;animation:vppulse 1.6s infinite}
@keyframes vppulse{50%{box-shadow:0 4px 14px #00000080,0 0 0 9px #c0392b1c}}
.vp-main{flex:1;min-width:0}
.vp-wave{display:flex;align-items:center;gap:3px;height:34px;cursor:pointer;padding:2px 0}
.vp-wave i{flex:1;min-width:2.5px;max-width:5px;height:var(--h,10px);background:#4a3f33;border-radius:2px;transition:background .12s}
.vp-wave i.on{background:#c9a24a;box-shadow:0 0 6px #c9a24a44}
.vp-wave:hover i:not(.on){background:#5d4f3e}
.vp.playing .vp-wave i.on:last-of-type{animation:wv .9s infinite ease-in-out}
.vp-meta{display:flex;align-items:center;gap:8px;margin-top:4px}
.vp-time,.vp-dur{font-family:'Oswald',sans-serif;font-size:9.5px;letter-spacing:.12em;color:#c9a24a;flex:none}
.vp-lbl{flex:1;text-align:center;font-family:'Oswald',sans-serif;font-size:8.5px;letter-spacing:.2em;
color:#8c7f6e;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* ---- share cell: one type scale, no giant COPY ---- */
.sp-dl{font-size:9.5px;padding:8px 11px}
.sp-btn{width:31px;height:31px}
.sp-btn svg{width:14px;height:14px}
.sp-link{font-size:11px;padding:8px 10px}
.sp-copy{font-size:9.5px;padding:0 12px;letter-spacing:.1em}
/* ===== PIXEL ALIGNMENT (measured with the UI inspector) ===== */
/* every block in the right rail shares the exact same left/right edges */
.va-rail .block{padding:0 !important}
.va-rail .form{border:none !important;background:transparent !important}
#grill-row{margin:0 !important;width:auto !important;flex-wrap:nowrap !important;
background:#1d1916;border:1px solid #3a322b;border-top:1px dashed #3a322b;
padding:8px !important;gap:8px !important;align-items:center !important}
#grill-input{border:none !important;background:transparent !important;padding:0 !important;min-width:0}
#grill-input textarea{height:38px !important;min-height:38px !important;max-height:38px !important;
padding:9px 10px !important;line-height:1.4 !important}
#grill-send,button#grill-send{height:38px !important;min-height:38px !important;
max-height:38px !important;line-height:1 !important}
/* the header fuses with the box below it (column gap is 10px) */
.rail-ph{margin-bottom:-10px !important;border-bottom:0 !important}
#grill-chat-wrap{margin-bottom:-10px !important}
.grill-box{border-top:1px solid #3a322b}
/* share: link field and COPY share one height and one type size */
.sp-row2{align-items:stretch}
.sp-link{height:34px;display:flex;align-items:center;padding:0 10px;font-size:10.5px}
.sp-copy{height:34px;font-size:9.5px}
.sp-dl{height:31px;display:inline-flex;align-items:center}
/* the input row tucks into the same 12px edges as the blocks above it */
#grill-row{margin:0 12px !important}
/* ===== SEAMS & LOADERS ROUND ===== */
/* The grey showing through every gap was the Gradio Group/Row surface.
Mimic the mockup: wrappers go transparent so gaps read as the page itself. */
body.uxc-verdict .screen-verdict,
body.uxc-verdict .screen-verdict .row,
body.uxc-verdict .screen-verdict .block,
body.uxc-verdict .screen-verdict .form,
body.uxc-verdict .screen-verdict .gr-group{
background:transparent !important;border:none !important;box-shadow:none !important}
/* fuse the interrogation header to its body: no gap at all in the rail */
.va-rail{gap:0 !important}
.va-rail > *{margin:0 !important}
.rail-ph{margin:10px 0 0 0 !important;border-bottom:0 !important}
#grill-chat-wrap{margin:0 !important}
#grill-row{margin:0 12px !important}
/* COPY matches DOWNLOAD THE SHARE CARD exactly */
.sp-dl{height:31px !important;font-size:9.5px !important}
.sp-copy{height:31px !important;font-size:9.5px !important;font-family:'Oswald',sans-serif !important;
letter-spacing:.12em !important;padding:0 11px !important;display:inline-flex;align-items:center}
.sp-link{height:31px;font-size:10.5px}
/* the lab loader: spinning gold ring + shimmer bar */
.lab-load{display:flex;align-items:center;gap:13px;padding:6px 2px}
.ll-ring{flex:none;width:30px;height:30px;border-radius:50%;
border:3px solid #3a322b;border-top-color:#c9a24a;animation:llspin 1s linear infinite}
@keyframes llspin{to{transform:rotate(360deg)}}
.ll-txt{flex:1;min-width:0;font-family:'Oswald',sans-serif;font-size:10.5px;
letter-spacing:.16em;color:#c9a24a;line-height:1.5}
.ll-bar{display:block;height:3px;margin:6px 0 4px;border-radius:2px;
background:linear-gradient(90deg,#3a322b 0%,#c9a24a 50%,#3a322b 100%);
background-size:200% 100%;animation:llshimmer 1.5s linear infinite}
@keyframes llshimmer{from{background-position:200% 0}to{background-position:-200% 0}}
.ll-txt small{display:block;font-family:'Courier Prime',monospace;font-size:10px;
letter-spacing:0;color:#8c7f6e;text-transform:none}
/* two internal 10px paddings (block tail + wrap head) sit between the header
and the chat body β€” pull the body up so they fuse into one panel */
#grill-chat-wrap{margin-top:0 !important}
#grill-row{margin:-10px 12px 0 !important}
/* ===== SECTIONED BLOCKS ROUND (user spec) ===== */
/* 1) the grey styler surface -> deep noir #100a09 */
body.uxc-verdict .styler{background:#100a09 !important}
/* 2) masthead: each piece boxed in its own bordered block, aligned in a row */
.vt-bar{display:flex;align-items:stretch;gap:10px;background:transparent;border:none;padding:0;min-height:58px}
.vt-seg{display:flex;align-items:center;gap:12px;background:linear-gradient(90deg,#1a1512,#241d18);
border:1px solid #3a322b;border-top:3px solid #c0392b;padding:7px 16px}
.vt-seg-case{flex:1;min-width:0}
.va-top{background:transparent !important;border:none !important;padding:0 !important}
#newcase-btn{border-top:3px solid #c0392b !important;align-self:stretch !important;height:auto !important}
/* 3) feed letterbox fills with #100a09 (not pure black) */
.va-feed{background:#100a09 !important}
/* 4) case file top == feed block top (the wrap adds 10px β€” pull it up) */
.va-file{margin-top:0 !important}
.file-panel{position:relative;top:0;margin-bottom:0}
/* 5) every section wears its border */
.rail-ph{border:1px solid #3a322b !important;border-bottom:0 !important}
.grill-box{border:1px solid #3a322b !important;border-bottom:0 !important}
#grill-row{border:1px solid #3a322b !important;border-top:1px dashed #3a322b !important}
.va-cell{border:1px solid #3a322b !important}
.file-panel{border:1px solid #3a322b !important}
/* ===== PIXEL GRID ROUND: one container, every row on the same axes ===== */
/* the wrapper column of reco+voice is invisible β€” only the grid matters */
.vb-left{padding:0 !important;background:transparent !important;border:none !important;
box-shadow:none !important;min-width:0}
.vb-left-row{gap:10px !important;margin:0 !important}
/* the bottom strip breathes the same 10px as file -> interrogation */
.va-bottom{margin-top:10px !important;gap:10px !important}
.va-main{gap:10px !important}
.va-top{gap:10px !important;margin-bottom:0 !important}
/* INVESTIGATE button fills its slot: same height + red rule as its siblings */
#newcase-btn{height:100% !important;min-height:58px !important;margin:0 !important}
.va-top > div:has(> #newcase-btn){display:flex;align-items:stretch}
/* hard grid: both rows share IDENTICAL column tracks (13/6 = 68.42/31.58) */
body.uxc-verdict .screen-verdict .block{padding:0 !important}
.va-rail{padding:0 !important}
.va-feed,.vb-left{flex:0 0 calc(68.42% - 5px) !important;max-width:calc(68.42% - 5px) !important;min-width:0 !important}
.va-rail,.va-share-cell{flex:0 0 calc(31.58% - 5px) !important;max-width:calc(31.58% - 5px) !important;min-width:0 !important}
@media (max-width:900px){
.va-feed,.vb-left,.va-rail,.va-share-cell{flex:1 1 100% !important;max-width:100% !important}
}
/* the universal 12px inset on every gr.HTML β€” gone inside the desk */
body.uxc-verdict .screen-verdict .html-container{padding:0 !important}
/* sit the input a snug gap below the trial chips */
#grill-row{margin:6px 0 0 !important}
/* ===== EQUAL HEIGHTS ROUND ===== */
/* bottom strip: the three cells always share the SAME height (tallest wins) */
.va-bottom{align-items:stretch !important}
.vb-left{display:flex !important;flex-direction:column !important}
.vb-left-row{flex:1 !important;align-items:stretch !important}
.vb-left-row > .va-cell{height:auto !important;align-self:stretch !important}
.va-share-cell{height:auto !important;align-self:stretch !important}
.va-cell{min-height:130px}
/* header: the three blocks are EXACTLY the same height, same gap below */
.vt-bar{min-height:0}
.vt-seg{height:62px;box-sizing:border-box}
#newcase-btn{height:62px !important;min-height:62px !important;max-height:62px !important;
align-self:flex-start !important}
.va-top{align-items:flex-start !important}
/* the case-file header wears the exact same uniform as its sibling headers */
.fp-head{color:#c9a24a !important;font-family:'Oswald',sans-serif !important;
font-size:11px !important;letter-spacing:.22em !important}
.fp-head b{color:#c0392b;margin-right:5px;font-weight:inherit}
/* the header SPAN itself was inheriting cream β€” force the mustard gold */
.fp-head span{color:#c9a24a !important}
.fp-head span b, .fp-head b{color:#c0392b !important}
.fp-head .fp-stamp{color:#e74c3c !important}
.file-panel.cleared .fp-head .fp-stamp{color:#2e9e5b !important}
/* bubble text survives any theme variant */
.grill-box .mu{color:#ede4cf !important}
.grill-box .mb{color:#d9e8dd !important}
/* mobile: the photo obeys the viewport (the 56vh desktop cap let it overflow) */
@media (max-width:900px){
.hero-wrap{width:100% !important}
.hero-wrap img.hero-photo{width:100% !important;max-height:none !important}
}
/* mobile: let every flex child actually shrink, wrap the long single-line bits */
@media (max-width:900px){
.screen-verdict *{min-width:0 !important}
.feed-tag{white-space:normal !important;display:block !important;width:auto !important}
.hero-legend{flex-wrap:wrap !important;max-width:100% !important}
.vt-bar{flex-wrap:wrap !important;min-height:0 !important}
.vt-seg{height:auto !important;min-height:52px;width:100% !important;box-sizing:border-box}
#newcase-btn{width:100% !important;height:48px !important;min-height:48px !important;max-height:none !important}
.rk-strip{max-width:100%}
}
/* mobile: rows become plain block stacks β€” no flex min-content surprises */
@media (max-width:900px){
.va-main,.va-bottom,.vb-left-row,.va-top{display:block !important}
.va-feed,.va-rail,.va-cell,.vb-left{display:block !important;width:100% !important;
max-width:100% !important;flex:none !important;margin-bottom:10px !important}
.screen-verdict .styler,.screen-verdict .row{width:100% !important;max-width:100% !important}
}
/* mobile: Gradio nests two .screen-verdict wrappers β€” pin the inner one to 100% */
@media (max-width:900px){
.screen-verdict .screen-verdict,
.screen-verdict .styler{width:100% !important;max-width:100% !important;min-width:0 !important}
body.uxc-verdict .screen-verdict{padding:8px !important}
}
/* ===== THE TRIAL β€” guiding move chips (minimal, fits the tight rail) ===== */
.trial-chips{display:grid;grid-template-columns:1fr 1fr;grid-auto-rows:36px;gap:6px;margin:6px 0 0;padding:0;align-items:stretch}
.trial-chip{font-family:'Oswald',sans-serif;font-size:10px;letter-spacing:.06em;
background:#221c18;border:1px solid #5a4b3c;color:#cbbfa8;padding:0 8px;border-radius:3px;
cursor:pointer;display:flex;align-items:center;justify-content:center;gap:5px;transition:.14s;
line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:36px;box-sizing:border-box}
.trial-chip .tc-ico{line-height:1;display:inline-flex;align-items:center}
.trial-chip:hover{background:#c0392b;border-color:#c0392b;color:#fff;transform:translateY(-1px)}
.trial-chip .tc-ico{color:#c9a24a;font-size:11px}
.trial-chip:hover .tc-ico{color:#fff}
/* =========================================================================
MOST WANTED β€” public city board (ludic noir wall; graceful without assets)
========================================================================= */
/* MOST WANTED β€” the secondary entry to the public board. Bone-outline at rest so it
sits clearly BELOW the red FILE THE CASE primary; on hover it ignites into an inked
red stamp (warm text + red frame + glow). Compact, so the desk keeps its space. */
/* one-line teaser above the MOST WANTED button β€” plain readable text, no pill */
.mw-enter-sub{margin:22px auto 0;text-align:center;white-space:nowrap;
font-family:'Special Elite',monospace;font-size:11px;letter-spacing:.02em;color:#dccca8;
text-shadow:0 1px 2px #000,0 0 7px rgba(0,0,0,.92)}
/* MOST WANTED β€” a filled DARK-red action (clearly active/clickable), one notch
below the brighter FILE THE CASE primary. Same button family + a soft sheen. */
#mw-enter-btn{
display:block !important;width:-moz-fit-content !important;width:fit-content !important;
min-width:244px !important;max-width:86% !important;margin:-4px auto 0 !important;
min-height:0 !important;height:auto !important;
background:linear-gradient(rgb(81 12 4), rgb(41 5 2)) !important;
border:1px solid #7a1f17 !important;border-radius:3px !important;
box-shadow:0 6px 15px rgba(0,0,0,.5),inset 0 1px 0 rgba(255,255,255,.08) !important;
padding:12px 30px !important;position:relative;overflow:hidden;
font-family:'Oswald',sans-serif !important;font-weight:700 !important;
font-size:13px !important;letter-spacing:.2em !important;text-transform:uppercase;text-align:center;
color:#f3e0cf !important;
transition:background .18s ease,border-color .18s ease,box-shadow .18s ease,color .18s ease !important}
#mw-enter-btn::after{content:"";position:absolute;top:0;left:-60%;width:38%;height:100%;
background:linear-gradient(100deg,transparent,rgba(255,255,255,.22),transparent);
transform:skewX(-20deg);animation:btnSheen 3.6s ease-in-out infinite;pointer-events:none}
#mw-enter-btn:hover{
background:linear-gradient(rgb(81 12 4), rgb(41 5 2)) !important;border-color:#c0392b !important;
box-shadow:0 9px 22px rgba(192,57,43,.38),inset 0 1px 0 rgba(255,255,255,.12) !important;
color:#fff !important}
/* The wall is now a FIXED full-viewport body background (see body.uxc-mostwanted),
so .mw-wrap is just a transparent content holder β€” no black fill, no seam line.
NOTE: never use 100vh / min-height:100vh here β€” inside the Space's auto-resizing
iframe it feeds back (content grows -> iframe grows -> 100vh grows…) = "infinite zoom". */
.mw-wrap{position:relative;border:none;overflow:visible;min-height:560px;background:transparent}
.mw-inner{position:relative;padding:24px 22px 34px;max-width:1240px;margin:0 auto}
/* The real Gradio back button is hidden β€” the VISIBLE one lives in the board's side
column (.mw-back-board, below the Inspector) and delegates its click to this. Kept
absolutely positioned (not display:none) so JS .click() still fires its handler, and
so the top of the board reclaims the pixels the old top button used to take. */
#mw-back-btn{position:absolute !important;width:1px !important;height:1px !important;
padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0,0,0,0) !important;
border:0 !important;opacity:0 !important;pointer-events:none !important}
/* collapse the Gradio wrapper around it so it adds no vertical space at the top */
body.uxc-mostwanted .block:has(> #mw-back-btn){min-height:0 !important;padding:0 !important;margin:0 !important;height:0 !important;overflow:hidden !important}
/* the prominent in-board back button β€” full width of the side column, hard to miss */
.mw-back-board{
display:block;width:100%;margin-top:14px;cursor:pointer;
background:linear-gradient(180deg,#8f2417,#56130d);
border:1px solid #7a1f17;border-radius:4px;padding:14px 16px;
font-family:'Oswald',sans-serif;font-weight:700;font-size:12.5px;letter-spacing:.16em;
text-transform:uppercase;color:#f3e0cf;text-align:center;line-height:1.3;
box-shadow:0 6px 16px rgba(0,0,0,.5),inset 0 1px 0 rgba(255,255,255,.08);
transition:background .18s ease,border-color .18s ease,box-shadow .18s ease,color .18s ease}
.mw-back-board:hover{background:linear-gradient(180deg,#b0301d,#7a1f17);border-color:#c0392b;color:#fff;
box-shadow:0 9px 22px rgba(192,57,43,.4)}
.mw-back-board:active{transform:translateY(1px)}
/* header */
.mw-head{text-align:center;margin-bottom:18px}
/* !important: gr.HTML lives inside Gradio's .prose, whose `img{max-width:100%}` rule
otherwise overrides these and blows the sign up + breaks the auto-margin centering. */
.mw-sign{max-width:360px !important;width:58% !important;display:block !important;
margin:0 auto !important;filter:drop-shadow(0 6px 16px #000)}
.mw-title{font-family:'Anton',sans-serif;font-size:clamp(34px,6vw,58px);letter-spacing:.05em;color:#ede4cf;
text-shadow:0 0 26px rgba(192,57,43,.4);margin:0}
.mw-title::before{content:"☠ ";color:#c0392b}
.mw-sub{font-family:'Oswald',sans-serif;font-size:11px;letter-spacing:.28em;color:#d8b25a;
display:table;margin:15px auto 0;padding:5px 16px;border-radius:11px;background:rgba(8,6,4,.6);
text-shadow:0 1px 3px #000,0 0 10px rgba(0,0,0,.85)}
.mw-stats{display:inline-flex;justify-content:center;gap:0;margin-top:22px;flex-wrap:wrap;
background:rgba(13,10,8,.86);border:1px solid #3a322b;border-top:2px solid #c0392b;border-radius:4px;
padding:12px 8px;box-shadow:0 7px 20px rgba(0,0,0,.45)}
.mw-stat{padding:2px 30px}
.mw-stat + .mw-stat{border-left:1px solid #3a322b}
.mw-stat b{display:block;font-family:'Anton',sans-serif;font-size:27px;color:#e8584a;line-height:1}
.mw-stat span{font-family:'Oswald',sans-serif;font-size:9px;letter-spacing:.18em;color:#b3a691}
/* grid: board + side report */
.mw-grid{display:grid;grid-template-columns:1.7fr 1fr;gap:14px;align-items:start}
.mw-bh{font-family:'Oswald',sans-serif;font-size:10.5px;letter-spacing:.2em;color:#c9a24a;margin-bottom:9px;
display:inline-block;padding:5px 12px;border-radius:11px;background:rgba(8,6,4,.6);
text-shadow:0 1px 3px #000,0 0 10px rgba(0,0,0,.85)}
/* a booked record = a wanted poster row */
.mw-rec{display:flex;gap:13px;align-items:center;background:linear-gradient(180deg,#211b18,#181311);
border:1px solid #3a322b;border-left:4px solid #c0392b;padding:10px 13px;margin-bottom:9px;
text-decoration:none !important;transition:all .15s ease;color:inherit}
.mw-rec:hover{border-left-color:#c9a24a;transform:translateX(3px);box-shadow:0 8px 22px rgba(0,0,0,.45)}
.mw-rank{font-family:'Anton',sans-serif;font-size:24px;color:#fff;background:#3a2c1a;width:42px;height:42px;
flex:none;border-radius:50%;display:grid;place-items:center;box-shadow:0 0 0 2px #5a4b3c}
.mw-rank.r1{background:#c0392b;box-shadow:0 0 0 2px #c9a24a}.mw-rank.r2{background:#7a1f17;box-shadow:0 0 0 2px #c9a24a}
.mw-rank.r3{background:#5a2018;box-shadow:0 0 0 2px #c9a24a}
.mw-shot{position:relative;flex:none;width:118px;height:64px;border:1px solid #4a3f33;overflow:hidden;background:#0d0b0a}
.mw-shot img{width:100%;height:100%;object-fit:cover;filter:grayscale(.35) contrast(1.05)}
.mw-meta{flex:1;min-width:0;display:flex !important;flex-direction:column;justify-content:center}
.mw-site{font-family:'Oswald',sans-serif;font-weight:700;font-size:15px;letter-spacing:.03em;color:#ede4cf;display:block}
.mw-charge{font-family:'Special Elite',monospace;font-size:11.5px;color:#cdbfa6;display:block;margin-top:3px;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mw-charge i{font-family:'Oswald',sans-serif;font-style:normal;font-size:8.5px;letter-spacing:.16em;
color:#e0584a;margin-right:6px;vertical-align:1px}
.mw-count{flex:none;text-align:center;min-width:56px;padding:0 10px;border-left:1px solid #312824}
.mw-count b{display:block;font-family:'Anton',sans-serif;font-size:26px;color:#e8584a;line-height:1}
.mw-count span{font-family:'Oswald',sans-serif;font-size:8px;letter-spacing:.14em;color:#9c8f7d;text-transform:uppercase}
.mw-seal{font-family:'Anton',sans-serif;font-size:20px;width:40px;height:40px;flex:none;border-radius:50%;
display:grid;place-items:center;color:#fff;border:2px solid #c9a24a}
.mw-seal.g-f{background:#7a1f17}.mw-seal.g-d{background:#a83227}.mw-seal.g-c{background:#b06a1e}.mw-seal.g-clear{background:#2e9e5b}
.mw-view{font-family:'Oswald',sans-serif;font-size:9px;letter-spacing:.16em;color:#c9a24a;flex:none}
/* side: AI city report + crime bars */
.mw-report{background:linear-gradient(180deg,#1d1916,#141110);border:1px solid #3a322b;padding:14px}
.mw-rh{font-family:'Oswald',sans-serif;font-size:11px;letter-spacing:.2em;color:#c9a24a}
.mw-rh b{color:#c0392b}
.mw-body{font-family:'Special Elite',monospace;font-size:12.5px;line-height:1.6;color:#cdbfa6;margin-top:8px}
.mw-body em{color:#c9a24a;font-style:normal}
.mw-epi{display:flex;align-items:center;gap:8px;margin:6px 0;font-size:11.5px}
.mw-en{flex:0 0 118px;color:#cbbfa8}
.mw-eb{flex:1;height:9px;background:#3a322b;border-radius:5px;overflow:hidden}
.mw-eb i{display:block;height:100%;background:linear-gradient(90deg,#7a1f17,#c0392b)}
.mw-ec{font-family:'Oswald',sans-serif;font-size:10px;color:#c9a24a;flex:none;width:26px;text-align:right}
/* the Inspector himself fills the space under the city report */
.mw-mug{margin-top:14px;background:linear-gradient(180deg,#1d1916,#141110);border:1px solid #3a322b;
border-top:2px solid #7a1f17;padding:14px 12px 12px;text-align:center;position:relative;overflow:hidden}
.mw-mug::before{content:"";position:absolute;inset:0;
background:radial-gradient(ellipse at 50% 18%,rgba(192,57,43,.12),transparent 60%);pointer-events:none}
.mw-mug img{position:relative;width:auto;max-width:90%;max-height:236px;display:block;margin:0 auto;
transform:scaleX(-1);filter:contrast(1.04) drop-shadow(0 8px 16px rgba(0,0,0,.55))}
.mw-mug span{position:relative;display:block;font-family:'Oswald',sans-serif;font-size:9px;
letter-spacing:.22em;color:#9c8f7d;margin-top:8px}
@media (max-width:900px){.mw-grid{grid-template-columns:1fr}.mw-shot{width:90px;height:52px}.mw-mug img{max-height:200px}}
/* MOST WANTED is its own full screen β€” kill the desk overlays that belong to the home */
body.uxc-mostwanted #laptop-overlay,
body.uxc-mostwanted #intro-alley,
body.uxc-mostwanted #bg-video-wrap,
body.uxc-mostwanted #sound-gate,
body.uxc-mostwanted #ux-header,
body.uxc-mostwanted .steps,
body.uxc-mostwanted .inspector-fig,
body.uxc-mostwanted .status-line{display:none !important}
/* full-screen wall: the precinct wall is a FIXED viewport background (with the dark
scrim + red glow layered on top) so it covers the ENTIRE screen β€” back button,
margins and all β€” with no black strip behind it. Fixed = no iframe zoom feedback. */
body.uxc-mostwanted{
background:
radial-gradient(ellipse at 50% 38%, rgba(8,6,4,0) 30%, rgba(8,6,4,.48) 70%, rgba(8,6,4,.92) 100%),
radial-gradient(ellipse at 50% 5%, rgba(192,57,43,.15), transparent 50%),
var(--mw-wall, none) center center / cover no-repeat fixed,
#0d0a08 !important;
}
/* Gradio paints a default gray block fill (#3f3f46) on its .styler / group wrappers,
which would sit OVER the body wall β€” force them transparent so the wall shows. */
body.uxc-mostwanted .styler,
body.uxc-mostwanted .gr-group,
body.uxc-mostwanted .gradio-container .block { background: transparent !important; }
/* full-bleed: the precinct wall fills the whole screen, content stays centred inside */
body.uxc-mostwanted .gradio-container{padding:0 !important;max-width:none !important;margin:0 !important}
body.uxc-mostwanted .gradio-container > .main,
body.uxc-mostwanted .main.fillable{max-width:none !important;width:100% !important;margin:0 !important}
.screen-mostwanted{position:relative;z-index:2}
/* opt-in publish to MOST WANTED */
#publish-btn{width:100%;margin-top:8px !important;font-family:'Oswald',sans-serif !important;
letter-spacing:.12em !important;font-size:11px !important;color:#ede4cf !important;
background:linear-gradient(180deg,#2a1614,#1a0f0d) !important;border:1px solid #7a1f17 !important;
border-radius:3px !important;padding:10px !important;transition:all .15s ease !important}
#publish-btn:hover{background:#c0392b !important;border-color:#c0392b !important}
/* SEE IT ON THE BOARD β€” same dark-red treatment + sheen as the home MOST WANTED button */
#mw-view-btn{width:100% !important;margin-top:8px !important;
background:linear-gradient(rgb(81 12 4), rgb(41 5 2)) !important;
border:1px solid #7a1f17 !important;border-radius:3px !important;
box-shadow:0 6px 15px rgba(0,0,0,.5),inset 0 1px 0 rgba(255,255,255,.08) !important;
padding:12px 16px !important;position:relative;overflow:hidden;
font-family:'Oswald',sans-serif !important;font-weight:700 !important;
font-size:12px !important;letter-spacing:.16em !important;text-transform:uppercase;color:#f3e0cf !important;
transition:background .18s ease,border-color .18s ease,box-shadow .18s ease,color .18s ease !important}
#mw-view-btn::after{content:"";position:absolute;top:0;left:-60%;width:38%;height:100%;
background:linear-gradient(100deg,transparent,rgba(255,255,255,.22),transparent);
transform:skewX(-20deg);animation:btnSheen 3.6s ease-in-out infinite;pointer-events:none}
#mw-view-btn:hover{background:linear-gradient(rgb(81 12 4), rgb(41 5 2)) !important;
border-color:#c0392b !important;color:#fff !important;
box-shadow:0 9px 22px rgba(192,57,43,.38),inset 0 1px 0 rgba(255,255,255,.12) !important}
.pub-note{font-family:'Courier Prime',monospace;font-size:11px;color:#9c8f7d;margin-top:7px;line-height:1.5}
.pub-note.ok{color:#9fe7b4} .pub-note.ok b{color:#c9a24a}
.mw-fresh{position:absolute;top:4px;right:4px;font-family:'Oswald',sans-serif;font-size:8px;
letter-spacing:.12em;background:#c0392b;color:#fff;padding:2px 5px;border-radius:2px}
/* ── THE PROSECUTION β€” its own section, BEFORE The Trial. Styled as a TWIN of the
other panels (same #171310 gold header + #1d1916 bordered body as the case file /
THE TRIAL header), with a 10px gap so it never mounts onto the case file above. ── */
.prosecution-slot{padding:0 !important;margin:0 !important;border:0 !important;
background:transparent !important;min-height:0 !important}
.prosecution-box{margin:10px 0 0 !important}
/* header strip β€” identical to .rail-ph (THE TRIAL) and .fp-head (case file) */
.prosecution-box .pb-head{
font-family:'Oswald',sans-serif;font-size:11px;letter-spacing:.22em;text-transform:uppercase;
color:#c9a24a;background:#171310;border:1px solid #3a322b;border-bottom:0;padding:9px 13px}
.prosecution-box .pb-seal{color:#c0392b;margin-right:5px}
.prosecution-box .pb-head small{
display:block;letter-spacing:0;text-transform:none;font-size:10px;margin-top:2px;
color:#8c7f6e;font-family:'Courier Prime',monospace}
/* body β€” the bordered noir card the statement sits in */
.prosecution-box .pb-body{
margin:0 !important;padding:11px 13px;border:1px solid #3a322b;border-top:0;background:#1d1916;
font-style:italic;line-height:1.5;font-size:11.5px;color:#cdbfa6;
max-height:172px;overflow-y:auto}
.prosecution-box .pb-body::-webkit-scrollbar{width:6px}
.prosecution-box .pb-body::-webkit-scrollbar-track{background:transparent}
.prosecution-box .pb-body::-webkit-scrollbar-thumb{background:rgba(201,162,74,.35);border-radius:3px}
/* ===== VERDICT LAYOUT v2 (user spec) β€” one body row, tall right rail ===== */
/* LEFT col (68.42%) = evidence feed on top + the three utility cards (recon Β·
voice Β· share) along the bottom. RIGHT col (31.58%) = a single tall rail
(case file β†’ prosecution β†’ trial chat) stretched to the left column's height,
so the chat fills the leftover space and nothing feels cramped. Appended last
so it overrides the old .va-main/.va-bottom grid rules above. */
.va-body{display:flex !important;align-items:stretch !important;gap:10px !important;margin-top:10px !important}
.va-left{flex:0 0 calc(68.42% - 5px) !important;max-width:calc(68.42% - 5px) !important;
min-width:0 !important;display:flex !important;flex-direction:column !important;gap:10px !important}
.va-body > .va-rail{flex:0 0 calc(31.58% - 5px) !important;max-width:calc(31.58% - 5px) !important;
min-width:0 !important;display:flex !important;flex-direction:column !important;gap:0 !important}
/* the EXHIBIT A feed absorbs the column's extra height (it grows); the utility row
stays at its NATURAL height and gets pushed to the bottom β€” so reco/share fill
their own content with no void and still bottom-align with the chat */
.va-left > .va-feed{flex:1 1 auto !important;max-width:none !important;min-height:300px}
.va-left > .va-utilities{flex:0 0 auto !important}
/* three utility cards share one row at the bottom of the left column */
.va-utilities{display:flex !important;align-items:stretch !important;gap:10px !important;margin:0 !important}
.va-utilities > .va-cell{min-width:0 !important;max-width:none !important;align-self:stretch !important;
min-height:150px}
/* reconstruction thumbnails scroll HORIZONTALLY if there are many (so the cell
can be narrow without wrapping into a tall stack) */
.va-utilities .va-reco-cell .rk-strip{flex-wrap:nowrap !important;overflow-x:auto;padding-bottom:4px}
.va-utilities .va-reco-cell .rk{flex:0 0 auto}
.va-reco-cell .rk-strip::-webkit-scrollbar{height:6px}
.va-reco-cell .rk-strip::-webkit-scrollbar-thumb{background:rgba(201,162,74,.4);border-radius:3px}
/* the rail: case file + prosecution at natural height; the CHAT grows to fill
the rest so the right column matches the tall left column ("more air") */
.va-body > .va-rail > #grill-chat-wrap{flex:1 1 auto !important;min-height:74px !important;
display:flex !important;flex-direction:column !important}
.va-body > .va-rail > #grill-chat-wrap .grill-box{flex:1 1 auto !important;overflow-y:auto;min-height:0}
.va-body > .va-rail .grill-box::-webkit-scrollbar{width:6px}
.va-body > .va-rail .grill-box::-webkit-scrollbar-thumb{background:rgba(58,50,43,.9);border-radius:3px}
/* responsive: stack everything on narrow screens */
@media (max-width:900px){
.va-body{display:block !important}
.va-left,.va-body > .va-rail{flex:1 1 100% !important;max-width:100% !important;width:100% !important;
display:block !important}
.va-utilities{display:block !important}
.va-utilities > .va-cell{width:100% !important;margin-bottom:10px !important}
.va-left > .va-feed{min-height:0 !important}
.va-body > .va-rail > #grill-chat-wrap{min-height:200px !important}
}
/* ===== UTILITY ROW β€” TWO cards (reconstruction Β· share) side by side, 50/50 width,
each at its OWN natural COMPACT height: content packed at the TOP, NO stretching,
NO min-height, NO centering, NO voids. THE VOICE lives up in the right rail. ===== */
.va-utilities{align-items:stretch !important}
.va-utilities > .va-cell{min-height:0 !important;align-self:stretch !important;
justify-content:flex-start !important}
/* RECONSTRUCTION: the FLUX thumbnails grow VERTICALLY to fill the (now equal-height)
card β€” object-fit:cover keeps them undistorted; the horizontal strip stays aligned */
.va-reco-cell .rk-strip{flex:1 1 auto !important;align-items:stretch !important;min-height:0 !important}
.va-reco-cell .rk{height:auto !important}
.va-reco-cell .rk img{height:100% !important;min-height:92px !important}
.va-utilities .cell-ph{padding-bottom:8px !important;border-bottom:1px solid rgba(201,162,74,.16) !important}
/* 50/50 widths (override the old 31.58% cap that constrained .va-share-cell) */
.va-utilities > .va-reco-cell{flex:1 1 0 !important;max-width:none !important;min-width:200px !important}
.va-utilities > .va-share-cell{flex:1 1 0 !important;max-width:none !important;min-width:200px !important}
/* SHARE: X joins the other socials (download takes the whole first line); the MOST
WANTED button sits right after the link/copy row β€” no gap */
.va-share-cell .sp-row1{gap:8px !important}
.va-share-cell .sp-dl{flex:1 1 100% !important;width:100% !important;justify-content:center !important}
/* SHARE: pack the MOST WANTED button snug under the link/copy row. The gradio block
wrappers inside the utility cells carry a stray ~34px min-height (zeroed in the rail
but not here) which was opening the void β€” kill it so each block hugs its content. */
.va-cell .block, .va-cell .html-container, .va-cell .form{min-height:0 !important;padding:0 !important}
.va-share-cell{gap:7px !important;justify-content:flex-start !important}
/* the share blocks must NOT flex-grow (gradio gives them flex:1, which expanded the
share-panel block and dropped the MOST WANTED button ~40px). Pin them to content
size so the button sits right under the link/copy row; any slack falls below it. */
.va-share-cell > *{margin:0 !important;flex:0 0 auto !important}
#publish-btn{margin-top:7px !important}
/* ===== THE VOICE β€” a slim player bar at the TOP of the right rail. The header
fuses with the player box below it (case-file look); the waveform is strictly
CONTAINED (fixed box + overflow clipped + bars capped) so it can NEVER spill. === */
.va-voice-head{margin-top:0 !important} /* first item in the rail β€” flush to the top */
.va-voice-out{margin:0 !important}
.va-voice-out .vp{border:1px solid #3a322b;border-top:0;background:#1d1916;
padding:9px 13px;margin:0;border-radius:0}
.va-voice-out .vp-wave{height:34px !important;overflow:hidden !important;align-items:center !important}
.va-voice-out .vp-wave i{max-height:30px !important}
/* a clean ~10px visible gap above the case file (the voice block contributes its
own ~5px, so 5px here lands the box-to-box gap at 10px like the others) */
.va-rail .va-file{margin-top:5px !important}
/* trim the case-file scroll + prosecution body a touch to make room for the voice bar */
.va-rail .fp-scroll{max-height:clamp(120px,19vh,220px) !important}
.va-rail .prosecution-box .pb-body{max-height:148px !important}
/* ===== URGENT FINAL FIXES ===== */
/* 1) THE VOICE loading state ("clearing his throat") must sit INSIDE the bordered
box with the shimmer bar clipped β€” it was overflowing the right border. */
.va-voice-out .lab-load{border:1px solid #3a322b !important;border-top:0 !important;
background:#1d1916 !important;padding:10px 13px !important;margin:0 !important;
overflow:hidden !important;border-radius:0 !important;max-width:100% !important;box-sizing:border-box}
.va-voice-out .ll-bar,.va-voice-out .ll-txt{max-width:100% !important;min-width:0 !important}
/* 2) the chat area must be noir β€” kill the grey Gradio surface showing through the
empty chat space above the chips. */
.va-body > .va-rail > #grill-chat-wrap{background:#141110 !important}
.va-body > .va-rail > #grill-chat-wrap > *,
.va-body > .va-rail > #grill-chat-wrap .grill-box{flex:1 1 auto !important;
background:#141110 !important;min-height:0 !important}
.va-rail .styler,.va-rail .gr-group,.va-rail .block{background:transparent !important}
/* 3) the 4 TRIAL move chips were showing the grey Gradio button fill β€” force NOIR */
.trial-chips .trial-chip,button.trial-chip{background:#221c18 !important;
border:1px solid #5a4b3c !important;color:#cbbfa8 !important;box-shadow:none !important}
.trial-chips .trial-chip:hover,button.trial-chip:hover{background:#2f2620 !important;border-color:#c9a24a !important}