TASK-birth-sky-polish-20260928.
- P1: chart page dialog shows only the picture on a dimmed stage, 「保存图片」 below, icon × in the corner.
- P2: BFF returns one server-built `caption` (「1994年5月18日 07:40 · 杭州」); printed under the fact line in all three places; adopted time adds 「 · 校正后」.
- P3: convergence 3 s + words 2.5 s + fade 1 s; settled frame still equals the exported cover.
- P4: redeem paywall no longer opens when the starter home appears; first send refused for credits opens it, later ones open billing; convergence dialog portalled to <body>. BUG-1080 investigating (Chrome repro rules out the overlay covering the convergence).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
Touch-screen message actions get a 44x44 hit once gap and bottom margin open to 20px; fine-pointer geometry stays 27x34. Tablet CSS cuts at 1023 to match sidebarViewportForWidth. Report centre/reader join the 767 mobile cut; TOC keeps 860. Occupies BUG-695/696/697.
The four account panes now share a fixed frame, chart profiles open as list then detail, and membership lives in the homepage dialog instead of a separate page.
Co-authored-by: Cursor <cursoragent@cursor.com>
Chat body, type weights, and radii were still a second scale; membership purchase now uses the shared Button so the filled terracotta action is one implementation.
Co-authored-by: Cursor <cursoragent@cursor.com>
page.tsx still owns the chat main chain, but the first product surfaces now
live in their own modules so later splits can land without editing the 4k-line
Home. Source-lock tests follow the moved tokens; the orphan user-data contract
is aligned and added to the quick gate.
Co-authored-by: Cursor <cursoragent@cursor.com>
The onboarding paywall wrote `dialog-header`, a class no stylesheet
defines, so its close button stacked under the title instead of sitting
beside it. The session-delete confirm buttons had the same problem, and a
`message-entry` wrapper added in August had quietly killed the
`.message + .message` spacing rule in the main chat.
Fixes all three against the patterns already in the codebase, then adds a
contract test so the next undefined class fails instead of shipping: it
scopes itself to our own class families so Tailwind utilities stay out,
strips CSS comments before deciding what counts as defined, and carries
an allowlist of the fifteen deliberate no-op modifiers that must shrink
rather than grow.
Also collapses the paywall's duplicate display heading into the intro
sentence, leaving one heading in the dialog.
BUG-434, BUG-435, BUG-436.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
Homepage bootstrap never escaped the loading shell when a new web image changed chunk hashes. Stamp deploymentId, stop caching chat HTML, and hard-reload failed chunk loads once.
Co-authored-by: Cursor <cursoragent@cursor.com>
The sidebar contract read a selector with indexOf, so it returned whichever
rule appeared earliest in the file. A responsive override added above the base
rule made the gate report a missing min-height that was never removed, which
blocked staging on a change that was correct.
Share one helper that strips comments, parses rule by rule, accepts a whole
group as the query, and returns the declarations of every matching rule. The
union also makes a negative assertion mean no rule may declare the property,
which is what these contracts intend.
The helper was duplicated in two files and had no tests of its own; it now has
regressions for each way it read the wrong block.
Co-authored-by: Cursor <cursoragent@cursor.com>