fix(as-S6): the operator's rulings — sentence tagline, no side stripe, matte dot, stripe on ::before

Operator, 2026-09-28: "go with your recommendations".
- Tagline: 'held for review · wipes in {ttl}h unless kept', mono, muted,
  12px, sentence case ("ephemeral" goes, as agreed with booth-dev).
- 'Needs you' rows lose the 3px side stripe; the '? N OPEN' stamp says it.
  The flagged filmstrip frame keeps its bottom stripe.
- The brand dot is matte (glow = live power; a live bench keeps its glow).
- Wipe now and the armed bulk delete carry the hazard stripe on a 3px
  ::before, so the button's own background is honestly what sits under its
  text; the stripe renders as before.

Contract: as_antislop S6. Falsifiers: antislop.toml 49/49 proved (S1-S4, S6);
all 12 tables 329/329 proved on this tree.
This commit is contained in:
vh
2026-09-28 13:30:31 -07:00
parent 1d6821b732
commit ec807fe41b
5 changed files with 116 additions and 16 deletions
+16 -12
View File
@@ -114,15 +114,15 @@
.brand:hover{text-decoration:none;color:var(--text-heading)}
/* Device 3. The service answering is the one thing on this page that is
live power, so the dot glows — statically. SVOS: nothing loops. */
.brand .dot{width:8px;height:8px;border-radius:var(--radius-pill);background:var(--accent);
box-shadow:var(--glow-armed)}
/* as S6: matte — glow means live power, and the brand mark is not live */
.brand .dot{width:8px;height:8px;border-radius:var(--radius-pill);background:var(--accent)}
.brand .name{font-weight:650;font-size:var(--size-h2);letter-spacing:var(--tracking-h)}
/* as S3: on the review and compare stages a phone keeps the header to one
line (brand + theme); the server marks those pages on <body> (<html> carries
data-booth, which the reveal scripts and their tests pin exactly) */
@media (max-width:600px){body.page-stage .topbar .tagline{display:none}}
.tagline{font-family:var(--font-mono);font-size:var(--size-micro);font-weight:500;
letter-spacing:var(--tracking-caps);text-transform:uppercase;color:var(--text-muted)}
/* as S6 (operator, 2026-09-28): a sentence in the muted mono voice, not tracked capitals */
.tagline{font-family:var(--font-mono);font-size:var(--size-caption);font-weight:500;color:var(--text-muted)}
/* THE THEME TOGGLE (r2b D3): a quiet three-way segment at the far end of the
top bar; the pressed choice carries the accent. */
.theme{margin-left:auto;display:inline-flex;border:1px solid var(--border-default);border-radius:var(--radius-md);overflow:hidden}
@@ -223,7 +223,8 @@
background:var(--surface-card);box-shadow:var(--shadow-sm);
transition:border-color var(--dur-2) var(--ease-out)}
.desk-row:hover,.desk-row:focus-within{border-color:var(--border-strong)}
.desk-row.is-needs{box-shadow:inset 3px 0 0 var(--warning),var(--shadow-sm)}
/* as S6: no side stripe; the row's "? N OPEN" stamp says it */
.desk-row.is-needs{box-shadow:var(--shadow-sm)}
.desk-strip{flex:0 0 210px;display:grid;grid-template-columns:repeat(4,1fr);gap:3px;height:58px;border-radius:var(--radius-md);
overflow:hidden;background:var(--surface-sunken)}
.desk-strip img{width:100%;height:100%;object-fit:cover;display:block}
@@ -376,10 +377,13 @@
back, so it alone wears the stripe. The fill waits for the hover. */
.wipe-lg button{padding:10px 14px 8px;border-radius:var(--radius-md);
font-family:var(--font-sans);font-size:var(--size-sm);font-weight:600;letter-spacing:.02em;
background:var(--hazard-stripe) top / 100% 3px no-repeat,transparent;
position:relative;overflow:hidden;background:transparent;
border:1px solid var(--danger);color:var(--danger-text);-webkit-backdrop-filter:none;backdrop-filter:none}
.wipe-lg button:hover{background:var(--hazard-stripe) top / 100% 3px no-repeat,var(--danger);
color:var(--danger-contrast);border-color:var(--danger)}
.wipe-lg button:hover{background:var(--danger);color:var(--danger-contrast);border-color:var(--danger)}
/* as S6: the stripe is its own 3px layer, so the button's background is honestly
what sits under its text (a detector read the 3px band as the whole background) */
.wipe-lg button::before,.board-del-sel:not(:disabled)::before{content:"";position:absolute;
inset:0 0 auto 0;height:3px;background:var(--hazard-stripe);pointer-events:none}
/* ---- marks: the Bureau ------------------------------------------------
Judgment is paperwork. A pick is a form, a note is a memo, a flag is a
@@ -952,10 +956,10 @@
/* Bulk delete: quiet until something is selected. Once rows are ticked it
is ARMED with the hazard stripe — device 2, earned by the selection. */
.board-del-sel{flex:0 0 auto;cursor:pointer;font-family:var(--font-mono);font-size:var(--size-micro);
padding:6px 10px 5px;border-radius:var(--radius-sm);
background:var(--hazard-stripe) top / 100% 3px no-repeat,transparent;
padding:6px 10px 5px;border-radius:var(--radius-sm);position:relative;overflow:hidden;
background:transparent;
border:1px solid var(--danger);color:var(--danger-text);transition:background var(--dur-1) var(--ease-out)}
.board-del-sel:hover:not(:disabled){background:var(--hazard-stripe) top / 100% 3px no-repeat,var(--danger);color:var(--danger-contrast)}
.board-del-sel:hover:not(:disabled){background:var(--danger);color:var(--danger-contrast)}
.board-del-sel:disabled{background:transparent;opacity:.5;cursor:default;border-color:var(--border-strong);color:var(--text-faint)}
.board-row{display:flex;align-items:center;gap:10px;padding:9px 14px;
@@ -1060,7 +1064,7 @@
<body{% block body_attrs %}{% endblock %}>
<header class="topbar">
<a class="brand" href="/"><span class="dot"></span><span class="name">The&nbsp;Booth</span></a>
<span class="tagline">ephemeral media · auto-wipes {{ ttl_hours }}h · kept boards don't</span>
<span class="tagline">held for review · wipes in {{ ttl_hours }}h unless kept</span>
{# r2b D3 — System · Light · Dark. JS-only (with scripts off the page follows
the OS), so it is markup with `hidden` and the script shows it. The top bar
is outside every data-region: no in-place swap replaces it. #}
+13 -4
View File
@@ -118,8 +118,17 @@ The run found that the Booth is sound on desktop and has a set of problems a vie
- **Heading scale.** h3 : body, h2 : h3 and h1 : h2 are each at least 1.18 (h3 `1.2em`, h2 `1.44em`, h1 `1.73em`). Before this, h3 was `1.08em` over its body.
- *Falsifiable:* `test_doc_headings_step_by_size`.
## S5 and S6
## S6 — the operator's rulings (2026-09-28: "go with your recommendations")
These are added as each slice is staged:
- S5, interaction and screen readers;
- S6, the operator's rulings (tagline, needs-you stripe, matte brand dot, the Wipe-now stripe on `::before`).
- **The tagline** is a sentence: `held for review · wipes in {ttl}h unless kept`. It is mono, muted and 12px, in sentence case (no tracked capitals). "Ephemeral" goes, and it stays true to held, kept and counting down, as agreed with booth-dev.
- *Falsifiable:* `test_the_tagline_is_a_sentence`: the rendered text, and no `text-transform:uppercase` on `.tagline`.
- **"Needs you" rows carry no side stripe.** The row's "? N OPEN" stamp says it. The flagged frame's bottom stripe in the filmstrip stays: it marks state on a thumbnail.
- *Falsifiable:* `test_needs_you_rows_carry_no_side_stripe`.
- **The brand dot is matte.** Glow means live power (SVOS), and the brand mark is not live. A live bench's dot keeps its glow.
- *Falsifiable:* `test_the_brand_dot_is_matte`.
- **The hazard stripe sits on a `::before`,** not on the button's background, for Wipe now and the armed bulk delete. The button's own background is honestly transparent (a detector read the 3px background band as the whole background, 1.0:1), and the stripe renders exactly as before.
- *Falsifiable:* `test_the_hazard_stripe_is_a_pseudo_element`, in a real browser: no gradient on the button, a 3px striped `::before`.
## S5
Interaction and screen readers: focus after a swap, a status slot the viewer cannot cover, live-region timing, names, keys, focus rings, hit areas, and the unsaved-draft guard. It rewires the in-place client, so it gets its own contract section and review before any code.
+46
View File
@@ -329,3 +329,49 @@ file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_doc_headings_step_by_size"
old = """.markdown-body h3{font-size:1.2em;font-weight:600}"""
new = """.markdown-body h3{font-size:1.08em;font-weight:600}"""
# ---- S6: the operator's rulings
[[mutation]]
label = "S6 the old tagline copy"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_the_tagline_is_a_sentence"
old = """ <span class="tagline">held for review · wipes in {{ ttl_hours }}h unless kept</span>"""
new = """ <span class="tagline">ephemeral media · auto-wipes {{ ttl_hours }}h · kept boards don't</span>"""
[[mutation]]
label = "S6 the tagline in tracked capitals again"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_the_tagline_is_a_sentence"
old = """ .tagline{font-family:var(--font-mono);font-size:var(--size-caption);font-weight:500;color:var(--text-muted)}"""
new = """ .tagline{font-family:var(--font-mono);font-size:var(--size-caption);font-weight:500;text-transform:uppercase;color:var(--text-muted)}"""
[[mutation]]
label = "S6 the needs-you side stripe back"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_needs_you_rows_carry_no_side_stripe"
old = """ .desk-row.is-needs{box-shadow:var(--shadow-sm)}"""
new = """ .desk-row.is-needs{box-shadow:inset 3px 0 0 var(--warning),var(--shadow-sm)}"""
[[mutation]]
label = "S6 the brand dot glows again"
file = "booth/templates/base.html"
test = "tests/test_antislop.py::test_the_brand_dot_is_matte"
old = """ .brand .dot{width:8px;height:8px;border-radius:var(--radius-pill);background:var(--accent)}"""
new = """ .brand .dot{width:8px;height:8px;border-radius:var(--radius-pill);background:var(--accent);box-shadow:var(--glow-armed)}"""
[[mutation]]
label = "S6 Wipe now paints the stripe on its own background again"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_the_hazard_stripe_is_a_pseudo_element"
old = """ position:relative;overflow:hidden;background:transparent;
border:1px solid var(--danger);color:var(--danger-text);-webkit-backdrop-filter:none;backdrop-filter:none}"""
new = """ position:relative;overflow:hidden;background:var(--hazard-stripe) top / 100% 3px no-repeat,transparent;
border:1px solid var(--danger);color:var(--danger-text);-webkit-backdrop-filter:none;backdrop-filter:none}"""
[[mutation]]
label = "S6 the stripe layer lost (no ::before)"
file = "booth/templates/base.html"
test = "tests/test_antislop_browser.py::test_the_hazard_stripe_is_a_pseudo_element"
old = """ .wipe-lg button::before,.board-del-sel:not(:disabled)::before{content:"";position:absolute;"""
new = """ .wipe-lg button::after,.board-del-sel:not(:disabled)::after{content:"";position:absolute;"""
+23
View File
@@ -283,3 +283,26 @@ def test_the_ask_tag_meets_the_label_floor():
i = js.index('".bk-ask-tag{')
m = re.search(r"font:\d+ (\d+(?:\.\d+)?)px", js[i:i + 400])
assert m and float(m.group(1)) >= 11, m and m.group(0)
# ---- S6: the operator's rulings ------------------------------------------------------
def test_the_tagline_is_a_sentence(client):
c, data = client
html = c.get("/").text
m = re.search(r'<span class="tagline">(.*?)</span>', html, flags=re.S)
assert m and _text(m.group(1)) == "held for review · wipes in 24h unless kept", m and m.group(1)
# the BASE rule (the phone rule `body.page-stage .topbar .tagline` is S3's)
decl = re.search(r"\n\s*\.tagline\{([^}]*)\}", (_T / "base.html").read_text()).group(1)
assert "uppercase" not in decl and _px(decl) >= 12, decl
def test_needs_you_rows_carry_no_side_stripe():
decl = _rule((_T / "base.html").read_text(), ".desk-row.is-needs")
assert "inset" not in decl, decl
def test_the_brand_dot_is_matte():
decl = _rule((_T / "base.html").read_text(), ".brand .dot")
assert "box-shadow" not in decl, decl
+18
View File
@@ -154,3 +154,21 @@ def test_doc_headings_step_by_size(browser, live):
for lo, hi, name in ((body, h3, "h3:body"), (h3, h2, "h2:h3"), (h2, h1, "h1:h2")):
assert hi / lo >= 1.18, f"{name} is {hi / lo:.2f} ({hi}px over {lo}px)"
page.close()
# ---- S6: the hazard stripe on a pseudo-element -----------------------------------
def test_the_hazard_stripe_is_a_pseudo_element(browser, live):
base, root = live
(root / "w").mkdir()
(root / "w" / "a.txt").write_text("x")
page = browser.new_page(viewport={"width": 1280, "height": 800})
page.goto(f"{base}/b/w/", wait_until="load")
got = page.locator(".wipe-lg button").evaluate("""b => {
const own = getComputedStyle(b), pre = getComputedStyle(b, '::before');
return {own: own.backgroundImage, pre: pre.backgroundImage, h: pre.height, content: pre.content};
}""")
assert got["own"] == "none", f"the button paints its own stripe: {got['own'][:80]}"
assert "repeating-linear-gradient" in got["pre"] and got["h"] == "3px" and got["content"] != "none", got
page.close()