diff --git a/README.md b/README.md index 9c39d2f8..dc73cf73 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Rule files hold rules; the reasoning behind them lives in `design/`. Where two d | Document | What it is | Read it when | |---|---|---| -| [`SPEC.md`](SPEC.md) | The mechanism, v0.146 — tables and numbered rules, each pointing at its reasons as `→ why: R-nnn`. The single source of truth. | First, to understand what the engine does. | +| [`SPEC.md`](SPEC.md) | The mechanism, v0.147 — tables and numbered rules, each pointing at its reasons as `→ why: R-nnn`. The single source of truth. | First, to understand what the engine does. | | [`SURFACE.md`](SURFACE.md) | What the surface tells a member and what a control does: the event matrix, the marks, the wallets, the founding order, the card kinds. Asserted against the page's own tables by `npm run spec-check`. | Second, to understand what a member sees. | | [`CLAUDE.md`](CLAUDE.md) | The project's operative reference: the vocabulary, the glossary of every named part, and the post-mortems that bite. | Before contributing. | | [`design/STYLE.md`](design/STYLE.md) | The surface-copy checklist every string a member can read has to pass. | Before touching `copy.js`. | diff --git a/SPEC.md b/SPEC.md index 7a85e3de..70730dd7 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,4 +1,4 @@ -# Group Drafting Engine — Specification v0.146 +# Group Drafting Engine — Specification v0.147 ### Working name deferred (direction: "draft") A compiler for group agreement. Input: a starting text, a roster, a constitution file. Output: the most-agreed text, plus a record of every disagreement, ranked and mapped. Institutional acts — provenance, adoption, ratification — belong to the convening context. The tool measures agreement; it does not confer legitimacy. @@ -309,7 +309,7 @@ The founding is optional in full: a convenor may set everything and delegate not | room proposes, convenor answers | — | held | | delegated | — | — | -How each state reads to a member is copy rather than mechanism: the power clause is `design/STYLE.md` T8's, in `design/copy.js`, and a delegated setting carries no governance sentence at all — the preamble's default applies. +How each state reads to a member is copy rather than mechanism: the power clause is `design/STYLE.md` T8's, in `design/copy.js`, and a delegated setting carries no governance sentence at all — the Founder's own ✒️ and 🛡️ clauses name what the Founder holds (SURFACE §8, F16). 1. **Birth.** Every setting is born convenor-held, both powers, question shut, value unset. → why: R-001 2. **Delegation is an act, one-way, the convenor's own, never a motion.** Before the start, on a delegable setting, it opens the founding question; otherwise — and always after the start — it is a **hand-over**: the value stands, only the holder changes. Each setting's hand-over moment is in §9.7.1 (*hand-over from*). → why: R-006, R-027 diff --git a/SURFACE.md b/SURFACE.md index bf5d3445..5f2d68cb 100644 --- a/SURFACE.md +++ b/SURFACE.md @@ -54,11 +54,11 @@ Vocabulary: **audience** — nobody / the actor / one member / every member owed | E1 | A founding question opens (delegated) | every arrived member, founder if a member | rail ask entry; task paragraph under the watching clause | an answer | ✓ | entry leaves; 🍾 carries the count (Q1176); revisable until resolved | chamber applications lapse removal ending quorum authorship judgments rate | | E2 | A founding question resolves | everyone who answered | clause (Q1176) | nothing (pre-start: no OK owed) | — | clause states the rule | — | | E3 | A setting is set pre-start | nobody | clause fades in | nothing | — | clause | title slug text hat myname mypic admission | -| E4 | The document begins 🍾 | every member | 💡 ⚖️ gate news cards, the Founded line; 🏛️ for anybody not yet handed a voice (E8) | **Accept ✏️** · **Accept ⚖️** each (Q1501) | the Accept | `ACK_KEYS` per seat; grants staged behind the constitutional OKs | begin canpropose canjudge | +| E4 | The document begins 🍾 | every member | 💡 ⚖️ gate news cards, 🍾's clause in the Schedule; 🏛️ for anybody not yet handed a voice (E8) | **Accept ✏️** · **Accept ⚖️** each (Q1501) | the Accept | `ACK_KEYS` per seat; grants staged behind the constitutional OKs | begin canpropose canjudge | | E5 | A constitutional setting set or changed post-start (pen or carried) | every member who had no say **and arrived when it was set**, lapsed included; a later joiner reads it as the document | news entry pinned ✔; clause change line (was / now, who, why); the card's body is the rule block, then *Set to · ‹value› · Decided by the members.* (or *Set by the founder when the document was made.*), then the change sentence and its reason — the one place that line is drawn (Q1232, 2026-09-07) | OK | OK | the record behind the rule's tab and the record's Amendments — no line on the clause (1564.5 (b), 2026-09-27) | — | | E6 | An ordinary setting first set | nobody | clause | nothing | — | clause | — | | E7 | An ordinary setting changed | as E5 | as E5; no mail | OK | OK | as E5 | — | -| E8 | A power arrives | the holder | news entry, wallet flight (the ✏️ storm from 💡's OK, per member — the founder's ✏️ arrives with no press and no storm, Y27); 🏛️ arrives when you first become a member — the founder-member at the save, an invitee at arrival, an admitted applicant at admission — as a tab on the Founded line beside ✒️ 🛡️, and **no blind question is served until its OK** (Q1365, Ed 2026-09-15, reversing Q605 and F20), **nor 💡 ✏️ or ⚖️ to a member** (Q1540, Ed 2026-09-24), so nobody proposes or votes before accepting 🏛️; **until ⚖️'s OK the charter holds no race for you** — no entry, tab, mark or wire, in any state — and the OK brings every one of them in at once (C9, Q1328) | **Accept ‹glyph›** — 🏛️'s **Activate 🏛️** (Q1501, Q1502) | the Accept | per seat; re-asked on each not-held → held | grant-pen grant-shield grant-voice canpropose | +| E8 | A power arrives | the holder | news entry, wallet flight (the ✏️ storm from 💡's OK, per member — the founder's ✏️ arrives with no press and no storm, Y27); 🏛️ arrives when you first become a member — the founder-member at its place in the founding walk, after 👥 (#223, G6), an invitee at arrival, an admitted applicant at admission — as a tab on the 🏛️ clause among the Proposals (G3), and **no blind question is served until its OK** (Q1365, Ed 2026-09-15, reversing Q605 and F20), **nor 💡 ✏️ or ⚖️ to a member** (Q1540, Ed 2026-09-24), so nobody proposes or votes before accepting 🏛️; **until ⚖️'s OK the charter holds no race for you** — no entry, tab, mark or wire, in any state — and the OK brings every one of them in at once (C9, Q1328) | **Accept ‹glyph›** — 🏛️'s **Activate 🏛️** (Q1501, Q1502) | the Accept | per seat; re-asked on each not-held → held | grant-pen grant-shield grant-voice canpropose | | E9 | A power is laid down | every member but the actor (Q918, Ed 2026-08-29) | the power card; **one news entry owed an OK, stating its whole batch — and none at 🍾** (C8a; Q571, Q1013, Q1184); clause deviation vanishes | OK | on commit / on OK | clause; record | — | | E10 | A constitutional motion is put — an invitation or removal too, where 🪪 / 🥾 stands at *assembly* (entry 94); and put again, to everyone but its mover, when a carry or the Founder's ✒️ moves the setting under it (Q1348, SPEC §9.6 | every active member | **the motion's own card** — a tab in the setting's pile behind the rule's own tab, and its own rail ask (M18, Q1367, Ed 2026-09-15: *other proposals should not be shown on the decision card but in tab stack and as more queue cards*) — none of it, for a member, until they have acknowledged 🏛️ (C9, Q1344); **several motions running on one setting are several tabs and several entries**, each card showing that motion alone; the setting's own tab opens the rule as it stands, and the composer | accept / keep / abstain | ✓ — an accept or an abstain files the entry ⏳ and stays revisable on the same tab until the settle; **a keep settles the motion at that press** (Q1473, Ed 2026-09-19; SPEC §9.6) and the entry becomes the ✖ record at once — news to its mover (E41), filed for everybody else; tab keeps its glyph | carried → E5 | invite remove | | E11 | An ordinary motion is put — an invitation or removal too, where 🪪 / 🥾 stands at *proposal* (entry 94) | every active member — not anyone whose membership has gone quiet (Q930, Ed 2026-08-29) | race card — none of it, for a member, until they have acknowledged ⚖️ (C9, Q1344). **The mover's own entry is theirs** (Q1370, Ed 2026-09-15; M3, §6's `yours` row): ✏️, force-kept, from the press — the mover stands at accept (K8), so their card is the two blocks with the proposed one marked *Proposed*, inert, and 🗑️ withdraw alone, no ✓; an invitation so put stands under *Applications for Membership* and in ✉️'s head under those words, untagged (Q1557) | a judgment | ✓ — closes and files ⏳ (Q576) | → E5 / E14 | invite remove | @@ -135,9 +135,9 @@ The card lifecycle, as the rows of the matrix a single card passes through — p | Y18 | A clause is not a button; the gutter tab is the only way into a card from the document — the text's 📝 included: a click in the prose in read mode beats it (K31) | C1 | clicking a clause puts a caret in it (in edit mode); in read mode it points at the door | K31 | | Y19 | **Retired on the settings cards** (answers.md Part 4 .19, Q1541 stage 3a): before Founder Actions is accepted the ✒️ commit is **not drawn at all**, and the card reads as the rule (§9's *watching* row) — and from the save no settings card is reachable before it at all, 🪶 and 📍's tabs withheld like every rule from 🌍 on (§8 `ORDER` rows 1–2, Q1560 (5)). The dark ✒️ with *Founder Actions ✒️ are waiting in your tasks — accept them and this turns.* (`penWait`) stands only on the cards stage 3b converts (🪪 🤝 🎩, the power cards) | C9 | Ed: *before an action is accepted we shouldn't show the button at all* | Q622; Part 4 .19 | | Y20 | The sealed record, the settled motion record — the mover's owed rejection (E41) included — and the backlog card have no 🗑️ (Q1522 (6)); nor have 🥂 and the departure news cards (E31, E32, E40), their row the OK alone while it is owed and nothing after (Q1527 (a), Ed 2026-09-24); every email card sends from the row's 📧 (reading 1194; the stranger's Apply since Q1390, the applicant's since Q1399); the applicant's Apply opens with *Begin* | C4 | nothing to put back, or the door has no provisional layer | Q613; Q744; CP5 (Q1100) | -| Y21 | 🍾's clause **is** the Proposals preamble, and heads the section while its task is last in the order | C14 | pressing 🍾 is what makes the preamble's first sentence true, so the tab belongs on it | Q616; Ed 2026-08-22 | +| Y21 | **Retired** (#223, G5, Ed 2026-10-03): 🍾's clause is its own, first in the Schedule — *The members are waiting on the Founder to begin* until the press, the moment after it — and it still heads its section while its task is last in the order | C14 | the press makes the clause true, so the tab belongs on it | Q616; #223 | | Y22 | ↻ is a character, styled as a drawn mark; ⏸ is this repo's own drawn glyph rather than the set's | §6 | ↻ has no partner whose weight it must equal, and one shape in two colours needs a shape a colour can reach; the set's ⏸ and ↻ are its two *buttons* — a plate with a white shape knocked out — which this alphabet has refused since 2026-08-17 | Ed 2026-08-17; Q1360 | -| Y23 | The Proposals opening is **one clause carrying three tabs** (🍾 💡 ⚖️; 🏛️'s tab is on the Founded line since Q1365): the gates and the voice grant contribute fragments, not paragraphs, and each gate joins the stack as it arrives | C14 | the four state one rule together — how a rule changes, and who may take part — so four stubs claimed four decisions where there is one | Q748–Q751 | +| Y23 | **Retired** (#223, G3, Ed 2026-10-03): the Proposals section is five clauses, one per power — ✏️ ⏱️ ⚖️ 👥 🏛️ — each gate and grant wearing its tab on its own clause, the preamble of three sentences gone | C14 | one clause per power reads as the rules it is | Q748–Q751; #223 | | Y24 | ✉️'s invitation field is drawn **only where the viewer's own word sends** (`directInvite`): the founder before the start; after it the founder while the **door's** ✒️ is held — `membershipReserved()`, which is `invite()`'s own test — and **any member while 🪪 stands at *pen***. Where it is not, the card composes the invitation as a motion at 🪪's price instead. And the field's send wears **✒️**, the mark of the word that sends — so the row is left with nothing to commit and only closes (§9.1) | C1 | a control that cannot act is worse than an absent one; the 🛡️ only refuses, so it can never open a door; the mark goes where the act is | Q812–Q813, entry 94, entry 37; §9.6a, R-048 | | Y25 | **Every command the document refuses says so** (Q1330, Ed 2026-09-11: *refusals should print an error on screen with a debug message*): where the page can name the card the command left from — the card open when it was sent — in **one sentence under that card**, the card re-opened, retired by the next keystroke on it — the module's own `(§…)` pointer dropped, a set reading *That could not be set: …* and everything else *That was refused: …*; and **always** one stagehand's line at the foot of the window, off the design system like the alpha flag — the sentence, then the command, its arguments, the document, the seat and the time — dismissed by a click and replaced by the next refusal. A wire that answers with a status and no sentence, or not at all, is refused the same way with the status for its reason. The host writes every refusal to its error log (`docs/OPERATING.md` §11). Until Q1330 this reached the two doors and a founder's set only. **A vote on the charter says so too** (Q1505, Ed 2026-09-23): a judgment the host refused or never answered is taken back (issue #37), and its pair card — re-opened once the press's own close has run, unless another card of the charter is open — carries *That was refused: …* just above its commit row, retired by the next choice on it; the connection itself is warned of before anybody acts (C17), so this is the rare press that fails in the instant it is made | C4 | every act on these cards was fire-and-forget, so a refusal and a success looked identical; a member is not expected to meet a refusal at all, so one that happens is a defect and has to be debuggable | Q811, Q815; backlog 51; Q1330; Q1505 **A pasted list is the one exception to the single sentence** (Q1342, Ed 2026-09-12: *one sentence per refused address, all standing*): ✉️ draws one line per refused address, naming the address and the reason, the lines standing together until the card closes or the next send, and the box keeps the refused addresses so a corrected paste is one edit away. | | Y26 | A 💤 change that returns lapsed members **names them in its own change line** — the change is the news (E5), and who it put back is part of what changed: one sentence after the was/now pair (`lapseReturned`), the names off the register from the module's own record of the set (`SettingView.returned`, filled by `member-returned` events the rule caused), on the member's news card and in the record behind the rule's tab; the founder's own lane says nothing, having no change line (E5) | C11 | a rule change that silently re-populates the electorate is a fact about people, not only about the rule | Ed, 2026-08-26 (Q902); §9.5a, entry 97 | @@ -182,7 +182,7 @@ Keys that are not settings: `hat` `myemail` `begin` (SPEC §9.7.1, decisions tha Lifted from SPEC.md; kept as a section by Ed's ruling (Q1004). -- **The head of the document** — the Rules block (Q1516 (5); `constitution-section` in code) carries the founder, the constituted-at time (*Founded by [name] 👑 at [time] on [date]*) and every setting's current value as a clause, governance stated by deviation under the Proposals preamble. (One bullet, by Ed's Q1004 and Q1238; the 👑 question is E12 and STYLE T8.) +- **The head of the document** — the Rules block (Q1516 (5); `constitution-section` in code) carries the founder (*Founded by [avatar] [name] 👑.*), the founder's ✒️ and 🛡️ clauses, every setting's current value as a clause with its governance stated by deviation, and the Schedule, whose 🍾 clause states the moment (*Members started making proposals at [time] on [day].*) (#223). (One bullet, by Ed's Q1004 and Q1238; the 👑 question is E12 and STYLE T8.) ## 6. The marks alphabet and the rail @@ -351,24 +351,24 @@ The refund flight and every grant's inbound flight take 640 ms. | 3 | myemail | 📧 | birth · identity | lead (pre-save); your own row after | yes | slug | Enter Your Email · Your Email | | 4 | myname | ✋ | personal | members — your own row | no | the save | Choose Your Name · Your Name | | 5 | mypic | 🖼️ | personal | members — your own row | no | the save | Choose Your Picture · Your Picture | -| 6 | grant-pen | ✒️ | grant | lead — a tab on the Founded line, no clause | yes | the save | Founder Actions | -| 7 | grant-shield | 🛡️ | grant | lead — a tab on the Founded line, no clause | no | the save | Founder Veto | -| 8 | chamber | 🌍 | constitutional · judge-gate | lead, penultimate | yes | the pen's OK | Who Can See the Document? · Visibility | -| 9 | admission | 🪪 | constitutional — **the price of admission** (entry 94) | members, first — its own clause, above 🤝 (Ed, 2026-08-26) | yes | chamber | How Does Somebody Join? · Admissions (the ✉️ door is the **remedy** while 🍾 waits on `one-voice`, F19) | -| 10 | applications | 🤝 | constitutional | members — above *Members*, ahead of 💤 🥾 (Q617) | yes | admission | Can Strangers Apply? · Applications | -| 11 | hat | 🎩 | decision, not a setting | members — a tab on the list; no sentence | yes | applications | Is the Founder a Member? · Founder’s Membership | -| 12 | lapse | 💤 | constitutional · judge-gate | members — above *Members*, with 🤝 (Q865) | yes | hat | Do Memberships Lapse? · Lapsing | +| 6 | hat | 🎩 | decision, not a setting | lead — a tab on the Founded line until 🍾, then hidden; no sentence (G2, G2b, #223) | yes | the save | Is the Founder a Member? · Founder’s Membership | +| 7 | grant-pen | ✒️ | grant | lead — its own clause after the Founded line, wearing 👑 news it laid down (G1); no clause and no tab where held nowhere (G1b) | yes | 🎩 | Founder Actions | +| 8 | grant-shield | 🛡️ | grant | lead — its own clause after ✒️'s, wearing 👑 news it laid down (G1); no clause and no tab where held nowhere (G1b) | no | the pen's OK | Founder Veto | +| 9 | chamber | 🌍 | constitutional · judge-gate | lead, last — after the founder's clauses (#223) | yes | the pen's OK | Who Can See the Document? · Visibility | +| 10 | admission | 🪪 | constitutional — **the price of admission** (entry 94) | members, first — its own clause, above 🤝 (Ed, 2026-08-26) | yes | chamber | How Does Somebody Join? · Admissions (the ✉️ door is the **remedy** while 🍾 waits on `one-voice`, F19) | +| 11 | applications | 🤝 | constitutional | members — above *Members*, ahead of 💤 🥾 (Q617) | yes | admission | Can Strangers Apply? · Applications | +| 12 | lapse | 💤 | constitutional · judge-gate | members — above *Members*, with 🤝 (Q865) | yes | applications | Do Memberships Lapse? · Lapsing | | 13 | removal | 🥾 | constitutional | members — the last rule above *Members* | yes | lapse | How Is a Member Removed? · Removals | -| 14 | canpropose | 💡 | gate (and the ✏️ grant) | rate — in the preamble's stack | no | 🍾 | Proposals | -| 15 | canjudge | ⚖️ | gate | rate — in the preamble's stack | no | 🍾 | Voting | -| 16 | grant-voice | 🏛️ | grant | lead — a tab on the Founded line beside ✒️ 🛡️, no clause (Q1365); its sentence is the preamble's third (Q750) | no | the moment you become a member — the save, your arrival, your admission (Q1365); **its OK gates every blind question** | Constitutional Proposals | -| 17 | rate | ⏱️ | ordinary, delegable | rate | yes | removal | Set the Proposal Rate · Proposal Rate | -| 18 | ending | ⏰ | constitutional, route inside it | deciding, first | yes | rate | When Does It End? · Ending | -| 19 | quorum | 👥 | constitutional · judge-gate | deciding, last | yes | ending | Choose the Quorum · Quorum | -| 20 | authorship | 👤 | constitutional · judge-gate | privacy | yes | quorum | Are Proposals Anonymous? · Anonymous Proposals | -| 21 | judgments | 👁️ | constitutional · judge-gate | privacy | yes | authorship | When Are Votes Revealed? · Vote Reveal | -| 22 | text | 📝 | ordinary | doc — the charter heading under the hairline; **never a task** — a card with two modes, settled from the save, its OK struck (K31, Q1082) | yes | judgments | Text | -| 23 | begin | 🍾 | decision, not a setting | rate — the preamble's stack, first (Y21, Y23) | no | until the founder can press it — `readiness().ready` and 🏛️ not still being served — **or until nothing but the ✉️ remedy is being served** (F5, F9, F18, F19), **or while every hold is a delegated question still collecting and the founder's own answers are given — then a ⏳ entry naming who is due** (F5, issue #76); members never see it pre-start | Begin | +| 14 | canpropose | 💡 | gate (and the ✏️ grant) | rate — the ✏️ clause, first (G3) | no | 🍾 | Proposals | +| 15 | rate | ⏱️ | ordinary, delegable | rate — second (G3) | yes | removal | Set the Proposal Rate · Proposal Rate | +| 16 | canjudge | ⚖️ | gate | rate — the ⚖️ clause, third (G3) | no | 🍾 | Voting | +| 17 | quorum | 👥 | constitutional · judge-gate | rate — the pass rule, fourth (G4) | yes | rate | Choose the Quorum · Quorum | +| 18 | grant-voice | 🏛️ | grant | rate — the 🏛️ clause, last (G3) | no | the founder-member: 👥, its place in the walk (G6); anybody else the moment they become a member — arrival, admission (Q1365) — before any question they are asked; **its OK gates every blind question** | Constitutional Proposals | +| 19 | authorship | 👤 | constitutional · judge-gate | privacy | yes | quorum | Are Proposals Anonymous? · Anonymous Proposals | +| 20 | judgments | 👁️ | constitutional · judge-gate | privacy | yes | authorship | When Are Votes Revealed? · Vote Reveal | +| 21 | ending | ⏰ | constitutional, route inside it | schedule — second, under 🍾's clause (G5) | yes | judgments | When Does It End? · Ending | +| 22 | text | 📝 | ordinary | doc — the charter heading under the hairline; **never a task** — a card with two modes, settled from the save, its OK struck (K31, Q1082) | yes | ending | Text | +| 23 | begin | 🍾 | decision, not a setting | schedule — its clause first (G5, #223) | no | until the founder can press it — `readiness().ready` and 🏛️ not still being served — **or until nothing but the ✉️ remedy is being served** (F5, F9, F18, F19), **or while every hold is a delegated question still collecting and the founder's own answers are given — then a ⏳ entry naming who is due** (F5, issue #76); members never see it pre-start | Begin | **Outside `ORDER`** — tabs that are not steps: @@ -378,18 +378,18 @@ The refund flight and every grant's inbound flight take 640 ms. | ✉️ · ❌ | ✉️ on *Applications for Membership* (Q1557), ❌ on *Proposed for removal*, both from the birth — **a door stands by its result** — each wearing its own ✒️/🛡️ pair over the act | the subsection · ✉️ stands in the founder's rail as a task once the Membership rules stand, until the first invitation or 🍾 (F23) | entries 94, 96, 181 | | the `adm:` admit cards | the *Applications for Membership* pile, behind ✉️ and its motions, one per applicant still asking; absent at 🪪 *pen*, where an application is admitted on submit | the applicant's row · a task at *proposal* and *assembly*, news at *pen* (E21); at *proposal* a race entry — 🪪 asking, ⏳ voted, the race's closeness as its fill (Q1371); the entry faced and named (Q1375) | entry 96 | | the ✒️/🛡️ power tabs | under every held-able setting — *Can the Founder Make Amendments at Will?* / *Does the Founder Have a Veto?*; on the doors *Can the Founder Invite / Remove at Will?* / *Does the Founder Have a Veto over Invitations / Removals?* | none · none — **and once the document has begun, a power the Founder does not hold has no tab at all**, for any reader (Q1404, Ed 2026-09-16; E39 and K32 retired); before 🍾 both tabs stand, a release being revisable until the start | Q615, Q1404 | -| 🥂 | *Decisions*, when closed | the closing card · pinned per member (E24) | — | +| 🥂 | *Schedule*, when closed (G5) | the closing card · pinned per member (E24) | — | **The band** (`SEC`), under the heading **Rules** — the section's name on the surface since Q1516 (5) (Ed, 2026-09-23: groups drafting a constitution read *the constitution of the constitution*; *Settings* set aside as reading like preferences), in the band and in the contents rail alike (`COPY.page.rulesHeading`); `constitution-section` and `#cs-constitution` stay the code's names — top to bottom: | section | `SEC` key | what it carries | |---|---|---| -| the opening run | `lead` | the title wearing 🪶 ✒️ 🛡️, the link, 📧 pre-save, 🌍, the Founded line wearing the three grants' ✒️ 🛡️ 🏛️ (Q1365) | -| **Membership** | `members` | the rules 🪪 · 🤝 · 💤 · 🥾, then one lvl-3 subsection per status, each carrying the control whose result it holds — *Members* with 🎩 and 📭, your row last (Y8) wearing ✋ 🖼️ 📧; *Applications for Membership* with ✉️, its power tabs, its motions and one admit card per person asking (Q1557); *Lapsed* with none; *Proposed for removal* with ❌ and its power tabs; *Alumni* with 🌂 and the 🥾 news (F21; entries 95 and 96; Q1557) | -| **Proposals** | `rate` | the preamble wearing 🍾 💡 ⚖️ · ⏱️ (Y21, Y23) | -| **Decisions** | `deciding` | ⏰ · 🥂 when closed · 👥 | +| the opening run | `lead` | the title wearing 🪶 ✒️ 🛡️, the link, 📧 pre-save, the Founded line wearing 🎩 until 🍾 and the 👑 news for the last power laid down (G1, G2), the ✒️ clause wearing its grant and the 👑 news that laid a ✒️ down, the 🛡️ clause the same (G1; none where the power is held nowhere, G1b), 🌍 (#223) | +| **Membership** | `members` | the rules 🪪 · 🤝 · 💤 · 🥾, then one lvl-3 subsection per status, each carrying the control whose result it holds — *Members* with 📭, your row last (Y8) wearing ✋ 🖼️ 📧; *Applications for Membership* with ✉️, its power tabs, its motions and one admit card per person asking (Q1557); *Lapsed* with none; *Proposed for removal* with ❌ and its power tabs; *Alumni* with 🌂 and the 🥾 news (F21; entries 95 and 96; Q1557) | +| **Proposals** | `rate` | the five clauses wearing 💡 ⏱️ ⚖️ 👥 🏛️ — ✏️ proposing, the rate, ⚖️ voting, the quorum as the pass rule, 🏛️ constitutional proposals (G3, G4, #223) | | **Anonymity** | `privacy` | 👤 👁️ | +| **Schedule** | `schedule` | the Rules' last section, just above the Text: 🍾 · ⏰ · 🥂 when closed (G5, #223) | | the hairline, then the charter | — | under its own name, the riding 📝 tab beside it — from the save, in `#ridetab`, never in the band's own row (K31) | | **The Document** | `doc` | a rail-only group: 🪶 📍 🌍 📝, 📝 never reaching the rail (K31) | @@ -428,7 +428,7 @@ Before 🌍 is decided its clause states the interim rule: *Until the Founder de - **F13 The constitution starts writing itself at the birth**; at the birth the founder holds everything by construction; the assent half waits for a membership. - **F14 What is born arrives**: a rail entry grows from zero (240 ms), a clause fades (840 ms), a batch cascades 55 ms apart capped at six, each column counting its own; reduced motion fades everything; a stagehand act mutes the pass. **Something that went and returns arrives again**: 🪶 and 📍's tabs, withheld from the save, fade in at the pen's OK as a clause does, once, by a birth key that exists only after the OK (`data-born`; Ed, Q1560 (5): *give them a nice fading in animation*). - **F15 A clause states *The Founder is deciding [what]* until decided, then the rule and its governance deviation; a settings card opens with that clause as its first line under the label *Current rule*, without the governance deviation, the rule wearing its standing pill once decided (Q1541 stage 3a, 1541.3, .10, .47 — Q1151's headless option-block card retired), and a heading-over-text card — the grants and the gates — no title head (Q1373); **a founder's power tab carries no head at the founding only**, its two blocks being the whole choice, and takes the rule as its head once the document has begun (Q1430, Ed 2026-09-17); 📝, personal cards and the blind answers keep their title.** -- **F16 The Founded line stands from the save and gains its moment at the press** — the *moment* is what is unknown before the start, not the founding — **and it is the clause ✒️ and 🛡️ hang their tabs on, neither writing a sentence of its own; 🎩 is settled once the document begins; the register asks for no minimum.** → why: Q639. +- **F16 The Founded line stands from the save and states no moment** — *Founded by [avatar] [name] 👑.*, the time being 🍾's clause's in the Schedule (#223) — **and it carries 🎩 until 🍾 (G2: hidden at the start, settled all the same) and the 👑 news for the last power laid down; ✒️ and 🛡️ each have a clause of their own beneath it, naming the settings held where the power is held on some, and gone with their tab where it is held on none (G1, G1b); the register asks for no minimum.** → why: Q639, #223. - **F17 A dead click beats the next task**: during the birth, a click landing on document space with no control under it — while a task is outstanding and no card is open — lifts and settles **both** that task's tab and its rail entry, twice, over 640 ms. It never opens the card, however often it is clicked, and **nothing ever beats unprompted**. Reduced motion holds the lift for the same span instead of moving. → why: Q650–Q653. @@ -436,11 +436,11 @@ Before 🌍 is decided its clause states the interim rule: *Until the Founder de - **F19 A wait the founding cannot end says why, and ✉️ is the remedy**: `readiness()` gives a reason beside each waiting question — `judge-gate`, `deps-unsettled` (the dependency named beside it), `invitation-open`, `one-voice`, `collecting` (`WaitingWhy`) — and **`one-voice` is the only one no amount of answering will clear**, a question handed to a membership of one having been handed to nobody. **`deps-unsettled` is served without the remedy** (entry 69): §9.0a serves a question only once its own dependencies stand, so a delegated question under an undecided dependency is waiting on another question and not on the room — ✉️ is not summoned for it, and 🍾 names the dependency and that it must settle first rather than an act. **No live example since 2026-09-15** (Q1362): 🌡️ and 🪜 were the only settings in the catalogue carrying a `deps` list, and both left the surface — the rule stands and nothing reaches it. Where the dependency is *itself* a `one-voice` hold, the ✉️ card that is served for **that** question says what is waiting on it in turn, so the second question is not read as a second problem. In that state 🍾 names it and both acts that end it, **✉️ stands as a task**, and the two are served **together**: the remedy is the dead end wearing a card, so it neither counts as *something else being served* (F5) nor as one of the *other tasks* that pace a founder's own delegated questions (F7). The ✉️ task is derived and acknowledges nothing — no OK, no `ACK_KEYS` key, nothing owed (F8) — and leaves the instant an invitation goes out (the reason becomes `invitation-open`) or the delegated setting is taken back. The remedy's card: §9's ✉️ row. → why: DECISIONS.md, *Begin says what it is waiting for* (Q826–Q830). -- **F20 — retired (Q1365, Ed 2026-09-15).** 🏛️ is served the moment you become a member, on the Founded line (E8, §8 row 16), so it needs no host question and no fallback; a founder-member is served it at the save. → why: DECISIONS.md, *Q1365*. +- **F20 — retired (Q1365, Ed 2026-09-15).** 🏛️ is served the moment you become a member (E8, §8 row 18), so it needs no host question and no fallback — on the 🏛️ clause among the Proposals since #223, and a founder-member at its place in the walk, after 👥 (G6). → why: DECISIONS.md, *Q1365*. - **F21 Membership is one subsection per status, and only the first is the membership.** Under the rules (🪪 🤝 💤 🥾) stand lvl-3 subsections — **Members** · **Applications for Membership** · **Lapsed** · **Proposed for removal** · **Alumni** (Q1557) — each the document's own text, not a card's. *Lapsed* stands only where 💤 lapses, and *Alumni* on its rows or wherever 🌂 is yours to press; **the two that carry a control stand when empty** (Applications for Membership, Proposed for removal), **each with a placeholder row saying so**. **A door stands by its result** (entry 96) — each a pile with its own ✒️/🛡️ tabs, never a button inside a row, and **no card draws the register**. *Members* is everybody else who has **arrived**, then every invitation sent and not yet followed, wearing *invited*, then your own row last (Y8, Q1316, Q1557), and *(nobody else here yet)* counts the arrived rows alone; *Applications for Membership* is every vote still running on letting somebody in. **A lapsed member moves to *Lapsed* and counts all the same** (Y10) — lapse is a stall, not a departure, and they abstain rather than leave; **a removal running against somebody names them under *Proposed for removal* whatever else is true of them**. **Every subsection folds**, like every other heading in the document (entry 96, Q406): the same `cs-` id, the same triangle, and never over an open card. The stranger reads the register under *Members* and *Alumni* alone — the per-subsection counts are unbuilt (Q508). Each status — its pile, its placeholder and its rows: §8's statuses table; your own ✋ 🖼️ 📧 card: §9's identity row. → why: DECISIONS.md, *The leaving rules before the register* (Q868–Q870, entry 95); *SURFACE's four long rules trimmed to their rulings* (Q1237). -- **F22 The rules about a membership stand above the register, and Proposals reads before Decisions**: 🤝 how people join, 💤 and 🥾 the two ways they leave, and only then *Members* and the list; and the whole Proposals run — 💡 ⚖️ 🏛️ ⏱️ — above ⏰ 👥 👤 👁️, 🍾 keeping its place last in `ORDER` though its clause opens Proposals, and 📝 last but one. → why: DECISIONS.md, *The leaving rules before the register* (Q865, Q871, Q617). +- **F22 The founding walk reads the Rules top to bottom** (#223, G6): 🪶 📍 📧 (✋ 🖼️ alongside), 🎩, ✒️ then 🛡️, 🌍; 🪪 🤝 💤 🥾 — the rules about a membership above the register, 🤝 how people join, 💤 and 🥾 the two ways they leave, and only then *Members* and the list; 💡 ⏱️ ⚖️ 👥 🏛️; 👤 👁️; ⏰ and then 🍾, 🍾 keeping its place last in `ORDER` though its clause opens the Schedule, and 📝 last but one. 💡 and ⚖️ are accepted for the founder at the start. → why: DECISIONS.md, *The leaving rules before the register* (Q865, Q871, Q617); #223. - **F23 The founder is asked to invite once the Membership rules stand**: the five rules of the section — 🪪 🤝 🎩 💤 🥾 — settled or handed to the membership, and no row on the register but the founder's. Then ✉️ stands in the founder's rail as a task. It is **derived and acknowledges nothing** — no OK, no `ACK_KEYS` key, nothing owed (F8) — it **blocks nothing** (F3), standing beside whatever the founding is asking next, and like the `one-voice` remedy it is counted neither as *something else being served* (F5/F18) nor as one of the *other tasks* that pace the founder's own delegated questions (F7): what is offered because the founder may want company must not withhold the questions or shut 🍾's last-resort door, so ✉️ and 🍾 stand side by side. Its card is the door's own, leading with the invitation box; the remedy's *why you are here* paragraph belongs to F19's case alone. It leaves at the first invitation or at 🍾, and 🍾 is never gated on one having been sent. → why: DECISIONS.md, *The founder is asked to invite once the Membership stands* (entry 181). @@ -451,8 +451,7 @@ Exceptions beyond §3: | the founder alone is served the delegated questions once nothing else is outstanding | F7 | an empty rail while the room fills is worse | Q408, Q413 | | 📧 post-save speaks in the second person | C10 | your own row | Y8 | | 🎩 ✋ 🖼️ 🪪 write no sentence | F13 | the answer is the list or a chip | — | -| 🌍 is asked sixth, ahead of Membership | F1 | it is the penultimate clause of the opening run | — | -| the locked 🎩 is read post-start: what stands as its first line, with its pill, and no other sentence and no row (Q1541 stage 3b) | C9 | a locked decision still has to be readable | CP9, CP11 | +| 🌍 is asked ahead of Membership | F1 | it is the last clause of the opening run, after the founder's (#223) | — | ## 9. The card kinds, the commit row, the composer @@ -489,9 +488,9 @@ Every card the surface draws: | 👑 question (the Text) | the founder's rail beside the clause — and the front tab of the clause's pile (1541.38: the strip led with the race's 💡) | ***Accept This Change?*** over the clause it rewrites | the membership-passed sentence, then the parked wording against the clause as one proposal block, *Proposed* or *Proposed by ‹name›* (Part 4 .8), under its speaker | none | none (1541.9) | 🛡️ refuse · ✒️ accept (CP5; 1541.38) | either power | the entry leaves | | park ⏳ (news; E36) | the ⏳ entry beside the clause, every seat but the Founder's and the author's | *Current text* (Part 4 .1) | one sentence, *Awaiting assent from the Founder 🛡️* — no wording, no speaker: they are the 👑 card's | none | none | OK while unread (`data-seen`, per seat); none once seen (1541.8 (a)) | OK | ⏳ until the park resolves, then the entry leaves; a race waiting behind the park keeps its own card, the waiting sentence a note (E36) | | news / owed OK | news entry ✔ | a settings card's news is the *watching* row's card (stage 3a) | as *watching*: the reason under the rule now standing, *Previous rule* beneath (Part 4 .11) — the change line retired (1564.5 (b)), the watch half too (Q1176) | none | none | OK | OK | grey; the clause keeps the line | -| gates 💡 ⚖️ | rail, hidden until 🍾, no subtitle (C13, Q1374); entry and tab read *Accept Proposals* / *Accept Voting* while owed, *Proposals* / *Voting* once accepted (Q1556 (14)) | drawn like a grant (Part 4 .24): labelled *Accept Proposals* / *Accept Voting* while owed, *Current rule* once accepted (.3, 6.1), over the Proposals clause the tab hangs on, never a title (Q1373) | none — label, line and Accept; no sentence names the commit (card review 2026-09-05, A5; T45, Q1129) | none | none | *Accept ✏️* on 💡, *Accept ⚖️* on ⚖️ (Q1501; Part 4 .24); none once accepted | the Accept (persisted) | the yours hue, sparkling in the rail, until accepted (Q1501); then grey ✔, gone from the rail | -| grants 🏛️ ✒️ 🛡️ | rail, staged, no subtitle (STYLE §4, card review 2026-09-02; C13); entry and tab read the *Accept …* words while owed, the power's name — *Founder Actions* · *Founder Veto* · *Constitutional Proposals* — once accepted (Q1556 (14)) | labelled *Accept Founder Actions* · *Accept the Founder Veto* · *Accept Constitutional Proposals* while owed (Part 4 .23), *Current rule* once accepted (.3, 6.1), over the Founded line the tab hangs on; no title head (Q1373) | what the power is, in Ed's 2026-09-02 sentences — no Founded line, no who-gave-it sentence (Q1170); 🏛️'s body about 🏛️ alone — what it is, that you are already a member, what activating it opens (Q1502; T45's one exception), `proposalsClause` staying the constitution's; the same accepted or not | none | none | *Accept ✒️* · *Accept 🛡️* · *Accept 🏛️* (1541.24), a click (Q1501; T44 amended for grants); none once accepted | the Accept → the flight | the yours hue, sparkling in the rail, until accepted (Q1501); then grey | -| 🍾 Begin | rail (founder) | labelled *Begin* (Part 6.8; Q1542), over the Proposals clause its tab hangs on | the batch, its first line what the power table was told to keep on the Text · the power table, one row per power-holding setting in document order (`BEGIN_ROWS`; Q1195 (c), R-098; F5): glyph and noun (the doors *Invites* / *Removals*; `machines`, retired, has no row), then ✒️ and 🛡️ as glyph toggles, no header row — plain for kept, the wallets' `--slash` strike for laid down, pressed to flip; struck and not a control where already promised away on the setting's own ✒️/🛡️ tab (a span since Q1541 stage 2; the tooltip says it comes back only there), read off the tab's own source; 📝 starts at *lay down*, every other row at *keep*, one direction only — *keep* never reclaims · the readiness readout (no *hold to begin* line since 2026-09-09 — the commit is a click and wears 🍾) | none — the toggle stands where the radio would (Q1181); no *Kept* / *Laid down* words | none — a toggle is undone by toggling it back (Q1556 (2)) | 🍾 (hold; T47), the Founder's alone, before the start — dark until it can be pressed, the reason its note, never in the body (Q1556 (3)): `one-voice` *Delegated questions need at least two responses.*; else members owing answers *Waiting for x members to answer questions.* (Part 4 .22); else `invitation-open` *Waiting for invited members to arrive.*; no note for `judge-gate` or `deps-unsettled` (F5, F19) | the hold | grey, restating the batch, no row (1541.8 (a)); the table gone | +| gates 💡 ⚖️ | rail, hidden until 🍾, no subtitle (C13, Q1374); entry and tab read *Accept Proposals* / *Accept Voting* while owed, *Proposals* / *Voting* once accepted (Q1556 (14)) | drawn like a grant (Part 4 .24): labelled *Accept Proposals* / *Accept Voting* while owed, *Current rule* once accepted (.3, 6.1), over the gate's own Proposals clause — ✏️'s for 💡, ⚖️'s for ⚖️ (G3) — never a title (Q1373) | none — label, line and Accept; no sentence names the commit (card review 2026-09-05, A5; T45, Q1129) | none | none | *Accept ✏️* on 💡, *Accept ⚖️* on ⚖️ (Q1501; Part 4 .24); none once accepted | the Accept (persisted) | the yours hue, sparkling in the rail, until accepted (Q1501); then grey ✔, gone from the rail | +| grants 🏛️ ✒️ 🛡️ | rail, staged, no subtitle (STYLE §4, card review 2026-09-02; C13); entry and tab read the *Accept …* words while owed, the power's name — *Founder Actions* · *Founder Veto* · *Constitutional Proposals* — once accepted (Q1556 (14)) | labelled *Accept Founder Actions* · *Accept the Founder Veto* · *Accept Constitutional Proposals* while owed (Part 4 .23), *Current rule* once accepted (.3, 6.1), over the clause the tab hangs on — ✒️'s and 🛡️'s in the lead, 🏛️'s among the Proposals (G1, G3); no title head (Q1373) | what the power is, in Ed's sentences (#223) — no who-gave-it sentence (Q1170); 🏛️'s about 🏛️ alone — what it is, one at a time, passing only when all members agree (Q1502, #223); the same accepted or not | none | none | *Accept ✒️* · *Accept 🛡️* · *Accept 🏛️* (1541.24), a click (Q1501; T44 amended for grants); none once accepted | the Accept → the flight | the yours hue, sparkling in the rail, until accepted (Q1501); then grey | +| 🍾 Begin | rail (founder) | labelled *Begin* (Part 6.8; Q1542), over 🍾's own clause in the Schedule (G5) | the batch, its first line what the power table was told to keep on the Text · the power table, one row per power-holding setting in document order (`BEGIN_ROWS`; Q1195 (c), R-098; F5): glyph and noun (the doors *Invites* / *Removals*; `machines`, retired, has no row), then ✒️ and 🛡️ as glyph toggles, no header row — plain for kept, the wallets' `--slash` strike for laid down, pressed to flip; struck and not a control where already promised away on the setting's own ✒️/🛡️ tab (a span since Q1541 stage 2; the tooltip says it comes back only there), read off the tab's own source; 📝 starts at *lay down*, every other row at *keep*, one direction only — *keep* never reclaims · the readiness readout (no *hold to begin* line since 2026-09-09 — the commit is a click and wears 🍾) | none — the toggle stands where the radio would (Q1181); no *Kept* / *Laid down* words | none — a toggle is undone by toggling it back (Q1556 (2)) | 🍾 (hold; T47), the Founder's alone, before the start — dark until it can be pressed, the reason its note, never in the body (Q1556 (3)): `one-voice` *Delegated questions need at least two responses.*; else members owing answers *Waiting for x members to answer questions.* (Part 4 .22); else `invitation-open` *Waiting for invited members to arrive.*; no note for `judge-gate` or `deps-unsettled` (F5, F19) | the hold | grey, restating the batch, no row (1541.8 (a)); the table gone | | 🥂 The Close | rail, pinned, per member | the sentence, labelled ***Add your closing comment*** while unsigned, *The Close* after | the moment once · the batch · your closing comment; one batch line per thing the close did, *‹n› motions were still open and did not pass* — either route's motions the clock found running (Q1450; E41 raises no card for them) — omitted at zero | none | nothing (Y20, Q1527) | OK = sign, while unsigned — the signature acknowledges everything else owed (C9) | OK → the farewell | — | | 🪪 Admissions | band tab, above 🤝 | the setting row's: *Current rule* over the price as it stands, with its pill (entry 94) — or the *watching* and composer rows by reader | the three options, each the clause it would set (Q1109), what stands omitted | Choose this (CP2) | 🗑️ dark until chosen | ✒️ | ✒️ | grey | | ✉️ Invite | the *Applications for Membership* pile, from the birth (Q1557); ✒️/🛡️ power tabs in its stack | labelled its ask, *Invite a Member* (Part 4; 1541.46 (a)); first line the subsection's rows — every vote still running on letting somebody in — or *There are no applications at the moment.* (1541.25 (a)) | where the viewer's word sends (Y24): one multi-line address box (Q1166), placeholder *name@example.com* / *one address per line*, no explanatory paragraphs, and the reason box where the Founder may also put it to the membership; a member's seat (the composer): 🪪's clause as the body, the same box and no *Invite* button (Q1187), one motion per address — at ✏️ price each line its own motion and ✏️, at 🏛️ price a single line — and the reason box; a refusal sentence under the box (Y25); the `one-voice` remedy leads (F19) · the plain task once the Membership rules stand (F23) | — | 🗑️ dark until an address is typed (1541.9); puts the card back, or discards the motion | the commit says which act (Q1166): ✒️ where the viewer's word sends (Y24), the route's ✏️ hold · 🏛️ hold where it composes at 🪪's price (🏛️ dark while yours is out), both where the viewer holds both, ✒️ immediately left (Q1154); ✒️ and 🏛️ dark over the empty box (Part 4 .18); the row follows the typing in place, never a render under the caret | commit, keeping what is typed | — | @@ -500,7 +499,7 @@ Every card the surface draws: | `adm:` an admission | the *Applications for Membership* pile, one card per applicant; a task at *assembly* and *proposal*, news at *pen* | labelled ✉️'s ask, *Invite a Member* (1567.3); first line the subsection's rows, the applicant's (face and name) among them while still asking; at *pen* they are a member from the asking, so the first line is the subsection as it stands (its sentence where nobody else asks), the body saying they joined, and the address is the name (Q912 (b)) | by 🪪's price, the address the body and the applicant's own words the sealed reason: *proposal* — *Admit them* / *Keep the membership as it is* / Indifferent, with the abstention clock (Q1460 (c)); *assembly* — *The membership stays as it is.*, ***Proposed*** *‹name› joins the membership.*, Indifferent, no blind note (issue #19); *pen* — one sentence saying they are already a member | *Prefer this* · Indifferent (CP2, CP4) | none (Q1500, 1541.9) | ✓ at *proposal*, 🏛️ at *assembly*; OK where it is news | ✓ / 🏛️ / OK | the race's own marks | | 🤝 Applications | band tab | the setting row's: *Current rule* over whether strangers may apply, with its pill (the cost is 🪪's) — or the *watching* and composer rows by reader | the other rung, *Invitation only* / *Anyone may apply* as the clause each would set, what stands omitted | Choose this (CP2) | 🗑️ dark until chosen | ✒️ | ✒️ | grey | | identity ✋ 🖼️ 📧 | your own row — the Founded line for a clerk, who has none (Q759); the birth run for 📧; the card opens in your row's place inside the list, at its foot (F21, entry 188, Q1316), *Applications for Membership* still below it; a folded *Members* renders its rows to hold it | labelled its ask — *Choose Your Name* · *Choose Your Picture* · *Enter Your Email* (Part 4; 1541.46 (a)) — over your row as the Members list draws it, its face and leading kept (grammar O6 (b)) | option blocks (card review 2026-09-02), what stands omitted (Q620) — it is your row, the first line, and where an answered Anonymous stands its block is left out too (#176): ✋ the name composer · *Anonymous* (Q1164: inert until typed; a blank is no answer); 🖼️ *Anonymous* first (Q1165: initials where you have a name), drawn as the anonymous avatar, never the word (card review 2026-09-05, A7) · *Upload an image* · *Pick an emoji*, the picker opening when its block is chosen; no labels, preview or helper paragraphs / 📧, once an address stands, that address on the first line under your row, and below the hairline one block: a new address's field, the one sent waiting in it until verified (issue #169); before one stands, the field alone | Choose this, per block (CP2); the first line wears the chosen radio wherever something stands — 📧's address, ✋'s name or answered blank, 🖼️'s picture or answered Anonymous — pressed while nothing new is chosen, choosing it again cancelling the change (1541.47, #169, #176); a card never answered has none (F6); the applicant's ✋ 🖼️ are not given it, nothing of theirs standing before Submit (Ed, 2026-10-02, #176) | 🗑️ puts back, never clears (1541.9) | ✓ (T47), dark over an answer that stands (#176); 📧: 🪶 at the birth, 📨 while unverified, ✓ once verified — dark over the empty box a verified address leaves (Part 4 .18); frozen after the close, the row and all beneath it (1541.28 (a)) | commit · send; 📧 re-opens on refusal | the row | -| 🎩 | the members paragraph — *Members* writes no sentence (F13), so its first line is its own (P14's exception) | labelled *Is the Founder a Member?* until the Founder answers (1541.46 (a)), *Current rule* after; first line *The Founder is part of the membership.* / *The Founder is not part of the membership.* — the fact, no consequences (card review 2026-09-02) — with its pill, or *The Founder is deciding whether to be part of the membership.* before an answer | the Founder's before 🍾: the other sentence as an option block, nothing pressed on open (F6); locked at 🍾 and from any other seat: first line and pill alone (CP11) | Choose this, and the pill | 🗑️ dark until chosen | ✒️ pre-start; locked: no row (CP9) | ✒️ | locked | +| 🎩 | the Founded line until 🍾, hidden after it (G2, G2b, #223) — no sentence of its own (F13), so its first line is its own (P14's exception) | labelled *Is the Founder a Member?* until the Founder answers (1541.46 (a)), *Current rule* after; first line *The Founder is part of the membership.* / *The Founder is not part of the membership.* — the fact, no consequences (card review 2026-09-02) — with its pill, or *The Founder is deciding whether to be part of the membership.* before an answer | the Founder's before 🍾: the other sentence as an option block, nothing pressed on open (F6); from any other seat: first line and pill alone (CP11) | Choose this, and the pill | 🗑️ dark until chosen | ✓ pre-start — answered above ✒️ in the walk, so no power (#223, G6); from any other seat: no row (CP9) | ✓ | hidden at 🍾 | | 📝 Text — the text section | the riding tab on the constitution's gutter from the save (K31), never the rail; it rests (Q1516 (2)), centred on the document's first line box as measured (`rideLine`), scrolling away with the heading in both modes; in edit mode the pile is the strip, ✒️ 🛡️ pressable, 📝 alone active (M12); **in read mode, beside the first clause's own tabs, it stacks above them** (#194, Ed 2026-10-02: *it should tuck into the card*) — the pile at rest, the strip of a card open there, 📝 a tab and the strip's gap above the first tab as measured (`ridePile`), and beside a card it takes a resting strip tab's tuck, never the active treatment (card-audit P38; `scripts/repro/ride-tab-tuck.mjs`); it carries the draft's count while one is pending. After 🍾, the floating 📝 (`edit-door`, `#editdoor`, Q1335): read mode only, for a member who may propose (C9 — none for the stranger, applicant, clerk, a member whose ✏️ is unacknowledged, or a closed document); the window's bottom-right corner, `--s5` off both edges, never the proposal row's place (Q1516 (3)); 4.5rem across, glyph at B6's size (Q1516 (4); card-audit D1, D4); may overlap the text (1541.17; P30's exception); the tab's tooltip, pressing it pressing the tab; sparkling in the *yours* hue until first pressed, reduced motion too (Q1516 (1), Q1501, a11y-audit A17), remembered per seat; the read-mode runway `#runway` | the section heading, the 📝 tab, the ✒️ 🛡️ power tabs beneath while held (Q1404) — no threshold line | the prose column. Read mode: no caret, no row, a click beats the tab. Edit mode: the column lifted onto a card (`--shadow-xl`) at the decision card's width, drawn by a pseudo-element so the column does not move, its outline `--measure-gutter` above the first line and running off the page's foot, the runway on the last segment alone (Q1336), `.doc`'s bottom border stepping aside; the caret live; a trailing gap block (*Start a new clause here.*); the viewport the card, tab at its top, row at its foot. The proposal-row: stuck to the window's foot, no ground, click-transparent between its buttons, obeying §9.1, its middle *2 places changed* (Q1089); circles `--s5` off the foot, 4.5rem across (1569.2), overlapping the text (1569.2 (c); P30); three faces (Q1380, Q1382) — the 📝 door; 🗑️ *discard all* · ✏️ *submit all*, the hold itself (Q1382); ❄️? · ✓ over an open patch site card, no 🗑️ (Q1500; `#patchrow`). The lane controls: one strip, B · *I*, at the card's top right (Q1294 (b)), sticky, riding with a ground and the tab's shadow, acting on the selection in the lane holding the caret, disabled where none, writing or removing the markdown marks (K22, Q1467); no lane controls of their own; none outside edit mode. The column is the source in edit mode (Q1467): each block its markdown line, marker and marks editable, dimmed and selectable, in the document's face at the marker's rank; the `[]` toggle retired. Before 🍾 the same strip (Q1313, `#prosectl`) and two views — read draws bold, italic and code and holds a typed `# ` or `- ` as the block's class (`relabel`, `textDivs`); edited, the exact characters, every rank kept (`srcDivs`), the caret carried as a source offset; `proseText` the one read-out for the stash, ✒️ and 🍾 | — | 🗑️ discards the whole draft; before 🍾 puts the column back to the text as it stands; with nothing changed, leaves edit mode (K31, #193) | before 🍾 the founder's ✒️ — `confirm-starting-text`, every press, no OK (Q1080); after, ✏️, with ✒️ beside it, pen first, where the Founder holds the pen (entries 160–161) — with nothing changed the way out of edit mode (K31, #193), once a place has changed opening the `editing-card`, which has no commit of its own in edit mode (1541.22 (a)); with that card open the row's glyph is its commit, the same flight by the draft's id (Q1296–Q1297) | 📝 again, a click on nothing outside the card (C2; the column, strip, row, riding tab and editing cards are inside; Q1315, both eras), or another card opening — the draft kept | grey; the ✒️ 🛡️ tabs beneath stay, where the pen is laid down; the tab carries the draft's count while one is pending | | the applicant's five | the applicant's rail | placeless — nothing of theirs is in the document yet — so each is labelled by its title and opens at the band's top, its first line what it asks: its opening sentence, or its address box (one drawing with the stranger's two); 🖼️, all blocks, carries the strip alone | apply: why · what the application holds so far — name, picture, words, each as given or not yet given (Q1366) · the needed/optional line; email (why · the address; 📧 in the row, Q1399) · name · picture · words — every one born blank, whoever the address was (Q1366: a departed seat's name and face are the register's; Submit is the whole identity it gives) | — | none but 🖼️'s, dark until a choice is made (1541.9); a submitted application has no row and no withdraw (1567.11; 1541.9 (b) does not apply) | *Begin* (Y20) → *Submit*, words on the glyph button's height; 📧 dark until a valid address; ✓ closes and keeps (Q1366), dark until the card is done | Submit · ✓ | ✏️ yours | | the stranger's two | the door's rail | placeless — the title the label, the first line what it asks: 📧 Log In the field itself (card review 2026-09-05, A5), Apply its why | 📧 Log In: the field alone, placeholder *you@example.com*; Apply: why · the address | — | 🗑️ dark until an address is typed, putting it back to empty (1541.9) | 📧 at the row's right on both (T47), dark over a malformed address (reading 1194; Apply since Q1390; Part 4 .18) | send | — | diff --git a/design/STYLE.md b/design/STYLE.md index 3a5d7e56..6c0a25d7 100644 --- a/design/STYLE.md +++ b/design/STYLE.md @@ -52,7 +52,7 @@ Glyph names are stable (tabulated 2026-09-07, Q1208; the founding order's own gl | visibility | 🌍 | — | | text | 📝 | 📄 until backlog 204; the applicant's words are 👋 | | founder-is-member | 🎩 | — | -| proposing gate | 💡 | keeps its glyph on the card titled *Proposals* — Ed's ruling of 2026-09-01 (Q1121), the gate being what the tab and the preamble stack show | +| proposing gate | 💡 | keeps its glyph on the card titled *Proposals* — Ed's ruling of 2026-09-01 (Q1121), the gate being what the tab on the ✏️ clause shows (#223) | | voting gate | ⚖️ | — | | crown | 👑 | — | | horn | 📯 | — | @@ -197,8 +197,8 @@ Glyph names are stable (tabulated 2026-09-07, Q1208; the founding order's own gl you · Ran out of time* — nothing was decided, so nothing ranks it. *Refused* is the Founder's 🛡️ alone; a wording the room closed early is *Rejected* (T8). **Nor does a heading-over-text card carry a title head** (Ed, - 2026-09-15, Q1373): the grants ✒️ 🛡️ open as the strip, the paragraph and - OK, and 🏛️ 💡 ⚖️ head with the Proposals clause, their rule. The card's name + 2026-09-15, Q1373): every grant and gate — ✒️ 🛡️ 🏛️ 💡 ⚖️ — heads with + the clause its tab hangs on, its rule (#223). The card's name survives on the rail entry, the tab tooltip and the record. ## 5. Bodies and notes @@ -208,7 +208,7 @@ Glyph names are stable (tabulated 2026-09-07, Q1208; the founding order's own gl - **T21 · Read-only copy must survive the spec it summarises.** "Fixed for the life of the document" predated motions and was false. **The lockline goes from the settings cards** (1541.14 (a); Q1541 stage 3a): who chose a rule is - its pill (T48), and what changing it takes is the constitution's preamble + its pill (T48), and what changing it takes is the Proposals clauses and the card's own commits. - **T22 · Section headings carry no intro prose.** - **T17 · The price is said in words exactly once**, at the act: *the edit is diff --git a/design/band.js b/design/band.js index da40eae0..1d913e23 100644 --- a/design/band.js +++ b/design/band.js @@ -173,11 +173,13 @@ window.BAND = (function () { // clause is a list rather than a sentence. It carries markup (the avatar, the // 👑 span), so it rides `ctx.clauseFor` beside `rosterClause()` and never // `headFor`, which escapes what it is given. + // …and it no longer carries the time (#223, Ed 2026-10-03): the moment + // moved to 🍾's clause in the Schedule const foundedClause = () => - (!env.cs ? '' : '

Founded by ' + + (!env.cs ? '' : '

' + esc(PAGE_COPY.clause.foundedLead) + avHtml(founderInfo()) + ' ' + esc(founderInfo().n.trim() || 'Anonymous') + // 👑 by any reservation (Ed, Q379 wide); 📯 = holds nothing. - ' ' + founderMark() + esc(foundedAt()) + '.

'); + ' ' + founderMark() + '.

'); // the close's moment, the same shape as the founding's const closedAtWords = () => { if (!env.cs || !env.cs.closed || env.cs.closedAt == null) return ''; @@ -580,7 +582,7 @@ window.BAND = (function () { // stacked field. The chips a shape's unit once grew above the field // left with 🧭 (Q1363). opt(V, 'ending', 'ends', - 'No more changes to the document may be made after ' + + 'Proposals may be made until ' + '.') + rungOpt(V, 'ending', 'perpetual', ENDING_NEVER) + ''; })(), @@ -644,9 +646,9 @@ window.BAND = (function () { '').join('') + ''; return opt(V, 'rateBy', 'founder', - 'Members may make a new proposal ✏️ every ' + + 'Members may make a new ✏️ proposal every ' + ' ' + sel + '.', ''); })() + + (shown === '' ? '' : ' value="' + shown + '"') + '> ' + sel + ', up to 3 at a time.', ''); })() + '', chamber: () => (() => { const V = ladderView('chamber'); @@ -1086,7 +1088,9 @@ window.BAND = (function () { // other seat (Q1503) if (c.k === 'hat') { if (stateOf(c, ctx) === 'news') return 'watching'; - return amFounder() && !constituted() && docOpen() && founderCanCommit() ? 'setting' : 'watching'; + // …answerable before ✒️ is accepted (#223, G6: 🎩 is the walk's second + // step, ✒️ its third), so not pen-gated + return amFounder() && !constituted() && docOpen() ? 'setting' : 'watching'; } if (!MANAGED_KEYS.includes(c.k)) return null; if (c.record || c.isGate || c.door || c.kind === 'personal' || c.admit || c.release || @@ -1169,9 +1173,12 @@ window.BAND = (function () { attrs: ' data-confirm="1"', title: PAGE_COPY.setIt, until: powerParts(c).chosen ? null : 'choose' }); return acts; } + // **🎩 is answered above the pen** (#223, G6): the walk meets it before + // ✒️ is accepted, so it commits with no power, as ✋ and 🖼️ do (Q980) — + // a ✒️ on it would be a power not yet held (T44) if (c.k === 'hat') { - const ready = !!S.hatPick && S.hatPick !== hatCurrent() && mayPen() && docOpen(); - acts.push({ kind: 'commit', glyph: '✒️', glyphHtml: glyphHtml('✒️'), cls: 'btn-approve emojibtn', + const ready = !!S.hatPick && S.hatPick !== hatCurrent() && docOpen(); + acts.push({ kind: 'commit', glyph: '✓', glyphHtml: window.CARDS.TICK, cls: 'btn-approve', attrs: ' data-confirm="1"', title: PAGE_COPY.setIt, until: ready ? null : 'choose' }); return acts; } diff --git a/design/begin.js b/design/begin.js index 31cdeacb..a9e99e04 100644 --- a/design/begin.js +++ b/design/begin.js @@ -265,7 +265,8 @@ window.BEGIN = (function () { const BEGIN_ROWS = [ 'title', 'slug', 'chamber', 'admission', 'applications', 'lapse', 'removal', 'invite', 'remove', - 'rate', 'ending', 'quorum', 'authorship', 'judgments', + // the Rules' own order since #223 (G6): ⏰ in the Schedule, after 👤 👁️ + 'rate', 'quorum', 'authorship', 'judgments', 'ending', 'text', // `machines` is not here (Ed, 2026-09-09: *we are not having machines*): // it has had no card since 🤖 left the surface (R-078) and stays in the diff --git a/design/copy.js b/design/copy.js index 272ef3d0..63da1dd0 100644 --- a/design/copy.js +++ b/design/copy.js @@ -997,8 +997,8 @@ window.COPY = (function () { pw: { phrase: { star: { u: 'amend this at will', a: 'refuse proposals that the membership pass' }, - invite: { u: 'invite people at will', a: 'refuse invitations and applications that the membership pass' }, - remove: { u: 'remove members at will', a: 'refuse removals that the membership pass' }, + invite: { u: 'invite people at will', a: 'block invitations that the membership have decided to make' }, + remove: { u: 'remove people at will', a: 'block removals that the membership have decided to make' }, text: { u: 'amend the text at will', a: 'refuse changes to the text that the membership pass' }, }, // **On the decision card the phrase names its setting** (Q1429, Ed @@ -1025,8 +1025,8 @@ window.COPY = (function () { // (entry 94) and are only ever the veto sentence's. noun: { title: 'the title', slug: 'the link', text: 'the text', - ending: 'the ending', quorum: 'the quorum rule', - authorship: 'the anonymity rule', judgments: 'the vote-reveal rule', + ending: 'the end time', quorum: 'the quorum', + authorship: 'anonymity of proposals', judgments: 'anonymity of votes', chamber: 'the visibility rule', rate: 'the proposal rate', lapse: 'the lapse rule', removal: 'the removal rule', admission: 'the admissions rule', applications: 'the applications rule', @@ -1121,19 +1121,38 @@ window.COPY = (function () { founderRefused: (what) => 'The Founder refused your proposal: ' + what + '.', couldNotBePut: 'Your proposal could not be put to the membership, and your ✏️ is back in your wallet.', }, - // the Proposals preamble (Y21): the gates' fragments, composed - preamble: { - beforeBegin: 'When the document begins, members may propose changes to rules and vote on proposals.', - voteOnly: 'Members may vote on proposals.', - voteWhenDecided: 'Members may begin voting on proposals when all the rules have been decided.', - proposeAtBegin: ' They may propose changes to rules when the document begins.', - proposeLead: 'Members may propose changes to rules ', - onceAnswered: 'once they have answered the questions the Founder delegated', - asArrive: 'as soon as they arrive', - andVote: ', and may vote on proposals.', - voteTail: '. They may begin voting on proposals when all the rules have been decided.', - passOrdinary: 'A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.', - passConstitutional: 'A constitutional proposal 🏛️ passes only when all members agree.', + // **The Rules' clauses for the grants, the gates and the Schedule** (#223, + // Ed 2026-10-03, G1–G5, every sentence his, verbatim): the ✒️ and 🛡️ + // clauses in the lead, the five Proposals clauses, the Schedule's 🍾 and + // 🥂. `list` joins the setting names (`pw.noun`) the way his examples + // read: *the title, the link, the visibility rule and the text*. + clause: { + foundedLead: 'Founded by ', + penAll: 'The Founder may ✒️ change anything at will.', + penSome: (list) => 'The Founder may ✒️ change ' + list + ' at will.', + shieldAll: 'The Founder may 🛡️ refuse any proposal the membership pass.', + shieldSome: (list) => 'The Founder may 🛡️ refuse proposals the membership pass on ' + list + '.', + // at the birth, with no aside: *(that's you!)* is the title clause's alone + // (Ed, 2026-10-03 20:01, on #224, overriding #223's *Before the save*) + penBirth: 'The Founder may ✒️ change anything at will.', + shieldBirth: 'The Founder may 🛡️ refuse proposals the membership pass.', + // *Friday 2 October*, his example's shape: the weekday, then the day + weekDay: (weekday, day) => weekday + ' ' + day, + list: (names) => (names.length < 2 ? names.join('') + : names.slice(0, -1).join(', ') + ' and ' + names[names.length - 1]), + propose: 'Members may propose changes to the text and the rules.', + proposeBefore: 'When the document begins, members may propose changes to the text and the rules.', + vote: 'Members may vote on proposals.', + voteBefore: 'When the document begins, members may vote on proposals.', + rate: (phrase, cap) => 'Members may make a new ✏️ proposal every ' + phrase + ', up to ' + cap + ' at a time.', + constitutional: 'A 🏛️ proposal passes only when all members agree to it.', + beginWaiting: 'The members are waiting on the Founder to begin.', + begun: (time, day) => 'Members started making proposals at ' + time + ' on ' + day + '.', + endingAfter: (when) => 'Proposals may be made until ' + when + '.', + endingNever: 'Proposals may be made indefinitely.', + // *[N] members have signed* — the verb agrees with one (*1 member has*) + closed: (time, day, n) => 'Proposals closed at ' + time + ' on ' + day + '. ' + + n + (n === 1 ? ' member has signed.' : ' members have signed.'), }, // 👥's rule, in Ed's own words (Q1439, ruling t, 2026-09-18: *A proposal // cannot pass until it is preferred by at least 50% of the membership @@ -1151,10 +1170,12 @@ window.COPY = (function () { // member's answer card and the composer's lane (Q1137) — and then the // `(x of y)` that follows *of the membership*. `val.quorumPct` is still // the one writer of a share where a share is printed whole. + // …and since #223 (G4, Ed 2026-10-03 17:10) it is the pass rule, the one + // clause saying what carries a ✏️ proposal, in his words quorumRule: { - share: (share, tail) => 'A proposal ✏️ cannot pass until it is preferred by at least ' + - share + ' of the membership' + (tail || '') + '.', - count: (n) => 'A proposal ✏️ cannot pass until it is preferred by at least ' + n + ' members.', + share: (share, tail) => 'A ✏️ proposal passes once at least ' + + share + ' of the membership' + (tail || '') + ' have voted for it and it’s the most preferred option.', + count: (n) => 'A ✏️ proposal passes once at least ' + n + ' members have voted for it and it’s the most preferred option.', }, // **👥 is the one card that says what its number comes to** (Q1490, Ed // 2026-09-21 → why: R-139), a deliberate exception to Q1439 ruling u, @@ -1357,13 +1378,13 @@ window.COPY = (function () { gate: { canpropose: { title: 'Proposals', - why: 'A proposal is a change you write to the document, for the membership to vote on.', + why: 'A proposal is a change to the document that the membership vote on.', waiting: 'Waiting on the start.', done: 'Open — members can propose as soon as they arrive.', }, canjudge: { title: 'Voting', - why: 'A vote is your say on a proposal: you are shown two at a time and choose the one you prefer, or neither.', + why: 'A vote is your say on a proposed change to the document.', waiting: 'Waiting on the rules.', done: 'Open — the rules are settled.', }, @@ -1376,7 +1397,7 @@ window.COPY = (function () { // and tab read *Accept Constitutional Proposals*, the card's own // words, and *Activate Your Membership* went title: 'Constitutional Proposals', - why: 'A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules.', + why: 'A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it.', waiting: 'Waiting on your arrival.', // *Accept 🏛️* like every grant (Q1541.24 (a), Ed 2026-09-25), // replacing Q1502's *Activate 🏛️* @@ -1384,12 +1405,12 @@ window.COPY = (function () { }, pen: { title: 'Founder Actions', - why: 'As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.', + why: 'As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.', waiting: 'Waiting on the save.', }, shield: { title: 'Founder Veto', - why: 'As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.', + why: 'As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.', waiting: 'Waiting on the save.', }, // **a grant is accepted, not OK'd** (Q1501, Ed 2026-09-22; T44 amended @@ -1591,11 +1612,11 @@ window.COPY = (function () { // until the page reads these, which is a change to the regular page and // waits for its own pass. rule: { - endingNever: 'Changes to the document may be made perpetually.', - endingAfter: (when) => 'No more changes to the document may be made after ' + when + '.', + endingNever: 'Proposals may be made indefinitely.', + endingAfter: (when) => 'Proposals may be made until ' + when + '.', lapseNever: 'Inactive members never lapse and are still counted towards votes.', lapseAfter: (spell) => 'After ' + spell + ', inactive members lapse and automatically abstain from votes.', - rate: (phrase) => 'Members may make a new proposal ✏️ every ' + phrase + '.', + rate: (phrase, cap) => 'Members may make a new ✏️ proposal every ' + phrase + ', up to ' + cap + ' at a time.', unit: { days: 'day', hours: 'hour', minutes: 'minute' }, units: (n, unit) => n + ' ' + unit, address: (slug) => 'The document lives at docs.vote/d/' + slug + '.', diff --git a/design/feed.js b/design/feed.js index 372d35b4..f4cc213e 100644 --- a/design/feed.js +++ b/design/feed.js @@ -94,7 +94,7 @@ // the floor a race is held to, max(⌈n·E/100⌉, min(2, E)) — product // before quotient (issue #24), the seconder under it (Q1490) Math.max(Math.ceil((v.n * roster) / 100), Math.min(2, roster)), roster)); - case 'rate': return dripPhrase(v.dripMinutes) ? T.rule.rate(dripPhrase(v.dripMinutes)) : ''; + case 'rate': return dripPhrase(v.dripMinutes) ? T.rule.rate(dripPhrase(v.dripMinutes), v.cap) : ''; case 'lapse': return v.afterMs === null ? T.rule.lapseNever : (spell ? T.rule.lapseAfter(spell) : ''); case 'ending': return v.endsAtMs === null ? T.rule.endingNever : T.rule.endingAfter(C.longWhen(v.endsAtMs)); // the page's words, 24-hour (STYLE T16) diff --git a/design/reference/band.js b/design/reference/band.js index da40eae0..1d913e23 100644 --- a/design/reference/band.js +++ b/design/reference/band.js @@ -173,11 +173,13 @@ window.BAND = (function () { // clause is a list rather than a sentence. It carries markup (the avatar, the // 👑 span), so it rides `ctx.clauseFor` beside `rosterClause()` and never // `headFor`, which escapes what it is given. + // …and it no longer carries the time (#223, Ed 2026-10-03): the moment + // moved to 🍾's clause in the Schedule const foundedClause = () => - (!env.cs ? '' : '

Founded by ' + + (!env.cs ? '' : '

' + esc(PAGE_COPY.clause.foundedLead) + avHtml(founderInfo()) + ' ' + esc(founderInfo().n.trim() || 'Anonymous') + // 👑 by any reservation (Ed, Q379 wide); 📯 = holds nothing. - ' ' + founderMark() + esc(foundedAt()) + '.

'); + ' ' + founderMark() + '.

'); // the close's moment, the same shape as the founding's const closedAtWords = () => { if (!env.cs || !env.cs.closed || env.cs.closedAt == null) return ''; @@ -580,7 +582,7 @@ window.BAND = (function () { // stacked field. The chips a shape's unit once grew above the field // left with 🧭 (Q1363). opt(V, 'ending', 'ends', - 'No more changes to the document may be made after ' + + 'Proposals may be made until ' + '.') + rungOpt(V, 'ending', 'perpetual', ENDING_NEVER) + ''; })(), @@ -644,9 +646,9 @@ window.BAND = (function () { '').join('') + ''; return opt(V, 'rateBy', 'founder', - 'Members may make a new proposal ✏️ every ' + + 'Members may make a new ✏️ proposal every ' + ' ' + sel + '.', ''); })() + + (shown === '' ? '' : ' value="' + shown + '"') + '> ' + sel + ', up to 3 at a time.', ''); })() + '', chamber: () => (() => { const V = ladderView('chamber'); @@ -1086,7 +1088,9 @@ window.BAND = (function () { // other seat (Q1503) if (c.k === 'hat') { if (stateOf(c, ctx) === 'news') return 'watching'; - return amFounder() && !constituted() && docOpen() && founderCanCommit() ? 'setting' : 'watching'; + // …answerable before ✒️ is accepted (#223, G6: 🎩 is the walk's second + // step, ✒️ its third), so not pen-gated + return amFounder() && !constituted() && docOpen() ? 'setting' : 'watching'; } if (!MANAGED_KEYS.includes(c.k)) return null; if (c.record || c.isGate || c.door || c.kind === 'personal' || c.admit || c.release || @@ -1169,9 +1173,12 @@ window.BAND = (function () { attrs: ' data-confirm="1"', title: PAGE_COPY.setIt, until: powerParts(c).chosen ? null : 'choose' }); return acts; } + // **🎩 is answered above the pen** (#223, G6): the walk meets it before + // ✒️ is accepted, so it commits with no power, as ✋ and 🖼️ do (Q980) — + // a ✒️ on it would be a power not yet held (T44) if (c.k === 'hat') { - const ready = !!S.hatPick && S.hatPick !== hatCurrent() && mayPen() && docOpen(); - acts.push({ kind: 'commit', glyph: '✒️', glyphHtml: glyphHtml('✒️'), cls: 'btn-approve emojibtn', + const ready = !!S.hatPick && S.hatPick !== hatCurrent() && docOpen(); + acts.push({ kind: 'commit', glyph: '✓', glyphHtml: window.CARDS.TICK, cls: 'btn-approve', attrs: ' data-confirm="1"', title: PAGE_COPY.setIt, until: ready ? null : 'choose' }); return acts; } diff --git a/design/reference/begin.js b/design/reference/begin.js index 31cdeacb..a9e99e04 100644 --- a/design/reference/begin.js +++ b/design/reference/begin.js @@ -265,7 +265,8 @@ window.BEGIN = (function () { const BEGIN_ROWS = [ 'title', 'slug', 'chamber', 'admission', 'applications', 'lapse', 'removal', 'invite', 'remove', - 'rate', 'ending', 'quorum', 'authorship', 'judgments', + // the Rules' own order since #223 (G6): ⏰ in the Schedule, after 👤 👁️ + 'rate', 'quorum', 'authorship', 'judgments', 'ending', 'text', // `machines` is not here (Ed, 2026-09-09: *we are not having machines*): // it has had no card since 🤖 left the surface (R-078) and stays in the diff --git a/design/reference/cards.js b/design/reference/cards.js index c60614d5..19d39d8f 100644 --- a/design/reference/cards.js +++ b/design/reference/cards.js @@ -2241,7 +2241,15 @@ window.CARDS = (function () { // opens the draft with that character already applied. Which is // `always-on-typing`, applied to a box instead of to a paragraph — the same // idea and, it turns out, the same function underneath. - function laneBoxHtml(d, site, blank) { + // `part` (MOBILE.md §6a.1, Ed 1585.1): `'lane'` is the editor alone, for + // the phone's card whose lane stands in the clause's own box; `'why'` the + // reasoning alone, drawn beneath it. Absent, the whole box as ever. + function laneBoxHtml(d, site, blank, part) { + if (part === 'lane') { + return '
' + laneBlocks(site.text, originText(site)) + '
'; + } + if (part === 'why') return whyBoxHtml((d && d.rationale) || '', ' data-why'); // **No controls of its own since Q1294 (b)** (Ed, 2026-09-10: *top right // of the edit box, identical to the existing composer control. You can // put bold and italic there too*): the lane is the text and nothing diff --git a/design/reference/composer.js b/design/reference/composer.js index aeb93a4d..866e9041 100644 --- a/design/reference/composer.js +++ b/design/reference/composer.js @@ -589,6 +589,46 @@ window.COMPOSER = (function () { startDraft(key, null, { text: src.slice(0, a) + ins + src.slice(b), caret: a + ins.length }); } + // **A tap on a clause makes it its lane** (MOBILE.md §6a.1, Ed 1585.1, + // 2026-10-02): on a coarse pointer the column never takes a caret, so in + // edit mode the tap is the door — the clause opens as a draft of itself, + // nothing changed yet, the caret where the finger landed (the point + // mapped to a source offset exactly as a keystroke's caret is). The same + // opening as a keystroke's, with no character applied. + function startDraftFromTap(p, x, y) { + const key = p.dataset.key; + if (!key) return false; + const src = markerFor(key) + currentTextFor(key); + let caret = src.length; + let r = null; + if (document.caretRangeFromPoint) r = document.caretRangeFromPoint(x, y); + else if (document.caretPositionFromPoint) { + const c = document.caretPositionFromPoint(x, y); + if (c) { r = document.createRange(); r.setStart(c.offsetNode, c.offset); r.collapse(true); } + } + if (r && p.contains(r.startContainer)) { + const off = offsetIn(p, r.startContainer, r.startOffset); + if (off != null) caret = Math.min(off, src.length); + } + // **the words do not move** (§6a.6 (2)): the head is a `.anch`'s box, + // its words 6px inside it, where an untouched clause's words stand at + // its own top — so the scroll takes up whatever the opening moved them + const wordsTop = (el) => { + const w = el && document.createTreeWalker(el, NodeFilter.SHOW_TEXT); + for (let n = w && w.nextNode(); n; n = w.nextNode()) { + if (!n.nodeValue.trim() || (n.parentElement && n.parentElement.closest('.chipcol, .nocaret'))) continue; + const q = document.createRange(); q.selectNodeContents(n); + const rs = q.getClientRects(); if (rs.length) return rs[0].top; + } + return null; + }; + const t0 = wordsTop(p); + startDraft(key, null, { text: src, caret }); + const t1 = wordsTop(env.doc.querySelector('[data-lane="' + key + '"]')); + if (t0 != null && t1 != null && Math.abs(t1 - t0) > 0.5) scrollBy(0, t1 - t0); + return true; + } + // **Backspace at the start of a clause joins it to the one above** (Q1302, // Ed's bot room 2026-09-10: *I should be able to backspace at the start of a // clause to join it to the previous clause*), and Delete at its end joins @@ -1004,6 +1044,13 @@ window.COMPOSER = (function () { block: '
' + esc(window.COPY.shell.yourDraft) + '' + anonSwitchHtml(d) + '
' + laneBoxHtml(d, s) + '
', + // the phone's form (MOBILE.md §6a.1, Ed 1585.1): the lane alone, to + // stand in the clause's own box, and beneath it the switch and the + // reasoning — the same parts, the head left out + lane: s.lost ? null : laneBoxHtml(d, s, null, 'lane'), + below: '
' + + (SIGNING() ? '
' + anonSwitchHtml(d) + '
' : '') + + laneBoxHtml(d, s, null, 'why') + '
', // what the card says under the words: a site the text moved out from // under (Q1463), and only the two facts that change what the row's ✏️ // *does* (Ed, 2026-08-17) — it joins a race, it goes in as one change @@ -1053,7 +1100,7 @@ window.COMPOSER = (function () { return { DRAFT_ID, draftOf, docIndexOfKey, siteFor, syncDraftKeys, dropDraft, dropDraftSite, caretRangeIn, selectedBlocks, laneCaret, placeCaret, - startDraft, startDraftFromTyping, startDraftFromRun, + startDraft, startDraftFromTyping, startDraftFromTap, startDraftFromRun, laneRemark, syncEditCtl, markSelection, commitBtnHtml, proposalRowHtml, proposeCtlTitles, draftRowState, setDraftSigned, draftSigned, ownParts, editParts, cardCommitActs }; diff --git a/design/reference/constitution.js b/design/reference/constitution.js index c7303809..dd5d83a9 100644 --- a/design/reference/constitution.js +++ b/design/reference/constitution.js @@ -3517,6 +3517,52 @@ var CONSTITUTION = (() => { } } } + /** + * **When `tick` next has something to do** (plan-scaling Stage 1, issue + * #210): the earliest moment any branch of `tick` above would emit, read + * off the same state and the same arithmetic, or null where none ever will + * until something else is folded — unbegun, closed, or no ending and no + * 💤 clock. A host may then skip this document's ticks before that moment + * and lose nothing, since `tick` at any earlier `t` emits nothing. + * + * **Every clock here is a state, never a moment that passes**: a lapse + * point already behind `t` still names a member in E, and a warning point + * behind it still names a lead shorter than the one sent, until `tick` + * folds them — so a point that was never ticked comes back from here as + * due, and the answer needs no *since when*. Kept beside `tick` because it + * is `tick` read backwards: a branch added there is a branch owed here. + */ + nextClockT() { + if (this.constitutedT === null || this.closedFlag) return null; + let next = Infinity; + const ending = this.settings.get("ending").value; + if (ending && ending.endsAtMs !== null) next = Math.min(next, ending.endsAtMs); + const lapse = this.settings.get("lapse").value; + if (lapse && lapse.afterMs !== null) { + const pointsOf = (lastT, warnedLead, warns) => { + const due = lapseDue(lastT, lapse.afterMs); + let at = due.lapseAtT; + if (warns) { + for (const w of due.warnAt) { + if (warnedLead === null || w.lead < warnedLead) at = Math.min(at, w.t); + } + } + return at; + }; + for (const m of this.members.values()) { + if (!inE(m)) continue; + next = Math.min(next, pointsOf(m.lastActivityT, m.lapseWarnedLead, true)); + } + if (!this.crownLapsedFlag && this.holdsAnythingReserved()) { + next = Math.min(next, pointsOf( + this.convenor.lastActivityT, + this.convenor.lapseWarnedLead, + !this.members.has(this.convenor.id) + )); + } + } + return next === Infinity ? null : next; + } holdsAnythingReserved() { for (const st of this.settings.values()) { if (st.holder === "convenor") return true; diff --git a/design/reference/copy.js b/design/reference/copy.js index 272ef3d0..63da1dd0 100644 --- a/design/reference/copy.js +++ b/design/reference/copy.js @@ -997,8 +997,8 @@ window.COPY = (function () { pw: { phrase: { star: { u: 'amend this at will', a: 'refuse proposals that the membership pass' }, - invite: { u: 'invite people at will', a: 'refuse invitations and applications that the membership pass' }, - remove: { u: 'remove members at will', a: 'refuse removals that the membership pass' }, + invite: { u: 'invite people at will', a: 'block invitations that the membership have decided to make' }, + remove: { u: 'remove people at will', a: 'block removals that the membership have decided to make' }, text: { u: 'amend the text at will', a: 'refuse changes to the text that the membership pass' }, }, // **On the decision card the phrase names its setting** (Q1429, Ed @@ -1025,8 +1025,8 @@ window.COPY = (function () { // (entry 94) and are only ever the veto sentence's. noun: { title: 'the title', slug: 'the link', text: 'the text', - ending: 'the ending', quorum: 'the quorum rule', - authorship: 'the anonymity rule', judgments: 'the vote-reveal rule', + ending: 'the end time', quorum: 'the quorum', + authorship: 'anonymity of proposals', judgments: 'anonymity of votes', chamber: 'the visibility rule', rate: 'the proposal rate', lapse: 'the lapse rule', removal: 'the removal rule', admission: 'the admissions rule', applications: 'the applications rule', @@ -1121,19 +1121,38 @@ window.COPY = (function () { founderRefused: (what) => 'The Founder refused your proposal: ' + what + '.', couldNotBePut: 'Your proposal could not be put to the membership, and your ✏️ is back in your wallet.', }, - // the Proposals preamble (Y21): the gates' fragments, composed - preamble: { - beforeBegin: 'When the document begins, members may propose changes to rules and vote on proposals.', - voteOnly: 'Members may vote on proposals.', - voteWhenDecided: 'Members may begin voting on proposals when all the rules have been decided.', - proposeAtBegin: ' They may propose changes to rules when the document begins.', - proposeLead: 'Members may propose changes to rules ', - onceAnswered: 'once they have answered the questions the Founder delegated', - asArrive: 'as soon as they arrive', - andVote: ', and may vote on proposals.', - voteTail: '. They may begin voting on proposals when all the rules have been decided.', - passOrdinary: 'A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.', - passConstitutional: 'A constitutional proposal 🏛️ passes only when all members agree.', + // **The Rules' clauses for the grants, the gates and the Schedule** (#223, + // Ed 2026-10-03, G1–G5, every sentence his, verbatim): the ✒️ and 🛡️ + // clauses in the lead, the five Proposals clauses, the Schedule's 🍾 and + // 🥂. `list` joins the setting names (`pw.noun`) the way his examples + // read: *the title, the link, the visibility rule and the text*. + clause: { + foundedLead: 'Founded by ', + penAll: 'The Founder may ✒️ change anything at will.', + penSome: (list) => 'The Founder may ✒️ change ' + list + ' at will.', + shieldAll: 'The Founder may 🛡️ refuse any proposal the membership pass.', + shieldSome: (list) => 'The Founder may 🛡️ refuse proposals the membership pass on ' + list + '.', + // at the birth, with no aside: *(that's you!)* is the title clause's alone + // (Ed, 2026-10-03 20:01, on #224, overriding #223's *Before the save*) + penBirth: 'The Founder may ✒️ change anything at will.', + shieldBirth: 'The Founder may 🛡️ refuse proposals the membership pass.', + // *Friday 2 October*, his example's shape: the weekday, then the day + weekDay: (weekday, day) => weekday + ' ' + day, + list: (names) => (names.length < 2 ? names.join('') + : names.slice(0, -1).join(', ') + ' and ' + names[names.length - 1]), + propose: 'Members may propose changes to the text and the rules.', + proposeBefore: 'When the document begins, members may propose changes to the text and the rules.', + vote: 'Members may vote on proposals.', + voteBefore: 'When the document begins, members may vote on proposals.', + rate: (phrase, cap) => 'Members may make a new ✏️ proposal every ' + phrase + ', up to ' + cap + ' at a time.', + constitutional: 'A 🏛️ proposal passes only when all members agree to it.', + beginWaiting: 'The members are waiting on the Founder to begin.', + begun: (time, day) => 'Members started making proposals at ' + time + ' on ' + day + '.', + endingAfter: (when) => 'Proposals may be made until ' + when + '.', + endingNever: 'Proposals may be made indefinitely.', + // *[N] members have signed* — the verb agrees with one (*1 member has*) + closed: (time, day, n) => 'Proposals closed at ' + time + ' on ' + day + '. ' + + n + (n === 1 ? ' member has signed.' : ' members have signed.'), }, // 👥's rule, in Ed's own words (Q1439, ruling t, 2026-09-18: *A proposal // cannot pass until it is preferred by at least 50% of the membership @@ -1151,10 +1170,12 @@ window.COPY = (function () { // member's answer card and the composer's lane (Q1137) — and then the // `(x of y)` that follows *of the membership*. `val.quorumPct` is still // the one writer of a share where a share is printed whole. + // …and since #223 (G4, Ed 2026-10-03 17:10) it is the pass rule, the one + // clause saying what carries a ✏️ proposal, in his words quorumRule: { - share: (share, tail) => 'A proposal ✏️ cannot pass until it is preferred by at least ' + - share + ' of the membership' + (tail || '') + '.', - count: (n) => 'A proposal ✏️ cannot pass until it is preferred by at least ' + n + ' members.', + share: (share, tail) => 'A ✏️ proposal passes once at least ' + + share + ' of the membership' + (tail || '') + ' have voted for it and it’s the most preferred option.', + count: (n) => 'A ✏️ proposal passes once at least ' + n + ' members have voted for it and it’s the most preferred option.', }, // **👥 is the one card that says what its number comes to** (Q1490, Ed // 2026-09-21 → why: R-139), a deliberate exception to Q1439 ruling u, @@ -1357,13 +1378,13 @@ window.COPY = (function () { gate: { canpropose: { title: 'Proposals', - why: 'A proposal is a change you write to the document, for the membership to vote on.', + why: 'A proposal is a change to the document that the membership vote on.', waiting: 'Waiting on the start.', done: 'Open — members can propose as soon as they arrive.', }, canjudge: { title: 'Voting', - why: 'A vote is your say on a proposal: you are shown two at a time and choose the one you prefer, or neither.', + why: 'A vote is your say on a proposed change to the document.', waiting: 'Waiting on the rules.', done: 'Open — the rules are settled.', }, @@ -1376,7 +1397,7 @@ window.COPY = (function () { // and tab read *Accept Constitutional Proposals*, the card's own // words, and *Activate Your Membership* went title: 'Constitutional Proposals', - why: 'A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules.', + why: 'A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it.', waiting: 'Waiting on your arrival.', // *Accept 🏛️* like every grant (Q1541.24 (a), Ed 2026-09-25), // replacing Q1502's *Activate 🏛️* @@ -1384,12 +1405,12 @@ window.COPY = (function () { }, pen: { title: 'Founder Actions', - why: 'As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.', + why: 'As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.', waiting: 'Waiting on the save.', }, shield: { title: 'Founder Veto', - why: 'As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.', + why: 'As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.', waiting: 'Waiting on the save.', }, // **a grant is accepted, not OK'd** (Q1501, Ed 2026-09-22; T44 amended @@ -1591,11 +1612,11 @@ window.COPY = (function () { // until the page reads these, which is a change to the regular page and // waits for its own pass. rule: { - endingNever: 'Changes to the document may be made perpetually.', - endingAfter: (when) => 'No more changes to the document may be made after ' + when + '.', + endingNever: 'Proposals may be made indefinitely.', + endingAfter: (when) => 'Proposals may be made until ' + when + '.', lapseNever: 'Inactive members never lapse and are still counted towards votes.', lapseAfter: (spell) => 'After ' + spell + ', inactive members lapse and automatically abstain from votes.', - rate: (phrase) => 'Members may make a new proposal ✏️ every ' + phrase + '.', + rate: (phrase, cap) => 'Members may make a new ✏️ proposal every ' + phrase + ', up to ' + cap + ' at a time.', unit: { days: 'day', hours: 'hour', minutes: 'minute' }, units: (n, unit) => n + ' ' + unit, address: (slug) => 'The document lives at docs.vote/d/' + slug + '.', diff --git a/design/reference/edit-mode.js b/design/reference/edit-mode.js index 00e2d2e4..892a98c8 100644 --- a/design/reference/edit-mode.js +++ b/design/reference/edit-mode.js @@ -202,6 +202,7 @@ window.EDIT_MODE = (function () { } window.PATCH.set(rt, tmp.innerHTML); rt.classList.toggle('editing', !!env.S.editMode); + rt.classList.toggle('tucked', !!(ridePile() || {}).card); // **the rest position is a measurement, not a constant** (the band's // gotcha about `fitBand`): the pile is lifted from the sticky box's // natural top — the band's bottom edge — back up to the **first line of @@ -222,8 +223,17 @@ window.EDIT_MODE = (function () { // clamp swallowed. const tab = rt.querySelector('.achip'); const tabH = tab ? tab.getBoundingClientRect().height : 30; + // **beside the first clause's own tabs, it stacks above them** (#194, + // Ed 2026-10-02: *it should tuck into the card*): a card open on the + // first clause puts 📝 at the head of its strip, tucked; a pile there + // at rest puts 📝 above the pile, which it had covered 1px off + const pile = ridePile(); rt.style.position = 'static'; - const up = rt.getBoundingClientRect().top - (line.top + (line.lineBox - tabH) / 2); + // the whole of 📝's pile stands clear, the ✒️ 🛡️ slivers beneath it + // a Founder's included + const ownCol = rt.querySelector('.chipcol'); + const pileH = ownCol ? ownCol.getBoundingClientRect().height : tabH; + const up = rt.getBoundingClientRect().top - (pile ? pile.top - pile.gap - pileH : line.top + (line.lineBox - tabH) / 2); rt.style.position = ''; rt.style.setProperty('--ride-up', up.toFixed(2) + 'px'); } @@ -248,6 +258,33 @@ window.EDIT_MODE = (function () { const r = col.getBoundingClientRect(), pt = parseFloat(getComputedStyle(col).paddingTop) || 0; return { top: r.top + pt, bottom: r.top + pt + 24, lineBox: 24 }; } + // **the first clause's own tabs**, when 📝 stands beside them (#194): the + // strip of a card open on the text's first block, or that block's pile at + // rest. Null in edit mode (the text is the card, K31), with no tab there, + // and before 🍾. `gap` is the strip's own, read off a strip — a pile is + // drawn with none, so its class comes off for the one reading. + function ridePile() { + if (env.S.editMode || !constituted()) return null; + const charter = document.getElementById('charter'); + if (!charter) return null; + let first = null; + for (const el of charter.children) { + first = el.classList.contains('prose') ? el.firstElementChild : el; + if (first) break; + } + if (!first) return null; + const card = first.classList.contains('sugg'); + const col = first.querySelector(card ? '.clausehead .chipcol' : ':scope > .chipcol'); + const tab = col && col.querySelector('.achip'); + if (!tab) return null; + const stack = col.classList.contains('stack'); + if (stack) col.classList.remove('stack'); + const gap = parseFloat(getComputedStyle(col).rowGap) || 0; + if (stack) col.classList.add('stack'); + // the column's top, not the tab's: the tab under the pointer that + // opened the card wears its hover lift, a transform the rect includes + return { top: col.getBoundingClientRect().top, gap, card }; + } // **The tab no longer rides** (Q1516 (2), Ed 2026-09-23: the floating // button being enough): it rests at the text's heading and scrolls away // with it, in read mode and in edit mode alike, so there is no detached @@ -275,8 +312,9 @@ window.EDIT_MODE = (function () { // patched (stage 10): the Founder's ✒️ here is a hold, and a render // under it keeps the button the pointer went down on window.PATCH.set(pr, show ? SESSION.proposalRowHtml({ - count: 0, pen: true, disabled: !proseDirty(), - discardDisabled: !proseDirty(), + // never dark (#193): with nothing changed both ends leave edit mode + count: 0, pen: true, disabled: false, + discardDisabled: false, discardTitle: PAGE_COPY.proseRow.discard, title: proseDirty() ? PAGE_COPY.proseRow.save : PAGE_COPY.proseRow.saved, }) : ''); @@ -286,10 +324,7 @@ window.EDIT_MODE = (function () { const b = pr && pr.querySelector('[data-act="row-commit"]'); if (!b) return; const dirty = proseDirty(); - b.disabled = !dirty; b.title = dirty ? PAGE_COPY.proseRow.save : PAGE_COPY.proseRow.saved; - const bin = pr.querySelector('[data-act="row-discard"]'); - if (bin) bin.disabled = !dirty; } // ---- the column's strip before the start (Q1313) -------------------------- // **The same strip as after 🍾** (Ed, 2026-09-11 14:08): B · I at the top diff --git a/design/reference/live.js b/design/reference/live.js index 98cf3653..77437958 100644 --- a/design/reference/live.js +++ b/design/reference/live.js @@ -1279,6 +1279,10 @@ window.LIVE = (function () { return { retry: true }; }).then((data) => { if (data && data.retry) { setTimeout(liveBoot, 4000); return; } + // **the opening skeleton is lifted by the first render** (Q1559): + // until one, the page wears `html.opening` from its and shows + // the bars in place of the birth's *Untitled*; a retry above keeps it + const opened = () => document.documentElement.classList.remove('opening'); if (!data) return; // **The stagehand's controls are asked for only where the host says // it has them** (Q1349, Ed 2026-09-12): `devMail` rides every view @@ -1303,6 +1307,7 @@ window.LIVE = (function () { // at once, so a phone that scanned is one tap from a seat if (data.demoJoin && new URLSearchParams(location.search).get('try') === '1') S.open = 'strtry'; render(); + opened(); api.pushOpen(LIVESLUG); setInterval(() => { // **Nothing rebuilds under a press.** A pen hold is a gesture in @@ -1332,6 +1337,7 @@ window.LIVE = (function () { hydrateApplicant(data.applicant); hydrateS(); render(); + opened(); api.pushOpen(LIVESLUG); setInterval(() => api.pollTick(), 4000); return; @@ -1359,6 +1365,7 @@ window.LIVE = (function () { if (data.text) setProse(data.text); else if (data.provisionalText) setProse(data.provisionalText); render(); + opened(); api.pushOpen(LIVESLUG); setInterval(() => { // **Nothing rebuilds under a press.** A pen hold is a gesture in diff --git a/design/reference/paper.js b/design/reference/paper.js index f6071958..46630a40 100644 --- a/design/reference/paper.js +++ b/design/reference/paper.js @@ -178,6 +178,96 @@ place(rules, left, top, width, breakTop - top); place(text, left, breakBottom, width, bottom - breakBottom); desk.dataset.sheets = '2'; + openAtText(breakBottom); + } + + // ---- the opening place (issue #197; the default since #212) -------------- + // Ed, 2026-10-02: *when the page opens, it aligns near the top of The Text + // page, and people could just scroll upwards* — the Rules being, in the + // members' word, a menu. SURFACE M26. The page opens with the Text sheet's + // top `--sheet-margin` under the bar: the desk gap and the Rules' blank foot + // show above it, never a Rules line. Behind `?rules=below` from #197 until + // Ed's *merge and make it the default* (2026-10-03, #212). + // + // **Decided once, at the first lay that has the break** — the docsep is + // drawn only once the page holds a document, so this is the boot's first + // render, whichever boot — and only for a begun one (`.doc.begun`, which + // the closed page and the stranger's door wear too): a founding page opens + // at the top, and a 🍾 pressed later is not an opening. A fragment in the + // address, or a card already open (`?try=1`'s 👋), is a place of its own and + // wins. + // + // **Pinned, not jumped**: `overflow-anchor` is off (system.css, *the page + // holds its own scroll*), and the faces landing, the band settling and the + // poll all move the Text after the first render, so every lay re-pins it, + // instantly, until the reader does anything at all — a wheel, a touch, a + // key, a press, or a scroll this code did not make. + // + // **A reload or a return keeps whatever restores the reader's place**: the + // browser's own restoration, landing after `load`. It lands where the + // reader was only where the page is already whole by then (the fixture); + // on the live path the column arrives on the view's fetch and the browser + // restores against a page a fraction of its height, which lands nowhere in + // particular (233px for a reader who left at 1800, measured on the demo). + // So the place is noted as the page is left, and on a reload or a return + // the decision waits for the restoration: where the browser put the reader + // back, it stands; where it did not, the page opens at the Text. + // **`?open=top` is a dev seam, not a member's option** (#212): the old + // opening, for a walk or a probe whose measurements are frozen at scroll 0. + const SEAM_TOP = new URLSearchParams(location.search).get('open') === 'top'; + const ON = !SEAM_TOP; + let pinState = ON ? 'undecided' : 'off'; + let pinnedY = null; + const PLACE_KEY = 'open-at:' + location.pathname + location.search; + const navType = (() => { + try { const n = performance.getEntriesByType('navigation')[0]; return n ? n.type : 'navigate'; } catch (e) { return 'navigate'; } + })(); + let leftAt = 0; + if (ON && (navType === 'reload' || navType === 'back_forward')) { + try { leftAt = Number(sessionStorage.getItem(PLACE_KEY)) || 0; } catch (e) { /* no place noted */ } + } + // the restoration is the browser's, a frame or so after `load`; a timer, + // never rAF, for the backgrounded tabs (CLAUDE.md, *Checking a mockup*) + let restoreSettled = !leftAt; + if (!restoreSettled) { + const settle = () => setTimeout(() => { restoreSettled = true; if (window.PAPER) window.PAPER.lay(); }, 150); + if (document.readyState === 'complete') settle(); else addEventListener('load', settle); + } + if (ON) { + addEventListener('pagehide', () => { + try { sessionStorage.setItem(PLACE_KEY, String(Math.round(window.scrollY))); } catch (e) { /* the place goes unnoted */ } + }); + } + function unpin() { + if (pinState !== 'pinned') return; + pinState = 'released'; + for (const t of ['wheel', 'touchstart', 'keydown', 'pointerdown']) removeEventListener(t, unpin, true); + } + function openAtText(breakBottom) { + if (pinState === 'undecided') { + if (!restoreSettled) return; + const restored = leftAt > 0 && Math.abs(window.scrollY - leftAt) <= 2; + const take = doc.classList.contains('begun') && !location.hash && !doc.querySelector('.gshell') && !restored; + pinState = take ? 'pinned' : 'off'; + if (!take) return; + for (const t of ['wheel', 'touchstart', 'keydown', 'pointerdown']) addEventListener(t, unpin, { capture: true, passive: true }); + addEventListener('scroll', () => { + if (pinState === 'pinned' && pinnedY != null && Math.abs(window.scrollY - pinnedY) > 1) unpin(); + }, { passive: true }); + } + if (pinState !== 'pinned') return; + // a card opened by anything — an evaluated click carries no pointerdown — + // is a place of its own, as at the decision + if (doc.querySelector('.gshell')) { unpin(); return; } + const max = document.documentElement.scrollHeight - innerHeight; + // …and so is a scroll made since the last pin and not yet announced by + // its event (a walk's `scrollTo(0, 0)`, a travel's first frame): the + // reader's, unless it is the page clamping a scroll it no longer has room for + if (pinnedY != null && Math.abs(window.scrollY - pinnedY) > 1 && window.scrollY < max - 1) { unpin(); return; } + const nav = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--nav-h')) || 58; + const y = Math.max(0, Math.min(max, Math.round(breakBottom - nav - token('--sheet-margin')))); + pinnedY = y; + if (Math.abs(window.scrollY - y) > 1) window.scrollTo(window.scrollX, y); } let queued = false; @@ -200,7 +290,7 @@ // the page lays itself out over several ticks after boot (the fixture's // washes, the fonts); a last pass once it has settled setTimeout(lay, 300); setTimeout(lay, 1200); - window.PAPER = { lay }; + window.PAPER = { lay, pinState: () => pinState }; } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); diff --git a/design/reference/session-view.html b/design/reference/session-view.html index 346c343e..84b87bdf 100644 --- a/design/reference/session-view.html +++ b/design/reference/session-view.html @@ -1,6 +1,6 @@ - + +