' +
card("END EVIDENCE", landing.endText, landing.endKnown, "") +
card("WHO CLAIMS IT FINISHED", landing.claimText, landing.claimKnown,
@@ -2408,8 +2408,8 @@ function nextCockpitHeldTo(group, observation){
harness and the session id are what the store keys on, so they are what
is shown. */
return '
WHAT YOU ASKED FOR
' +
- `${esc(sessKey(session))}` +
- `${esc(revision)}` +
+ `${esc(sessKey(session))}` +
+ `${esc(revision)}` +
'' +
NEXT_COCKPIT_HELD_FIELDS.map(spec =>
nextCockpitHeldField(session, annotation, spec, cap)).join("") + '
' +
diff --git a/cargento/skills/cargento/cargento_runtime/web/styles.css b/cargento/skills/cargento/cargento_runtime/web/styles.css
index 28fe13b91..f86e3dc61 100644
--- a/cargento/skills/cargento/cargento_runtime/web/styles.css
+++ b/cargento/skills/cargento/cargento_runtime/web/styles.css
@@ -16,12 +16,20 @@
--fs-2xs:11.5px; --fs-xs:12.5px; --fs-sm:14px; --fs-md:15.5px; --fs-lg:17px; --fs-xl:19px;
--fs-2xl:23px; --fs-num:28px; --fs-brand:38px; --fs-hero:52px;
/* Additional steps come from Cargento-v2.dc.html, not a re-derived scale. */
- /* 9px is the operations table's column header, the smallest label the
- prototype draws. It is a label, never a sentence: see ruling R9. */
- --fs-column:9px; --fs-label:9.5px; --fs-meta:10px; --fs-meta-detail:10.5px; --fs-machine:11px;
- --fs-breadcrumb:12px; --fs-body:13px; --fs-summary:13.5px; --fs-lead:14.5px;
+ /* One label tier and one sentence tier. Four of the six sub-12px steps sat
+ inside a 3px band no reader can rank, and three of them had no callers at
+ all. 15px rather than a brighter ink because the audit put --ink, the
+ brightest colour here, below the body-text requirement at 12.5px: the
+ colour had already been spent. Which selectors are sentences is a list in
+ docs/design-next-ui.md, so a reviewer argues with it rather than
+ re-deriving it from the cascade. */
+ --fs-label:11px; --fs-machine:11px; --fs-sentence:15px;
+ --fs-body:13px; --fs-summary:13.5px; --fs-lead:14.5px;
--fs-section:15px; --fs-subheading:16px; --fs-project-title:22px;
--fs-counter:27px; --fs-figure:32px;
+ /* 72 characters at the sentence tier. Sans prose only: a machine string a
+ source published wraps anywhere instead, and keeps the full column. */
+ --measure:540px;
--sel-bg:color-mix(in oklab,var(--accent) 26%,transparent);
--sel-bd:color-mix(in oklab,var(--accent) 52%,transparent);
--bg:#14140f; --panel:#1c1c16; --sunk:#11110c; --line:#2c2c23; --line2:#403f33;
@@ -77,7 +85,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-menu button{min-block-size:44px;display:flex;justify-content:space-between;width:100%;padding:8px;text-align:left}
.next-header nav[aria-label="Primary"] a:focus-visible,.next-menu summary:focus-visible,.next-menu button:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
.next-menu kbd{font-family:var(--mono);color:var(--ink3)}
-.next-stalled{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:18px;padding:10px 12px;border-left:3px solid var(--clay);background:var(--panel);color:var(--ink2);font-size:var(--fs-xs);line-height:1.45}
+.next-stalled{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:18px;padding:10px 12px;border-left:3px solid var(--clay);background:var(--panel);color:var(--ink2);font-size:var(--fs-sentence);font-weight:500;line-height:1.55}
.next-stalled strong{color:var(--ink);font-weight:500}
.next-stalled[data-next-state="history-reset"]{border-left-color:var(--ink3)}
.next-stalled button{margin-left:auto;padding:5px 9px;border:1px solid var(--line2);border-radius:3px;background:transparent;color:var(--ink);font:inherit;cursor:pointer}
@@ -152,12 +160,12 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-project-scope{overflow-wrap:anywhere;font-size:var(--fs-xs)}
.next-project-summary{display:flex;flex-wrap:wrap;gap:4px 10px}
.next-project-summary,.next-project-detail-count{color:var(--ink2);font-size:var(--fs-xs)}
-.next-project-collision,.next-project-detail-collision{margin:0;color:var(--ink3);font-size:var(--fs-xs);line-height:1.5}
+.next-project-collision,.next-project-detail-collision{margin:0;color:var(--ink3);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
.next-project-sessions{min-width:0}
.next-project-session{display:grid;grid-template-columns:6px 64px minmax(80px,1fr) minmax(110px,172px) minmax(110px,150px);align-items:center;gap:9px;width:100%;padding:7px 0;border:0;border-bottom:1px solid var(--line);border-radius:0;background:transparent;color:var(--ink);font:inherit;text-align:left;cursor:pointer}
.next-project-session:last-child{border-bottom:0}
.next-project-session:hover .next-project-session-title{color:var(--ink);text-decoration:underline}
-.next-project-session-harness{color:var(--ink3);font-family:var(--mono);font-size:10px;overflow-wrap:anywhere}
+.next-project-session-harness{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);overflow-wrap:anywhere}
.next-project-session-title{min-width:0;overflow-wrap:anywhere;font-family:var(--sans);font-size:var(--fs-xs)}
.next-project-session-now,.next-project-session-next{min-width:0;overflow-wrap:anywhere;font-size:var(--fs-xs)}
.next-project-dot{display:inline-block;flex:none;width:6px;height:6px;border:1px solid var(--project-tone);border-radius:50%;background:var(--project-tone)}
@@ -176,14 +184,14 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-project-detail-count,.next-project-detail-collision{flex-basis:100%;margin:0}
.next-project-goal{display:flex;flex-direction:column;gap:9px;padding:15px 17px;border:1px solid var(--line);border-radius:10px;background:var(--panel)}
.next-project-goal>header{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
-.next-project-goal h2,.next-project-activity>h2{margin:0;color:var(--ink3);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.14em}
-.next-project-goal-source{color:var(--ink3);font-family:var(--mono);font-size:10px}
+.next-project-goal h2,.next-project-activity>h2{margin:0;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
+.next-project-goal-source{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
.next-project-goal-text{max-width:640px;font-size:14.5px;line-height:1.55;white-space:pre-wrap;overflow-wrap:anywhere}
.next-project-goal-text.next-project-value--known{color:var(--ink)}
.next-project-goal-gap{margin:0;color:var(--ink3);font-size:var(--fs-xs);line-height:1.5}
.next-project-goal-row{display:flex;flex-direction:column;gap:5px;padding-left:9px;border-left:1px solid var(--line2)}
.next-project-goal-row+.next-project-goal-row{margin-top:2px}
-.next-project-goal-tag{color:var(--ink3);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.13em}
+.next-project-goal-tag{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
.next-project-activity-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:24px;align-items:start}
.next-project-activity{min-width:0}
.next-project-activity>h2{margin-bottom:11px}
@@ -197,12 +205,12 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-activity-title>.next-project-value--known,.next-project-ending-title>.next-project-value--known{color:var(--ink)}
.next-activity-harness{color:var(--ink3);font-family:var(--mono);font-size:11px}
.next-activity-stuck{color:var(--ink2);font-size:var(--fs-xs)}
-.next-activity-question{padding-top:7px;border-top:1px solid var(--line);color:var(--ink);font-size:var(--fs-xs);line-height:1.5;white-space:pre-wrap}
+.next-activity-question{padding-top:7px;border-top:1px solid var(--line);color:var(--ink);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;white-space:pre-wrap;max-width:var(--measure)}
.next-activity-metric{font-size:var(--fs-xs)}
.next-activity-instruction{font-size:var(--fs-xs);color:var(--ink2)}
.next-project-ending-glyph{color:var(--project-tone)}
.next-project-ending-outcome.next-project-value--known{color:var(--ink2)}
-.next-project-ending-outcome{font-family:var(--sans);font-size:var(--fs-xs);line-height:1.5}
+.next-project-ending-outcome{font-family:var(--sans);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
.next-project-detail-status{margin:0 0 12px;color:var(--ink3);font-size:var(--fs-xs)}
.next-project-progress-bar{display:block;width:120px;height:4px;margin:5px 0 9px;accent-color:var(--accent)}
.next-project-plan{margin-bottom:30px;border-top:1px solid var(--line2)}
@@ -247,7 +255,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-workstream-empty{min-height:42px;margin:0;padding:12px 10px;border-bottom:1px solid var(--line);color:var(--ink2);font-size:var(--fs-xs)}
.next-project-detail-empty{padding:24px;border:1px solid var(--line);color:var(--ink2);font-size:var(--fs-sm)}
-.next-workstream-header button{gap:12px;align-items:baseline;padding:0 0 11px;font-size:10px;letter-spacing:.1em}
+.next-workstream-header button{gap:12px;align-items:baseline;padding:0 0 11px;font-size:var(--fs-label);letter-spacing:.09em}
.next-workstream-header small{color:var(--ink3);font-family:var(--sans);font-size:var(--fs-xs);letter-spacing:normal;text-align:right}
.next-project-change{display:grid;grid-template-columns:52px 16px minmax(0,1fr) 90px;gap:12px;align-items:center;min-height:30px;padding:6px 0;border-bottom:1px solid var(--line);color:var(--ink2);font-size:var(--fs-xs)}
.next-project-change time,.next-project-change-harness{color:var(--ink3);font-family:var(--mono);font-size:10.5px}
@@ -286,14 +294,14 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-rail-panel:first-child{padding-top:20px}
.next-rail-panel:last-child{border-bottom:0}
.next-rail-header{display:flex;align-items:baseline;gap:9px}
-.next-rail-header h2{margin:0;color:var(--ink3);font:700 9.5px var(--mono);letter-spacing:.14em}
-.next-rail-meta{margin-left:auto;color:var(--ink3);font:10px var(--mono);text-align:right}
+.next-rail-header h2{margin:0;color:var(--ink3);font:700 var(--fs-label) var(--mono);letter-spacing:.09em}
+.next-rail-meta{margin-left:auto;color:var(--ink3);font:var(--fs-label) var(--mono);text-align:right}
.next-rail-meta--sentence{font:var(--fs-xs) var(--sans)}
.next-rail-meta--amber{color:var(--amber)}
.next-project-detail-rail .next-rail-reason{margin:0;color:var(--ink3);font-family:var(--sans);font-size:var(--fs-xs);line-height:1.5}
.next-delegation-figure{display:flex;gap:9px;align-items:baseline}
.next-delegation-figure>strong{color:var(--accent);font:700 32px/1 var(--mono);letter-spacing:-.02em}
-.next-delegation-caption{margin:0;color:var(--ink2);font-size:var(--fs-xs);line-height:1.35}
+.next-delegation-caption{margin:0;color:var(--ink2);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
.next-delegation progress{display:block;width:100%;height:5px;border:0;border-radius:3px;overflow:hidden;background:var(--line);accent-color:var(--accent-dim)}
.next-delegation progress::-webkit-progress-bar{background:var(--line)}
.next-delegation progress::-webkit-progress-value{background:var(--accent-dim)}
@@ -305,12 +313,12 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-delegation-trend{color:var(--ink3);font:var(--fs-xs) var(--mono)}
.next-rail-wait{display:flex;flex-direction:column;gap:8px;padding:12px 13px;border:1px solid var(--line);border-left:2px solid var(--amber);border-radius:9px;background:var(--panel)}
.next-rail-wait-heading{display:flex;align-items:baseline;gap:9px}
-.next-rail-wait-heading a{min-width:0;color:var(--ink);font-size:var(--fs-xs);line-height:1.4;overflow-wrap:anywhere;text-decoration:none}
+.next-rail-wait-heading a{min-width:0;color:var(--ink);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;overflow-wrap:anywhere;text-decoration:none}
.next-rail-wait-heading a:hover{text-decoration:underline}
.next-rail-wait-duration{margin-left:auto;flex-shrink:0;color:var(--amber);font:700 11px var(--mono)}
-.next-rail-question{margin:0;color:var(--ink2);font-size:var(--fs-xs);line-height:1.5;overflow-wrap:anywhere}
+.next-rail-question{margin:0;color:var(--ink2);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;overflow-wrap:anywhere}
.next-rail-wait-controls{display:flex;flex-wrap:wrap;gap:7px;padding-top:2px}
-.next-rail-wait-controls button{margin-top:0;padding:5px 10px;border-radius:6px;font-size:10px}
+.next-rail-wait-controls button{margin-top:0;padding:5px 10px;border-radius:6px;font-size:var(--fs-label)}
.next-rail-capacity-window{display:flex;flex-direction:column;gap:6px}
.next-rail-capacity-heading{display:flex;align-items:baseline;gap:8px;color:var(--ink3);font:11px var(--mono)}
.next-rail-capacity-heading b,.next-rail-capacity-heading strong{color:var(--ink)}
@@ -327,12 +335,12 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-project-detail-rail .next-usage-consent{margin:0;padding:12px;font-size:var(--fs-xs)}
.next-project-detail-rail .next-usage-consent-note{font-family:var(--sans);font-size:var(--fs-xs)}
.next-project-detail-rail .next-usage-switch{margin:0;font-size:var(--fs-xs)}
-.next-control>header{color:var(--ink3);font-family:var(--mono);font-size:10px}
+.next-control>header{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
.next-steer{padding-top:12px;border-top:1px solid var(--line)}
.next-steer form{display:flex;gap:7px;padding:12px 0}
.next-steer label,.next-guardrail-add-input label{flex:1;min-width:0}
.next-steer input,.next-guardrail-add-input input{width:100%;min-width:0;padding:8px;border:1px solid var(--line2);border-radius:6px;background:var(--panel);color:var(--ink);font:var(--fs-xs) var(--sans)}
-.next-steer button,.next-guardrail-add,.next-guardrail-add-input button{padding:5px 8px;border:1px solid var(--line2);border-radius:6px;background:transparent;color:var(--ink2);font:10px var(--mono);cursor:pointer}
+.next-steer button,.next-guardrail-add,.next-guardrail-add-input button{padding:5px 8px;border:1px solid var(--line2);border-radius:6px;background:transparent;color:var(--ink2);font:var(--fs-label) var(--mono);cursor:pointer}
.next-guardrail-add{align-self:flex-start;padding:0;border:0;color:var(--ink3);font-size:10.5px}
.next-steer button:hover,.next-guardrail-add-input button:hover{border-color:var(--accent)}
.next-guardrail-add:hover{color:var(--ink)}
@@ -343,8 +351,8 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-guardrail-row{display:grid;grid-template-columns:10px minmax(0,1fr);gap:10px;width:100%;padding:9px 11px;border:1px solid var(--line);border-radius:9px;background:transparent;color:var(--ink2);font:inherit;text-align:left;cursor:pointer}
.next-guardrail-row:hover{border-color:var(--line2)}
.next-guardrail-row[aria-checked="false"]{color:var(--ink3)}
-.next-guardrail-glyph{margin-top:2px;color:var(--ink3);font:10px var(--mono)}
-.next-guardrail-copy strong,.next-guardrail-copy small{display:block;overflow-wrap:anywhere;font-size:var(--fs-xs);font-weight:400;line-height:1.45}
+.next-guardrail-glyph{margin-top:2px;color:var(--ink3);font:var(--fs-label) var(--mono)}
+.next-guardrail-copy strong,.next-guardrail-copy small{display:block;overflow-wrap:anywhere;font-size:var(--fs-sentence);font-weight:500;line-height:1.55}
.next-guardrail-copy small,.next-guardrail-empty{margin:0;color:var(--ink3);font-size:var(--fs-xs);line-height:1.5}
.next-guardrail-add-input{display:flex;align-items:center;gap:9px}
.next-guardrail-add-input button{flex-shrink:0;color:var(--ink)}
@@ -356,23 +364,23 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
/* ===== SESSIONS ===== */
.next-operations{min-width:0;padding:26px 0 34px}
.next-operations-header{display:grid;grid-template-columns:132px minmax(0,1fr);gap:5px 16px;align-items:baseline}
-.next-operations-header>span{grid-row:1 / span 2;color:var(--accent-dim);font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.14em}
+.next-operations-header>span{grid-row:1 / span 2;color:var(--accent-dim);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
.next-operations-header h1{margin:0;font-size:23px;font-weight:700;letter-spacing:-.02em}
.next-operations-header p{margin:0;color:var(--ink2);font-size:13.5px;line-height:1.5}
.next-operations-fleet{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));margin-top:24px;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.next-operations-fleet>section{min-width:0;padding:15px 18px}
.next-operations-fleet>section+section{border-left:1px solid var(--line)}
.next-operations-fleet span,.next-operations-fleet strong,.next-operations-fleet small{display:block}
-.next-operations-fleet span{color:var(--ink3);font-family:var(--mono);font-size:9.5px}
+.next-operations-fleet span{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
.next-operations-fleet strong{margin-top:7px;font-family:var(--mono);font-size:27px;font-weight:400;line-height:1}
-.next-operations-fleet small{margin-top:7px;color:var(--ink3);font-size:var(--fs-xs);line-height:1.4}
+.next-operations-fleet small{margin-top:7px;color:var(--ink3);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
.next-operation-group{margin-top:30px}
.next-operation-group--history{margin-top:44px}
.next-operation-group>header{display:flex;justify-content:space-between;gap:24px;align-items:baseline;padding-bottom:10px;border-bottom:1px solid var(--line2)}
.next-operation-group>header h2{margin:0;font-size:16px;font-weight:600}
-.next-operation-group>header p{max-width:620px;margin:0;color:var(--ink3);font-size:var(--fs-xs);line-height:1.45;text-align:right}
+.next-operation-group>header p{max-width:620px;margin:0;color:var(--ink3);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;text-align:right}
.next-operations-columns,#app .next-operation-row{grid-template-columns:minmax(0,1.5fr) 190px minmax(0,1fr) minmax(0,1fr) 170px}
-.next-operations-columns{display:grid;border-bottom:1px solid var(--line2);color:var(--ink3);font-family:var(--mono);font-size:9px;font-weight:700;letter-spacing:.13em}
+.next-operations-columns{display:grid;border-bottom:1px solid var(--line2);color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
.next-operations-columns span{padding:9px 12px}
#app .next-operation-row{display:grid;grid-template-columns:minmax(0,1.5fr) 190px minmax(0,1fr) minmax(0,1fr) 170px;position:relative;width:100%;min-width:0;border-bottom:1px solid var(--line);color:var(--ink);text-decoration:none;cursor:pointer}
.next-operation-row:hover{background:var(--panel)}
@@ -389,8 +397,8 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-operation-identity strong,.next-operation-fact strong{margin-top:7px;font-size:var(--fs-sm);font-style:normal;font-weight:500;line-height:1.4}
.next-operation-harness{color:var(--ink3);font-family:var(--mono);font-size:10.5px}
.next-operation-live-glyph{display:inline-block;margin-right:7px}
-.next-operation-assignment{margin-top:8px;color:var(--ink2);font-size:var(--fs-xs);line-height:1.4}
-.next-operation-collision{margin-top:8px;color:var(--ink);font-size:var(--fs-xs);line-height:1.4}
+.next-operation-assignment{margin-top:8px;color:var(--ink2);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
+.next-operation-collision{margin-top:8px;color:var(--ink);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
.next-operation-unread,.next-operation-scan-only{display:block;margin-top:8px;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-xs);line-height:1.4}
.next-operation-fact em{margin-top:6px;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-xs);font-style:normal;line-height:1.4}
.next-operation-fact--blocked strong{color:var(--amber)}
@@ -412,7 +420,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-capacity{margin-top:22px;border-top:1px solid var(--line2)}
.next-capacity-head,.next-capacity-row{display:grid;align-items:center;grid-template-columns:190px 56px minmax(0,1fr) 74px 190px 92px;gap:0 14px}
.next-capacity-head{padding:9px 4px 8px;border-bottom:1px solid var(--line2)}
-.next-capacity-head span{color:var(--ink3);font-family:var(--mono);font-size:9px;font-weight:700;letter-spacing:.06em}
+.next-capacity-head span{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.06em}
.next-capacity-row{position:relative;padding:13px 4px;border-bottom:1px solid var(--line)}
.next-capacity-window{min-width:0}
.next-capacity-window b{display:block;font-family:var(--mono);font-weight:700;font-size:12.5px;overflow-wrap:anywhere}
@@ -442,19 +450,19 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-capacity-prospect p{margin:0}
.next-capacity-prospect p+p{margin-top:7px}
.next-capacity-prospect b{font-weight:500;font-family:var(--mono);font-variant-numeric:tabular-nums}
-.next-capacity-prospect small{display:block;margin-top:9px;color:var(--ink2);font-family:var(--sans);font-size:var(--fs-xs);line-height:1.45}
+.next-capacity-prospect small{display:block;margin-top:9px;color:var(--ink2);font-family:var(--sans);font-size:var(--fs-sentence);font-weight:500;line-height:1.55}
.next-capacity-scope{color:var(--ink3);font-family:var(--mono);font-size:10.5px}
-.next-operations-fleet span{font-weight:700;letter-spacing:.13em}
+.next-operations-fleet span{font-weight:700;letter-spacing:.09em}
.next-operation-identity .next-operation-title--unknown{color:var(--ink3)}
.next-operation-row--bad{box-shadow:inset 2px 0 var(--clay)}
.next-operation-row--want{box-shadow:inset 2px 0 var(--amber)}
.next-operation-row--ok{box-shadow:inset 2px 0 var(--accent-dim)}
.next-operation-row--unknown{box-shadow:none}
-.next-operation-outcome{display:block;margin-top:6px;color:var(--ink2);font-size:var(--fs-xs);line-height:1.5}
+.next-operation-outcome{display:block;margin-top:6px;color:var(--ink2);font-size:var(--fs-sentence);font-weight:500;line-height:1.55}
.next-operation-outcome--bad{color:var(--ink2)}
.next-operation-outcome--unknown{color:var(--ink3)}
-.next-operation-state{padding:13px 8px;text-align:right;color:var(--ink3);font:700 9.5px var(--mono);letter-spacing:.1em}
+.next-operation-state{padding:13px 8px;text-align:right;color:var(--ink3);font:700 var(--fs-label) var(--mono);letter-spacing:.09em}
.next-operation-group--history .next-operations-columns,#app .next-operation-row[data-next-operation-history="true"]{grid-template-columns:minmax(0,1.5fr) 190px minmax(0,1fr) 92px}
.next-capacity-window button{display:block;padding:0;border:0;background:transparent;color:var(--ink);text-align:left;font:inherit;cursor:pointer}
.next-capacity-window button::after{content:"";position:absolute;inset:0;z-index:1}
@@ -536,16 +544,16 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-attention-heading,.next-attention-section-heading{display:flex;align-items:baseline;gap:14px}
.next-attention-heading{margin-bottom:24px}
.next-attention-heading h1{margin:0;font-size:var(--fs-2xl);font-weight:700;letter-spacing:-.02em}
-.next-attention-heading p,.next-attention-section-heading p{margin:0;color:var(--ink3);font-size:var(--fs-xs);line-height:1.5}
+.next-attention-heading p,.next-attention-section-heading p{margin:0;color:var(--ink3);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
.next-attention-brief{display:grid;gap:10px;padding:16px 18px;border:1px solid var(--line);border-left:2px solid var(--accent-dim);border-radius:10px;background:var(--panel)}
.next-attention-brief p{margin:0;color:var(--ink2);font-size:var(--fs-summary);line-height:1.6}
.next-attention-brief>p,.next-attention-coverage>p{display:grid;grid-template-columns:104px minmax(0,1fr);gap:16px;align-items:baseline}
.next-attention-brief>p>span:last-child{color:var(--ink)}
.next-attention-quiet{color:var(--ink2)}
.next-attention-coverage{padding-top:10px;border-top:1px solid var(--line)}
-.next-attention-brief-label,.next-attention-label{display:block;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.13em;text-transform:uppercase}
+.next-attention-brief-label,.next-attention-label{display:block;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.09em;text-transform:uppercase}
.next-attention-coverage-details{margin:10px 0 0 120px;color:var(--ink3);font-size:var(--fs-xs)}
-.next-attention-coverage-details summary{display:list-item;width:max-content;max-width:100%;font-family:var(--mono);font-size:var(--fs-meta-detail);cursor:pointer}
+.next-attention-coverage-details summary{display:list-item;width:max-content;max-width:100%;font-family:var(--mono);font-size:var(--fs-label);cursor:pointer}
.next-attention-coverage-details ul{margin:0;padding:0;list-style:none}
.next-attention-coverage-details li{display:grid;grid-template-columns:96px 20px minmax(0,1fr);gap:12px;align-items:center;min-height:27px;padding:5px 0;border-bottom:1px solid var(--line);color:var(--ink2);font-size:var(--fs-xs)}
.next-attention-coverage-details strong{font-family:var(--mono);font-size:var(--fs-machine);font-weight:700}
@@ -553,7 +561,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-attention-square{width:6px;height:6px;border-radius:1px;background:var(--line2)}
.next-attention-square[data-known="true"]{background:var(--accent-dim)}
.next-attention-caveats{display:grid;gap:4px;padding-top:9px}
-.next-attention-caveats p{color:var(--ink3);font-size:var(--fs-xs);line-height:1.5}
+.next-attention-caveats p{color:var(--ink3);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
.next-attention-section{min-width:0;margin-top:24px}
.next-attention-section-heading{margin-bottom:10px}
.next-attention-section-heading h2:after{content:"";flex:1;min-width:20px;height:1px;background:var(--line)}
@@ -570,11 +578,11 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-attention-item>h3,.next-attention-item>p,.next-attention-item>div{min-width:0;margin:0}
.next-attention-index{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-machine)}
.next-attention-risk-identity h3{margin:0 0 5px;font-size:var(--fs-summary);font-weight:600;line-height:1.4}
-.next-attention-risk-identity>span,.next-attention-risk-source{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-meta-detail);line-height:1.5}
-.next-attention-risk-assignment{margin:5px 0 0;color:var(--ink2);font-size:var(--fs-xs);line-height:1.5}
+.next-attention-risk-identity>span,.next-attention-risk-source{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);line-height:1.5}
+.next-attention-risk-assignment{margin:5px 0 0;color:var(--ink2);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
.next-attention-risk-identity a,.next-attention-why a{display:flex;align-items:center;color:var(--ink);text-decoration-color:var(--line2);text-underline-offset:4px}
.next-attention-risk-observation p{margin:0;color:var(--ink2);font-size:var(--fs-body);line-height:1.5}
-.next-attention-risk-detail{display:grid;gap:5px;margin-top:5px;color:var(--ink3);font-size:var(--fs-xs);line-height:1.5}
+.next-attention-risk-detail{display:grid;gap:5px;margin-top:5px;color:var(--ink3);font-size:var(--fs-sentence);font-weight:500;line-height:1.55}
.next-attention-risk-detail small{display:block;font-size:inherit}
.next-attention-risk-source{display:grid;gap:5px}
.next-attention-risk-next{color:var(--ink2);font-size:var(--fs-xs)}
@@ -589,9 +597,9 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-attention a:focus-visible,.next-attention summary:focus-visible,.next-attention-disclosure:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.next-attention-open{margin:0;padding:0;list-style:none}
.next-attention-open li{display:grid;grid-template-columns:34px 210px minmax(0,1fr);gap:16px;align-items:baseline;padding:9px 18px;border-bottom:1px solid var(--line)}
-.next-attention-open li>span{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-meta-detail);font-weight:700}
+.next-attention-open li>span{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700}
.next-attention-open strong{color:var(--ink2);font-size:var(--fs-body);font-weight:400}
-.next-attention-open p{margin:0;color:var(--ink3);font-size:var(--fs-xs);line-height:1.55}
+.next-attention-open p{margin:0;color:var(--ink3);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
@media(min-width:900px){
.next-attention-item--legacy{grid-template-columns:minmax(150px,.8fr) minmax(180px,1fr) minmax(180px,1.2fr) minmax(140px,.8fr) minmax(130px,.7fr)}
}
@@ -639,7 +647,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-session-command-facts>section+section{border-left:1px solid var(--line)}
.next-session-source-coverage{margin-top:12px;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-2xs)}
.next-session-source-coverage summary{width:max-content;cursor:pointer}
-.next-session-source-coverage p{margin:8px 0 0;font-family:var(--sans);font-size:var(--fs-xs);line-height:1.5}
+.next-session-source-coverage p{margin:8px 0 0;font-family:var(--sans);font-size:var(--fs-sentence);font-weight:500;line-height:1.55;max-width:var(--measure)}
.next-session-health{margin:20px 0 0;padding:14px 16px;border-left:3px solid var(--amber);background:var(--panel);color:var(--ink2);font-size:var(--fs-sm);line-height:1.5}
.next-session-health strong{color:var(--ink);font-family:var(--mono);font-size:var(--fs-2xs);font-weight:500}
.next-session-health-separator{color:var(--ink3)}
@@ -670,13 +678,13 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-session-controls .next-session-raise{font-weight:700}
.next-session-section[data-next-session-section="ask"]{padding:13px 16px;border:1px solid var(--line);border-left:2px solid var(--amber);border-radius:10px;background:var(--panel)}
.next-session-ask-callout{margin:0;padding:0;border:0;background:transparent}
-.next-session-ask-callout>span{color:var(--amber);font-size:var(--fs-label);letter-spacing:.13em}
+.next-session-ask-callout>span{color:var(--amber);font-size:var(--fs-label);letter-spacing:.09em}
.next-session-wait{color:var(--ink3);font-family:var(--sans);font-size:var(--fs-xs);letter-spacing:normal;text-transform:none}
.next-session-wait[data-known="true"]{color:var(--amber);font-family:var(--mono);font-size:var(--fs-label)}
.next-session-ask-callout h2{margin:7px 0 0;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:400}
.next-session-facts{margin:20px 0 0}
.next-session-facts>div{display:grid;grid-template-columns:120px minmax(0,1fr);gap:18px;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--line)}
-.next-session-facts dt{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.13em}
+.next-session-facts dt{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
.next-session-facts dd{min-width:0;margin:0;color:var(--ink2);font-family:var(--mono);font-size:var(--fs-xs);line-height:1.55;overflow-wrap:anywhere}
.next-session-fact-note{display:block;margin-top:4px;color:var(--ink3);font-family:var(--sans);font-size:var(--fs-xs)}
.next-session-evidence{margin-top:20px}
@@ -704,7 +712,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-project-workflow-definition>small{display:block;padding:0 10px 12px;font-size:var(--fs-2xs)}
.next-cockpit-task-subject{padding:22px 12px 14px;border-bottom:1px solid var(--line)}
.next-cockpit-task-subject>.next-scope-cue{margin-bottom:7px}
-.next-cockpit-task-subject>span,.next-cockpit-now-state span{color:var(--ink3);font:var(--fs-2xs) var(--mono);letter-spacing:.08em}
+.next-cockpit-task-subject>span,.next-cockpit-now-state span{color:var(--ink3);font:var(--fs-2xs) var(--mono);letter-spacing:.09em}
.next-cockpit-task-subject h2{margin:5px 0 0;font-size:var(--fs-xl);font-weight:600}
.next-cockpit-task-subject h2 small{color:var(--ink2);font-size:inherit;font-weight:450}
.next-cockpit-tabs{display:flex;gap:24px;border-bottom:1px solid var(--line2);padding:0 12px}
@@ -778,12 +786,12 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-cockpit-scope-tree,.next-cockpit-scope-options{display:grid;gap:2px;padding:12px 12px 12px 0}
.next-cockpit-scope-switcher{display:none}
.next-cockpit-stale-session{margin:24px 0;padding:24px;border:1px solid var(--line)}
-.next-cockpit-stale-session>span{color:var(--ink3);font:var(--fs-2xs) var(--mono);letter-spacing:.08em}
+.next-cockpit-stale-session>span{color:var(--ink3);font:var(--fs-2xs) var(--mono);letter-spacing:.09em}
.next-cockpit-stale-session h1{margin:8px 0;font-size:var(--fs-xl)}
.next-cockpit-stale-session p{color:var(--ink3);font:var(--fs-xs) var(--mono);overflow-wrap:anywhere}
.next-cockpit-stale-session a{color:var(--ink2);font:var(--fs-xs) var(--mono)}
.next-cockpit-scope-name{font:var(--fs-sm) var(--mono)}
-.next-cockpit-scope-heading{padding:0 8px 7px;color:var(--ink3);font:var(--fs-2xs) var(--mono);letter-spacing:.08em}
+.next-cockpit-scope-heading{padding:0 8px 7px;color:var(--ink3);font:var(--fs-2xs) var(--mono);letter-spacing:.09em}
#app .next-cockpit-scope-tree a,#app .next-cockpit-scope-options a{display:grid;grid-template-columns:minmax(0,1fr);gap:3px;padding:7px 8px;color:var(--ink2);text-decoration:none}
.next-cockpit-scope-tree a[data-scope-kind="session"],.next-cockpit-scope-options a[data-scope-kind="session"]{padding-left:14px}
.next-cockpit-scope-tree a[aria-current="page"],.next-cockpit-scope-options a[aria-current="page"]{box-shadow:inset 2px 0 var(--ink2);background:var(--panel);color:var(--ink)}
@@ -811,7 +819,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-cockpit-project-status details>summary{cursor:pointer;color:var(--ink3)}
.next-cockpit-status-empty{color:var(--ink3)}
.next-cockpit-recovery{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));border-bottom:1px solid var(--line)}
-.next-cockpit-recovery>header{grid-column:1/-1;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:7px 12px;border-bottom:1px solid var(--line);font:var(--fs-2xs) var(--mono);letter-spacing:.08em}
+.next-cockpit-recovery>header{grid-column:1/-1;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:7px 12px;border-bottom:1px solid var(--line);font:var(--fs-2xs) var(--mono);letter-spacing:.09em}
.next-cockpit-recovery>header button{padding:4px 7px;border:1px solid var(--line2);background:transparent;color:var(--ink2);font:inherit;letter-spacing:0;cursor:pointer}
.next-cockpit-recovery>div{min-width:0;padding:10px 12px;border-right:1px solid var(--line);font:var(--fs-xs) var(--mono)}
.next-cockpit-recovery>div:last-child{border-right:0}
@@ -826,7 +834,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-cockpit-recovery-memos[data-next-cockpit-memo-empty]>button{border:0;background:none;color:var(--ink2);font:inherit;cursor:pointer}
.next-cockpit-recovery-memos textarea{grid-column:1/-1;width:100%;min-height:54px;resize:vertical;padding:7px;border:1px solid var(--line2);background:var(--panel);color:var(--ink);font:inherit}
.next-cockpit-recovery span,.next-cockpit-recovery strong,.next-cockpit-recovery small{display:block}
-.next-cockpit-recovery span{margin-bottom:5px;color:var(--ink3);font-size:var(--fs-2xs);letter-spacing:.08em}
+.next-cockpit-recovery span{margin-bottom:5px;color:var(--ink3);font-size:var(--fs-2xs);letter-spacing:.09em}
.next-cockpit-recovery strong{font-weight:500;overflow-wrap:anywhere}
.next-cockpit-recovery small{margin-top:4px;color:var(--ink3);overflow-wrap:anywhere}
.next-cockpit-recovery details{margin-top:5px;color:var(--ink3)}
@@ -854,14 +862,14 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
words reach the server. */
.next-cockpit-held{display:flex;flex-direction:column;gap:11px;padding:14px 16px}
.next-cockpit-held>header{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
-.next-cockpit-held h2{margin:0;color:var(--ink3);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.14em}
-.next-cockpit-held-revision{color:var(--ink3);font-family:var(--mono);font-size:10px}
+.next-cockpit-held h2{margin:0;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
+.next-cockpit-held-revision{color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-cockpit-held-fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:start}
.next-cockpit-held-field{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:baseline;gap:4px 8px;padding-left:9px;border-left:1px solid var(--line2)}
-.next-cockpit-held-label{grid-column:1/-1;color:var(--ink3);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.13em}
-.next-cockpit-held-sub{grid-column:1/-1;color:var(--ink3);font-family:var(--mono);font-size:10px}
+.next-cockpit-held-label{grid-column:1/-1;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
+.next-cockpit-held-sub{grid-column:1/-1;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
.next-cockpit-held-field textarea{grid-column:1/-1;width:100%;min-height:56px;box-sizing:border-box;resize:vertical;padding:7px;border:1px solid var(--line2);background:var(--panel);color:var(--ink);font:14px/1.55 var(--sans)}
-.next-cockpit-held-count{color:var(--ink3);font-family:var(--mono);font-size:10px}
+.next-cockpit-held-count{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
.next-cockpit-held-field button{border:0;background:none;color:var(--ink2);font:var(--fs-xs) var(--mono);cursor:pointer}
.next-cockpit-held-field button[hidden]{display:none}
.next-cockpit-held-absent{grid-column:1/-1;margin:0;color:var(--ink3);font-size:var(--fs-xs);line-height:1.5}
@@ -877,17 +885,17 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
so it is sans. */
.next-cockpit-work{display:flex;flex-direction:column;gap:8px;padding:0 16px 14px}
.next-cockpit-work>header{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
-.next-cockpit-work h2{margin:0;color:var(--ink3);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.14em}
+.next-cockpit-work h2{margin:0;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
.next-cockpit-work-row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:baseline;gap:3px 10px;padding-left:9px;border-left:1px solid var(--line)}
.next-cockpit-work-row+.next-cockpit-work-row{margin-top:5px}
-.next-cockpit-work-type{color:var(--ink3);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.1em}
+.next-cockpit-work-type{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.1em}
.next-cockpit-work-summary{font:var(--fs-xs)/1.55 var(--mono);overflow-wrap:anywhere}
/* A model's paraphrase inside the observed record takes the same third
treatment a reading does: italic sans on a dotted rule, no colour. */
.next-cockpit-work-derived{font:italic 13px/1.55 var(--sans);color:var(--ink2);padding-left:7px;border-left:1px dotted var(--line2);overflow-wrap:anywhere}
-.next-cockpit-work-mix{margin:0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
-.next-cockpit-work-at{color:var(--ink3);font-family:var(--mono);font-size:10px}
-.next-cockpit-work-source{grid-column:1/-1;color:var(--ink3);font-family:var(--mono);font-size:10px}
+.next-cockpit-work-mix{margin:0;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
+.next-cockpit-work-at{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
+.next-cockpit-work-source{grid-column:1/-1;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
.next-cockpit-work-absent,.next-cockpit-work-limit,.next-cockpit-work-dropped{margin:0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
/* The third treatment, for a model reading: italic Space Grotesk on the
secondary ink over a dotted rule, so it reads as neither a published string
@@ -898,22 +906,22 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
revisions rather than part of the reading. */
.next-cockpit-reading,.next-cockpit-departures{display:flex;flex-direction:column;gap:8px;padding:0 16px 14px}
.next-cockpit-reading>header,.next-cockpit-departures>header{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
-.next-cockpit-reading h2,.next-cockpit-departures h2{margin:0;color:var(--ink3);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.14em}
-.next-cockpit-reading-stamp{color:var(--ink3);font-family:var(--mono);font-size:10px}
+.next-cockpit-reading h2,.next-cockpit-departures h2{margin:0;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
+.next-cockpit-reading-stamp{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
/* The review section holds two collections and two more parts, so each is
fenced and named. The label takes the mono uppercase every other source label
on this panel takes; it names a collection rather than quoting one, which is
note 7 of the design's own compromise list and is kept deliberately. */
.next-cockpit-departure-part{display:flex;flex-direction:column;gap:4px;padding:8px 0 0}
.next-cockpit-departure-part+.next-cockpit-departure-part{margin-top:6px;border-top:1px solid var(--line)}
-.next-cockpit-departure-label{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.08em}
+.next-cockpit-departure-label{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.09em}
/* Figures, one per line and never side by side: two numbers on one line invite
the division neither of them survives. */
.next-cockpit-departure-counts{display:flex;flex-direction:column;gap:3px}
.next-cockpit-count{display:flex;justify-content:space-between;gap:12px}
-.next-cockpit-count-label{color:var(--ink2);font:var(--fs-xs)/1.5 var(--sans)}
+.next-cockpit-count-label{color:var(--ink2);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-cockpit-count-value{color:var(--ink);font:var(--fs-xs)/1.5 var(--mono)}
-.next-cockpit-departures-kept{margin:0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-cockpit-departures-kept{margin:0;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-cockpit-departures-kept a{color:var(--ink);text-decoration-color:var(--line2);text-underline-offset:4px}
.next-cockpit-departures-kept a:hover{text-decoration-color:var(--accent)}
/* ONE departure row treatment, and the same one the design writes down (note 2
@@ -930,18 +938,18 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
/* The constraint name and the stamp on one line. A float does not survive the
flex column above, so the row carries them together. */
.next-session-departure-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
-.next-cockpit-reading-name,.next-session-departure-name{color:var(--ink3);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.13em}
+.next-cockpit-reading-name,.next-session-departure-name{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
.next-cockpit-reading-clause,.next-session-departure-clause{font:var(--fs-xs)/1.5 var(--mono);color:var(--ink2);overflow-wrap:anywhere}
/* The board saying the revision's words are not retained, which is the board
talking and not a quotation, so sans for the reason the limit line below
gives. */
.next-cockpit-reading-clause-absent{font:var(--fs-xs)/1.5 var(--sans);color:var(--ink3);overflow-wrap:anywhere}
.next-cockpit-reading-result,.next-cockpit-reading-detail,.next-session-departure-reading{font:italic 13.5px/1.55 var(--sans);color:var(--ink2);overflow-wrap:anywhere}
-.next-cockpit-reading-evidence,.next-session-departure-base{color:var(--ink3);font-family:var(--mono);font-size:10px;overflow-wrap:anywhere}
+.next-cockpit-reading-evidence,.next-session-departure-base{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);overflow-wrap:anywhere}
/* The board talking, not a string a source published, so sans. The
evidence line above it names a type and a source and stays mono. */
-.next-cockpit-reading-limit{color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans);overflow-wrap:anywhere}
-.next-cockpit-reading-why{margin:0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-cockpit-reading-limit{color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);overflow-wrap:anywhere}
+.next-cockpit-reading-why{margin:0;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-cockpit-reading-stale,.next-session-departure-stale{margin:0;color:var(--amber);font:var(--fs-xs)/1.5 var(--sans)}
.next-cockpit-reading button{align-self:flex-start;padding:5px 10px;border:1px solid var(--line2);background:var(--panel);color:var(--ink);font:var(--fs-xs) var(--sans);cursor:pointer}
.next-cockpit-reading button[disabled]{color:var(--ink3);cursor:default}
@@ -951,9 +959,9 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
any state. */
.next-cockpit-landed{display:flex;flex-direction:column;gap:8px;padding:0 16px 14px}
.next-cockpit-landed>header{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
-.next-cockpit-landed h2{margin:0;color:var(--ink3);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.14em}
-.next-cockpit-landed-axes{color:var(--ink3);font-family:var(--mono);font-size:10px}
-.next-cockpit-held-bound{margin-left:auto;color:var(--ink3);font-family:var(--mono);font-size:10px}
+.next-cockpit-landed h2{margin:0;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
+.next-cockpit-landed-axes{color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
+.next-cockpit-held-bound{margin-left:auto;color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
/* What the unasked lane raised. The same label shape as every other fact on
this panel, for the reason the delivery block beside it carries one.
@@ -966,21 +974,21 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-session-departures{margin:14px 0 0;padding:10px 0 0;border-top:1px solid var(--line2)}
.next-session-departures h2{margin:0;color:var(--ink3);font-family:var(--mono);
font-size:var(--fs-label);font-weight:400}
-.next-session-departures-count{margin:6px 0 0;color:var(--ink2);font:var(--fs-xs)/1.5 var(--sans)}
+.next-session-departures-count{margin:6px 0 0;color:var(--ink2);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
/* The sentence saying why there is nothing. Dimmer than a departure, because a
spent cap and a quiet board are both absences and neither is a finding. */
-.next-session-departures-why{margin:6px 0 0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-session-departures-why{margin:6px 0 0;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
/* Everything typographic about this row is set with the cockpit's above. What
is left here is spacing and the one element that row has no counterpart for:
the stamp saying when the raise happened. */
.next-session-departure{margin:8px 0 0}
-.next-session-departure-at{color:var(--ink3);font-family:var(--mono);font-size:10px}
+.next-session-departure-at{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
.next-session-departure-reading,.next-session-departure-base{margin:0}
/* What later evidence showed. Sans and upright, because it is the board
reporting a derivation over its own store and not a model reading, and no
colour on any of its four sentences: the common one is unknown, and unknown
never takes colour. Dimmer than the reading above it, which is the finding. */
-.next-session-departure-next{margin:2px 0 0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-session-departure-next{margin:2px 0 0;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
/* What became of a raise. The heading carries the count and the paragraphs
carry the sentences the server composed.
@@ -1000,18 +1008,18 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
panel is stating. Matches .next-session-evidence h2. */
.next-session-delivery h2{margin:0;color:var(--ink3);font-family:var(--mono);
font-size:var(--fs-label);font-weight:400}
-.next-session-delivery-count{margin:6px 0 0;color:var(--ink2);font:var(--fs-xs)/1.5 var(--sans)}
-.next-session-delivery-why{margin:4px 0 0;color:var(--ink);font:var(--fs-xs)/1.5 var(--sans)}
+.next-session-delivery-count{margin:6px 0 0;color:var(--ink2);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
+.next-session-delivery-why{margin:4px 0 0;color:var(--ink);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
/* A qualifier on the sentence above, not a sentence of its own weight. */
-.next-session-delivery-note{margin:4px 0 0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-session-delivery-note{margin:4px 0 0;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
/* Dimmer than the outcome above it on purpose. The browser lane is weaker
evidence than a server-side outcome, and the two reading alike is what would
let the reader take a tab that was open once for a notification that landed. */
-.next-session-delivery-lane{margin:4px 0 0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-session-delivery-lane{margin:4px 0 0;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
[data-next-delivery="refused"] .next-session-delivery-why,
[data-next-delivery="not-launched"] .next-session-delivery-why{color:var(--amber)}
[data-next-delivery="no-lane"] .next-session-delivery-why{color:var(--ink3)}
-.next-session-held-link{margin:10px 0 0;font:var(--fs-xs)/1.5 var(--sans)}
+.next-session-held-link{margin:10px 0 0;font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
/* The board's own ink, not the browser's default link colour. Nothing else
on this page is blue, and an unstyled anchor announced itself as the one
thing here that came from outside the design. */
@@ -1021,7 +1029,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
anchor announces itself as the one thing on the page from outside the design,
and the `#app a` rules at the top of this file win on specificity against a
class-only selector. */
-.next-cockpit-held-reentry{margin:0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-cockpit-held-reentry{margin:0;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-cockpit-held-reentry a{color:var(--ink);text-decoration-color:var(--line2);text-underline-offset:4px}
.next-cockpit-held-reentry a:hover{text-decoration-color:var(--accent)}
/* The baseline question. Sans and no colour throughout: none of it is a model
@@ -1032,32 +1040,32 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
/* The Intent log. A list of the reader's own words, so the words take the
published-string register and everything the board says about them is sans. */
.next-intent{display:flex;flex-direction:column;gap:10px}
-.next-intent-note{margin:0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-intent-note{margin:0;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-intent-row{display:flex;flex-direction:column;gap:3px;padding-left:9px;border-left:1px solid var(--line2)}
-.next-intent-key{font-family:var(--mono);font-size:10px;color:var(--ink3);letter-spacing:.08em}
+.next-intent-key{font-family:var(--mono);font-size:var(--fs-label);color:var(--ink3);letter-spacing:.08em}
.next-intent-key a{color:var(--ink);text-decoration-color:var(--line2);text-underline-offset:4px}
.next-intent-key a:hover{text-decoration-color:var(--accent)}
.next-intent-gone{color:var(--ink3)}
.next-intent-words{font:var(--fs-xs)/1.5 var(--mono);color:var(--ink2);overflow-wrap:anywhere}
-.next-intent-revision,.next-intent-why{color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-intent-revision,.next-intent-why{color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-cockpit-conflict{display:flex;flex-direction:column;gap:8px;padding:10px 12px;border:1px solid var(--line2);background:var(--panel)}
-.next-cockpit-conflict-why,.next-cockpit-conflict-settled{margin:0;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-cockpit-conflict-why,.next-cockpit-conflict-settled{margin:0;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
/* The settle cue, in the held-cue register and its own class, per next-cockpit.js. */
-.next-cockpit-conflict-cue{color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
-.next-cockpit-conflict-open{margin:0;color:var(--ink);font:var(--fs-xs)/1.5 var(--sans)}
+.next-cockpit-conflict-cue{color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
+.next-cockpit-conflict-open{margin:0;color:var(--ink);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-cockpit-conflict-row{display:flex;flex-direction:column;gap:2px;padding-left:9px;border-left:1px solid var(--line2)}
/* A direction is a string the reader published, so mono, the same register the
observed record's own rows take. */
.next-cockpit-conflict-text{font:var(--fs-xs)/1.5 var(--mono);color:var(--ink2);overflow-wrap:anywhere}
-.next-cockpit-conflict-at{color:var(--ink3);font-family:var(--mono);font-size:10px}
+.next-cockpit-conflict-at{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label)}
.next-cockpit-conflict-choices{display:flex;flex-wrap:wrap;gap:8px}
.next-cockpit-conflict-choices button{padding:5px 10px;border:1px solid var(--line2);background:var(--panel);color:var(--ink);font:var(--fs-xs) var(--sans);cursor:pointer}
.next-cockpit-landed-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;align-items:start}
.next-cockpit-landed-card{display:flex;flex-direction:column;gap:4px;padding-left:9px;border-left:1px solid var(--line2)}
-.next-cockpit-landed-label{color:var(--ink3);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.13em}
-.next-cockpit-landed-value{font:var(--fs-xs)/1.5 var(--sans);color:var(--ink)}
+.next-cockpit-landed-label{color:var(--ink3);font-family:var(--mono);font-size:var(--fs-label);font-weight:700;letter-spacing:.09em}
+.next-cockpit-landed-value{font:500 var(--fs-sentence)/1.55 var(--sans);color:var(--ink);max-width:var(--measure)}
.next-cockpit-landed-value--absent{color:var(--ink3)}
-.next-cockpit-landed-note{color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.next-cockpit-landed-note{color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-cockpit-scope{display:flex;align-items:baseline;gap:12px;padding:7px 12px;font:var(--fs-xs) var(--mono);color:var(--ink3)}
.next-cockpit-scope strong{font-size:var(--fs-2xs);letter-spacing:.08em;color:var(--ink)}
.next-cockpit-scope--project{border-bottom:1px solid var(--line)}
@@ -1067,12 +1075,13 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-menu>summary[aria-label="More"]:before{content:attr(aria-label);font:var(--fs-xs) var(--sans)}
.next-menu>summary[aria-label="More"]:after{content:"▾";font:var(--fs-xs) var(--mono)}
.next-menu[open]>.next-menu-items{top:100%}
-.next-cockpit-content{font:var(--fs-xs)/1.55 var(--sans)}
+.next-cockpit-content{font:500 var(--fs-sentence)/1.55 var(--sans)}
.next-cockpit-recovery>div>strong,.next-cockpit-content .next-project-detail-empty{color:var(--ink3)}
.next-cockpit-recovery [data-next-cockpit-task-known]>strong,.next-cockpit-recovery .next-cockpit-recovery-evidence>strong{color:var(--ink)}
-.next-cockpit-recovery>div{font:var(--fs-xs)/1.55 var(--sans)}
+.next-cockpit-recovery>div{font:500 var(--fs-sentence)/1.55 var(--sans)}
.next-cockpit-recovery span{font-family:var(--mono)}
.next-cockpit-recovery strong,.next-cockpit-recovery small{font-size:var(--fs-xs)}
+.next-cockpit-recovery small{font-size:var(--fs-xs)}
.next-cockpit-recovery-evidence>strong,.next-cockpit-execution-root>strong,.next-cockpit-child-row>strong,
.next-cockpit-recovery [data-next-cockpit-memo-field]>strong,.next-cockpit-recovery textarea,
.next-course-episode p,.next-course-episode ul,.next-course-direction p{font-family:var(--mono)}
@@ -1080,13 +1089,13 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-cockpit-recovery span.next-cockpit-source{display:inline;margin:0;color:inherit;font-size:inherit;letter-spacing:0}
.next-cockpit-scope-tree small[data-next-withheld],.next-cockpit-scope-options small[data-next-withheld]{font-family:var(--sans);color:var(--ink3)}
.next-cockpit-authority>strong,.next-cockpit-authority>small{font-family:var(--sans)}
-.next-cockpit-authority>span{font:var(--fs-xs)/1.5 var(--sans);letter-spacing:0}
-.next-cockpit-empty,.next-cockpit-decision-summary,.next-cockpit-viewing-session,
+.next-cockpit-authority>span{font:500 var(--fs-sentence)/1.55 var(--sans);letter-spacing:0;max-width:var(--measure)}
+.next-cockpit-decision-summary,.next-cockpit-viewing-session,
.next-cockpit-now-state small,.next-project-workflow-definition>small,
-.next-cockpit-system-details ul,.next-cockpit-memos label>small,
-.next-cockpit-evidence-missing{font:var(--fs-xs)/1.55 var(--sans)}
+.next-cockpit-system-details ul,.next-cockpit-memos label>small{font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
+.next-cockpit-empty,.next-cockpit-evidence-missing{font:var(--fs-xs)/1.55 var(--sans)}
.next-cockpit-content .next-cockpit-evidence-missing{margin:6px 0;color:var(--ink3);font:var(--fs-xs)/1.55 var(--sans)}
-.next-cockpit-content .next-course-evidence{font:var(--fs-xs)/1.55 var(--sans)}
+.next-cockpit-content .next-course-evidence{font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-cockpit-content .next-course-evidence .next-cockpit-source{display:inline;font:inherit;font-family:var(--mono)}
.next-cockpit-recovery .next-project-goal{margin-top:12px;padding:10px 0;border:0;border-top:1px solid var(--line);border-radius:0;background:transparent}
.next-cockpit-recovery .next-project-goal-text{font:var(--fs-xs)/1.55 var(--mono)}
@@ -1094,20 +1103,20 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.next-cockpit-recovery .next-project-goal-gap{font:var(--fs-xs)/1.55 var(--sans)}
.next-cockpit-recovery .next-project-goal-source{font-size:var(--fs-2xs)}
.next-cockpit-recovery details>summary,.next-course-evidence>summary,
-.next-cockpit-plan-details>summary,.next-cockpit-console-status>summary{align-content:center;padding:4px 0;color:var(--ink2);font:var(--fs-xs)/1.5 var(--sans);text-decoration:underline;text-decoration-color:var(--line2);text-underline-offset:4px}
+.next-cockpit-plan-details>summary,.next-cockpit-console-status>summary{align-content:center;padding:4px 0;color:var(--ink2);font:500 var(--fs-sentence)/1.55 var(--sans);text-decoration:underline;text-decoration-color:var(--line2);text-underline-offset:4px;max-width:var(--measure)}
.next-cockpit-recovery details>summary:hover,.next-course-evidence>summary:hover{color:var(--ink)}
.next-cockpit-content summary:focus-visible,#app .next-cockpit-scope-tree a:focus-visible,
#app .next-cockpit-scope-options a:focus-visible,.next-cockpit-scope-switcher>summary:focus-visible{outline:2px solid var(--ink2);outline-offset:3px}
.next-cockpit-panel[data-next-cockpit-panel="now"]{gap:18px;padding:18px 12px}
.next-cockpit-scope-tree .next-scope-cue--session,.next-cockpit-scope-options .next-scope-cue--session{margin-left:0}
-.next-cockpit-scope-switcher>summary{font:var(--fs-xs)/1.5 var(--sans)}
+.next-cockpit-scope-switcher>summary{font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.next-cockpit-content .next-rail-meta--amber{color:var(--ink3)}
@media(max-width:1279px){
.next-cockpit-shell{grid-template-columns:1fr}
.next-cockpit-content{border-top:1px solid var(--line);border-left:0}
.next-cockpit-scope-tree{display:none}
.next-cockpit-scope-switcher{display:block;padding:10px 0}
- .next-cockpit-scope-switcher>summary{display:flex;justify-content:space-between;gap:12px;padding:8px;color:var(--ink2);font:var(--fs-xs)/1.5 var(--sans);cursor:pointer}
+ .next-cockpit-scope-switcher>summary{display:flex;justify-content:space-between;gap:12px;padding:8px;color:var(--ink2);font:500 var(--fs-sentence)/1.55 var(--sans);cursor:pointer}
.next-cockpit-scope-options{padding:4px 0}
}
@media(max-width:760px){
@@ -1140,13 +1149,13 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.pc-semantic-graph{min-width:0}
.pc-semantic-timeline{--lane-count:1;min-width:0;position:relative;padding:10px 0 4px}
.pc-history-band{margin-top:8px;border-top:1px solid var(--line);padding-top:7px}
-.pc-history-band>summary,.pc-trail-history>summary,.pc-entry-suppressed>summary,.pc-event-evidence>summary{cursor:pointer;color:var(--ink3);font:var(--fs-xs)/1.5 var(--sans)}
+.pc-history-band>summary,.pc-trail-history>summary,.pc-entry-suppressed>summary,.pc-event-evidence>summary{cursor:pointer;color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);max-width:var(--measure)}
.pc-lane-legend{display:grid;grid-template-columns:74px calc(var(--lane-count) * 24px) minmax(0,1fr);margin-bottom:8px}
.pc-lane-labels{grid-column:3;display:flex;gap:6px;flex-wrap:wrap;color:var(--ink2);font:var(--fs-machine) var(--mono)}
.pc-lane-labels span{padding:1px 6px;border-left:2px solid var(--line2);white-space:normal;overflow-wrap:anywhere}
.pc-graph-row{display:grid;grid-template-columns:74px calc(var(--lane-count) * 24px) minmax(0,1fr);align-items:start;min-height:48px}
.pc-graph-row>time,.pc-graph-time{padding:8px 6px 0 0;color:var(--ink3);text-align:right;overflow-wrap:anywhere}
-.pc-graph-row>time{font:var(--fs-meta-detail)/1.5 var(--mono)}
+.pc-graph-row>time{font:var(--fs-label)/1.5 var(--mono)}
.pc-graph-filter{display:flex;justify-content:flex-end;gap:4px;margin-bottom:8px;flex-wrap:wrap}
.pc-graph-filter button{padding:4px 8px;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--ink3);font:var(--fs-machine) var(--mono);cursor:pointer}
.pc-graph-filter button.selected{border-bottom-color:var(--accent);color:var(--ink)}
@@ -1160,7 +1169,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.pc-graph-row>.pc-trail-body{min-width:0;padding:5px 0 15px}
.pc-timeline-event>summary{cursor:pointer;border-radius:4px}
.pc-trail-summary,.pc-trail-summary>span{display:block}
-.pc-entry-details{display:grid;gap:7px;margin-top:9px;padding:10px 12px;border-left:2px solid var(--line2);background:var(--sunk);color:var(--ink3);font:var(--fs-xs)/1.55 var(--sans);overflow-wrap:anywhere}
+.pc-entry-details{display:grid;gap:7px;margin-top:9px;padding:10px 12px;border-left:2px solid var(--line2);background:var(--sunk);color:var(--ink3);font:500 var(--fs-sentence)/1.55 var(--sans);overflow-wrap:anywhere}
.pc-entry-details b{color:var(--ink2);font:var(--fs-machine) var(--mono)}
.pc-entry-details time,.pc-event-evidence time{font:var(--fs-machine) var(--mono)}
.pc-entry-source-list{display:grid;gap:6px;margin-top:6px}
@@ -1179,7 +1188,7 @@ body{font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:o
.pc-terminal-identity p{margin:3px 0}
.pc-terminal-identity code{margin-right:10px}
.pc-terminal-viewport{height:340px;overflow:auto;overscroll-behavior:contain}
-.pc-terminal-screen{min-width:100%;min-height:340px;padding:6px;box-sizing:content-box;font:var(--fs-xs)/1.55 var(--sans)}
+.pc-terminal-screen{min-width:100%;min-height:340px;padding:6px;box-sizing:content-box;font:500 var(--fs-sentence)/1.55 var(--sans)}
.pc-terminal-screen>.xterm{width:100%;height:100%}
@media(max-width:760px){
.pc-lane-legend{display:none}
diff --git a/cargento/skills/cargento/tests/css_cascade.py b/cargento/skills/cargento/tests/css_cascade.py
new file mode 100644
index 000000000..4c8726746
--- /dev/null
+++ b/cargento/skills/cargento/tests/css_cascade.py
@@ -0,0 +1,218 @@
+"""A restricted CSS cascade resolver, for asking what size a slot renders at.
+
+A value and the absence that replaces it never co-exist in one render: a ternary
+in the emitter picks one class or the other. No sweep of co-existing selectors
+and no reading of a populated board can see such a pair, so the only way to
+compare them is to resolve each branch on its own. That is what this module is
+for, and `AnAbsenceNeverOutranksTheValueItReplacesTest` is its caller.
+
+Restricted deliberately. It handles the selector forms this stylesheet uses,
+descendant and child combinators over tags, classes and attribute presence, and
+refuses sibling combinators rather than guessing. Every number it returns for
+that test's census was checked against `getComputedStyle` in a real browser
+before it was committed; a form it cannot express belongs here and gets checked
+the same way, never approximated.
+
+`@media` blocks are dropped on purpose: the census is the default viewport, and
+a rule applying at one width only would otherwise outrank the base rule and
+report a size nobody sees.
+"""
+
+from __future__ import annotations
+
+import pathlib
+import re
+
+Rule = tuple[str, str, int]
+Node = dict[str, object]
+Compound = dict[str, object]
+
+_SIMPLE = re.compile(r"[.#][\w-]+|\[[^\]]+\]|::?[\w-]+(?:\([^)]*\))?")
+_BLOCK = re.compile(r"([^{}]+)\{([^{}]*)\}")
+
+
+def _strip_media(css: str) -> str:
+ """Drop `@media` blocks, and refuse the case where that would hide a rule.
+
+ Every media font rule in this stylesheet is `max-width`, so dropping them
+ leaves the default viewport intact. A `min-width` block could raise or lower
+ a size at a width people actually use, and silently ignoring it would let an
+ always-true `@media(min-width:1px)` escape the guard, so it raises instead.
+ """
+ for block in re.finditer(r"@media\s*\(([^)]*)\)", css):
+ if "min-width" in block.group(1) and "font" in css[block.end() : block.end() + 400]:
+ raise UnsupportedSelectorError(f"@media ({block.group(1)}) sets a font size")
+ out: list[str] = []
+ index = 0
+ while index < len(css):
+ if not css.startswith("@media", index):
+ out.append(css[index])
+ index += 1
+ continue
+ cursor = css.find("{", index)
+ depth = 0
+ while cursor < len(css):
+ if css[cursor] == "{":
+ depth += 1
+ elif css[cursor] == "}":
+ depth -= 1
+ if depth == 0:
+ break
+ cursor += 1
+ index = cursor + 1
+ return "".join(out)
+
+
+def load(path: pathlib.Path | str) -> tuple[dict[str, float], list[Rule]]:
+ """Return the `--fs-*` token table and every non-media rule, in source order."""
+ source = pathlib.Path(path).read_text(encoding="utf-8")
+ body = _strip_media(re.sub(r"/\*.*?\*/", "", source, flags=re.DOTALL))
+ tokens = {
+ name: float(value) for name, value in re.findall(r"--(fs-[a-z0-9-]+):([0-9.]+)px", body)
+ }
+ rules: list[Rule] = []
+ for order, block in enumerate(_BLOCK.finditer(body)):
+ for selector in block.group(1).split(","):
+ cleaned = selector.strip()
+ if cleaned and not cleaned.startswith("@"):
+ rules.append((cleaned, block.group(2), order))
+ return tokens, rules
+
+
+def _compound(text: str) -> Compound:
+ tag: str | None = None
+ classes: set[str] = set()
+ attrs: set[str] = set()
+ ids = 0
+ pseudo = 0
+ name = re.match(r"^([a-zA-Z*][\w-]*)", text)
+ if name:
+ tag = name.group(1)
+ text = text[name.end() :]
+ for part in _SIMPLE.findall(text):
+ if part.startswith("."):
+ classes.add(part[1:])
+ elif part.startswith("#"):
+ ids += 1
+ elif part.startswith("["):
+ attr = re.match(r"\[([\w-]+)", part)
+ if attr:
+ attrs.add(attr.group(1))
+ elif not part.startswith("::"):
+ pseudo += 1
+ return {
+ "tag": None if tag == "*" else tag,
+ "classes": classes,
+ "attrs": attrs,
+ "ids": ids,
+ "weight": len(classes) + len(attrs) + pseudo,
+ }
+
+
+class UnsupportedSelectorError(Exception):
+ """A selector form this resolver cannot express.
+
+ Raised rather than returned, because a `None` here reads to the caller as
+ "did not match" and the rule then leaves the cascade in silence. A resolver
+ that quietly forgets rules produces plausible numbers, which is the failure
+ this whole guard exists to stop.
+ """
+
+
+def _steps(selector: str) -> list[tuple[str, Compound]]:
+ """Selector as [(combinator, compound)], the first combinator ignored."""
+ parts = [p for p in re.split(r"\s*(>|\+|~)\s*|\s+", selector.strip()) if p]
+ if any(part in ("+", "~") for part in parts):
+ raise UnsupportedSelectorError(selector)
+ steps: list[tuple[str, Compound]] = []
+ combinator = " "
+ for part in parts:
+ if part == ">":
+ combinator = ">"
+ continue
+ steps.append((combinator, _compound(part)))
+ combinator = " "
+ return steps
+
+
+def _node_matches(node: Node, compound: Compound) -> bool:
+ tag = compound["tag"]
+ if tag is not None and tag != node["tag"]:
+ return False
+ classes = compound["classes"]
+ attrs = compound["attrs"]
+ assert isinstance(classes, set) and isinstance(attrs, set)
+ node_classes = node["classes"]
+ node_attrs = node["attrs"]
+ assert isinstance(node_classes, set) and isinstance(node_attrs, set)
+ return classes <= node_classes and attrs <= node_attrs
+
+
+def _walk(path: list[Node], ancestors: list[tuple[str, Compound]]) -> bool:
+ """Match the ancestor steps right to left; the leaf is already matched.
+
+ Each entry pairs a compound with the combinator that joins it to the step on
+ its RIGHT, not the one on its left, which is the off-by-one that makes a
+ child combinator behave like a descendant one.
+ """
+ index = len(path) - 2
+ for combinator, compound in reversed(ancestors):
+ if combinator == ">":
+ if index < 0 or not _node_matches(path[index], compound):
+ return False
+ index -= 1
+ continue
+ while index >= 0 and not _node_matches(path[index], compound):
+ index -= 1
+ if index < 0:
+ return False
+ index -= 1
+ return True
+
+
+def matches(path: list[Node], selector: str) -> tuple[int, int, int] | None:
+ """Specificity if `selector` matches the leaf of `path`, else None."""
+ steps = _steps(selector)
+ if not steps or not _node_matches(path[-1], steps[-1][1]):
+ return None
+ ancestors = [(steps[i + 1][0], steps[i][1]) for i in range(len(steps) - 1)]
+ if not _walk(path, ancestors):
+ return None
+ ids = sum(int(step[1]["ids"]) for step in steps) # type: ignore[call-overload]
+ weight = sum(int(step[1]["weight"]) for step in steps) # type: ignore[call-overload]
+ tags = sum(1 for step in steps if step[1]["tag"])
+ return (ids, weight, tags)
+
+
+def declared_size(body: str, tokens: dict[str, float]) -> float | None:
+ token = re.search(r"font-size:\s*var\(--(fs-[a-z0-9-]+)\)", body) or re.search(
+ r"font:[^;]*?var\(--(fs-[a-z0-9-]+)\)", body
+ )
+ if token:
+ return tokens.get(token.group(1))
+ # The lookbehind is load-bearing: without it `font:10.5px/1.5` matched the
+ # optional weight against `10` and the size resolved to 0.5px.
+ literal = re.search(r"font-size:\s*([0-9.]+)px", body) or re.search(
+ r"font:[^;{}]*?(? float | None:
+ """The font size the leaf of `path` renders at, inheriting where undeclared."""
+ for depth in range(len(path), 0, -1):
+ subject = path[:depth]
+ best: tuple[tuple[tuple[int, int, int], int], float] | None = None
+ for selector, body, order in rules:
+ size = declared_size(body, tokens)
+ if size is None:
+ continue
+ specificity = matches(subject, selector)
+ if specificity is None:
+ continue
+ key = (specificity, order)
+ if best is None or key > best[0]:
+ best = (key, size)
+ if best is not None:
+ return best[1]
+ return None
diff --git a/cargento/skills/cargento/tests/test_focus.py b/cargento/skills/cargento/tests/test_focus.py
index c3f18b6ab..8a5a8177f 100644
--- a/cargento/skills/cargento/tests/test_focus.py
+++ b/cargento/skills/cargento/tests/test_focus.py
@@ -1021,7 +1021,7 @@ def test_the_pinned_assembly_is_untouched(self) -> None:
# would fail on the reader rather than on an injected token.
self.assertNotIn(b' None:
self.assertEqual(0, out["inputs"])
self.assertTrue(out["reason"])
+ def test_both_absence_explanations_read_as_sentences_not_header_labels(self) -> None:
+ """DRC-4587 AC-2. `docs/design-next-ui.md` already rules that an absence
+ explanation is a sentence and loses to the sentence floor; both of these
+ were drawn at 10px mono inside an ``'s own ``, where no size
+ change alone can reach them.
+ """
+ out = self.run_fixture(
+ self.ANNOTATED
+ + r"""
+navigateNext({view:"project", project:"cargento", focus:"codex:focus-1", tab:"held-to"});
+await __settle();
+const html = __els.app.innerHTML;
+const headers = [...html.matchAll(/([\s\S]*?)<\/header>/g)].map(m => m[1]).join("|");
+console.log(JSON.stringify({
+ revisionInHeader: headers.includes("next-cockpit-held-revision"),
+ axesInHeader: headers.includes("next-cockpit-landed-axes"),
+ revisionPresent: html.includes('class="next-cockpit-held-revision"'),
+ axesPresent: html.includes('class="next-cockpit-landed-axes"'),
+}));
+"""
+ )
+
+ # Then: both still render, and neither is a child of a `` any
+ # more, so each can carry the sentence tier without dragging its ``
+ # label with it.
+ assert isinstance(out, dict)
+ self.assertTrue(out["revisionPresent"])
+ self.assertTrue(out["axesPresent"])
+ self.assertFalse(out["revisionInHeader"])
+ self.assertFalse(out["axesInHeader"])
+
+ # And the register they are drawn in is the sentence one, not a label
+ # bumped to 15px while keeping mono -- the falsifier AC-2 names.
+ styles = (
+ pathlib.Path(__file__).resolve().parents[1] / "cargento_runtime" / "web" / "styles.css"
+ ).read_text(encoding="utf-8")
+ for cls in ("next-cockpit-held-revision", "next-cockpit-landed-axes"):
+ with self.subTest(rule=cls):
+ rule = next(line for line in styles.split("\n") if line.startswith("." + cls + "{"))
+ self.assertIn("var(--sans)", rule)
+ self.assertIn("var(--fs-sentence)", rule)
+ self.assertNotIn("var(--mono)", rule)
+
+
+def _node(tag: str, *classes: str, attrs: tuple[str, ...] = ()) -> dict[str, object]:
+ return {"tag": tag, "classes": set(classes), "attrs": set(attrs)}
+
+
+# `next-project.js:396` wraps the cockpit in `.next-cockpit-content`. Leaving it
+# out is not cosmetic: a rule qualified by it outranks one that is not, so a
+# fixture without it resolves a DOM the application never builds.
+_CONTENT = [_node("div", "next-cockpit-content")]
+
+
+class AnAbsenceNeverOutranksTheValueItReplacesTest(unittest.TestCase):
+ """DRC-4587. A stated absence must never render larger than the fact it
+ stands in for: "you can tell a label from its answer, a figure from a gap".
+
+ **This asserts only what the pull request claims.** It raises two absence
+ rules, both named by string in the issue's own criterion, and each is listed
+ below with the value it is drawn against. Enumerating them exhaustively is
+ the whole claim; the wider class of sub-floor absences is DRC-4602's, and
+ nothing here asserts anything about it.
+
+ Three earlier versions of this guard passed over the defect they were named
+ for, and the shape of each failure is why this one is written as it is. One
+ compared a value against its absence and could not see both move together.
+ One built a DOM the application never renders. One checked an absence's size
+ without its value, and so asserted an inversion as correct. **An absence is
+ only ever read beside the value it replaces.**
+ """
+
+ # (name, emitter, absence path, the value it is drawn against).
+ RAISED_ABSENCES = (
+ (
+ "two axes, read separately",
+ "next-cockpit.js nextCockpitLanded",
+ [
+ *_CONTENT,
+ _node("section", "next-cockpit-landed"),
+ _node("span", "next-cockpit-landed-axes"),
+ ],
+ [
+ *_CONTENT,
+ _node("section", "next-cockpit-landed"),
+ _node("div", "next-cockpit-landed-cards"),
+ _node("div", "next-cockpit-landed-card"),
+ _node("span", "next-cockpit-landed-value"),
+ ],
+ ),
+ )
+
+ css: str
+ tokens: dict[str, float]
+ rules: list[tuple[str, str, int]]
+
+ @classmethod
+ def setUpClass(cls) -> None:
+ web = pathlib.Path(__file__).resolve().parents[1] / "cargento_runtime" / "web"
+ cls.tokens, cls.rules = css_cascade.load(web / "styles.css")
+ cls.cockpit_js = (web / "next-cockpit.js").read_text(encoding="utf-8")
+
+ cockpit_js: str
+
+ def size(self, path: list[dict[str, object]]) -> float:
+ resolved = css_cascade.resolve(path, self.tokens, self.rules)
+ self.assertIsNotNone(resolved, f"no size resolves for {path}")
+ assert resolved is not None
+ return resolved
+
+ def test_every_absence_this_change_raises_is_read_beside_its_value(self) -> None:
+ for name, emitter, absence_path, value_path in self.RAISED_ABSENCES:
+ with self.subTest(absence=name):
+ absence, value = self.size(absence_path), self.size(value_path)
+ self.assertGreaterEqual(
+ value,
+ absence,
+ f'"{name}" ({emitter}): absence {absence}px against value {value}px, '
+ "so the absence outranks the fact it replaces",
+ )
+
+ def test_the_revision_slot_cannot_invert_because_one_class_carries_both(self) -> None:
+ """The other raised absence has no value to be read beside.
+
+ `nextCockpitHeldTo` fills one span from a chain: a discard stamp, then a
+ revision line, then "No revision saved yet". Value and absence are the
+ same element with the same class, so they resolve identically whatever
+ the tier is. That is a stronger guarantee than a comparison, and it
+ holds only while the chain stays in one assignment, which is what this
+ asserts.
+ """
+ chain = (
+ "nextAnnotationDiscardStamp(annotation) ||\n"
+ ' nextProjectRevisionLine(annotation) || "No revision saved yet"'
+ )
+ self.assertIn(chain, self.cockpit_js)
+ emitted = re.findall(r'class="next-cockpit-held-revision"', self.cockpit_js)
+ self.assertEqual(
+ 1,
+ len(emitted),
+ "the revision slot is emitted more than once, so the chain may have split",
+ )
+
@unittest.skipUnless(shutil.which("node"), "node not available")
class CockpitReadingShapeTest(NextPageJsHarness):
diff --git a/cargento/skills/cargento/tests/test_next_flag.py b/cargento/skills/cargento/tests/test_next_flag.py
index 0430e7c48..3ffc6767b 100644
--- a/cargento/skills/cargento/tests/test_next_flag.py
+++ b/cargento/skills/cargento/tests/test_next_flag.py
@@ -64,9 +64,9 @@ def test_retired_next_query_is_not_a_page_alias(self) -> None:
def test_the_canonical_loader_is_the_released_ui_bundle(self) -> None:
page = frontend_page.load_page()
- self.assertEqual(911_302, len(page))
+ self.assertEqual(913_998, len(page))
self.assertEqual(
- "7c0dbcb341cd1652e60821fde01b3c1080bc58022100bfa9ba25317fefb1c52a",
+ "fabba3bc613af4f76e9c7d327d3a42f4a4e49acf4da73d2e5aa8b3e0f4d19866",
hashlib.sha256(page).hexdigest(),
)
diff --git a/cargento/skills/cargento/tests/test_next_page.py b/cargento/skills/cargento/tests/test_next_page.py
index 6ea2d483d..b1f95fad8 100644
--- a/cargento/skills/cargento/tests/test_next_page.py
+++ b/cargento/skills/cargento/tests/test_next_page.py
@@ -681,7 +681,7 @@ def test_load_page_preserves_its_byte_oracles(self) -> None:
),
"next-cockpit.js": (
198_105,
- "3c3fa0ebd4be8c51ad5b79d8f2919e090022f6832ae094b9bd28322be736853c",
+ "16f67be92f133b9e837d137a96847723861a0128d4d91a0164ad63d9ab2190f6",
),
"next-render.js": (
8_960,
@@ -700,16 +700,16 @@ def test_load_page_preserves_its_byte_oracles(self) -> None:
self.assertEqual(digest, hashlib.sha256(data).hexdigest())
styles = frontend_page.asset_path("styles.css").read_bytes()
- self.assertEqual(108_008, len(styles))
+ self.assertEqual(110_704, len(styles))
self.assertEqual(
- "ea8b79ed9addea2fefb6463c2ec563a891ce4282a1fd2f29a2f02719be8de195",
+ "02aa394649d128bef1a44d728e061d643a0535a6060650022931738add0dc194",
hashlib.sha256(styles).hexdigest(),
)
assembled = frontend_page.load_page()
- self.assertEqual(911_302, len(assembled))
+ self.assertEqual(913_998, len(assembled))
self.assertEqual(
- "7c0dbcb341cd1652e60821fde01b3c1080bc58022100bfa9ba25317fefb1c52a",
+ "fabba3bc613af4f76e9c7d327d3a42f4a4e49acf4da73d2e5aa8b3e0f4d19866",
hashlib.sha256(assembled).hexdigest(),
)
diff --git a/docs/design-next-ui.md b/docs/design-next-ui.md
index e95ec561d..938f978fc 100644
--- a/docs/design-next-ui.md
+++ b/docs/design-next-ui.md
@@ -52,7 +52,7 @@ would defeat its purpose as a way to choose a session. The Scope rail now has a
1280px and above, with separate readable space for harness names and session titles; below 1280px
it becomes a scope switcher. Missing terminal registration, evidence, history
or delegation renders the reason for the missing reading. Source strings use mono; sentences the
-board says use sans at 12.5px or larger. Evidence and More remain named disclosures.
+board says use sans at 15px or larger. Evidence and More remain named disclosures.
The terminal bridge, semantic history and model-assisted goal analysis remain prototypes.
Human context and tripwires stay browser-local and deliver no instruction to an agent. Semantic
@@ -109,12 +109,105 @@ Assign one owner to each region during parallel work. Keep media queries at the
region; a shared responsive block would make every view edit the same tail. Moving a rule between
regions is an ownership change, not incidental cleanup.
-Board sentences have a 12.5px floor (`--fs-xs`). Labels, identifiers, timestamps, rates and
-compact controls retain their smaller design sizes, down to 9px column headers. The prototype
-placed some absence explanations at 10px; those are sentences the board asks a person to read, so
-the sentence floor wins. The stylesheet retains scale tokens and literal sizes. The asset test
-pins the dark palette and checks text inks above 4.5:1 on the ground, panel and inset surfaces; it
-does not enforce all font sizes or spacing between contrast steps.
+Board sentences have a 15px floor (`--fs-sentence`), at weight 500 and line-height 1.55. Labels
+identifiers, timestamps, rates and compact controls sit on one tier, `--fs-label` at 11px. Four
+`:root` steps used to sit below 11px, at 9px, 9.5px, 10px and 10.5px, a 3px band nobody can rank,
+and 9px was the smallest step in the file. **Counts here read "at or below 12px" inclusively**, so
+the band held seven tokens: `--fs-column` 9px, `--fs-label` 9.5px, `--fs-meta` 10px,
+`--fs-meta-detail` 10.5px, `--fs-machine` 11px, `--fs-2xs` 11.5px and `--fs-breadcrumb` 12px. Three
+of the seven had no callers anywhere and were deleted: `--fs-column`, `--fs-meta` and
+`--fs-breadcrumb`. `--fs-meta-detail` had four callers and was folded into `--fs-label`; `--fs-2xs`
+has forty-one and stays. The issue said six because it omitted `--fs-2xs`.
+
+The floor left 12.5px on the strength of an APCA reading that put `--ink`, the brightest colour in
+the palette, below the body-text requirement there. **Those figures are audit-only: no APCA
+implementation, table or fixture exists in this repository, so they cannot be reproduced from the
+tree**, which is the same caveat the milestone carries. What the tree does say is that this was
+never a contrast defect: `--ink` on `--bg` recomputes to 16.36:1 and the asset test asserting more
+than 4.5:1 is green. DRC-4596 adds the guardrail that would close the gap, and lands after the
+change the figures justify. The absence explanations the prototype placed at 10px are sentences, so
+they take the sentence tier, and two of them had to leave an ``'s `` to get there.
+**Raising an absence is not safe on its own.** An absence and the value it replaces are chosen by a
+ternary, so they never co-exist in one render: no sweep of co-existing selectors sees the pair, and
+neither does reading a populated board, because only one branch is ever on screen. Raise the
+absence without its value and the gap reads larger than the fact it stands in for. So only two
+absences are on the sentence tier here, the two named above, and each is safe for a reason that
+does not depend on finding every pair: "two axes, read separately" is drawn beside values already
+on that tier, and "No revision saved yet" shares one class with the revision line it alternates
+with, so both branches resolve identically whatever the tier is.
+`AnAbsenceNeverOutranksTheValueItReplacesTest` asserts exactly those two and nothing wider.
+**Every other absence keeps the size it had**, and DRC-4602 owns the audit that can raise them
+safely, because doing so needs a census of the emitters that this stylesheet cannot supply. The
+stylesheet
+retains scale tokens and literal sizes. The asset test pins the dark palette and checks text inks
+above 4.5:1 on the ground, panel and inset surfaces; it does not enforce all font sizes or spacing
+between contrast steps.
+
+What counts as a sentence is a test, not a judgement call, so a reviewer argues with a list:
+**an element is on the sentence tier when its resolved style is sans with a prose line-height.**
+Resolved, not declared. Both properties come through the cascade, either may be inherited from an
+ancestor, and the two may arrive from different rules, so the element is the unit and a single rule
+is not. Mono is a string a source published, and a label resolves to no prose line-height at all.
+
+That distinction is the whole difficulty. **A census that reads one rule at a time will understate
+the set**, because it cannot see an element whose family, size and line-height are assembled from
+three rules, and it will report clean while such an element still renders below the floor on
+screen. Two of those are named below; how many exist, and what to do about them, is DRC-4602's.
+
+Sixty-five rules on the old `--fs-xs` step carry the whole declaration themselves, plus the two
+absence explanations above, and all sixty-seven resolve to `var(--fs-sentence)`. Forty-nine of them
+also cap at `--measure` (540px, about 72 characters at this tier). The eighteen that do not are
+the ones a cap would clamp wrongly: eight carry `overflow-wrap:anywhere`, eight are layout boxes
+rather than single lines, and
+`.next-cockpit-content` and `.next-cockpit-recovery>div` are the prose containers, where 540px
+would clamp the cards inside them instead of the sentences.
+
+**The floor is not yet universal, and DRC-4602 owns both the audit and the remainder.** Sans text
+still resolves below 15px in two shapes. The first is a rule that declares a smaller size outright,
+on the `--fs-sm`, `--fs-body` and `--fs-summary` steps and on a handful of literals. The second is
+the composed kind the definition above warns about: `.next-operation-fact--unknown strong` takes
+its size and mono family from one rule, a flip back to sans from a second and its line-height from
+a third, and `.next-cockpit-recovery small` takes its size from its own rule and inherits sans and
+the line-height from the cell around it. Neither appears in a census that reads one rule at a time,
+and both render at 12.5px, below anything the first shape reaches.
+
+**The size of that set is not stated here**, because every figure this branch produced for it was
+produced by a per-rule census and is therefore a floor rather than a count. One of them was wrong
+for a second reason worth keeping: a `font:` shorthand pattern that matched the weight instead of
+the size read `font:13px/1.5` as no size at all and dropped two rules silently. A census that
+cannot fail loudly on a shorthand it does not understand will keep producing plausible totals.
+
+**Element counts carry the date and the commit they were taken at, or they do not belong here.**
+The board renders whatever sessions exist, so one shape counted 163 elements and then 183 forty
+minutes later, at `3cc7ef49` and `4fb5ee6d` on 2026-09-17, both reported by the reviewer rather
+than measured here. The stylesheet was not identical across that pair, `4fb5ee6d` changed it by
+thirteen lines, but none of those lines matches `operation-fact`, so the rules resolving the shape
+that was counted were the same for both readings. The narrower claim is the true one. A bare number reads as a
+property of the code when it is a property of an afternoon. The stable unit is the rule, or the
+element shape, never its population.
+
+They were left alone rather than swept, and **how many there are is not stated here in any form**,
+including by comparison with the tier this branch did move. Every figure this branch produced was a
+per-rule floor, and a floor cannot be reported as a count in either direction: asserting a
+magnitude above the highest of them would be the same error pointing the confident way. What can be
+said is the shape. Some of the set is not sentences at all, a textarea and the prototype terminal
+among them, so it needs reading one element at a time. That is the work DRC-4602 carries, and the
+size is its to measure.
+
+Twenty-four declarations carry `.09em`: the whole `.13em` and `.14em` groups, plus eight of the
+eleven in the `.1em` and `.08em` groups. The other three of those eleven keep their own value
+because their content is not uppercase. `.next-cockpit-work-type` prints a fact type such as
+`gate_decision`, `.next-intent-key` a session key, and `.next-cockpit-scope strong` a scope name.
+
+**The uppercase label tier is not uniform, and this change did not make it so.** Six further rules
+print capitals on some other value: `.next-capacity-head span` (`WINDOW`, `USED`),
+`.next-capacity-models small` (`WITHIN THIS WEEKLY BUDGET`) and `.next-capacity-row small` (`USED`,
+`PACE`, `BUDGET ENDS`, `RESETS`) at `.06em`; the course and direction header badges
+(`EXACT DIRECTION`, `EXACT DECISION`) and `.next-scope-cue` (`PROJECT`, `SESSION`, `SCOPE UNKNOWN`)
+at `.07em`; and the recovery memo field labels (`OUTCOME`, `FOCUS`) at `.04em`. They survived
+because the issue inventoried four tracking values and the stylesheet had seven, so sweeping the
+four named groups never reached the other six declarations. Count the values in the tree, not in
+the issue.
Space Grotesk and Space Mono subsets travel inside the assembled page as data URLs. A missing or
malformed font is a canonical asset failure and prevents startup before the socket binds. There is
diff --git a/docs/roadmap-burndown/README.md b/docs/roadmap-burndown/README.md
index 199c7e0d3..7704fd3ef 100644
--- a/docs/roadmap-burndown/README.md
+++ b/docs/roadmap-burndown/README.md
@@ -17,6 +17,7 @@ stages:
- name: triage
gate: true
model: opus
+ concurrency: 6
- name: implementation
worktree: true
model: opus
@@ -223,6 +224,45 @@ the only stage whose product is a change to the roadmap records rather than to t
clauses, each split as **offline** (a test, command, or on-disk state a fresh agent reproduces)
or **interactive** (needs a human or a live drive). The split is declared here, at the gate, so
a plan to build a harness that automates an interactive AC is visible before the harness exists.
+ **The heading is exactly `## Acceptance criteria`, and that is machine-read rather than
+ stylistic.** `status --read {slug} --ac-scan` matches that string literally: `## Acceptance`
+ and `## Acceptance criteria, with verification` both return `Error: no ## Acceptance criteria
+ section in this file`, which leaves the triage gate and every later review gate without their
+ structured acceptance read — and the error appears only when the FO runs the read, not when
+ the criteria are written. Measured 2026-09-17 on DRC-4587 and DRC-4588, which drew the heading
+ from the Linear issue's own `## Acceptance` section and each cost a repair round. The captured
+ original under `## Linear edits made` keeps whatever heading Linear holds: it is a verbatim
+ record, so it is exempt, and renaming it would falsify the restore point.
+ **Two further things must hold, and both fail silently.** The heading alone is not enough: a
+ correct heading over criteria the scanner cannot see returns `{"acs":[]}`, which reads as "this
+ entity has no acceptance criteria" rather than as an error. Worse, a criterion list where only
+ some items parse returns a partial scan that looks like a complete one.
+
+ 1. **The id is hyphenated** — `AC-1`, never `AC1`.
+ 2. **The bold label opens and closes on the same line.** A label whose bold run wraps before
+ its closing marker is skipped, so in a hard-wrapped file a long property silently drops its
+ own criterion.
+
+ Write the bullet form, which satisfies both by construction — the bold closes right after the
+ mark, so the property may wrap freely:
+
+ ```text
+ - **AC-1 — offline:** {end-state property, wraps freely}. **Verified by:** {command, test or
+ on-disk state, and what it returns today}. **Falsified by:** {the change that flips it}.
+ ```
+
+ Measured 2026-09-17 in an isolated throwaway workflow, one file per run: unhyphenated `AC4` is
+ skipped; a bold label wrapped across a newline is skipped; a bullet whose bold closes after the
+ mark parses however far the property wraps. The live cost was three repair rounds across
+ DRC-4587 and DRC-4588, two of them spent on first officer guesses — the heading, then a
+ supposedly-required bullet list — made before anything was measured. The partial-scan state was
+ found by the DRC-4587 ensign, whose paragraph rewrite resolved exactly one of seven criteria:
+ the only one short enough to close its bold on a single line. **Probe shapes in a throwaway
+ workflow, never in the state checkout**, which has concurrent writers.
+
+ Citations resolve from later stage reports that name `AC-N`, so criteria authored here are
+ expected to scan as unevidenced at this gate. That is correct, not a defect — `implementation`
+ and `review` supply the evidence, and the cross-check earns its keep at the review gate.
- At least one acceptance criterion that is a property a user can see, with its own `Verified
by:` clause. When the move is `none`, one sentence in the brief on why no user sees this
change instead, and the gate is told so up front.
@@ -783,6 +823,44 @@ this workflow and override the defaults above wherever the two differ.
happens without a confirmation round. Ask only for choices that are hard to reverse or genuinely
the captain's to make. "I want to get things done."
+### Given 2026-09-17, for the Clean and Cogent UI/UX milestone
+
+Two rulings, in the captain's own words, scoped to this milestone's burndown.
+
+- **Group the PRs by dependency tier, not one per issue.** All twelve issues in the milestone touch
+ `cargento_runtime/web/`, and exactly one in-flight PR may. The `burndown` skill already provides
+ for it — "the second issue rides that PR or waits for it" — so a tier lands as one branch carrying
+ several issues. **One-issue-per-branch above is suspended for this milestone only**, and the cost
+ it was buying is paid another way: the post-merge Linear reconcile still runs **once per issue**,
+ and the PR body carries one `Implements [DRC-####](url)` line per issue it closes. A tier is the
+ merge-risk unit; an issue remains the reconcile unit.
+- **Standing conn for this milestone: "I pre-approve all the triage and merge gates, just automate
+ this entire process and do it."** The first officer renders triage and review gate decisions
+ itself, recorded `agent:first-officer` with the grant quoted, and drives to terminal without
+ stopping. It still stops for anything only the captain may change: approved scope, accepted value,
+ thresholds, tolerance, or acceptance criteria — item 5 of `## Review-finding disposition` is not
+ delegated by this grant. The `pr-merge` merge hook's push approval is covered, because the grant
+ names the merge gates and says to do it; the draft is still presented before the push.
+
+### Given 2026-09-17, in the captain's own words, and they override the defaults above
+
+- **Take the recommended route; do not wait for a decision you have already made.** "If you have a
+ strong recommendation, I want you to take that recommended route. Do not wait for me unless you
+ absolutely need my input." A first officer that recommends one direction and then waits has not
+ saved the captain a decision, it has added one. Act, and report what was done and why. This
+ extends the "do not wait for yes, that's fine" directive from anything reversible to anything the
+ FO holds a clear view on — including acceptance-criterion judgments the FO would otherwise route
+ up under `## Review-finding disposition` item 5, **unless** the call genuinely turns on product
+ intent only the captain holds.
+- **When input IS genuinely needed, pull it out where it cannot be missed.** "It is EXTREMELY
+ difficult for me to see that you need me because you are burying the need for my attention within
+ a lot of prose and content that is not applicable to me." The ask goes at the TOP of the message
+ under its own heading, before any status, with the question, the recommendation, and what saying
+ yes does. Everything not waiting on the captain goes after it, or is left out.
+- **A filed follow-up is not a disposal route.** "Don't just let it dangle." A gap deferred out of
+ one PR is worked inside the same milestone, and the milestone is not complete while it is open.
+ Filing it records the gap; scheduling it is what closes it.
+
## Workflow State
View the workflow overview: