One card, seven quotes ranked Q1 to Q7 from four participants (p1 to p4), drawn as the SPA draws it: the shipped cap of 4 quotes shown (VISIBLE_QUOTES = 4 in SignalsPage.tsx), not the 3 of the verbal example. The quotes, headline and participant codes are sample data. Frames B to F are proposed behaviour, not shipped.
Rank is the calculation’s order. The card does not draw in rank order: like the SPA, it draws whatever set it chose in participant, then time, order (so Q2 above Q4 above Q1 in frame A). Each frame’s grey bar names which ranks are drawn.
| rank | participant | timecode | intensity (of 3) |
|---|---|---|---|
| Q1 | p3 | [04:12] | 3 |
| Q2 | p1 | [02:31] | 2 |
| Q3 | p3 | [11:47] | 2 |
| Q4 | p2 | [06:05] | 3 |
| Q5 | p4 | [03:20] | 1 |
| Q6 | p1 | [14:02] | 2 |
| Q7 | p2 | [09:44] | 1 |
Every frame is drawn at one pane width: 780px, the Mac app’s default 1000 x 700 window (BristlenoseApp.swift) less its 220px sidebar (DetailFloor.swift), so the card is 668px; the narrow frames use a 520px pane. Real values were measured with getBoundingClientRect in headless WebKit and Chromium against the e2e smoke fixture, served by bristlenose serve with the app’s platform and palette settings (a copy of the fixture, port 8151, so the tree was not written to), the window at 780 and 520 wide, in embedded mode. The mockup was measured with the same script. Agreement to the pixel, both engines.
| measurement (px unless stated) | real | mockup | |
|---|---|---|---|
| pane width (default window) | 780 | 780 | agree |
| card left gutter | 56 | 56 | agree |
| card right gutter | 56 | 56 | agree |
| card width | 668 | 668 | agree |
| card padding + border | 48px + 1px | 48px + 1px | agree |
| headline top, from card top | 49 | 49 | agree |
| headline line height | 22.5 | 22.5 | agree |
| hero chip height | 22.8 | 22.8 | agree |
| hero chip width | 127.5 WK / 126.3 CR | 127.5 WK / 126.3 CR | agree |
| hero right inset, from card right | 49 | 49 | agree |
| quote box inset / width / padding | 49 / 570 / 12px 0px | 49 / 570 / 12px 0px | agree |
| quote font / line height / timecode width | 15px / 21px / 56.3 WK / 15px / 21px / 54.8 CR | 15px / 21px / 56.3 WK / 15px / 21px / 54.8 CR | agree |
| footer margin-top / footer height / link height | 24px / 20 / 16 | 24px / 20 / 16 | agree |
| contribution cell, default pane | 24.8 x 20, 10px | 24.8 x 20, 10px | agree |
| cell right inset, from card right | 49 | 49 | agree |
| narrow pane 520: card width | 408 | 408 | agree |
| narrow pane 520: cell (pip) w x h, font | 10.4 x 16, 0px | 10.4 x 16, 0px | agree |
| page header, 780px pane: heading size / line height / row height | 26px / 32.00 / 39.4 | 26px / 32.00 / 39.4 | agree |
| page header, 780px pane: banner font / line height / height | 13px / 16.00 / 16 | 13px / 16.00 / 16 | agree |
| page header, 780px pane: rule to banner, no counter | 16 | 16 | agree |
| page header, 780px pane: banner bottom to first section, no counter | 24 | 24 | agree |
| page header, 780px pane: rule to banner, counter on the line | 16 | 18.3 WK / 18.4 CR | DIFFER |
| page header, 780px pane: banner bottom to first section, counter on the line | 24 | 26.4 WK / 25.4 CR | DIFFER |
| page header, 780px pane: counter right edge, inset from pane edge | 56 | 56 | agree |
| page header, 520px pane: heading size / line height / row height | 26px / 32.00 / 39.4 | 26px / 32.00 / 39.4 | agree |
| page header, 520px pane: banner font / line height / height | 13px / 16.00 / 16 | 13px / 16.00 / 16 | agree |
| page header, 520px pane: rule to banner, counter on the line | 16 | 18.4 | DIFFER |
| page header, 520px pane: banner bottom to first section, counter on the line | 24 | 26.3 WK / 25.4 CR | DIFFER |
| page header, 520px pane: counter right edge, inset from pane edge | 56 | 56 | agree |
Not measured, derived from the CSS: quote row heights and the header’s total height (the fixture’s text differs from the sample text); the 12px gap between quotes that are not a same-speaker run (--bn-signal-card-evidence-gap; the fixture’s two quotes are a run, which fuses them to -2.4px); the Mac window and sidebar sizes (Swift source); and where the Screens lens puts its counter, because the fixture has no screens (screens.css: a right-ranged slot at the end of the line under the heading, its list opening below and right-aligned). The 500px-window rule that stacks the card’s top keys on the window, which this page is not.
People expected to see the full price earlier, and read the final total as a surprise.
[02:31]I’d have liked to know the total before I put my address in. p1
[06:05]It was only after I’d typed in my card details that the delivery fee appeared.
[04:12]I got all the way to the payment page and then there was a delivery charge I hadn’t seen anywhere. p3
[11:47]It said free returns, but I couldn’t tell what the postage would be until the very end. p3
Seven quotes, ranked Q1 to Q7: the card shows the first four and puts the other three behind the link, which counts every quote the card holds. Nothing has been hidden, so this is today’s behaviour.
People expected to see the full price earlier, and read the final total as a surprise.
[02:31]I’d have liked to know the total before I put my address in. p1
[06:05]It was only after I’d typed in my card details that the delivery fee appeared.
[04:12]I got all the way to the payment page and then there was a delivery charge I hadn’t seen anywhere. p3
[11:47]It said free returns, but I couldn’t tell what the postage would be until the very end. p3
[03:20]I scrolled back up to check whether I’d missed a price somewhere. p4
Both hidden quotes were behind the link, so the four shown do not move; the link now counts what the card still holds (5) and opening it reveals one further quote, Q5, where it used to reveal three.
People expected to see the full price earlier, and read the final total as a surprise.
[02:31]I’d have liked to know the total before I put my address in. p1
[14:02]Normally I’d compare the total with another shop before paying, but there wasn’t a total yet. p1
[06:05]It was only after I’d typed in my card details that the delivery fee appeared.
[03:20]I scrolled back up to check whether I’d missed a price somewhere. p4
[09:44]I wasn’t sure whether the price at the bottom included the delivery. p2
Hiding two shown quotes refills the shown set from the remaining ranked list (Q2, Q4, Q5, Q6), so the card still opens on four quotes and the link reads 5; Q7 is the one further quote. The signal, strength and headline above are the calculation’s and do not change.
People expected to see the full price earlier, and read the final total as a surprise.
[02:31]I’d have liked to know the total before I put my address in. p1
[06:05]It was only after I’d typed in my card details that the delivery fee appeared.
[04:12]I got all the way to the payment page and then there was a delivery charge I hadn’t seen anywhere. p3
[03:20]I scrolled back up to check whether I’d missed a price somewhere. p4
Unhiding Q1 returns it to its ranked place (first), which pushes Q6 back behind the link and the count to 6; Q3 stays hidden, so the shown set is Q1, Q2, Q4, Q5.
Based on 70 accepted tags
People expected to see the full price earlier, and read the final total as a surprise.
[02:31]I’d have liked to know the total before I put my address in. p1
[06:05]It was only after I’d typed in my card details that the delivery fee appeared.
[04:12]I got all the way to the payment page and then there was a delivery charge I hadn’t seen anywhere. p3
[11:47]It said free returns, but I couldn’t tell what the postage would be until the very end. p3
[03:20]I scrolled back up to check whether I’d missed a price somewhere. p4
Based on 70 accepted tags
People expected to see the full price earlier, and read the final total as a surprise.
[02:31]I’d have liked to know the total before I put my address in. p1
[06:05]It was only after I’d typed in my card details that the delivery fee appeared.
[04:12]I got all the way to the payment page and then there was a delivery charge I hadn’t seen anywhere. p3
[11:47]It said free returns, but I couldn’t tell what the postage would be until the very end. p3
[03:20]I scrolled back up to check whether I’d missed a price somewhere. p4
Based on 70 accepted tags
People expected to see the full price earlier, and read the final total as a surprise.
[02:31]I’d have liked to know the total before I put my address in. p1
[06:05]It was only after I’d typed in my card details that the delivery fee appeared.
[04:12]I got all the way to the payment page and then there was a delivery charge I hadn’t seen anywhere. p3
[11:47]It said free returns, but I couldn’t tell what the postage would be until the very end. p3
[03:20]I scrolled back up to check whether I’d missed a price somewhere. p4
Based on 70 accepted tags
People expected to see the full price earlier, and read the final total as a surprise.
[02:31]I’d have liked to know the total before I put my address in. p1
[06:05]It was only after I’d typed in my card details that the delivery fee appeared.
[04:12]I got all the way to the payment page and then there was a delivery charge I hadn’t seen anywhere. p3
[11:47]It said free returns, but I couldn’t tell what the postage would be until the very end. p3
[03:20]I scrolled back up to check whether I’d missed a price somewhere. p4
Placement decided: page level, on the “Based on N accepted tags” line directly under the Signals heading, ranged right, as the Screens lens ranges its counter (a right-ranged slot, its list opening below it). It is the shipped Counter button, as on the Quotes and Screens lenses, not a blue text link, so the only link on a card stays Show all / Show fewer. Scope decided: the hidden quotes of the section or theme the page shows, not the project, read live from the database, not from the snapshot. A quote hidden before the snapshot was built is listed even though it never reached a card (the third line here), so it can be unhidden from here but returns to its card only after the next rebuild. That is the case the build must handle: rebuild or refetch, or accept a quote that reappears with no card to show it. An unhide from here is also a quote moving while the researcher watches, so the Quotes lens’s fly-down and .bn-handover question applies. Measured in WebKit: the counter ends on the heading’s keyline edge, 56px in from the pane edge, and the open list (439px wide, 112px tall) is right-aligned to that same edge, opens at the bottom of the line and overlays the first card by 60px. In the 780px pane it starts 285px inside the pane; in the 520px pane it starts 25px inside it and runs 31px past the cards’ left edge, within the pane. Putting a padded button on the line costs rhythm: the banner moves down 2.3px and the first section starts 4.8px lower than the real page, which has no button there. With nothing hidden the counter draws nothing and the page is exactly the real one (the table below); to keep that, the banner’s own 12px bottom margin is moved from the paragraph to the line. Not measured against a real Screens counter, which the fixture lacks: its placement is derived from screens.css.
A participant’s cell comes only from the quotes the card still holds: p3’s two quotes were Q1 and Q3, so in C p3 drops to the outlined, absent cell; the other three stay filled. Levels are relative to the card, so p4 moves from 1 to 2 in C without a word of p4’s changing, because the bottom of the range left with p3.
People expected to see the full price earlier, and read the final total as a surprise.
[02:31]I’d have liked to know the total before I put my address in. p1
[04:12]I got all the way to the payment page and then there was a delivery charge I hadn’t seen anywhere. p3
[11:47]It said free returns, but I couldn’t tell what the postage would be until the very end. p3
[03:20]I scrolled back up to check whether I’d missed a price somewhere. p4
The assertion’s one source is hidden, so the assertion list goes and the card shows the claim alone, with no second paragraph. Today’s code, when assertions fall to zero, would take its other branch and draw the elaboration’s second paragraph (the rest that renderLead returns); the plan keeps the lead-only branch whenever the stored card had assertions, so the card does not grow text the researcher never saw. The shown set refills to Q1, Q2, Q3, Q5.
People could not find a way back to change the address once a shipping option was chosen.
People expected to see the full price earlier, and read the final total as a surprise.
[03:20]I scrolled back up to check whether I’d missed a price somewhere. p4
People could not find a way back to change the address once a shipping option was chosen.
Hiding the last remaining quote removes the card from the column: the neighbours close up and nothing marks where it was. Decided by the owner on 30 Oct: a card with no counted quote left is dropped. The all-hidden design beyond dropping (anything that would say so, or let the researcher bring the card back) stays parked.
People expected to see the full price earlier, and read the final total as a surprise.
[02:31]I’d have liked to know the total before I put my address in. p1
[06:05]It was only after I’d typed in my card details that the delivery fee appeared.
[04:12]I got all the way to the payment page and then there was a delivery charge I hadn’t seen anywhere. p3
[11:47]It said free returns, but I couldn’t tell what the postage would be until the very end. p3
Below 35rem of card content (a card under 658px, a pane under 770px, ten pixels short of the default window) the shipped container query turns the contribution cells into bare pips, one per participant, with no code. The same quotes, the same footer and the same link; only the cells change. The 500px viewport rule that stacks the card’s top is not drawn: it keys on the window, and this page is wider.
Commentary, not product.
selectQuotes also holds one of the four slots for a dissenting voice; whether it re-runs on the remaining quotes is a build question this mockup does not decide.| locale key (common.json) | en value |
|---|---|
quotes.hiddenCount_other | {{count}} hidden quotes |
quotes.unhide | Unhide |
quotes.unhideAll | Unhide all |
quotes.unhideHeader | Unhide: |
signals.accepted | {{count}} accepted |
signals.basedOnTags_other | Based on {{parts}} tags |
signals.evidenceContribution | Evidence contribution by participant |
signals.evidenceContributionLevel | {{participant}}, relative evidence contribution level {{level}} of 3 |
signals.focusEvidenceAssertion | Participant {{participant}}; focus evidence assertion using this quote |
signals.heading | Signals |
signals.intensityTooltip | Intensity {{value}}/3 |
signals.noEvidenceContribution | {{participant}}, no evidence contribution |
signals.oneQuote | 1 quote |
signals.showAllQuotes | Show all {{count}} quotes → |
signals.showEvidenceSource | Participant {{participant}}; show source quote supporting this evidence |
| NEW · owner-written, no key yet | Show fewer |
signals.hide · replaced by the row above | Hide (collides with the researcher’s Hide; a 21-locale change when it goes) |
One owner-written string: Show fewer, NEW, replacing signals.hide on the expanded toggle in frames B, C and D. Everything else is a shipped key. sentiment.frustration comes from enums.json. The quote text, headline, lead sentence and assertion are sample data, as they are generated per project.
bristlenose/theme/tokens.css — whole file, lines 1–444bristlenose/theme/tokens-typography.css — whole file, lines 1–100bristlenose/theme/tokens-desktop.css — whole file, lines 1–70bristlenose/theme/colors/palette-default.css — whole file, lines 1–176bristlenose/theme/colors/palette-edo.css — whole file, lines 1–189bristlenose/theme/atoms/badge.css — whole file, lines 1–337bristlenose/theme/atoms/icon.css — whole file, lines 1–36bristlenose/theme/atoms/interactive.css — whole file, lines 1–193bristlenose/theme/atoms/timecode.css — whole file, lines 1–97bristlenose/theme/atoms/section-heading.css — whole file, lines 1–97bristlenose/theme/atoms/lead-paragraph.css — whole file, lines 1–96bristlenose/theme/molecules/person-badge.css — whole file, lines 1–214bristlenose/theme/molecules/hidden-quotes.css — whole file, lines 1–157bristlenose/theme/organisms/blockquote.css — whole file, lines 1–129bristlenose/theme/organisms/signals.css — whole file, lines 1–1050bristlenose/theme/organisms/inspector.css — rule .signals-layout, lines 68–74bristlenose/theme/organisms/inspector.css — rule .signals-center, lines 76–86bristlenose/theme/templates/report.css — lines 1–30bristlenose/theme/templates/report.css — rule h1, h2, h3, .description, lines 68–70bristlenose/theme/templates/report.css — rule h1, lines 72–77bristlenose/theme/templates/report.css — rule .description, lines 208–212bristlenose/theme/templates/report.css — rule .signals-center > .section-heading:first-of-type, lines 106–109