diff --git a/apps/web/src/lib/components/theme/SiteLayout.svelte b/apps/web/src/lib/components/theme/SiteLayout.svelte
index cc727bf..d806d17 100644
--- a/apps/web/src/lib/components/theme/SiteLayout.svelte
+++ b/apps/web/src/lib/components/theme/SiteLayout.svelte
@@ -86,7 +86,7 @@
- {site.handle}
+ {site.handle}
{postmarked}
@@ -302,7 +302,7 @@
letter-spacing: 0.14em;
text-transform: uppercase;
color: color-mix(in srgb, var(--airmail-blue) 75%, var(--ink));
- /* Long handles stay inside the circle. */
+ text-align: center;
padding: 0.5rem;
box-sizing: border-box;
overflow: hidden;
@@ -310,9 +310,15 @@
.postmark span {
max-width: 100%;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
+ }
+
+ /* A monospace character plus its tracking is about 0.74em wide, and 5.9rem of
+ the ring's 7.5rem is clear of the border and padding. The type shrinks with
+ the handle to stay inside the ring; past the floor it wraps to a second line. */
+ .postmark-handle {
+ font-size: clamp(0.45rem, calc(5.9rem / (0.74 * var(--handle-chars))), 0.62rem);
+ overflow-wrap: anywhere;
+ line-height: 1.25;
}
.postmark-rule {
diff --git a/docs/NEXT.md b/docs/NEXT.md
index 8b95200..1dad8ab 100644
--- a/docs/NEXT.md
+++ b/docs/NEXT.md
@@ -2,7 +2,7 @@
The flight plan. Each item carries enough context to start cold; update this file whenever an item lands (move it to "Done") or a new one is queued. Decisions made while working an item still go through `decisions/` as usual.
-_Last updated: 2026-08-26 (**PRs #27, #28 and #31 are all merged and deployed.** mooring.page now serves the demo-first landing page (PD-10) — verified live: the apex carries the Open Graph card, `/s/jzweifel.dev` renders with the claim banner and `noindex`, and the domain lock, sign-off band and republished `signOff` lexicon from the earlier PRs are all in place. **Nothing is in flight; the queue below is current.** Critique trends, two separate targets: the rendered **theme** ran 25 → 31 → 29 → 32, and the **landing page** has one run at 21/40 — snapshots in `.impeccable/critique/`)._
+_Last updated: 2026-08-26 (**PRs #27, #28 and #31 are all merged and deployed.** mooring.page now serves the demo-first landing page (PD-10) — verified live: the apex carries the Open Graph card, `/s/jzweifel.dev` renders with the claim banner and `noindex`, and the domain lock, sign-off band and republished `signOff` lexicon from the earlier PRs are all in place. **In flight: the postmark cut-off fix on `claude/signoff-stamp-cutoff-aab504` (below), not yet merged.** The queue below is otherwise current. Critique trends, two separate targets: the rendered **theme** ran 25 → 31 → 29 → 32, and the **landing page** has one run at 21/40 — snapshots in `.impeccable/critique/`)._
## Where things stand
@@ -34,6 +34,7 @@ Done so far: OAuth login (loopback dev client; hosted-client path ready pending
## Done
+- 2026-08-26 — **The sign-off postmark stopped cutting off long handles** (committed on `claude/signoff-stamp-cutoff-aab504`; **not merged, not deployed**). A new user's site — `timheffley.xyz`, 14 characters — rendered as "TIMHEFFLEY.X…" in the postmark ring. The ring's type was fixed at 0.62rem with `white-space: nowrap` + `text-overflow: ellipsis`, so every handle wider than the ring was guaranteed to truncate; `timheffley.xyz` was simply the first one to cross the line. The handle span now carries `--handle-chars` from `site.handle.length` and sizes itself: `clamp(0.45rem, calc(5.9rem / (0.74 * var(--handle-chars))), 0.62rem)` — the design's size for short handles, shrinking through the middle, floored where the mono type stops being readable, and past the floor `overflow-wrap: anywhere` wraps to a second line instead of truncating. The ellipsis is gone rather than layered over. The arithmetic only works because the ring is monospace (character count *is* text width at ~0.74em per character including tracking); the usable 5.9rem is the 7.5rem diameter less the 2px ring, which `box-sizing: border-box` folds in, and the 0.5rem padding — a first pass at 6.3rem overflowed by 1.4px and wrapped. Verified in a browser against the live render of `/s/timheffley.xyz`: 14 characters on one line inside the ring, `jacobzweifel.bsky.social` (24) and a 31-character DID fallback wrapping to two, none clipped. The landing page's postmark reads a hardcoded `mooring.page` and the OG card is a static PNG, so neither shares the fault.
- 2026-08-25 — **The demo-first landing page landed** (PD-10). The apex asks for a handle ("Your site is already written.") and redirects to `/s/[handle]`, resolving the handle server-side so a typo fails inline instead of 404ing; input is normalized (trim, lowercase, leading `@` dropped) by a helper shared with the login action. Every `/s/` preview now renders a claim banner — "This is me — claim it" links to `/login?handle=…` prefilled (deliberately one click, never straight to `authorize()` on GET: prefetchers and crawlers would fire real OAuth flows) — and always carries `noindex`, since the preview path is never the canonical address and unclaimed handles shouldn't rank. The landing page finally wears the site theme (airmail band, warm paper, postmark "par avion"). Found and fixed in verification: the banner needed `color-scheme: dark` so auto-darkening browsers (Chrome's force-dark, default on many Androids) don't invert its hardcoded navy. Banner shows unconditionally on `/s/`, owner or not — revisit if it grates. **Merged as PR #31 and deployed 2026-08-26**, with the critique fixes below in the same deploy; smoke-tested live (apex OG tags, `/s/` preview + banner + `noindex`).
- 2026-08-26 — **The landing page took its first critique, and the P1s are fixed** (dual-assessment, 21/40, snapshot in `.impeccable/critique/`; remaining design-decision items are queued above). Copy first, on Jacob's call: em-dash asides removed throughout, and "Own the record" became **"Yours to keep"** — portability as a plain benefit ("leave anytime and everything comes with you"), since the record-ownership framing only sells to protocol-literate readers. Then the three P1s: the lookup form runs through `use:enhance` so the button disables into "Assembling…" and a status line holds through the redirect (the wait for a live PDS read was the page's least-designed moment and the whole demo-first thesis hinges on it); the landing branch emits Open Graph tags against a 1200×630 airmail card at `apps/web/static/og-card.png` (authored as SVG, rasterized with `sharp` — `qlmanage` mangles the scale, and base64-through-the-browser is a context sink); and "Or peek at an example site first" links `/s/jzweifel.dev` so a skeptical visitor sees output before typing their own handle. Mechanical P2s in the same pass: a distinct `--warn` wine for errors with a leading mark, so failure stops wearing the CTA's exact accent red; `aria-invalid`/`aria-describedby` wired and focus kept in the field on a failed submit; 44px controls and a 16px input (iOS zoom-on-focus); input border raised to non-text contrast; the airmail stripe keeps paper-white bands in the dark scheme (they were resolving to `--paper` = `#101520`, degrading the barber pole to generic diagonal stripes); fact titles promoted to real `
`s; the facts joined the entrance rise. Verified live in both schemes at desktop and 375px.
- 2026-08-25 — **The airmail sign-off band landed — the letter signs off, with Atmosphere-native actions** (PR #28, merged and deployed; verified live on jzweifel.dev. Tail items "sign-off block" and "Atmosphere-native actions", resolved together). Three directions were mocked on the design canvas (letter closing / postmark band / "From" block); Jacob picked the **postmark band**: a sunk-paper band above the footer where the airmail stripe returns to bookend the page, a rotated circular postmark (handle + render-month, airmail-blue ink), the owner's name, and three actions — Follow (Bluesky profile link, where Follow/Message actually live; Bluesky has no follow/DM intent URL), Say hello (`bsky.app/intent/compose` prefilled with the @mention), Write to me (mailto, only when `contactEmail` is set). The valediction is **configurable, not default**: new optional `signOff` field on `page.mooring.site` (add-optional-only per ADR 0012; republished on-network 2026-08-25, runbook §§3–4 — the live schema record verified to carry `signOff`) flowing lexicon → record builder → settings form → `SiteView` → `SiteLayout`; empty renders the name alone — Jacob was unsure "Warmly," suits every site, so it's a choice ("whimsy seasons the dish"). Band renders on every page like the footer; verified locally against live PDS data (jzweifel.dev): actions resolve, dark tokens flip, no horizontal overflow at 375px, chips ≥24px targets.