/** * The always-present range bar above the rectification conversation. * * Read-only by design: it answers "how wide is the range now", never "which * minute do I take". Adoption stays on the delivery card, so this component * takes no callbacks and renders no controls. * * It carries the axis and a one-line readout: range, width, working minute and * the answered-question count. The working minute is here because on a narrow * viewport the board is an overlay and closed by default, which used to leave * the one number the reader tracks off screen entirely (D10, 2026-09-16). * * Still cut, and staying cut: a legend, the stage name, and the range-change * sentence — that sentence is already written into the persisted assistant * message (BUG-588 requires it there, so repeating it would put one sentence on * screen twice). * * Every figure is echoed from the server projection. The working minute comes * from the same `workingRectificationTime(result)` call the board's title-row * clock makes, so the two surfaces cannot disagree; the count is the server's * `answered_probe_count`. Nothing is computed here. * * The bar lives outside the scroll container as its own grid row, so its height * must never change — see `.rectification-timeline` in globals.css. The readout * stays one line for that reason: it grew wider, never taller. An unready case * renders the same-height skeleton rather than nothing. * * One line has a width budget, and on a 375px phone it is spent by the fourth * item (BUG-918). Every span below is always rendered; globals.css decides how * many of them a narrow viewport shows, and it hides `__dated` there. The axis * `aria-label` below is assembled from all five labels regardless, so nothing a * screen reader gets depends on the viewport. Adding a sixth item means * re-measuring that budget, not appending a span. */ "use client"; import type { RectificationTimelineView } from "@/lib/rectification-timeline-scale"; export function RectificationTimeline({ view }: { view: RectificationTimelineView | null }) { if (!view) { return
; } return ({view.rangeLabel} {view.widthLabel} {view.workingLabel ? ( {view.workingLabel} ) : null} {view.answeredLabel ? ( {view.answeredLabel} ) : null} {/* Hidden below 768px by globals.css, never omitted here: the label is part of the accessible axis description either way. */} {view.datedEventLabel ? ( {view.datedEventLabel} ) : null}