‹ the toolbox

The top bar — who gets the width

Pinned cards, a running clock, a board, obligations and notices all want the same strip. On a 375px phone they cannot all have it. The strip below is live — try the scenarios. It shipped in v635; the two as-built notes below record where the build corrected the design.

The reframe.「Pinned」is not a category. Pinning is a property of a CARD; a pill is about your relationship to it. A pinned poll you have already answered is not an obligation — it is just something you might want to find again. Once that is separated, five competitors collapse into two zones and one overlay.

1 · Three behaviours, not five widgets

ZoneWhat lives thereBehaviour
状态
state
the play kit · the board · your pad · a running clock · a pinned card you owe nothing onThings you consult. Always present, never urgent, no verb. Yields width first — this zone compresses down to bare icons before anything else gives an inch.
待办
obligation
a collect you have not answered · a reveal only you can fire · your own row to openThings you owe. Never compress, never scroll away. Two visible, then +N. Tapping locates the card and the pill stays — you have found it, you still owe it.
通知
notice
a timed reveal that just fired · a collect that just openedThings that happened. Ephemeral: slides in over the STATE zone only, never over an obligation, retracts on its own or on tap. Occupies no permanent width.
as built · v636状态 is the only zone with a PER-VIEWER member. The pad(the eighth tool)pins here and is visible only to its owner — two people looking at the same room see different strips, which no other entry does. It belongs in 状态 by elimination: nothing is owed and nothing expires, so it is not an obligation; and it is not news, so it cannot borrow 通知. It compresses down the same ladder as everything else. Its chip carries a character COUNT, never a first line — the board's chip previews its content because the board is public, and copying that here would print private notes across the top of a phone somebody else can see. The whole strip is computed server-side per viewer(strip_payload), so the pad's absence from everyone else's is structural rather than a client-side hide.
as built · v662THE PLAY KIT LEADS 状态the kit shelf). It passes every test the zone rule names — a fact about the table, nobody owes it an action, and it survives being reduced to its rulebook glyph — and it goes FIRST within the zone because it is the one entry that explains the others: a board and a clock under 《狼人杀》 are that game's board and that game's clock. Tapping opens its sheet rather than a glance panel, which is the pad's rule for the pad's reason: there is nothing to glance at(the rulebook is the ROOM's reading, never the browser's)and the one thing anybody wants from this chip is the door to「撤下」. It is also the first 状态 member that is not viewer-scoped and not a card — it is the room's own furniture, computed client-side from the same replay projection the board and the clock use.
the priority rule待办 > 状态 > 通知. An obligation is the only thing a person can fail to do, so it is the only thing that may never be squeezed out. State is glanceable and survives being reduced to an icon. A notice is momentary by definition and can borrow space rather than own it.

2 · The strip, live at 375px

聊天内容…

3 · The compression ladder

State items shed detail in a fixed order as obligations take width. They never vanish — a board you cannot reach is worse than a board you cannot read.

LevelBoard · PadClockWhen
0 roomy▤ 记分板 · 12–8◷ 4:32no obligations
1 tight▤ 12–8◷ 4:32one obligation
2 icons◷ 4:32two obligations
3 merged▤◷ — one chip, taps to a chooserthree or more(+N showing)

The clock keeps its digits one level longer than the board keeps its numbers, because a countdown that does not show time is not a clock — it is a decoration. The pad rides the board's column exactly▤ 线索 · 28▤ 28)— its value is the count, so the rungs shed the same two things in the same order.

as built · v635 → corrected v638The rung is chosen by the tape measure alone. The table above is the design; the build found it is a promise about pixels made without measuring any. The same two obligation pills measure 249px in English against ~210 in Chinese, and a reader at --read-scale 1.3 blows through both — so at level 2 in English the state zone was clipped, which is the one thing §3 forbids. The count now picks the STARTING rung and the measure picks the final one: 状态 escalates until it stops being clipped, and the merged chip drops from three pictures to two to one rather than ever being sliced. Two rungs were added below level 3 for exactly that(4 = two pictures, 5 = one), and the chip still taps to the chooser at every one of them. 待办 bends only one way: when two WHOLE pills will not fit it shows fewer, never smaller — a pill is either whole or it is behind the +N.
owner · v648「The top bar can show both chips and cut off some text in the chip, no?」— yes, and the sentence above was wrong. I had hardened「待办 never compresses」into「a pill is whole or it is behind the +N」, and those are not the same rule. What may never be lost is the OBLIGATION — which tool, and what is wanted — and that is the ICON and the VERB, both still fixed. A title is a locator, and half a locator beside a whole verb still points at the card; a folded pill points at nothing and leaves the row half empty. The owner's photograph was one pill, a +1 and ~150px of nothing. So the concession order gains a middle step and the fold becomes the LAST resort: 状态 sheds detail → the title ellipsises(140→118→98→82→68→56px)→ a pill folds. It is v624's own rule restored:「the title is the only elastic part; the tail never is」. Same room, 375px: both pills now, the long title clipped at 82px, 20px of slack; folding starts at 300px, where even floor-width titles genuinely do not fit.
owner · v638The FOLD follows the width too —「two, then +N」was the same mistake as the counted rung, and I only fixed half of it. v635 still folded 待办 at a fixed two. On a wide screen that hid chips behind a +N with 400px of empty row beside them; on a phone in a longer language, or at --read-scale 1.3, it over-folded. So the count is gone from both zones and the fit now executes the priority rule rather than asserting it: 状态 sheds detail first, down to one merged picture, and only once it is at its floor does an obligation fold. §3's table above stays the design — it describes what fits at 375px, which is where it was drawn. Measured across eight widths with nothing clipped at any: 1100/917 three pills, no +N, state at level 0 · 760 level 1 · 640 level 2 · 520 three pills + merged ×3 · 430/375 two pills + +1 · 330 one pill + +2. The row re-fits on a ResizeObserver, so the list pane opening beside it or a rotation re-fits it with no reload.
owner · v638A chip must appear and leave on time —「sometimes I have to refresh」means it does not. The cause was T12's pad: its writer repainted the strip it already held instead of re-reading it, so the chip arrived one reload late. Every writer of something the strip SHOWS must re-read the strip, never merely repaint it. The pad has no SSE by design(a fan-out has no viewer and would hand a private pad to the room), so its POST answer now carries the strip. Three more leaks closed with it: a sequence guard(a tap's refresh racing the broadcast it caused could answer out of order and put back a pill the tap had just discharged), a re-read on fronting(every other path is an event, and a slept phone or a dropped stream has no way to know it missed one — being fronted is when a person looks at the bar), and a 通知 that no longer survives a room switch. And the pad's chip is a PICTURE ONLY — a new icon flag: its value is a character count nobody acts on and its title is the owner's own private wording, which should not be printed across the top of a shared phone. The words stay in the accessible name.

4 · The edges worth deciding now

a pinned card you oweIt appears once, in 待办 — not twice. Pinning made it findable; the obligation is why you are being told. Two pills for one card is the failure this whole reframe exists to prevent.
the strip is per-viewerTwo people in the same room see different strips, so it cannot ride the viewer-less SSE broadcast — same constraint as _shows_content. Get it wrong and the strip leaks who has not answered in exactly the rounds built to hide that.
empty is a stateWith nothing owed and nothing pinned the bar is gone, not empty — no reserved band, no placeholder. The chat starts at the top. A strip that is always there stops being read.
decided before building · 2026-07-27Four owner calls, all taken as recommended.A notice is ephemeral and client-side — born from the live SSE event, gone on reload; a notice you have already seen should not return, and the durable copy is the notifications rail's job. So the server payload has no notice field at all. ② This room only — cross-room obligations stay the rail's job, and no new cross-room read path was opened. ③ The +N opens a LIST SHEET(the who-voted shell, reused)rather than expanding in place, because expanding would push the strip past the budget the ladder was designed around; the two visible pills never move. ④ One clock. The first reading of the ladder's ◷ was「the soonest deadline in the room」and the build rejected it: that lets a poll closing in 40s hide a wall clock somebody deliberately set, and a card's own deadline already has two closer homes — the card(ruling ⑨)and, when it is pinned, the chip just above it. The strip's ◷ is the room's wall clock, always. A card's deadline is mirrored here only as its own chip's fused value, or as the soon ring on its obligation pill.
what the build found · ruling ⑫The closed-card riding note told a persona「nothing is wanted of you」on a card the panel itself had to open. That is the one sentence a host would obey, and it had been there since task A. The note now names the act in the pill's own verb(「it is YOURS TO REVEAL — say <reveal>…</reveal>」), once(stamped owed_told on the gate, so a restart does not restart the nagging), and only for a door the grammar actually has: a timer's「open now」and a per-row reveal have no persona door, so the note stays silent about them — the pill's own law(A PILL EXISTS IFF A BUTTON DOES)read from the other side. The same law cost a second fix: a sealed roll's per-row reveal(v634's 各自点开)had a server record and no button on the card, so the dial was settable and unusable — and the strip would have raised an obligation pointing at nothing. The button is built.
owner · v649An obligation wears its TOOL’s own colour, not coral(the study: how a pill says「yours」, option H). The complaint was that the coral pill「doesn’t look too good」, and the diagnosis is that coral is the app’s press-me colour — the send button,「Call the panel」, every link — so two coral-filled capsules in a 36px strip read as the most pressable things on screen. That is backwards for an object whose whole law is a pill is a LOCATOR, never a button. It also spent the accent twice(a tinted surface AND coral words on it, the lowest-contrast pairing in the palette)and in dark the tint landed as a muddy brown rather than a signal. Now --accent comes from each tool’s own hue, so the pill and the card it points at are one object, and coral means one thing again. ⚠ The +N is ink and weight now, deliberately: it names no tool, and one remaining coral blob would have been the loudest thing on the strip. ⚠ Recorded: the wheel’s own accent IS coral, so a spin obligation still looks much as it did — correct, not a miss.
owner · v660 · the strip belongs to ONE room「Sometimes when I start a new chat, or switch to another chat, the tool top bar will be carried out to the new chat.」 Every tool projection — the strip, the gates, the dice, the board, the pad, the clock, my card — was reconciled only from a replay(if (rep) … in paintRoom). Correct for a room that arrives with one, and silent for every path that does not: a NEW chat has no replay until /api/rooms answers and the kickoff runs; an UNCACHED switch has none for the whole skeleton window; a switch whose fetch errors never gets one. ⚠ The shape of the bug is the part worth keeping: clearing state as a side effect of new data ARRIVING only covers the paths where data arrives. resetRoomTools() makes it an explicit act at the moment the room identity changes — switchTo · beginNewChat · abortNewChat — and it runs in the same task as the paint that follows, so the cached-snapshot path never flashes an empty bar between the two.
what this buysThe fight was never over width — it was over five things claiming the same rank. With obligation as the only protected class, the phone case resolves itself: two pills plus a compressed board and clock fit 375px with room to spare, and the only thing that ever disappears is detail nobody was acting on.
The top bar · designed 2026-07-27 from the owner's question about pills fighting for the bar · extends ruling ⑭(a pill is a tray icon, not an info widget)with the per-viewer obligation test. Architecture: toolbox.html · the lifecycle: tool-lifecycle.html · the sheets: setting-sheet.html. BUILT v635, corrected v638 — the set is computed per viewer on the server(strip_payload, no broadcast channel), the three zones are #tzState / #tzAct / #tzNote, and all six scenarios above reproduce in the real app at 375px, paint-verified in both themes at 37px with nothing clipped — and v638 re-verified the fit across eight widths from 330px to 1100px. The UI reference: the Style Guide.