rafmacalaba's picture
add SEIS 2025 usage/impact sample
dea762f verified
Raw History Blame Contribute Delete
67.2 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Data-use extraction — WebGPU demo</title>
<script>
(function () {
try {
if (localStorage.getItem("datause-theme") === "dark") {
document.documentElement.setAttribute("data-theme", "dark");
}
} catch (e) {}
})();
</script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
<style>
:root {
--bg: #eaf3fb;
--bg-dot: #d6dee8;
--grad-1: #e9f4fc;
--grad-2: #b9dcef;
--grad-3: #7cc1e8;
--grad-4: #009cde;
--grad-glow: rgba(0, 156, 222, 0.28);
--grad-haze: rgba(255, 255, 255, 0.65);
--surface: #ffffff;
--surface-2: #f1f5f9;
--surface-3: #e7eef6;
--text: #0f172a;
--text-muted: #475569;
--text-subtle: #64748b;
--border: #e2e8f0;
--border-strong: #cbd5e1;
--accent: #002244;
--accent-2: #009cde;
--accent-hover: #00172e;
--accent-soft: rgba(0, 156, 222, 0.10);
--accent-glow: rgba(0, 156, 222, 0.35);
--wb: #009cde;
--ok: #16a34a;
--warn: #d97706;
--err: #dc2626;
--err-soft: rgba(220, 38, 38, 0.08);
--btn-fg: #ffffff;
--log-bg: #0b1220;
--log-fg: #cbd5e1;
--cat-fur: #ede7dc;
--cat-fur-shadow: #d8cfbe;
--cat-inner: #ffc7d6;
--cat-eye: #1a1a1a;
--cat-pupil: #ede7dc;
--cat-nose: #ff7a8a;
--cat-z: #94a3b8;
--cat-sparkle: #009cde;
--na: #1d4ed8; --na-bg: #dbeafe;
--de: #002244; --de-bg: #e7edf5;
--va: #475569; --va-bg: #f1f5f9;
--radius: 16px;
--radius-sm: 10px;
--radius-lg: 20px;
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04);
--shadow: 0 1px 3px rgba(15, 23, 42, 0.06), 0 12px 32px -16px rgba(15, 23, 42, 0.18);
--shadow-lg: 0 4px 6px rgba(15, 23, 42, 0.05), 0 24px 48px -20px rgba(15, 23, 42, 0.25);
--font: "Inter", system-ui, -apple-system, sans-serif;
--mono: "JetBrains Mono", ui-monospace, monospace;
--ease: cubic-bezier(0.22, 1, 0.36, 1);
}
[data-theme="dark"] {
--bg: #05080d;
--bg-dot: #1c2333;
--grad-1: #07142a;
--grad-2: #0e2a4f;
--grad-3: #155184;
--grad-4: #009cde;
--grad-glow: rgba(0, 156, 222, 0.22);
--grad-haze: rgba(125, 211, 252, 0.10);
--surface: #0c1119;
--surface-2: #131a23;
--surface-3: #1a2230;
--text: #f1f5f9;
--text-muted: #94a3b8;
--text-subtle: #64748b;
--border: #1e293b;
--border-strong: #334155;
--accent: #38bdf8;
--accent-2: #38bdf8;
--accent-hover: #7dd3fc;
--accent-soft: rgba(56, 189, 248, 0.12);
--accent-glow: rgba(56, 189, 248, 0.35);
--wb: #38bdf8;
--ok: #4ade80;
--warn: #fbbf24;
--err: #f87171;
--err-soft: rgba(248, 113, 113, 0.12);
--btn-fg: #05080d;
--log-bg: #020617;
--log-fg: #94a3b8;
--cat-fur: #f5ebd7;
--cat-fur-shadow: #d8c8a8;
--cat-inner: #ff9eb5;
--cat-eye: #0a0e0f;
--cat-pupil: #f5ebd7;
--cat-nose: #ff7a8a;
--cat-z: #64748b;
--cat-sparkle: #38bdf8;
--na: #93c5fd; --na-bg: rgba(59, 130, 246, 0.18);
--de: #cbd5e1; --de-bg: rgba(203, 213, 225, 0.10);
--va: #94a3b8; --va-bg: rgba(148, 163, 184, 0.15);
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 12px 32px -16px rgba(0, 0, 0, 0.5);
--shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.4), 0 24px 48px -20px rgba(0, 0, 0, 0.6);
}
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: var(--font);
background-color: var(--bg);
background-image:
radial-gradient(ellipse 70% 55% at 18% 12%, var(--grad-haze), transparent 62%),
radial-gradient(ellipse 60% 50% at 92% 88%, var(--grad-glow), transparent 70%),
linear-gradient(165deg, var(--grad-1) 0%, var(--grad-2) 45%, var(--grad-3) 88%, var(--grad-4) 130%);
background-attachment: fixed;
color: var(--text);
line-height: 1.55;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
transition: background-color 0.3s var(--ease), color 0.3s var(--ease);
}
.num { font-variant-numeric: tabular-nums; }
.wrap { max-width: 1200px; margin: 0 auto; padding: 1.75rem 1.5rem 4rem; }
/* Loader ---------------------------------------------------------- */
.loader {
position: fixed; inset: 0; z-index: 100;
background-color: var(--bg);
background-image:
radial-gradient(ellipse 70% 55% at 18% 12%, var(--grad-haze), transparent 62%),
radial-gradient(ellipse 60% 50% at 92% 88%, var(--grad-glow), transparent 70%),
linear-gradient(165deg, var(--grad-1) 0%, var(--grad-2) 45%, var(--grad-3) 88%, var(--grad-4) 130%);
background-attachment: fixed;
display: flex; align-items: center; justify-content: center;
transition: opacity .55s var(--ease), visibility .55s;
}
.loader.hidden { opacity: 0; visibility: hidden; pointer-events: none; }
.loader-theme-toggle {
position: absolute; top: 24px; right: 28px; z-index: 101;
display: inline-flex; align-items: center; gap: 0.5rem;
font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--text-muted); background: var(--surface);
border: 1px solid var(--border); border-radius: 999px;
padding: 0.5rem 0.85rem; cursor: pointer;
box-shadow: var(--shadow-sm);
transition: color .2s var(--ease), border-color .2s var(--ease);
}
.loader-theme-toggle:hover { color: var(--text); border-color: var(--accent-2); }
.loader-theme-toggle svg { width: 14px; height: 14px; }
.loader-inner { width: min(1120px, 94vw); text-align: left; }
.loader-layout {
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(200px, 0.85fr) minmax(0, 1fr);
align-items: center;
gap: clamp(1.5rem, 4vw, 3.25rem);
}
.loader-copy { min-width: 0; }
.loader-visual {
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.loader-about {
min-width: 0;
padding-left: clamp(1rem, 2vw, 1.5rem);
border-left: 1px solid var(--border);
}
.loader-about h2 {
margin: 0 0 0.85rem;
font-family: var(--mono); font-size: 12px; letter-spacing: 0.18em;
text-transform: uppercase; color: var(--text-muted); font-weight: 600;
}
.loader-about p {
margin: 0 0 0.85rem; color: var(--text-muted);
font-size: 0.82rem; line-height: 1.6;
}
.loader-about p b { color: var(--text); font-weight: 600; }
.loader-about ul {
margin: 0; padding: 0; list-style: none;
display: grid; gap: 0.5rem;
}
.loader-about li {
display: flex; gap: 0.6rem; align-items: flex-start;
font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.02em;
color: var(--text-muted); line-height: 1.5;
}
.loader-about li::before {
content: "—"; color: var(--accent-2); flex: none;
}
.loader-about .privacy {
display: flex; gap: 0.55rem; align-items: flex-start;
margin-top: 1rem; padding-top: 0.85rem;
border-top: 1px solid var(--border);
color: var(--text-subtle); font-size: 0.72rem; line-height: 1.5;
}
.loader-about .privacy svg {
width: 14px; height: 14px; flex: none; margin-top: 0.1rem;
color: var(--accent-2);
}
.loader-about .privacy b { color: var(--text); font-weight: 600; }
.cat-sculpture {
width: 100%;
max-width: 280px;
height: auto;
max-height: min(52vh, 360px);
filter: drop-shadow(0 16px 36px rgba(0, 0, 0, 0.42));
transition: transform 0.3s var(--ease);
}
.cat-sculpture:hover {
transform: scale(1.02) translateY(-2px);
}
.loader-brand {
display: inline-flex; align-items: center; gap: .7rem;
font-family: var(--mono); font-size: 12px; letter-spacing: .24em;
text-transform: uppercase; margin-bottom: 2.2rem; color: var(--text-muted);
}
.loader-brand::before {
content: ""; display: inline-block;
width: 8px; height: 8px; border-radius: 50%;
background: var(--accent-2); box-shadow: 0 0 14px var(--accent-glow);
animation: pulse 1.4s ease-in-out infinite;
}
.loader-pct {
font-family: var(--mono); font-size: 72px; font-weight: 500; line-height: 1;
color: var(--text); letter-spacing: -.03em; font-variant-numeric: tabular-nums;
}
.loader-pct i { margin-left: .12em; color: var(--text-subtle); font-size: .36em; font-style: normal; font-weight: 400; }
.loader-rule {
position: relative; width: 100%; height: 3px; margin: 20px 0 14px;
background: var(--surface-2); border-radius: 999px; overflow: hidden;
}
.loader-rule i {
position: absolute; inset: 0 auto 0 0; display: block; width: 0%;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
box-shadow: 0 0 14px var(--accent-glow);
transition: width .3s var(--ease); border-radius: 999px;
}
.loader-status {
font-family: var(--mono); font-size: 12.5px; letter-spacing: .18em;
text-transform: uppercase; color: var(--text); font-variant-numeric: tabular-nums;
font-weight: 600;
}
.loader-truth { margin: 8px 0 0; color: var(--text-muted); font-family: var(--mono); font-size: 12px; letter-spacing: .02em; }
.loader-cta {
display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
min-width: 220px; margin-top: 2.2rem;
font: inherit; font-family: var(--mono); font-size: 13px; letter-spacing: .14em;
text-transform: uppercase; font-weight: 600;
color: #071512; background: var(--accent-2);
border: none; border-radius: var(--radius-sm);
padding: 0.9rem 1.5rem; cursor: pointer;
box-shadow: 0 0 18px var(--accent-glow);
transition: background .18s var(--ease), transform .06s ease, box-shadow .18s var(--ease), opacity .3s var(--ease), color .3s var(--ease);
}
.loader-cta:hover:not(:disabled) { background: var(--accent); box-shadow: 0 0 26px var(--accent-glow); }
.loader-cta:active:not(:disabled) { transform: translateY(1px); }
.loader-cta:disabled {
opacity: 0.35; cursor: not-allowed; box-shadow: none;
background: var(--surface-3); color: var(--text-subtle);
}
@keyframes pulse { 50% { opacity: 0.35; transform: scale(0.85); } }
@media (max-width: 720px) {
.loader-layout { grid-template-columns: 1fr; gap: 1.2rem; }
.loader-visual { order: -1; }
.cat-sculpture { max-width: 170px; max-height: 22vh; }
.loader-brand { margin-bottom: 1.2rem; }
.loader-pct { font-size: 54px; }
.loader-cta { margin-top: 1.4rem; width: 100%; }
.loader-theme-toggle { top: 16px; right: 16px; }
.loader-about { border-left: none; padding-left: 0; }
.loader-about p, .loader-about li { font-size: 0.7rem; }
}
/* Header ---------------------------------------------------------- */
header {
display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
margin-bottom: 1.75rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border);
}
.brand { display: flex; align-items: center; gap: .9rem; min-width: 0; }
.brand-mark {
width: 42px; height: 42px; border-radius: 12px;
background: linear-gradient(135deg, var(--accent), var(--accent-2));
display: flex; align-items: center; justify-content: center;
color: #ffffff;
box-shadow: 0 6px 20px -6px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.22);
flex: none;
}
.brand-mark svg { width: 22px; height: 22px; }
.brand-text { min-width: 0; }
.brand-text h1 {
margin: 0; font-size: 0.98rem; font-weight: 700;
font-family: var(--mono);
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--text);
}
.brand-text .sub {
margin: 3px 0 0; color: var(--text-muted); font-size: 0.72rem;
font-family: var(--mono); letter-spacing: 0.08em;
text-transform: uppercase;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.header-actions { display: flex; align-items: center; gap: 0.6rem; flex: none; }
.icon-btn, .status-pill {
display: inline-flex; align-items: center; gap: 0.5rem;
font-size: 0.8rem; font-weight: 500; color: var(--text-muted);
background: var(--surface); border: 1px solid var(--border);
border-radius: 999px; padding: 0.45rem 0.9rem; white-space: nowrap;
box-shadow: var(--shadow-sm);
transition: color .2s var(--ease), border-color .2s var(--ease);
}
.icon-btn { cursor: pointer; font-family: var(--font); }
.icon-btn:hover { color: var(--text); border-color: var(--border-strong); }
.icon-btn svg { display: block; }
.status-dot {
width: 8px; height: 8px; border-radius: 50%; background: var(--border-strong); flex: none;
transition: background .2s var(--ease);
}
.status-pill.ready { color: var(--text); }
.status-pill.ready .status-dot { background: var(--ok); box-shadow: 0 0 10px rgba(22, 163, 74, 0.4); }
.status-pill.err { color: var(--text); }
.status-pill.err .status-dot { background: var(--err); box-shadow: 0 0 10px rgba(220, 38, 38, 0.4); }
.status-pill.busy { color: var(--text); }
.status-pill.busy .status-dot { background: var(--warn); animation: pulse 1.2s ease-in-out infinite; }
/* Pipeline step indicator ----------------------------------------- */
.pipeline {
display: flex; align-items: center; gap: 0;
margin-bottom: 1.5rem;
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); box-shadow: var(--shadow-sm);
padding: 0.95rem 1.25rem;
overflow-x: auto;
}
.step { display: flex; align-items: center; gap: 0.65rem; flex: 1; min-width: 0; }
.step-num {
width: 26px; height: 26px; border-radius: 50%;
background: var(--surface-2); color: var(--text-muted);
display: flex; align-items: center; justify-content: center;
font-family: var(--mono); font-size: 11px; font-weight: 600;
border: 1.5px solid var(--border);
flex: none; transition: all .2s var(--ease);
}
.step-text { font-size: 0.78rem; color: var(--text-muted); font-weight: 500; white-space: nowrap; }
.step-text b { color: var(--text); font-weight: 600; }
.step-arrow { flex: none; color: var(--text-subtle); margin: 0 0.75rem; display: flex; align-items: center; }
.step-arrow svg { display: block; }
.step.active .step-num { background: var(--accent); color: var(--btn-fg); border-color: var(--accent); box-shadow: 0 0 14px var(--accent-glow); }
.step.active .step-text { color: var(--text); }
.step.done .step-num { background: var(--ok); color: white; border-color: var(--ok); }
/* Models strip — bento tiles --------------------------------------- */
.models-strip {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
margin-bottom: 1.5rem;
}
.model-card {
position: relative;
display: flex; flex-direction: column; gap: 0.7rem;
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: 1.15rem 1.25rem;
text-decoration: none; color: var(--text);
box-shadow: var(--shadow-sm);
transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
overflow: hidden;
}
.model-card::before {
content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
background: linear-gradient(180deg, var(--accent), var(--accent-2));
opacity: 0; transition: opacity .2s var(--ease);
}
.model-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow); }
.model-card:hover::before { opacity: 1; }
.model-card-top { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.model-card-name { display: flex; align-items: center; gap: 0.6rem; font-weight: 600; font-size: 0.95rem; min-width: 0; }
.model-card-name-icon {
width: 28px; height: 28px; border-radius: 8px;
background: var(--surface-2); color: var(--accent-2);
display: flex; align-items: center; justify-content: center; flex: none;
}
.model-role {
font-size: 0.62rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em;
color: var(--accent-2); background: var(--accent-soft);
border-radius: 999px; padding: 0.22rem 0.6rem;
font-family: var(--mono); flex: none;
}
.model-card-repo {
font-family: var(--mono); font-size: 0.74rem; color: var(--text);
overflow-wrap: anywhere;
}
.model-card-repo .repo-ns { color: var(--text-muted); }
.model-card-file {
font-family: var(--mono); font-size: 0.7rem; color: var(--text-muted);
overflow-wrap: anywhere; padding-left: 0.9rem;
}
/* Status bar ------------------------------------------------------- */
.status-bar {
display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); box-shadow: var(--shadow-sm);
padding: 0.85rem 1.1rem; margin-bottom: 1.5rem;
}
.status-bar-label {
font-size: 0.82rem; color: var(--text-muted); min-width: 0; flex: 1 1 240px;
display: flex; align-items: center; gap: .55rem;
}
.status-bar-label .label-icon { color: var(--text-subtle); flex: none; }
.status-bar-label b { color: var(--text); font-family: var(--mono); font-weight: 500; }
.status-readout {
flex: 1 1 280px; min-width: 0;
display: flex; flex-direction: column; gap: 0.5rem;
}
.status-readout-head {
display: flex; align-items: center; gap: 0.55rem;
font-family: var(--mono); font-size: 0.7rem;
text-transform: uppercase; letter-spacing: 0.18em;
color: var(--text-sub);
}
.status-readout-head .label-icon { color: var(--text-subtle); flex: none; }
.status-readout-head b { color: var(--text); font-weight: 500; }
.status-readout-pct {
margin-left: auto;
color: var(--text); font-weight: 600; font-size: 0.88rem;
letter-spacing: 0.04em;
}
.status-readout-rule {
position: relative; height: 1px; background: var(--border-strong);
overflow: hidden;
}
.status-readout-rule i {
position: absolute; inset: 0 auto 0 0; display: block; width: 0%;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
box-shadow: 0 0 8px var(--accent-glow);
transition: width .3s var(--ease);
}
.status-readout-status {
font-family: var(--mono); font-size: 0.78rem;
color: var(--text); font-variant-numeric: tabular-nums;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Chips ----------------------------------------------------------- */
.chip {
display: inline-flex; align-items: center; gap: 0.4rem;
font: inherit; font-size: 0.78rem; font-weight: 500;
color: var(--text); background: var(--surface);
border: 1px solid var(--border); border-radius: 999px;
padding: 0.4rem 0.85rem; cursor: pointer;
transition: color .15s var(--ease), border-color .15s var(--ease), background .15s var(--ease);
font-family: var(--font);
}
.chip:hover { border-color: var(--accent-2); color: var(--accent); background: var(--accent-soft); }
.chip svg { display: block; }
/* Sample selector & Segmented Controls ----------------------------- */
.sample-controls {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
flex-wrap: wrap;
}
.sample-seg {
display: inline-flex;
padding: 3px;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 9px;
}
.sample-seg-btn {
font: inherit;
font-family: var(--mono);
font-size: 0.68rem;
font-weight: 600;
padding: 0.35rem 0.75rem;
border: none;
background: transparent;
color: var(--text-muted);
border-radius: 6px;
cursor: pointer;
transition: color .15s var(--ease), background .15s var(--ease), box-shadow .15s var(--ease);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.sample-seg-btn:hover:not(.active) { color: var(--text); }
.sample-seg-btn.active {
background: var(--surface);
color: var(--text);
box-shadow: var(--shadow-sm);
}
.sample-shuffle-btn {
display: inline-flex;
align-items: center;
gap: 0.4rem;
font: inherit;
font-family: var(--mono);
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 0.35rem 0.7rem;
color: var(--accent-2);
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 7px;
cursor: pointer;
transition: all .15s var(--ease);
}
.sample-shuffle-btn:hover {
background: var(--accent-soft);
border-color: var(--accent-2);
color: var(--accent);
box-shadow: 0 0 12px var(--accent-glow);
}
.sample-shuffle-btn svg { width: 13px; height: 13px; }
.samples-group {
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
margin-top: 0.15rem;
}
.samples-group[hidden] { display: none; }
.samples-group-label {
flex-basis: 100%;
font-family: var(--mono);
font-size: 0.6rem;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--text-subtle);
}
.sample-chip {
display: inline-flex;
align-items: center;
gap: 0.4rem;
font: inherit;
font-family: var(--mono);
font-size: 0.72rem;
font-weight: 500;
letter-spacing: 0.02em;
color: var(--text-muted);
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 8px;
padding: 0.38rem 0.75rem;
cursor: pointer;
transition: all .15s var(--ease);
}
.sample-chip:hover {
color: var(--text);
border-color: var(--accent-2);
background: var(--accent-soft);
}
.sample-chip.active {
color: var(--text);
background: var(--surface);
border-color: var(--accent-2);
box-shadow: 0 0 12px var(--accent-glow);
font-weight: 600;
}
/* Bento main grid -------------------------------------------------- */
.bento {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.25rem;
margin-bottom: 1.25rem;
}
@media (max-width: 900px) { .bento { grid-template-columns: 1fr; } }
.card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); box-shadow: var(--shadow-sm);
padding: 1.5rem;
display: flex; flex-direction: column; gap: 1rem;
min-width: 0;
}
.card-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.card-head h2 {
margin: 0; font-size: 0.7rem; font-weight: 600;
text-transform: uppercase; letter-spacing: 0.1em;
color: var(--text-muted);
}
.badge-pill {
font-family: var(--mono); font-size: 0.62rem; font-weight: 500;
color: var(--text-subtle); background: var(--surface-2);
padding: 0.22rem 0.55rem; border-radius: 6px;
letter-spacing: 0.04em; text-transform: uppercase;
}
.card-hint { margin: 0; color: var(--text-muted); font-size: 0.82rem; line-height: 1.55; }
/* Segmented toggle ------------------------------------------------ */
.seg-toggle {
display: inline-flex; padding: 3px;
background: var(--surface-2); border: 1px solid var(--border);
border-radius: 9px;
}
.seg-opt {
font: inherit; font-family: var(--font);
font-size: 0.68rem; font-weight: 600;
padding: 0.32rem 0.72rem; border: none; background: transparent;
color: var(--text-muted); border-radius: 6px; cursor: pointer;
transition: color .15s var(--ease), background .15s var(--ease), box-shadow .15s var(--ease);
letter-spacing: 0.02em;
}
.seg-opt:hover:not(:disabled) { color: var(--text); }
.seg-opt.active {
background: var(--surface); color: var(--text);
box-shadow: var(--shadow-sm);
}
.seg-opt:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent-2); }
.seg-opt:disabled { opacity: 0.45; cursor: not-allowed; }
/* Results panes -------------------------------------------------- */
.results-host { display: contents; }
.result-pane { display: block; }
.result-pane[hidden] { display: none; }
.result-json .json-meta, .result-trace .json-meta {
display: flex; flex-wrap: wrap; gap: 0.5rem 1rem;
font-family: var(--mono); font-size: 0.72rem;
color: var(--text-muted); margin-bottom: 0.7rem;
padding-bottom: 0.6rem;
border-bottom: 1px dashed var(--border);
}
.result-json .json-meta b, .result-trace .json-meta b { color: var(--text); font-weight: 600; }
.result-json pre, .result-trace pre {
margin: 0; padding: 1rem 1.1rem;
background: #0b1220; color: #cbd5e1;
border: 1px solid var(--border); border-radius: var(--radius-sm);
font-family: var(--mono); font-size: 0.78rem; line-height: 1.65;
white-space: pre-wrap; word-break: break-word;
max-height: 36rem; overflow: auto;
font-variant-numeric: tabular-nums;
tab-size: 2;
}
[data-theme="dark"] .result-json pre, [data-theme="dark"] .result-trace pre { background: #020617; }
.result-json .jk, .result-trace .jk { color: #7dd3fc; }
.result-json .js, .result-trace .js { color: #fcd34d; }
.result-json .jn, .result-trace .jn { color: #c4b5fd; }
.result-json .jb, .result-trace .jb { color: #f472b6; }
/* Pretty tree ------------------------------------------------------ */
.tree {
background: var(--surface); border: 1px solid var(--border);
border-left: 3px solid var(--accent-2); border-radius: var(--radius-sm);
padding: 0.9rem 1.1rem;
}
.tree + .tree { margin-top: 0.6rem; }
.tree-root { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.tree-name { font-weight: 600; font-size: 0.92rem; color: var(--text); }
.tree-ms { color: var(--text-subtle); font-size: 0.7rem; font-family: var(--mono); margin-left: auto; }
.tree-conf {
font-family: var(--mono); font-size: 0.68rem; font-weight: 600;
padding: 0.12rem 0.5rem; border-radius: 999px; letter-spacing: 0.02em;
white-space: nowrap;
}
.tree-conf.c-high { color: var(--ok); background: color-mix(in srgb, var(--ok) 14%, transparent); }
.tree-conf.c-mid { color: var(--warn); background: color-mix(in srgb, var(--warn) 14%, transparent); }
.tree-conf.c-low { color: var(--err); background: color-mix(in srgb, var(--err) 14%, transparent); }
.tree-body {
position: relative; margin: 0.4rem 0 0 0.4rem;
padding-left: 1.15rem; border-left: 1px solid var(--border-strong);
}
.tree-leaf {
position: relative; display: flex; align-items: baseline; gap: 0.45rem;
padding: 0.13rem 0; font-size: 0.82rem; line-height: 1.5;
}
.tree-leaf::before {
content: ""; position: absolute; left: -1.15rem; top: 0.75em;
width: 0.9rem; height: 1px; background: var(--border-strong);
}
.tree-leaf .k { color: var(--accent-2); font-weight: 600; font-family: var(--mono); font-size: 0.74rem; white-space: nowrap; }
.tree-leaf .arrow { color: var(--text-subtle); }
.tree-leaf .v { color: var(--text); }
.tree-group {
position: relative; margin: 0.15rem 0 0; padding-left: 1.15rem;
border-left: 1px solid var(--border-strong);
}
.tree-group::before {
content: ""; position: absolute; left: -1.15rem; top: 0.75em;
width: 0.9rem; height: 1px; background: var(--border-strong);
}
.tree-label {
font-weight: 600; color: var(--text-muted); font-size: 0.74rem;
font-family: var(--mono); letter-spacing: 0.03em; padding: 0.1rem 0;
}
.tree-slot { display: block; }
.tree-stream {
font-family: var(--mono); font-size: 0.78rem; color: var(--text-subtle);
padding: 0.15rem 0; font-style: italic; line-height: 1.5;
}
.tree-stream.streaming::after {
content: "▌"; margin-left: 2px; color: var(--accent-2);
animation: blink 1s steps(2) infinite;
}
.tree-none { font-size: 0.78rem; color: var(--text-subtle); font-style: italic; margin: 0.35rem 0 0 0.4rem; padding-left: 1.15rem; }
/* Samples --------------------------------------------------------- */
.samples-block { display: flex; flex-direction: column; gap: 0.55rem; }
.samples-label {
font-size: 0.68rem; font-weight: 600; color: var(--text-subtle);
text-transform: uppercase; letter-spacing: 0.08em;
display: flex; align-items: center; gap: 0.45rem;
}
.samples-label .count {
font-family: var(--mono); font-weight: 500;
color: var(--text-subtle); background: var(--surface-2);
padding: 0.05rem 0.45rem; border-radius: 999px;
font-size: 0.6rem; letter-spacing: 0;
}
.samples { display: flex; flex-wrap: wrap; gap: 0.4rem; }
/* Textarea -------------------------------------------------------- */
.textarea-wrap { position: relative; }
textarea {
width: 100%; min-height: 11rem; resize: vertical;
font: inherit; font-size: 0.9rem; line-height: 1.6;
color: var(--text); background: var(--surface-2);
border: 1px solid var(--border); border-radius: var(--radius-sm);
padding: 0.85rem 1rem;
transition: border-color .15s var(--ease), box-shadow .15s var(--ease), background .15s var(--ease);
}
textarea::placeholder { color: var(--text-subtle); }
textarea:hover { border-color: var(--border-strong); }
textarea:focus {
outline: none; border-color: var(--accent-2);
background: var(--surface);
box-shadow: 0 0 0 3px var(--accent-soft);
}
/* Actions / CTA --------------------------------------------------- */
.actions { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.actions .meta-text { font-family: var(--mono); font-size: 0.72rem; color: var(--text-subtle); }
.threshold-control {
display: flex; align-items: center; gap: 0.7rem;
font-family: var(--mono); font-size: 0.72rem; color: var(--text-muted);
}
.threshold-control .thr-label {
letter-spacing: 0.08em; text-transform: uppercase;
font-weight: 600; color: var(--text-subtle); white-space: nowrap;
}
.threshold-control input[type="range"] {
flex: 1; accent-color: var(--accent-2); cursor: pointer; height: 18px;
}
.threshold-control .thr-val {
color: var(--accent-2); font-weight: 600; min-width: 3ch; text-align: right;
font-variant-numeric: tabular-nums;
}
.btn {
display: inline-flex; align-items: center; gap: 0.55rem;
font: inherit; font-weight: 600; font-size: 0.88rem; color: var(--btn-fg);
background: linear-gradient(180deg, var(--accent), var(--accent-hover));
border: none; border-radius: var(--radius-sm);
padding: 0.72rem 1.4rem; cursor: pointer;
transition: box-shadow .2s var(--ease);
box-shadow: 0 1px 2px rgba(0,0,0,.12), 0 8px 22px -8px var(--accent-glow);
font-family: var(--font);
letter-spacing: -0.005em;
}
.btn:hover:not(:disabled) { box-shadow: 0 2px 4px rgba(0,0,0,.14), 0 12px 28px -8px var(--accent-glow); }
.btn:active:not(:disabled) { box-shadow: 0 1px 2px rgba(0,0,0,.12), 0 4px 12px -4px var(--accent-glow); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; box-shadow: none; }
.btn svg { display: block; }
/* Results --------------------------------------------------------- */
#results { min-height: 14rem; }
.empty {
display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 0.7rem; padding: 2.5rem 1rem;
color: var(--text-muted); text-align: center;
}
.empty .empty-icon { width: 42px; height: 42px; color: var(--text-subtle); opacity: 0.7; }
.empty .empty-title { font-size: 0.9rem; font-weight: 600; color: var(--text-muted); }
.empty .empty-sub { font-size: 0.78rem; color: var(--text-subtle); max-width: 32ch; line-height: 1.5; }
/* Perf stats ------------------------------------------------------- */
.perf {
display: flex; flex-wrap: wrap; gap: 0.5rem 0.9rem; align-items: center;
background: var(--surface-2); border: 1px solid var(--border);
border-radius: var(--radius-sm); padding: 0.65rem 0.95rem;
}
.perf-stat {
display: inline-flex; align-items: center; gap: 0.4rem;
color: var(--text-muted); font-family: var(--mono); font-size: 0.72rem;
}
.perf-stat b {
color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums;
}
.perf-stat .dot { width: 3px; height: 3px; border-radius: 50%; background: var(--text-subtle); }
/* Legacy .stat class — emitted by main.js inline HTML, kept for compat. */
.perf .stat { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--text-muted); font-family: var(--mono); font-size: 0.72rem; }
.perf .stat b { color: var(--accent); font-family: var(--mono); font-weight: 700; font-variant-numeric: tabular-nums; }
/* Mention card ----------------------------------------------------- */
.mention-list { display: flex; flex-direction: column; gap: 0.6rem; }
.mention {
display: flex; flex-direction: column; gap: 0.55rem;
padding: 1rem 1.1rem;
background: var(--surface); border: 1px solid var(--border);
border-left: 3px solid var(--accent-2);
border-radius: var(--radius-sm);
transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.mention:hover { box-shadow: var(--shadow-sm); }
.mention .head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.mention .name { font-weight: 600; font-size: 0.92rem; color: var(--text); }
.mention .ms { color: var(--text-subtle); font-size: 0.7rem; font-family: var(--mono); margin-left: auto; }
.mention .attr {
font-family: var(--mono); font-size: 0.8rem; color: var(--text);
padding: 0.55rem 0.75rem;
background: var(--surface-2); border-radius: 6px;
line-height: 1.65;
}
.mention .attr .k { color: var(--accent-2); font-weight: 600; }
.mention .attr .v { color: var(--text); }
.mention .attr .v.miss { color: var(--text-subtle); font-style: italic; }
.mention .attr.streaming::after {
content: "▌"; margin-left: 2px;
animation: blink 1s steps(2) infinite;
color: var(--accent-2);
}
@keyframes blink { 50% { opacity: 0; } }
.mention-summary {
font-size: 0.85rem; color: var(--text); line-height: 1.55;
padding: 0.6rem 0.8rem;
background: var(--surface-2); border-radius: 6px;
border-left: 2px solid var(--border-strong);
}
/* Badges ----------------------------------------------------------- */
.badge {
font-size: 0.6rem; font-weight: 700; letter-spacing: 0.06em;
text-transform: uppercase; border-radius: 6px; padding: 0.2rem 0.55rem;
font-family: var(--mono);
}
.badge-NAMED_DATA { background: var(--na-bg); color: var(--na); }
.badge-DESCRIPTIVE_DATA { background: var(--de-bg); color: var(--de); }
.badge-VAGUE_DATA { background: var(--va-bg); color: var(--va); }
/* Details / log ---------------------------------------------------- */
details.log-wrap, details.models-wrap {
margin-top: 1rem;
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow-sm);
overflow: hidden;
}
details > summary {
cursor: pointer; font-size: 0.7rem; font-weight: 600;
color: var(--text-muted);
text-transform: uppercase; letter-spacing: 0.1em;
padding: 0.95rem 1.25rem;
user-select: none; list-style: none;
display: flex; align-items: center; gap: 0.6rem;
transition: background .15s var(--ease), color .15s var(--ease);
}
details > summary::-webkit-details-marker { display: none; }
details > summary::before {
content: ""; width: 6px; height: 6px;
border-right: 1.5px solid currentColor;
border-bottom: 1.5px solid currentColor;
transform: rotate(-45deg); transition: transform .2s var(--ease);
display: inline-block;
}
details[open] > summary::before { transform: rotate(45deg); }
details > summary:hover { background: var(--surface-2); color: var(--text); }
.summary-body { padding: 0 1.25rem 1.25rem; }
#log {
background: var(--log-bg); color: var(--log-fg);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 0.9rem 1rem;
font-family: var(--mono); font-size: 0.74rem; line-height: 1.65;
max-height: 16rem; overflow-y: auto; white-space: pre-wrap;
font-variant-numeric: tabular-nums;
}
#log .ok { color: var(--ok); }
#log .warn { color: var(--warn); }
#log .err { color: var(--err); }
#models-info { display: grid; gap: 1rem; }
.info-card {
background: var(--surface-2); border: 1px solid var(--border);
border-radius: var(--radius-sm); padding: 1rem 1.2rem;
}
.info-card h3 {
margin: 0 0 0.35rem; font-size: 0.9rem;
display: flex; align-items: center; gap: 0.5rem;
}
.info-card h3 a { color: var(--accent-2); text-decoration: none; }
.info-card h3 a:hover { text-decoration: underline; }
.info-card h3 svg { color: var(--text-subtle); flex: none; }
.info-card .task { color: var(--text-muted); font-size: 0.82rem; margin: 0 0 0.65rem; line-height: 1.55; }
.info-card .meta {
font-size: 0.76rem; color: var(--text-muted);
font-family: var(--mono); white-space: pre-wrap; line-height: 1.6;
}
/* Focus rings ------------------------------------------------------ */
.icon-btn:focus-visible,
.btn:focus-visible,
.chip:focus-visible,
textarea:focus-visible,
details > summary:focus-visible {
outline: none;
box-shadow: 0 0 0 3px var(--accent-soft), 0 0 0 1px var(--accent-2);
}
/* Reduced motion --------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}
/* Responsive ------------------------------------------------------- */
@media (max-width: 640px) {
.wrap { padding: 1.1rem 1rem 3rem; }
header { flex-direction: column; align-items: stretch; gap: 1rem; padding-bottom: 1rem; }
.header-actions { justify-content: space-between; }
.brand-text .sub { font-size: 0.7rem; }
.pipeline { padding: 0.75rem 1rem; }
.step-text { font-size: 0.7rem; }
.step-arrow { margin: 0 0.5rem; }
.step-arrow svg { width: 12px; height: 12px; }
.models-strip { grid-template-columns: 1fr; }
.card { padding: 1.2rem; }
.loader-pct { font-size: 56px; }
}
</style>
</head>
<body>
<div class="loader" id="loader" data-state="sleeping">
<button class="loader-theme-toggle theme-toggle" type="button" title="Toggle light/dark" aria-label="Toggle theme">
<svg class="theme-icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
<svg class="theme-icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="display:none"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
<span class="theme-label">Dark</span>
</button>
<div class="loader-inner">
<div class="loader-layout">
<div class="loader-copy">
<div class="loader-brand">Data-use extraction · WebGPU</div>
<div class="loader-pct num"><span id="loader-pct">0</span><i>%</i></div>
<div class="loader-rule"><i id="loader-rule-fill"></i></div>
<div class="loader-status" id="loader-status">Initialising</div>
<div class="loader-truth" id="loader-truth">Loading models…</div>
<button class="loader-cta" id="loader-cta" type="button" disabled>Get started</button>
</div>
<div class="loader-visual">
<svg class="cat-sculpture" viewBox="0 0 240 320" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<defs>
<filter id="cat-glow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="3.5" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<linearGradient id="cat-laser" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#7cf0de" stop-opacity="0" />
<stop offset="25%" stop-color="#7cf0de" stop-opacity="0.9" />
<stop offset="50%" stop-color="#ebfffa" stop-opacity="1" />
<stop offset="75%" stop-color="#7cf0de" stop-opacity="0.9" />
<stop offset="100%" stop-color="#7cf0de" stop-opacity="0" />
</linearGradient>
<clipPath id="cat-fill-clip">
<rect id="cat-fill-rect" x="0" y="320" width="240" height="320" />
</clipPath>
</defs>
<!-- Base Obsidian Low-Poly Facets -->
<g class="cat-base" stroke="#0e1318" stroke-width="0.8" stroke-linejoin="round">
<!-- Crown & Ears -->
<polygon points="120,54 98,28 108,68" fill="#1b2028" />
<polygon points="120,54 108,68 116,78" fill="#242b36" />
<polygon points="120,54 116,78 128,78" fill="#2d3542" />
<polygon points="120,54 128,78 138,66" fill="#28303c" />
<polygon points="120,54 138,66 148,34" fill="#1f252f" />
<polygon points="148,34 138,66 156,72" fill="#161b22" />
<polygon points="98,28 108,68 88,66" fill="#14181f" />
<!-- Forehead & Eyes -->
<polygon points="116,78 128,78 122,96" fill="#323b49" />
<polygon points="108,68 116,78 102,92" fill="#28303c" />
<polygon points="116,78 122,96 102,92" fill="#364050" />
<polygon points="128,78 138,66 142,88" fill="#202631" />
<polygon points="128,78 142,88 122,96" fill="#2a323f" />
<polygon points="102,92 122,96 118,110" fill="#3c4758" />
<polygon points="122,96 142,88 132,108" fill="#242b36" />
<polygon points="122,96 132,108 118,110" fill="#2e3745" />
<!-- Muzzle & Chin -->
<polygon points="102,92 118,110 96,116" fill="#262d38" />
<polygon points="118,110 126,122 114,130" fill="#343e4e" />
<polygon points="118,110 132,108 126,122" fill="#2b3340" />
<polygon points="132,108 144,118 126,122" fill="#1d222b" />
<polygon points="114,130 126,122 120,138" fill="#3a4555" />
<polygon points="96,116 114,130 102,142" fill="#202630" />
<!-- Neck & Throat -->
<polygon points="114,130 120,138 116,162" fill="#323c4a" />
<polygon points="102,142 114,130 116,162" fill="#272e3a" />
<polygon points="120,138 136,148 116,162" fill="#232a35" />
<polygon points="126,122 144,118 136,148" fill="#1a1f27" />
<polygon points="102,142 116,162 94,172" fill="#1e242e" />
<polygon points="88,66 102,92 84,106" fill="#12161c" />
<polygon points="84,106 96,116 78,138" fill="#10141a" />
<polygon points="78,138 102,142 94,172" fill="#151920" />
<polygon points="78,138 94,172 72,176" fill="#0f1217" />
<!-- Chest & Front Shoulders -->
<polygon points="116,162 128,190 108,198" fill="#343e4e" />
<polygon points="116,162 136,148 128,190" fill="#262d39" />
<polygon points="94,172 116,162 108,198" fill="#2a323f" />
<polygon points="94,172 108,198 88,206" fill="#1d222b" />
<polygon points="72,176 94,172 88,206" fill="#14181f" />
<polygon points="128,190 146,182 138,214" fill="#1b2028" />
<polygon points="136,148 146,182 128,190" fill="#1e232c" />
<!-- Straight Forelegs -->
<polygon points="108,198 128,190 120,230" fill="#384252" />
<polygon points="108,198 120,230 104,234" fill="#2b3340" />
<polygon points="88,206 108,198 104,234" fill="#202630" />
<polygon points="120,230 128,190 138,214" fill="#242b36" />
<polygon points="120,230 138,214 132,248" fill="#202631" />
<polygon points="104,234 120,230 114,272" fill="#323c4a" />
<polygon points="120,230 132,248 126,274" fill="#262d38" />
<polygon points="104,234 114,272 100,274" fill="#222832" />
<polygon points="114,272 126,274 122,306" fill="#2d3644" />
<polygon points="100,274 114,272 108,306" fill="#242b35" />
<polygon points="126,274 138,272 134,306" fill="#1a1f27" />
<!-- Seated Hindquarters (Haunches & Back) -->
<polygon points="72,176 88,206 64,216" fill="#12151b" />
<polygon points="64,216 88,206 78,246" fill="#161b22" />
<polygon points="64,216 78,246 52,254" fill="#0f1217" />
<polygon points="88,206 104,234 78,246" fill="#1b2029" />
<polygon points="78,246 104,234 92,268" fill="#1f252f" />
<polygon points="52,254 78,246 62,284" fill="#11151b" />
<polygon points="78,246 92,268 76,288" fill="#181d24" />
<polygon points="62,284 76,288 68,306" fill="#14181f" />
<polygon points="76,288 92,268 86,306" fill="#1c222b" />
<polygon points="92,268 100,274 108,306" fill="#222833" />
<polygon points="92,268 108,306 86,306" fill="#1e242d" />
<!-- Tail along floor -->
<polygon points="52,254 62,284 38,288" fill="#0d1014" />
<polygon points="38,288 62,284 48,304" fill="#11141a" />
<polygon points="38,288 48,304 24,302" fill="#0e1116" />
<polygon points="24,302 48,304 32,310" fill="#13171e" />
<polygon points="24,302 32,310 16,304" fill="#0b0e12" />
</g>
<!-- Overlaid Glowing Energy Layer (clipped dynamically) -->
<g clip-path="url(#cat-fill-clip)">
<!-- Glowing Facets -->
<g stroke="#7cf0de" stroke-width="1.2" stroke-linejoin="round" opacity="0.92">
<polygon points="120,54 98,28 108,68" fill="#7cf0de" fill-opacity="0.28" />
<polygon points="120,54 108,68 116,78" fill="#7cf0de" fill-opacity="0.38" />
<polygon points="120,54 116,78 128,78" fill="#ebfffa" fill-opacity="0.45" />
<polygon points="120,54 128,78 138,66" fill="#7cf0de" fill-opacity="0.35" />
<polygon points="120,54 138,66 148,34" fill="#7cf0de" fill-opacity="0.25" />
<polygon points="148,34 138,66 156,72" fill="#38bdf8" fill-opacity="0.22" />
<polygon points="98,28 108,68 88,66" fill="#38bdf8" fill-opacity="0.22" />
<polygon points="116,78 128,78 122,96" fill="#ebfffa" fill-opacity="0.52" />
<polygon points="108,68 116,78 102,92" fill="#7cf0de" fill-opacity="0.36" />
<polygon points="116,78 122,96 102,92" fill="#7cf0de" fill-opacity="0.48" />
<polygon points="128,78 142,88 122,96" fill="#7cf0de" fill-opacity="0.4" />
<polygon points="102,92 122,96 118,110" fill="#ebfffa" fill-opacity="0.55" />
<polygon points="122,96 132,108 118,110" fill="#7cf0de" fill-opacity="0.42" />
<polygon points="102,92 118,110 96,116" fill="#38bdf8" fill-opacity="0.32" />
<polygon points="118,110 126,122 114,130" fill="#ebfffa" fill-opacity="0.5" />
<polygon points="118,110 132,108 126,122" fill="#7cf0de" fill-opacity="0.38" />
<polygon points="114,130 126,122 120,138" fill="#7cf0de" fill-opacity="0.5" />
<polygon points="96,116 114,130 102,142" fill="#38bdf8" fill-opacity="0.3" />
<polygon points="114,130 120,138 116,162" fill="#7cf0de" fill-opacity="0.46" />
<polygon points="102,142 114,130 116,162" fill="#7cf0de" fill-opacity="0.38" />
<polygon points="120,138 136,148 116,162" fill="#38bdf8" fill-opacity="0.32" />
<polygon points="102,142 116,162 94,172" fill="#38bdf8" fill-opacity="0.28" />
<polygon points="78,138 102,142 94,172" fill="#0284c7" fill-opacity="0.24" />
<polygon points="116,162 128,190 108,198" fill="#ebfffa" fill-opacity="0.5" />
<polygon points="116,162 136,148 128,190" fill="#7cf0de" fill-opacity="0.36" />
<polygon points="94,172 116,162 108,198" fill="#7cf0de" fill-opacity="0.42" />
<polygon points="94,172 108,198 88,206" fill="#38bdf8" fill-opacity="0.3" />
<polygon points="108,198 128,190 120,230" fill="#ebfffa" fill-opacity="0.52" />
<polygon points="108,198 120,230 104,234" fill="#7cf0de" fill-opacity="0.4" />
<polygon points="120,230 128,190 138,214" fill="#7cf0de" fill-opacity="0.36" />
<polygon points="104,234 120,230 114,272" fill="#7cf0de" fill-opacity="0.46" />
<polygon points="120,230 132,248 126,274" fill="#38bdf8" fill-opacity="0.35" />
<polygon points="114,272 126,274 122,306" fill="#ebfffa" fill-opacity="0.48" />
<polygon points="100,274 114,272 108,306" fill="#7cf0de" fill-opacity="0.4" />
<polygon points="64,216 88,206 78,246" fill="#0284c7" fill-opacity="0.25" />
<polygon points="88,206 104,234 78,246" fill="#38bdf8" fill-opacity="0.32" />
<polygon points="78,246 104,234 92,268" fill="#7cf0de" fill-opacity="0.35" />
<polygon points="78,246 92,268 76,288" fill="#38bdf8" fill-opacity="0.3" />
<polygon points="76,288 92,268 86,306" fill="#7cf0de" fill-opacity="0.38" />
<polygon points="92,268 100,274 108,306" fill="#ebfffa" fill-opacity="0.45" />
<polygon points="38,288 48,304 24,302" fill="#0284c7" fill-opacity="0.22" />
<polygon points="24,302 48,304 32,310" fill="#38bdf8" fill-opacity="0.25" />
</g>
<!-- Glowing Meniscus / Laser Line at Liquid Boundary -->
<line id="cat-meniscus" x1="10" y1="320" x2="230" y2="320" stroke="url(#cat-laser)" stroke-width="3.5" filter="url(#cat-glow)" />
</g>
</svg>
</div>
<div class="loader-about">
<h2>While you wait</h2>
<p>
<b>Data-use extraction</b> runs entirely in your browser on WebGPU —
no server, no uploads. It spots data mentions in research text, then
attributes each with provenance and usage/impact.
</p>
<ul>
<li><b>GLiNER</b> — detects spans, labels specificity</li>
<li><b>LFM2.5-350M</b> — provenance (producer, year, geography, acronym) + usage &amp; impact</li>
</ul>
<p class="privacy">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
<span><b>Private by design.</b> Nothing is sent anywhere — your text never leaves your browser.</span>
</p>
</div>
</div>
</div>
</div>
<div class="wrap">
<header>
<div class="brand">
<div class="brand-mark" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" />
<circle cx="12" cy="12" r="3" fill="currentColor" fill-opacity="0.3" />
</svg>
</div>
<div class="brand-text">
<h1>DATA USE EXTRACTION</h1>
<p class="sub">WebGPU · GLiNER + LFM2.5 multitask</p>
</div>
</div>
<div class="header-actions">
<div class="status-pill" id="status-pill"><span class="status-dot" aria-hidden="true"></span><span id="status">Initialising…</span></div>
<button class="icon-btn theme-toggle" title="Toggle light/dark" aria-label="Toggle theme">
<svg class="theme-icon-moon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
<svg class="theme-icon-sun" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="display:none"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
<span class="theme-label">Dark</span>
</button>
</div>
</header>
<nav class="pipeline" aria-label="Pipeline">
<div class="step active" data-step="load">
<div class="step-num">1</div>
<div class="step-text">Load <b>models</b></div>
</div>
<div class="step-arrow" aria-hidden="true"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 5l7 7-7 7"/></svg></div>
<div class="step" data-step="input">
<div class="step-num">2</div>
<div class="step-text">Paste <b>text</b></div>
</div>
<div class="step-arrow" aria-hidden="true"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 5l7 7-7 7"/></svg></div>
<div class="step" data-step="extract">
<div class="step-num">3</div>
<div class="step-text"><b>Extract</b></div>
</div>
<div class="step-arrow" aria-hidden="true"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 5l7 7-7 7"/></svg></div>
<div class="step" data-step="results">
<div class="step-num">4</div>
<div class="step-text">Review <b>mentions</b></div>
</div>
</nav>
<div class="models-strip">
<a class="model-card" href="https://huggingface.co/rafmacalaba/gliner_datause_extended-onnx" target="_blank" rel="noopener">
<div class="model-card-top">
<div class="model-card-name">
<span class="model-card-name-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
</span>
<span>GLiNER</span>
</div>
<span class="model-role">span</span>
</div>
<div class="model-card-repo"><span class="repo-ns">huggingface/</span>rafmacalaba/gliner_datause_extended-onnx</div>
<div class="model-card-file">onnx/gliner_datause_extended_fp16.onnx</div>
</a>
<a class="model-card" href="https://huggingface.co/rafmacalaba/lfm2.5-350M-datause-multitask-onnx" target="_blank" rel="noopener">
<div class="model-card-top">
<div class="model-card-name">
<span class="model-card-name-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/></svg>
</span>
<span>LFM2.5-350M</span>
</div>
<span class="model-role">attribution</span>
</div>
<div class="model-card-repo"><span class="repo-ns">huggingface/</span>rafmacalaba/lfm2.5-350M-datause-multitask-onnx</div>
<div class="model-card-file">onnx/lfm2_multitask_fp16_v2.onnx</div>
</a>
</div>
<div class="status-bar">
<div class="status-readout">
<div class="status-readout-head">
<svg class="label-icon" viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
<span>WebGPU pipeline</span>
<span class="status-readout-pct num" id="status-pct">0%</span>
</div>
<div class="status-readout-rule"><i id="status-rule-fill"></i></div>
<div class="status-readout-status" id="model-status-text">Loading models…</div>
</div>
</div>
<div class="bento">
<section class="card" aria-labelledby="input-card-title">
<div class="card-head">
<h2 id="input-card-title">Input text</h2>
<span class="badge-pill">stage 2 → 3</span>
</div>
<p class="card-hint">Paste research text, or load a sample — real-world research excerpts or synthetic examples.</p>
<div class="sample-controls">
<div class="sample-seg" role="tablist" aria-label="Sample category">
<button type="button" class="sample-seg-btn active" data-category="holdout" role="tab" aria-selected="true">REAL WORLD (14)</button>
<button type="button" class="sample-seg-btn" data-category="synthetic" role="tab" aria-selected="false">SYNTHETIC (3)</button>
</div>
<button type="button" class="sample-shuffle-btn" id="sample-shuffle" title="Pick a random sample from active category">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67"/></svg>
<span>Random</span>
</button>
</div>
<div class="samples-group" data-group="holdout">
<span class="samples-group-label">EXAMPLES</span>
<button type="button" class="sample-chip" data-sample="3">Acronym panels (H8BS)</button>
<button type="button" class="sample-chip" data-sample="4">GPS plot (EAC-I)</button>
<button type="button" class="sample-chip" data-sample="5">NFHS India</button>
<button type="button" class="sample-chip" data-sample="6">Spatial (DHS + GLSS)</button>
<button type="button" class="sample-chip" data-sample="7">Geospatial (SRTM + GAEZ)</button>
<button type="button" class="sample-chip" data-sample="8">Energy poverty (HBS)</button>
<button type="button" class="sample-chip" data-sample="9">COVID NLPS r4</button>
<button type="button" class="sample-chip" data-sample="10">GIS (MODIS + Lloyd's)</button>
<button type="button" class="sample-chip" data-sample="11">Governance (WGI)</button>
<button type="button" class="sample-chip" data-sample="12">Niger fiscal (ECVMA)</button>
<button type="button" class="sample-chip" data-sample="13">Sending areas (VHLSS)</button>
<button type="button" class="sample-chip" data-sample="14">Gender norms (DHS/WVS)</button>
<button type="button" class="sample-chip" data-sample="15">Return (IOM Index)</button>
<button type="button" class="sample-chip" data-sample="16">SEIS 2025 usage/impact</button>
</div>
<div class="samples-group" data-group="synthetic" hidden>
<span class="samples-group-label">EXAMPLES</span>
<button type="button" class="sample-chip" data-sample="0">Multiple mentions</button>
<button type="button" class="sample-chip" data-sample="1">Single mention</button>
<button type="button" class="sample-chip" data-sample="2">No mention</button>
</div>
<div class="textarea-wrap">
<textarea id="text" placeholder="Paste research text here…">the Demographic and Health Survey (DHS) by EuStatistics in 2021 is the main dataset</textarea>
</div>
<div class="threshold-control">
<span class="thr-label">Span threshold</span>
<input type="range" id="span-threshold" min="0" max="1" step="0.05" value="0.6" aria-label="Span detection threshold">
<span class="thr-val" id="span-threshold-val">0.60</span>
</div>
<div class="actions">
<span class="meta-text num">WebGPU · client-side · no server</span>
<button class="btn" id="run" disabled>
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg>
<span>Extract</span>
</button>
</div>
</section>
<section class="card" aria-labelledby="results-card-title">
<div class="card-head">
<h2 id="results-card-title">Extracted mentions</h2>
<div class="seg-toggle" role="tablist" aria-label="Output format">
<button type="button" class="seg-opt active" data-mode="pretty" role="tab" aria-selected="true">Pretty</button>
<button type="button" class="seg-opt" data-mode="json" role="tab" aria-selected="false" disabled>JSON</button>
<button type="button" class="seg-opt" data-mode="trace" role="tab" aria-selected="false" disabled>Trace</button>
</div>
</div>
<p class="card-hint">Spans as a tree — provenance and usage/impact. Toggle JSON for clean extraction or Trace for timings.</p>
<div id="results" class="results-host">
<div class="result-pane result-pretty" data-pane="pretty" aria-live="polite">
<div class="empty">
<svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M16 13H8M16 17H8M10 9H8"/></svg>
<div class="empty-title">No mentions yet</div>
<div class="empty-sub">Paste text or pick a sample, then press Extract.</div>
</div>
</div>
<div class="result-pane result-json" data-pane="json" aria-live="polite" hidden>
<div class="empty">
<svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2h-2M8 3a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2M8 3a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2"/><path d="M7 12h10M7 16h6"/></svg>
<div class="empty-title">No JSON yet</div>
<div class="empty-sub">Run Extract to populate the JSON view.</div>
</div>
</div>
<div class="result-pane result-trace" data-pane="trace" aria-live="polite" hidden>
<div class="empty">
<svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 17l6-5-6-5M12 19h8"/></svg>
<div class="empty-title">No trace yet</div>
<div class="empty-sub">Run Extract to populate the trace view.</div>
</div>
</div>
</div>
</section>
</div>
</div>
<script>
(function () {
var toggles = document.querySelectorAll(".theme-toggle");
function sync() {
var dark = document.documentElement.getAttribute("data-theme") === "dark";
toggles.forEach(function (btn) {
var moon = btn.querySelector(".theme-icon-moon");
var sun = btn.querySelector(".theme-icon-sun");
var label = btn.querySelector(".theme-label");
if (moon) moon.style.display = dark ? "none" : "block";
if (sun) sun.style.display = dark ? "block" : "none";
if (label) label.textContent = dark ? "Light" : "Dark";
btn.title = dark ? "Switch to light mode" : "Switch to dark mode";
});
}
toggles.forEach(function (btn) {
btn.addEventListener("click", function () {
var dark = document.documentElement.getAttribute("data-theme") === "dark";
if (dark) document.documentElement.removeAttribute("data-theme");
else document.documentElement.setAttribute("data-theme", "dark");
try { localStorage.setItem("datause-theme", dark ? "light" : "dark"); } catch (e) {}
sync();
});
});
sync();
})();
</script>
<script type="module" src="./main.js"></script>
</body>
</html>