/* ═══════════════════════════════════════════════════════════════════════ LEGIBILITY (2026-08-01) — units, the run strip, and the decision trace. All of it lives here rather than in app.css for the reason PR #14 kept the catalog out: app.css is ~8,600 lines with a hostile cascade, and three people are editing this tree at once. Everything below is either a NEW class or a deliberate, commented override of an app.css rule; nothing redefines a token. Colours are app.css tokens only (--line, --line-strong, --ink, --ink-strong, --ink-soft, --ink-faint, --bg-card, --bg-subtle, --bg-app, --danger, --success, --font-mono, --r-2..--r-4, --elev-4, --topbar-h). Inventing a var() name here would compute the whole shorthand to nothing — see ENGINEERING.md §13.1. ═══════════════════════════════════════════════════════════════════════ */ /* ── 1. UNITS ───────────────────────────────────────────────────────────── "Fitness 1.0" had its unit in a hover title only, so for anyone who did not hover the right 60px it did not exist. These carry it in the page. */ /* The unit chip inside a column header. Subordinate to the header word but not hidden — it has to survive being read at a glance. */ .th-unit { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.02em; font-weight: 400; color: var(--ink-faint); /* The header cell is .num (right-aligned) and already tight; keep the chip on the same baseline rather than wrapping it to its own line. */ white-space: nowrap; } /* The always-visible legend that answers "what does 1.0 represent?" without opening the collapsed "How to read this" block. */ .unit-legend { margin: 0 0 10px; padding: 8px 11px; border-left: 2px solid var(--line-strong); background: var(--bg-subtle); border-radius: 0 var(--r-2) var(--r-2) 0; font-size: 12px; line-height: 1.55; color: var(--ink-soft); } .unit-legend strong { color: var(--ink-strong); font-weight: 600; } .unit-legend em { font-style: italic; } .unit-legend--tight { margin: 6px 0 10px; font-size: 11.5px; } /* Inline unit after a score, in the Radar rows and the round-2 learned rows. Mono so it reads as a unit rather than as part of the number. */ .rs-unit, .ls-unit { font-family: var(--font-mono); font-size: 9.5px; color: var(--ink-faint); letter-spacing: 0.02em; } /* ── 2. THE CHAT-LIBRARY CAP ────────────────────────────────────────────── The reviewer typed 30 into "Variants to generate" and a Turing-run library came back with 10 rows. The only disclosure was a banner in a different section of the page. This one sits under the input itself. */ .setting-note { margin: 6px 0 0; font-size: 11px; line-height: 1.5; color: var(--ink-faint); } .setting-note strong { color: var(--ink-soft); font-weight: 600; } /* Requested-vs-delivered, stated on the result itself. Not an error state — a chat run capping at 20 is correct behaviour, it just has to be said. */ .count-note { display: block; margin-top: 4px; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.02em; color: var(--ink-faint); } .count-note b { color: var(--ink-soft); font-weight: 600; } /* ── 3. INTERACTION RADAR PROGRESS ──────────────────────────────────────── One ESM-2 forward pass per variant, serial, with no signal at all beyond a button reading "Analyzing…". Tool rows in the rail have had an elapsed clock since the CRISPR index made two minutes of silence read as a hang; this path never used it. */ .radar-elapsed { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.03em; color: var(--ink-faint); font-variant-numeric: tabular-nums; /* so the seconds don't jitter */ } .radar-progress { display: block; height: 2px; margin-top: 7px; background: var(--line); overflow: hidden; border-radius: 1px; } /* Indeterminate on purpose. The server returns one response for the whole library, so there is no honest per-variant fraction to draw — a bar that filled to 60% would be inventing progress. This says "working", and the elapsed clock next to it says how long for. */ .radar-progress::after { content: ""; display: block; width: 34%; height: 100%; background: var(--ink-soft); animation: tdRadarSlide 1.25s var(--ease-snap) infinite; } @keyframes tdRadarSlide { 0% { transform: translateX(-100%); } 100% { transform: translateX(320%); } } @media (prefers-reduced-motion: reduce) { .radar-progress::after { animation: none; width: 100%; opacity: 0.35; } } /* ── 4. THE RUN STRIP (was the unlabelled "context 0% · $0.11") ─────────── Two numbers with no nouns read as noise. They are kept — cost is exactly what someone deciding whether to keep going wants — but named, given a denominator so a sub-1% reading isn't rendered as a flat "0%", and given the trace as somewhere to go and read what they mean in full sentences. */ /* The bar was flex:1 and ate the row; the text now carries the meaning, so the bar becomes a fixed-width glance indicator. Specificity (0,2,0) beats app.css's (0,1,0) `.cp-meter-bar`, and this file loads after it — both conditions are needed (ENGINEERING.md §13.2/§13.3). */ .cp-meter .cp-meter-bar { flex: 0 0 28px; } /* Wraps rather than truncates. Measured at 390px (the rail's real width): "Context 41k / 1M (4%) · $0.14 model cost" overflows by 7px, and an ellipsis there eats "model cost" — putting the label back out of reach on exactly the narrow screens this change set exists to serve. A second line costs 12px; a hidden noun costs the whole fix. */ .cp-meter .cp-meter-t { flex: 1 1 auto; min-width: 0; white-space: normal; line-height: 1.35; font-variant-numeric: tabular-nums; } /* The way into the trace. Text, not a glyph: the whole complaint upstream of this file is unlabelled controls. */ .cp-trace-btn { flex: 0 0 auto; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.06em; text-transform: uppercase; padding: 3px 8px; border: 1px solid var(--line-strong); border-radius: 999px; background: transparent; color: var(--ink-soft); cursor: pointer; white-space: nowrap; } .cp-trace-btn:hover { border-color: var(--ink-soft); color: var(--ink-strong); } .cp-trace-btn[hidden] { display: none; } /* Matches .cp-icon's rule: the 57px collapsed rail has room for nothing. */ body[data-cockpit="min"] .cp-trace-btn { display: none; } /* ── 5. THE DECISION TRACE ──────────────────────────────────────────────── The rail already showed every step; it showed them as a river. Ten minutes in, "what did it actually do and why" is unanswerable because the answer scrolled past. This is the same events, held still. */ .td-trace { position: fixed; inset: 0; z-index: 60; /* above the rail (z 40s) and the canvas */ display: flex; align-items: center; justify-content: center; padding: 24px; } .td-trace[hidden] { display: none; } .td-trace-scrim { position: absolute; inset: 0; background: rgba(10, 10, 10, 0.42); border: 0; padding: 0; cursor: pointer; } .td-trace-panel { position: relative; display: flex; flex-direction: column; width: min(760px, 100%); max-height: min(82vh, 900px); background: var(--bg-card); border: 1px solid var(--line-strong); border-radius: var(--r-4); box-shadow: var(--elev-4); overflow: hidden; } .td-trace-head { display: flex; align-items: flex-start; gap: 12px; padding: 16px 18px 12px; border-bottom: 1px solid var(--line); flex: 0 0 auto; } .td-trace-head h2 { margin: 0; font-size: 15px; letter-spacing: -0.01em; color: var(--ink-strong); } .td-trace-sub { margin: 4px 0 0; font-size: 12px; line-height: 1.5; color: var(--ink-faint); } .td-trace-x { margin-left: auto; flex: 0 0 auto; width: 26px; height: 26px; line-height: 1; font-size: 15px; border: 1px solid var(--line-strong); border-radius: var(--r-2); background: transparent; color: var(--ink-soft); cursor: pointer; } .td-trace-x:hover { border-color: var(--ink-soft); color: var(--ink-strong); } /* Goal + the two meters, spelled out in words. This is where "context 3%" is allowed the room to say what it is. */ .td-trace-meta { flex: 0 0 auto; padding: 12px 18px; border-bottom: 1px solid var(--line); background: var(--bg-subtle); font-size: 12px; line-height: 1.6; color: var(--ink-soft); } .td-trace-goal { margin: 0 0 8px; color: var(--ink-strong); font-size: 13px; } .td-trace-goal b { display: block; font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-faint); font-weight: 500; margin-bottom: 2px; } .td-trace-facts { margin: 0; padding: 0; list-style: none; } .td-trace-facts li { margin: 0; } .td-trace-facts b { color: var(--ink-strong); font-weight: 600; } .td-trace-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; margin: 0; padding: 0; list-style: none; -webkit-overflow-scrolling: touch; } .td-step { border-bottom: 1px solid var(--line); } .td-step:last-child { border-bottom: 0; } .td-step > details > summary { display: flex; align-items: baseline; gap: 10px; padding: 10px 18px; cursor: pointer; list-style: none; } .td-step > details > summary::-webkit-details-marker { display: none; } .td-step > details > summary:hover { background: var(--bg-subtle); } .td-step > details[open] > summary { background: var(--bg-subtle); } .td-step-n { flex: 0 0 auto; min-width: 20px; font-family: var(--font-mono); font-size: 10px; color: var(--ink-faint); font-variant-numeric: tabular-nums; } .td-step-main { flex: 1 1 auto; min-width: 0; } .td-step-obj { display: block; font-size: 13px; color: var(--ink-strong); overflow-wrap: break-word; } .td-step-args { display: block; margin-top: 1px; font-family: var(--font-mono); font-size: 10.5px; color: var(--ink-faint); overflow-wrap: break-word; } .td-step-status { flex: 0 0 auto; font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.08em; text-transform: uppercase; padding: 2px 7px; border-radius: 999px; border: 1px solid var(--line-strong); color: var(--ink-faint); } .td-step--ok .td-step-status { color: var(--success); border-color: var(--success); } .td-step--fail .td-step-status { color: var(--danger); border-color: var(--danger); } .td-step--run .td-step-status { color: var(--ink-soft); } .td-step-t { flex: 0 0 auto; font-family: var(--font-mono); font-size: 10px; color: var(--ink-faint); font-variant-numeric: tabular-nums; min-width: 34px; text-align: right; } .td-step-detail { padding: 2px 18px 14px 48px; font-size: 12.5px; line-height: 1.6; color: var(--ink); } .td-step-lbl { margin: 10px 0 2px; font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-faint); } .td-step-detail p { margin: 0; overflow-wrap: break-word; } .td-step-detail p.td-muted { color: var(--ink-faint); font-style: italic; } .td-step-kv { margin: 0; display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: 1px 12px; font-family: var(--font-mono); font-size: 11px; } .td-step-kv dt { color: var(--ink-faint); } .td-step-kv dd { margin: 0; color: var(--ink-strong); overflow-wrap: anywhere; } /* Plan steps replayed inside a "Plan" entry. */ .td-plan { margin: 0; padding: 0; list-style: none; font-size: 12.5px; } .td-plan li { display: flex; gap: 8px; align-items: baseline; } .td-plan-g { flex: 0 0 auto; width: 12px; color: var(--ink-faint); font-family: var(--font-mono); font-size: 11px; } .td-plan--done > span:last-child { color: var(--ink-faint); } .td-trace-empty { margin: 0; padding: 28px 18px; text-align: center; font-size: 12.5px; color: var(--ink-faint); } .td-trace-empty[hidden] { display: none; } .td-trace-foot { flex: 0 0 auto; padding: 10px 18px; border-top: 1px solid var(--line); font-size: 11px; line-height: 1.5; color: var(--ink-faint); } /* The rail is ~390px and phones are narrower still; a 24px inset frame there wastes a third of the screen. Full-bleed sheet instead. */ @media (max-width: 720px) { .td-trace { padding: 0; align-items: stretch; } .td-trace-panel { width: 100%; max-height: none; height: 100%; border: 0; border-radius: 0; } .td-step-detail { padding-left: 18px; } }