Renjun An
Give intent a three-way decision band
48aaeec
Raw History Blame Contribute Delete
18.8 kB
/* ABD Security Models Playground.
Dark operations console: slate surfaces, one blue for interaction, and a
green/amber/red scale reserved for what a score actually means. */
:root,
.dark,
.gradio-container {
--abd-bg: #0b1220;
--abd-surface: #111a2b;
--abd-raised: #172236;
--abd-border: #1f2c44;
--abd-border-strong: #2d3d59;
--abd-text: #e8eef8;
--abd-text-dim: #93a3bd;
--abd-text-faint: #8798b5;
--abd-accent: #4cc2ff;
--abd-safe: #34d399;
--abd-warn: #fbbf24;
--abd-risk: #f87171;
--abd-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
--abd-sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--abd-radius: 10px;
}
body,
gradio-app,
.gradio-container {
background: var(--abd-bg) !important;
color: var(--abd-text) !important;
font-family: var(--abd-sans) !important;
}
.gradio-container {
/* Without an explicit width the container is a content-sized flex item, so a
wide result grew the whole app and every column inside it. */
width: 100% !important;
max-width: 1520px !important;
margin: 0 auto !important;
padding: 0 40px 56px !important;
}
/* Gradio paints its own light chrome on a lot of wrappers, and pads .block but
not rows or columns — which left HTML blocks and form fields inset 12px from
the buttons beside them. Flatten and unpad; components opt back in below. */
.gradio-container .block,
.gradio-container .form,
.gradio-container .panel,
.gradio-container .wrap,
.gradio-container .gr-group,
.gradio-container .styler {
background: transparent !important;
border: none !important;
box-shadow: none !important;
padding: 0 !important;
}
/* gr.HTML nests its content one level deeper, with padding of its own. */
.gradio-container .html-container,
.gradio-container .prose {
padding: 0 !important;
}
/* ---------- header and hero ---------- */
.demo-header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 16px;
padding: 26px 0 20px;
border-bottom: 1px solid var(--abd-border);
}
.demo-header .brand {
font-size: 1.3rem;
font-weight: 500;
letter-spacing: -0.01em;
color: var(--abd-text);
}
.demo-header .brand b {
font-weight: 700;
color: var(--abd-accent);
letter-spacing: 0.04em;
}
.demo-header .header-meta {
font-family: var(--abd-mono);
font-size: 0.76rem;
text-transform: uppercase;
letter-spacing: 0.14em;
color: var(--abd-text-faint);
}
.demo-hero {
padding: 34px 0 30px;
max-width: 78ch;
}
.demo-hero h1 {
margin: 0;
font-size: clamp(1.7rem, 4.2vw, 2.5rem);
line-height: 1.15;
font-weight: 700;
letter-spacing: -0.02em;
color: var(--abd-text);
}
/* ---------- workbench ---------- */
/* A fixed rail and a pane that takes everything else, rather than a ratio that
leaves the working column narrow on a wide screen. */
#workbench {
gap: 34px !important;
align-items: flex-start !important;
flex-wrap: nowrap !important;
}
#model-rail {
flex: 0 0 290px !important;
min-width: 0 !important;
}
/* Gradio's own wrappers are content-sized and carry a 1280px cap of their own,
so the workbench row grew whenever a result was wider than the form — an
error message, a long marked span. Make the chain fill its parent, then
flex-basis 0 splits that fixed width regardless of what is in it. */
.gradio-container .app,
.gradio-container .app > .wrap,
.gradio-container main.contain,
.gradio-container main.contain > .column,
#workbench {
width: 100% !important;
max-width: 100% !important;
/* flex items default to min-width:auto, so content can still push a row
wider than its own width. */
min-width: 0 !important;
}
#inference-pane {
flex: 1 1 0 !important;
width: auto !important;
min-width: 0 !important;
}
.rail-title,
.examples-title,
.result-heading {
font-family: var(--abd-mono);
font-size: 0.72rem;
/* an exact line box, so the two columns' headings are the same height to the
pixel and the rows under them start level */
line-height: 16px;
text-transform: uppercase;
letter-spacing: 0.15em;
color: var(--abd-text-faint);
}
.result-heading {
margin: 30px 0 12px;
padding-top: 22px;
border-top: 1px solid var(--abd-border);
}
/* Model selector: stacked cards rather than a list of dots. */
#model-selector .wrap,
#model-selector > div {
display: flex !important;
flex-direction: column !important;
gap: 10px !important;
}
#model-selector label {
display: flex !important;
align-items: center;
gap: 10px;
margin: 0 !important;
/* same box as an attack chip: both are a row of choices, one per column */
height: 52px !important;
padding: 0 14px !important;
background: var(--abd-surface) !important;
border: 1px solid var(--abd-border) !important;
border-radius: var(--abd-radius) !important;
color: var(--abd-text-dim) !important;
font-size: 0.9rem !important;
font-weight: 500 !important;
cursor: pointer;
transition: border-color 180ms ease, background 180ms ease, color 180ms ease;
}
#model-selector label:hover {
border-color: var(--abd-border-strong) !important;
color: var(--abd-text) !important;
}
#model-selector label:has(input:checked) {
background: var(--abd-raised) !important;
border-color: var(--abd-accent) !important;
color: var(--abd-text) !important;
}
/* Gradio paints the native control in its own accent; draw our own instead. */
#model-selector input[type="radio"] {
appearance: none;
-webkit-appearance: none;
width: 14px;
height: 14px;
flex: 0 0 auto;
margin: 0;
border: 1.5px solid var(--abd-border-strong);
border-radius: 50%;
background: transparent;
transition: border-color 180ms ease, box-shadow 180ms ease;
}
#model-selector input[type="radio"]:checked {
border-color: var(--abd-accent);
box-shadow: inset 0 0 0 3px var(--abd-bg), inset 0 0 0 9px var(--abd-accent);
}
#model-selector input[type="radio"]:focus-visible {
outline: 2px solid var(--abd-accent);
outline-offset: 2px;
}
#model-details {
margin-top: 18px;
}
.model-card {
padding: 16px;
background: var(--abd-surface);
border: 1px solid var(--abd-border);
border-radius: var(--abd-radius);
}
.model-card h2 {
margin: 0 0 8px;
font-size: 0.98rem;
font-weight: 600;
color: var(--abd-text);
}
.model-card p {
margin: 0 0 14px;
font-size: 0.86rem;
line-height: 1.6;
color: var(--abd-text-dim);
}
.model-card dl {
margin: 0;
display: grid;
gap: 8px;
padding-top: 12px;
border-top: 1px solid var(--abd-border);
}
.model-card dt {
font-family: var(--abd-mono);
font-size: 0.66rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--abd-text-faint);
}
.model-card dd {
margin: 3px 0 0;
font-family: var(--abd-mono);
font-size: 0.76rem;
color: var(--abd-text-dim);
word-break: break-all;
}
/* ---------- the example row, first thing in every form ---------- */
.examples-head {
display: flex;
align-items: baseline;
gap: 12px;
flex-wrap: wrap;
}
.example-row {
display: grid !important;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 10px !important;
margin-bottom: 10px !important;
}
/* One fixed cell per case, so a longer label never makes its box taller or
wider than the rest of the set. */
button.example-chip {
display: block !important;
width: 100% !important;
min-width: 0 !important;
height: 52px !important;
padding: 0 14px !important;
line-height: 50px !important;
background: var(--abd-raised) !important;
border: 1px solid var(--abd-border-strong) !important;
border-radius: var(--abd-radius) !important;
color: var(--abd-text) !important;
font-family: var(--abd-sans) !important;
font-size: 0.9rem !important;
font-weight: 500 !important;
text-align: left !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
cursor: pointer;
transition: border-color 180ms ease, transform 180ms ease, background 180ms ease;
}
button.example-chip.risk {
border-left: 3px solid var(--abd-risk) !important;
padding-left: 13px !important;
}
button.example-chip.custom {
background: transparent !important;
border-style: dashed !important;
color: var(--abd-text-dim) !important;
}
button.example-chip.custom:hover {
background: var(--abd-raised) !important;
color: var(--abd-text) !important;
}
button.example-chip.active {
background: #17324e !important;
border-color: var(--abd-accent) !important;
box-shadow: inset 0 0 0 1px var(--abd-accent);
color: #ffffff !important;
font-weight: 600 !important;
}
button.example-chip.risk.active {
border-left-color: var(--abd-risk) !important;
}
button.example-chip:hover {
background: #1c2941 !important;
border-color: var(--abd-accent) !important;
transform: translateY(-1px);
}
button.example-chip:active {
transform: translateY(0);
}
button.example-chip:focus-visible {
outline: 2px solid var(--abd-accent) !important;
outline-offset: 2px;
}
/* ---------- form fields ---------- */
/* The rail column spaces its children with a 16px gap and Gradio resets margins
inside a block, so space the task form the same way rather than with margins
that do not survive. Both columns then keep one rhythm. */
.task-form,
.task-form .styler,
.task-form .form {
display: flex !important;
flex-direction: column !important;
/* Gradio nests the real children in a .styler with a 1px gap; that is the box
the spacing has to land on. */
gap: 16px !important;
}
.gradio-container label > span,
.gradio-container .block > label > span:first-child {
font-size: 0.84rem !important;
font-weight: 600 !important;
color: var(--abd-text) !important;
margin-bottom: 4px !important;
}
.gradio-container .info,
.gradio-container div[data-testid="block-info"] {
font-size: 0.79rem !important;
line-height: 1.5 !important;
color: var(--abd-text-faint) !important;
margin-bottom: 8px !important;
}
.gradio-container textarea,
.gradio-container input[type="text"] {
background: var(--abd-surface) !important;
border: 1px solid var(--abd-border) !important;
border-radius: var(--abd-radius) !important;
color: var(--abd-text) !important;
padding: 12px 14px !important;
transition: border-color 160ms ease, box-shadow 160ms ease;
}
.mono-field textarea,
.mono-field input[type="text"] {
font-family: var(--abd-mono) !important;
font-size: 0.83rem !important;
line-height: 1.6 !important;
}
/* Gradio sizes a textarea by writing an inline height that tracks its content,
so every case would resize the form. Pin the height per field and scroll. */
.field-h2 textarea { --field-lines: 2; }
.field-h3 textarea { --field-lines: 3; }
.field-h5 textarea { --field-lines: 5; }
.field-h6 textarea { --field-lines: 6; }
.field-h8 textarea { --field-lines: 8; }
.field-h2 textarea,
.field-h3 textarea,
.field-h5 textarea,
.field-h6 textarea,
.field-h8 textarea {
height: calc(var(--field-lines) * 1.6 * 0.83rem + 24px) !important;
min-height: calc(var(--field-lines) * 1.6 * 0.83rem + 24px) !important;
max-height: calc(var(--field-lines) * 1.6 * 0.83rem + 24px) !important;
overflow-y: auto !important;
}
.gradio-container textarea:focus,
.gradio-container input[type="text"]:focus {
border-color: var(--abd-accent) !important;
box-shadow: 0 0 0 3px rgba(76, 194, 255, 0.16) !important;
outline: none !important;
}
.task-form > .block,
.task-form .form > .block {
margin-bottom: 0 !important;
}
/* The JSON viewer's copy button keeps a white chip in both colour schemes. */
.gradio-container .icon-button-wrapper {
background: var(--abd-surface) !important;
border: 1px solid var(--abd-border) !important;
border-radius: 6px !important;
color: var(--abd-text-dim) !important;
}
.gradio-container .icon-button-wrapper button,
.gradio-container button.icon-button {
background: transparent !important;
color: var(--abd-text-dim) !important;
}
/* The JSON viewer keeps a light chip for its label; flatten it like the rest. */
#inference-pane label {
background: transparent !important;
border: none !important;
box-shadow: none !important;
}
#spans-json .json-holder,
#inference-pane .json-holder {
max-height: 260px;
overflow: auto;
background: var(--abd-surface) !important;
border: 1px solid var(--abd-border) !important;
border-radius: var(--abd-radius) !important;
font-family: var(--abd-mono) !important;
font-size: 0.8rem !important;
}
/* ---------- run button ---------- */
#run-button {
margin-top: 8px !important;
min-height: 46px;
width: auto !important;
min-width: 190px;
flex: 0 0 auto !important;
padding: 0 30px !important;
align-self: flex-start;
background: var(--abd-accent) !important;
border: none !important;
border-radius: var(--abd-radius) !important;
color: #04121d !important;
font-family: var(--abd-sans) !important;
font-size: 0.92rem !important;
font-weight: 600 !important;
cursor: pointer;
transition: filter 160ms ease, transform 160ms ease;
}
#run-button:hover {
filter: brightness(1.1);
transform: translateY(-1px);
}
#run-button[disabled] {
filter: none;
opacity: 0.55;
cursor: progress;
transform: none;
}
/* The browser's default ring is a 1px near-black outline: invisible on both the
dark chips and the accent button. */
#run-button:focus-visible {
outline: 2px solid var(--abd-text) !important;
outline-offset: 2px;
}
#run-button:active {
transform: translateY(0);
}
/* Gradio's progress tracker renders in its own near-invisible grey down in the
corner; put it where the result will be and make it readable. */
.gradio-container [data-testid="status-tracker"] {
background: transparent !important;
}
.gradio-container .progress-text,
.gradio-container [data-testid="status-tracker"] .progress-text {
background: var(--abd-surface) !important;
border: 1px solid var(--abd-border) !important;
border-radius: var(--abd-radius) !important;
color: var(--abd-text-dim) !important;
font-family: var(--abd-mono) !important;
font-size: 0.78rem !important;
}
/* ---------- result ---------- */
.score-card {
padding: 20px;
background: var(--abd-surface);
border: 1px solid var(--abd-border);
border-left: 3px solid var(--abd-text-faint);
border-radius: var(--abd-radius);
}
.score-card.band-low { border-left-color: var(--abd-safe); }
.score-card.band-medium { border-left-color: var(--abd-warn); }
.score-card.band-high { border-left-color: var(--abd-risk); }
.score-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.score-label {
font-family: var(--abd-mono);
font-size: 0.74rem;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--abd-text-faint);
}
.score-verdict {
font-size: 0.76rem;
font-weight: 600;
padding: 3px 9px;
border-radius: 999px;
}
.score-verdict.high {
color: var(--abd-risk);
background: rgba(248, 113, 113, 0.12);
}
.score-verdict.medium {
color: var(--abd-warn);
background: rgba(251, 191, 36, 0.12);
}
.score-verdict.low {
color: var(--abd-safe);
background: rgba(52, 211, 153, 0.12);
}
/* Gradio colours bare spans, which beats inheritance: say it explicitly. */
.score-verdict .edge {
color: inherit !important;
margin-left: 6px;
font-family: var(--abd-mono);
font-size: 0.7rem;
font-weight: 400;
opacity: 0.8;
}
.score-value {
margin: 10px 0 14px;
font-family: var(--abd-mono);
font-size: 2.6rem;
font-weight: 700;
line-height: 1;
letter-spacing: -0.03em;
color: var(--abd-text);
}
.score-track {
height: 5px;
border-radius: 999px;
background: var(--abd-border);
overflow: hidden;
}
.score-track span {
display: block;
height: 100%;
border-radius: 999px;
background: var(--abd-text-faint);
transition: width 320ms ease;
}
.band-low .score-track span { background: var(--abd-safe); }
.band-medium .score-track span { background: var(--abd-warn); }
.band-high .score-track span { background: var(--abd-risk); }
.span-summary {
display: flex;
align-items: baseline;
gap: 12px;
flex-wrap: wrap;
padding: 14px 18px;
background: var(--abd-surface);
border: 1px solid var(--abd-border);
border-left: 3px solid var(--abd-text-faint);
border-radius: var(--abd-radius);
font-size: 0.9rem;
color: var(--abd-text-dim);
}
.span-summary.marked { border-left-color: var(--abd-risk); }
.span-summary.clean { border-left-color: var(--abd-safe); }
.span-summary strong {
font-family: var(--abd-mono);
font-size: 1.3rem;
color: var(--abd-text);
}
.span-cutoff {
margin-left: auto;
font-family: var(--abd-mono);
font-size: 0.74rem;
color: var(--abd-text-faint);
}
#result-summary:empty,
#marked-text:empty {
display: none !important;
}
#marked-text {
margin-top: 14px;
}
/* The marked text and the span list are as long as whatever was pasted in, so
cap them and let them scroll rather than push the page down. */
.highlighted-output {
max-height: 340px;
overflow-y: auto;
padding: 16px 18px;
background: var(--abd-surface);
border: 1px solid var(--abd-border);
border-radius: var(--abd-radius);
font-family: var(--abd-mono);
font-size: 0.84rem;
line-height: 1.75;
color: var(--abd-text-dim);
white-space: pre-wrap;
word-break: break-word;
}
mark.drop-mark {
padding: 1px 3px;
border-radius: 3px;
background: rgba(248, 113, 113, 0.18);
color: #fecaca;
text-decoration: line-through;
text-decoration-color: rgba(248, 113, 113, 0.7);
}
.demo-error {
padding: 14px 18px;
background: rgba(248, 113, 113, 0.08);
border: 1px solid rgba(248, 113, 113, 0.32);
border-radius: var(--abd-radius);
font-size: 0.88rem;
line-height: 1.6;
color: #fca5a5;
}
/* ---------- responsive ---------- */
@media (max-width: 860px) {
.gradio-container {
padding: 0 16px 40px !important;
}
#workbench {
flex-direction: column !important;
flex-wrap: wrap !important;
gap: 22px !important;
}
#model-rail,
#inference-pane {
flex: 1 1 100% !important;
width: 100% !important;
min-width: 0 !important;
}
.demo-hero {
padding: 28px 0 24px;
}
.example-row {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
#run-button {
width: 100% !important;
align-self: stretch;
}
.score-value {
font-size: 2.1rem;
}
.span-cutoff {
margin-left: 0;
}
}
@media (max-width: 620px) {
.example-row {
grid-template-columns: minmax(0, 1fr);
}
button.example-chip {
height: 48px !important;
line-height: 46px !important;
}
}
@media (prefers-reduced-motion: reduce) {
* {
transition: none !important;
animation: none !important;
}
button.example-chip:hover,
#run-button:hover {
transform: none;
}
}