From 0987932b5c287368dfb70926feb880fa06099445 Mon Sep 17 00:00:00 2001 From: Wells Riley <884715+wr@users.noreply.github.com> Date: Wed, 14 Oct 2026 13:54:01 -0400 Subject: [PATCH 1/6] The cover says what it is, what it costs and who makes it, and why to join now The hero is tuned for three readers: a technical gift giver, a gift giver who is not technical, and a visitor from social or an ad. - The sentence says what it is in plain words: a picture frame that shows the birds singing near your home, in their portraits from the 1800s. - Three facts under it: From $349 with no subscription or extras; an easy gift that sets up from their phone; built by hand in Connecticut, open source. - The cover's note gives the reason to sign up now: the first batch is small, the waitlist hears first, no spam. The close keeps its own note. - The description, Open Graph and Twitter text say the same, with the price, so a shared link reads the same as the cover. - W-1020 kept every price off the page; Wells chose to show "From $349" (14 Oct 2026). No sale, date or offer, and no other price. - The cover's height budget (--gnd, --words) is refit to the new block at every size, and a portrait window's frame may go a little smaller (.38 of the screen, from .4) so the signup stays on the first screen. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V3jLSJPncDMoCx3zZsEeRR --- site/public/index.html | 17 +++++++++++------ site/public/styles.css | 23 ++++++++++++++++------- site/test/site.spec.ts | 19 +++++++++++-------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/site/public/index.html b/site/public/index.html index feda50d0..501e3b91 100644 --- a/site/public/index.html +++ b/site/public/index.html @@ -4,13 +4,13 @@ Featherframe: a picture frame for bird lovers that shows who’s singing nearby - + - + @@ -21,7 +21,7 @@ - + @@ -52,7 +52,7 @@ "@type": "Product", "@id": "https://featherframe.app/#product", "name": "Featherframe", - "description": "Your neighborhood birds, shown as they’re heard, in illustrations from the finest natural history books of the 1800s. A framed e-paper display with no subscription.", + "description": "A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s. A framed e-paper display with no subscription.", "image": ["https://featherframe.app/img/og.jpg"], "brand": { "@id": "https://wells.ee/#org" }, "manufacturer": { "@id": "https://wells.ee/#org" }, @@ -84,8 +84,13 @@

Let the outside in.

-

Your neighborhood birds, shown as they’re heard, in illustrations from the finest natural history books of the 1800s. A framed e-paper display with no subscription.

-

One email when Featherframe is available, and nothing else.

+

A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s.

+ +

The first batch is small. The waitlist hears first. No spam.

diff --git a/site/public/styles.css b/site/public/styles.css index 28b865ce..163a447d 100644 --- a/site/public/styles.css +++ b/site/public/styles.css @@ -163,6 +163,15 @@ h2 { font-weight: 400; text-wrap: balance; } .stage.live .poster { opacity: 0; transition: opacity .6s ease; } .cover .cta .wl { position: relative; z-index: 2; } .cover .copy > p { font-size: var(--t-lede); line-height: var(--lh-lede); margin: 0 0 26px; text-wrap: pretty; } +/* what a buyer asks first, under the sentence: the price, the gift, who makes it. A check before each, the field's + ink, set small and upright. */ +.cover .facts { list-style: none; margin: -8px 0 26px; padding: 0; font-size: var(--t-small); line-height: var(--lh-lede); } +.cover .facts li { position: relative; padding-left: 24px; text-wrap: pretty; } +.cover .facts li + li { margin-top: 4px; } +.cover .facts li::before { + content: ""; position: absolute; left: 2px; top: .42em; width: 10px; height: 5px; + border-left: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); +} /* I. The art: a spread, the frame showing the flamingo in its left half, pinned there, centred below the running head, while the text scrolls past. */ @@ -588,7 +597,7 @@ h2 { font-weight: 400; text-wrap: balance; } --W: calc(100vw - 2 * var(--edge)); --V: calc(100svh - var(--nav)); /* under 1000px the sentence is set at the body size, so the frame has room beside it */ - --gnd: 220px; --measure: 400px; + --gnd: 287px; --measure: 400px; --F: clamp(72px, min((var(--W) - .9521 * var(--gnd)) / 5.0466, (var(--W) - var(--measure) - .9521 * var(--gnd)) / 1.2026, (var(--V) - 112px - var(--gnd)) / 1.913), 240px); --ht: calc(1.913 * var(--F) + var(--gnd)); /* the words: the headline's cap line to the note's last baseline */ @@ -614,10 +623,10 @@ h2 { font-weight: 400; text-wrap: balance; } } } @media (max-width: 820px) and (min-aspect-ratio: 1/1) { - .cover { --gnd: 214px; } + .cover { --gnd: 283px; } } @media (min-width: 1000px) and (min-aspect-ratio: 1/1) { - .cover { --gnd: 248px; --measure: calc(17.7 * var(--t-lede)); } + .cover { --gnd: 308px; --measure: calc(17.7 * var(--t-lede)); } .cover .copy > p { font-size: var(--t-lede); } } @media (min-width: 1100px) and (min-aspect-ratio: 1/1) { @@ -627,7 +636,7 @@ h2 { font-weight: 400; text-wrap: balance; } } } @media (min-width: 1200px) and (min-aspect-ratio: 1/1) { - .cover { --gnd: 211px; --measure: calc(22 * var(--t-lede)); } + .cover { --gnd: 272px; --measure: calc(22 * var(--t-lede)); } } /* A portrait window or a tablet: the phone's order, the headline @@ -643,8 +652,8 @@ h2 { font-weight: 400; text-wrap: balance; } --pad: 40px; --g1: calc(.3 * var(--F)); /* the headline's last baseline to the frame's top edge */ --g2: 40px; /* the frame's foot to the sentence */ - --words: 217px; /* the sentence, the waitlist's field and the note under it (200px under 821px) */ - --fh: min(max(.4 * var(--V), var(--V) - 2 * var(--pad) - 1.513 * var(--F) - var(--g1) - var(--g2) - var(--words)), .72 * var(--W) / .843); + --words: 278px; /* the sentence, the facts, the waitlist's field and the note under it (267px under 821px) */ + --fh: min(max(.38 * var(--V), var(--V) - 2 * var(--pad) - 1.513 * var(--F) - var(--g1) - var(--g2) - var(--words)), .72 * var(--W) / .843); --hb: calc(var(--fh) / .7379); --wb: calc(var(--hb) * .78376); display: flex; flex-direction: column; justify-content: center; min-height: var(--V); padding: var(--pad) 0; @@ -664,7 +673,7 @@ h2 { font-weight: 400; text-wrap: balance; } } } @media (min-width: 701px) and (max-width: 820px) and (aspect-ratio < 1) { - .cover { --words: 200px; } + .cover { --words: 267px; } } @media (max-width: 820px) { diff --git a/site/test/site.spec.ts b/site/test/site.spec.ts index 56c5692f..82f0e641 100644 --- a/site/test/site.spec.ts +++ b/site/test/site.spec.ts @@ -13,7 +13,7 @@ test('social card and search basics', async ({ page, request }) => { const product = ld['@graph'].find((n: { '@type': string }) => n['@type'] === 'Product'); expect(product).toMatchObject({ name: 'Featherframe', url: 'https://featherframe.app/' }); expect(product).not.toHaveProperty('offers'); - await expect(page.locator('meta[name="twitter:description"]')).toHaveAttribute('content', 'Your neighborhood birds, shown as they’re heard, in illustrations from the finest natural history books of the 1800s. No subscription.'); + await expect(page.locator('meta[name="twitter:description"]')).toHaveAttribute('content', 'A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s. From $349, no subscription.'); await expect(page.locator('link[rel="canonical"]')).toHaveAttribute('href', 'https://featherframe.app/'); expect((await request.get('/robots.txt')).ok()).toBe(true); expect((await request.get('/sitemap.xml')).ok()).toBe(true); @@ -68,11 +68,13 @@ test('every section and its key copy is there', async ({ page }) => { await expect(page.locator('#specs .spec dd').nth(6)).toHaveText('Open source. See it on GitHub'); await expect(page.locator('#specs .spec dd').nth(6).getByRole('link', { name: 'See it on GitHub' })).toHaveAttribute('href', 'https://github.com/wr/featherframe'); await expect(page.locator('#specs .spec')).not.toContainText('Service'); - // no price, sale or date anywhere (W-1020): the hero holds the signup alone, and each size is named for its display + // no sale or date anywhere (W-1020); the one price is the hero's "From $349", for buyers who ask what it costs first. + // The signup holds the form alone, and each size is named for its display await expect(page.locator('.cover .cta > *')).toHaveCount(1); await expect(page.locator('#specs .ho figcaption')).toHaveText(['10.3-inch · B&W', '13.3-inch · Color']); await expect(page.locator('s')).toHaveCount(0); - await expect(page.locator('body')).not.toContainText(/Black Friday|November|December|holiday|\$(299|379|349|479)/i); + await expect(page.locator('body')).not.toContainText(/Black Friday|November|December|holiday|\$(299|379|479)/i); + await expect(page.locator('.cover .facts')).toContainText('$349'); await expect(page.locator('#specs .ho .diag span')).toHaveText(['10.3-inch display', '13.3-inch display']); await expect(page.locator('#specs .dim')).toHaveCount(0); // the close says the cover's line again @@ -86,9 +88,10 @@ test('every section and its key copy is there', async ({ page }) => { await expect(page.locator('.wall .folio')).toHaveText('From the collection'); await expect(page.locator('.close .words > p')).toHaveText('A framed e-paper display with no subscription.'); await expect(page.locator('body')).not.toContainText('Reserve'); - await expect(page.locator('.cover .copy > p')).toHaveText('Your neighborhood birds, shown as they’re heard, in illustrations from the finest natural history books of the 1800s. A framed e-paper display with no subscription.'); - await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', 'Your neighborhood birds, shown as they’re heard, in illustrations from the finest natural history books of the 1800s. A framed e-paper display, no subscription.'); - await expect(page.locator('meta[property="og:description"]')).toHaveAttribute('content', 'Your neighborhood birds, shown as they’re heard, in illustrations from the finest natural history books of the 1800s. No subscription.'); + await expect(page.locator('.cover .copy > p')).toHaveText('A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s.'); + await expect(page.locator('.cover .facts li')).toHaveText(['From $349. No subscription, no extras.', 'An easy gift: it sets up from their phone.', 'Built by hand in Connecticut. Open source.']); + await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', 'A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s. From $349, no subscription.'); + await expect(page.locator('meta[property="og:description"]')).toHaveAttribute('content', 'A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s. From $349, no subscription.'); // no exploded drawing anywhere: the reservation is its headline, the frame on its kickstand, its line and button await expect(page.locator('.exploded, img[src*="exploded"]')).toHaveCount(0); await expect(page.locator('.close > *')).toHaveCount(2); @@ -185,7 +188,7 @@ test('every section and its key copy is there', async ({ page }) => { await expect(page.locator(`${sel} .field .btn`)).toHaveText('Join the waitlist'); await expect(page.locator(`${sel} .field input`)).toHaveAttribute('placeholder', 'you@example.com'); // the note under the field says the one email, upright where the status that replaces it is italic (W-1021) - await expect(page.locator(`${sel} .wl-note`)).toHaveText('One email when Featherframe is available, and nothing else.'); + await expect(page.locator(`${sel} .wl-note`)).toHaveText(sel === '.cover' ? 'The first batch is small. The waitlist hears first. No spam.' : 'One email when Featherframe is available, and nothing else.'); await expect(page.locator(`${sel} .wl-note`)).toBeVisible(); await expect(page.locator(`${sel} .wl-note`)).toHaveCSS('font-style', 'normal'); const [field, note] = [await page.locator(`${sel} .field`).boundingBox(), await page.locator(`${sel} .wl-note`).boundingBox()]; @@ -373,7 +376,7 @@ test('the waitlist signs up, then asks which size', async ({ page }) => { await shopRoute(page, 'waitlist', { ok: true, details: 'tok-1', state: 'interest', count: 3 }, signups); await shopRoute(page, 'waitlist/details', { ok: true }, answers); await page.goto('/'); - await expect(page.locator('.cover .wl-note')).toHaveText('One email when Featherframe is available, and nothing else.'); + await expect(page.locator('.cover .wl-note')).toHaveText('The first batch is small. The waitlist hears first. No spam.'); await page.fill('#wl-hero-email', 'ada@example.com'); await page.click('.cover .wl button[type=submit]'); await expect.poll(() => signups.length).toBe(1); From 941cebd3739c4beb8314aa40da545e44c5fa9873 Mon Sep 17 00:00:00 2001 From: Wells Riley <884715+wr@users.noreply.github.com> Date: Wed, 14 Oct 2026 14:15:03 -0400 Subject: [PATCH 2/6] The cover's facts say where it is made, how it sets up and what it costs, each with its icon - Hand-assembled in New England, beside Connecticut with a heart in it. - Sets up from your phone. No extra hardware needed. (A phone.) - From $349. No subscription, no camera, no surprises. (A price tag.) The icons are the page's family: a 24-unit grid, a 1.5px line, no fill. The cover's height budget is refit to the taller rows. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V3jLSJPncDMoCx3zZsEeRR --- site/public/index.html | 6 +++--- site/public/styles.css | 27 ++++++++++++--------------- site/test/site.spec.ts | 2 +- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/site/public/index.html b/site/public/index.html index 501e3b91..2408eb98 100644 --- a/site/public/index.html +++ b/site/public/index.html @@ -86,9 +86,9 @@

Let the outside in.

A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s.

    -
  • From $349. No subscription, no extras.
  • -
  • An easy gift: it sets up from their phone.
  • -
  • Built by hand in Connecticut. Open source.
  • +
  • Hand-assembled in New England.
  • +
  • Sets up from your phone. No extra hardware needed.
  • +
  • From $349. No subscription, no camera, no surprises.

The first batch is small. The waitlist hears first. No spam.

diff --git a/site/public/styles.css b/site/public/styles.css index 163a447d..06f158d7 100644 --- a/site/public/styles.css +++ b/site/public/styles.css @@ -163,15 +163,12 @@ h2 { font-weight: 400; text-wrap: balance; } .stage.live .poster { opacity: 0; transition: opacity .6s ease; } .cover .cta .wl { position: relative; z-index: 2; } .cover .copy > p { font-size: var(--t-lede); line-height: var(--lh-lede); margin: 0 0 26px; text-wrap: pretty; } -/* what a buyer asks first, under the sentence: the price, the gift, who makes it. A check before each, the field's - ink, set small and upright. */ -.cover .facts { list-style: none; margin: -8px 0 26px; padding: 0; font-size: var(--t-small); line-height: var(--lh-lede); } -.cover .facts li { position: relative; padding-left: 24px; text-wrap: pretty; } -.cover .facts li + li { margin-top: 4px; } -.cover .facts li::before { - content: ""; position: absolute; left: 2px; top: .42em; width: 10px; height: 5px; - border-left: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); -} +/* what a buyer asks first, under the sentence: who makes it, how it sets up, what it costs. Each with its icon, set + small and upright in the field's ink. */ +.cover .facts { list-style: none; margin: -8px 0 24px; padding: 0; font-size: var(--t-small); line-height: var(--lh-lede); } +.cover .facts li { display: flex; align-items: flex-start; gap: 10px; text-wrap: pretty; } +.cover .facts li + li { margin-top: 6px; } +.cover .facts .icon { margin-top: 0; color: var(--ink); } /* I. The art: a spread, the frame showing the flamingo in its left half, pinned there, centred below the running head, while the text scrolls past. */ @@ -597,7 +594,7 @@ h2 { font-weight: 400; text-wrap: balance; } --W: calc(100vw - 2 * var(--edge)); --V: calc(100svh - var(--nav)); /* under 1000px the sentence is set at the body size, so the frame has room beside it */ - --gnd: 287px; --measure: 400px; + --gnd: 290px; --measure: 400px; --F: clamp(72px, min((var(--W) - .9521 * var(--gnd)) / 5.0466, (var(--W) - var(--measure) - .9521 * var(--gnd)) / 1.2026, (var(--V) - 112px - var(--gnd)) / 1.913), 240px); --ht: calc(1.913 * var(--F) + var(--gnd)); /* the words: the headline's cap line to the note's last baseline */ @@ -623,10 +620,10 @@ h2 { font-weight: 400; text-wrap: balance; } } } @media (max-width: 820px) and (min-aspect-ratio: 1/1) { - .cover { --gnd: 283px; } + .cover { --gnd: 286px; } } @media (min-width: 1000px) and (min-aspect-ratio: 1/1) { - .cover { --gnd: 308px; --measure: calc(17.7 * var(--t-lede)); } + .cover { --gnd: 311px; --measure: calc(17.7 * var(--t-lede)); } .cover .copy > p { font-size: var(--t-lede); } } @media (min-width: 1100px) and (min-aspect-ratio: 1/1) { @@ -636,7 +633,7 @@ h2 { font-weight: 400; text-wrap: balance; } } } @media (min-width: 1200px) and (min-aspect-ratio: 1/1) { - .cover { --gnd: 272px; --measure: calc(22 * var(--t-lede)); } + .cover { --gnd: 275px; --measure: calc(22 * var(--t-lede)); } } /* A portrait window or a tablet: the phone's order, the headline @@ -652,7 +649,7 @@ h2 { font-weight: 400; text-wrap: balance; } --pad: 40px; --g1: calc(.3 * var(--F)); /* the headline's last baseline to the frame's top edge */ --g2: 40px; /* the frame's foot to the sentence */ - --words: 278px; /* the sentence, the facts, the waitlist's field and the note under it (267px under 821px) */ + --words: 281px; /* the sentence, the facts, the waitlist's field and the note under it (269px under 821px) */ --fh: min(max(.38 * var(--V), var(--V) - 2 * var(--pad) - 1.513 * var(--F) - var(--g1) - var(--g2) - var(--words)), .72 * var(--W) / .843); --hb: calc(var(--fh) / .7379); --wb: calc(var(--hb) * .78376); @@ -673,7 +670,7 @@ h2 { font-weight: 400; text-wrap: balance; } } } @media (min-width: 701px) and (max-width: 820px) and (aspect-ratio < 1) { - .cover { --words: 267px; } + .cover { --words: 269px; } } @media (max-width: 820px) { diff --git a/site/test/site.spec.ts b/site/test/site.spec.ts index 82f0e641..791868ba 100644 --- a/site/test/site.spec.ts +++ b/site/test/site.spec.ts @@ -89,7 +89,7 @@ test('every section and its key copy is there', async ({ page }) => { await expect(page.locator('.close .words > p')).toHaveText('A framed e-paper display with no subscription.'); await expect(page.locator('body')).not.toContainText('Reserve'); await expect(page.locator('.cover .copy > p')).toHaveText('A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s.'); - await expect(page.locator('.cover .facts li')).toHaveText(['From $349. No subscription, no extras.', 'An easy gift: it sets up from their phone.', 'Built by hand in Connecticut. Open source.']); + await expect(page.locator('.cover .facts li')).toHaveText(['Hand-assembled in New England.', 'Sets up from your phone. No extra hardware needed.', 'From $349. No subscription, no camera, no surprises.']); await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', 'A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s. From $349, no subscription.'); await expect(page.locator('meta[property="og:description"]')).toHaveAttribute('content', 'A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s. From $349, no subscription.'); // no exploded drawing anywhere: the reservation is its headline, the frame on its kickstand, its line and button From d1526875302b914f45be90923d5d45132a5de99a Mon Sep 17 00:00:00 2001 From: Wells Riley <884715+wr@users.noreply.github.com> Date: Wed, 14 Oct 2026 14:51:48 -0400 Subject: [PATCH 3/6] The cover adds where it works, and Connecticut's heart is solid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - A fourth fact, beside a globe: "Works almost anywhere: 7,000+ public bird stations worldwide." BirdWeather's Great Backyard Bird Count 2026 page counts 7,420 stations reporting that weekend; 7,000+ stays under it. The page still never says "listening station" (the frame does not listen). - The heart inside Connecticut is filled; the outline is the family's line. - The cover's height budget is refit for four rows. On a window wider than 1200px the words' measure is 21 ledes (from 22), so the frame clears them; a portrait window's frame may go to .35 of the screen (from .38); and a small landscape window (701–999px) sets the facts at the fine size, tighter. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V3jLSJPncDMoCx3zZsEeRR --- site/public/index.html | 3 ++- site/public/styles.css | 23 +++++++++++++++-------- site/test/site.spec.ts | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/site/public/index.html b/site/public/index.html index 2408eb98..7458d770 100644 --- a/site/public/index.html +++ b/site/public/index.html @@ -86,8 +86,9 @@

Let the outside in.

A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s.

    -
  • Hand-assembled in New England.
  • +
  • Hand-assembled in New England.
  • Sets up from your phone. No extra hardware needed.
  • +
  • Works almost anywhere: 7,000+ public bird stations worldwide.
  • From $349. No subscription, no camera, no surprises.

The first batch is small. The waitlist hears first. No spam.

diff --git a/site/public/styles.css b/site/public/styles.css index 06f158d7..d21d79ec 100644 --- a/site/public/styles.css +++ b/site/public/styles.css @@ -163,12 +163,13 @@ h2 { font-weight: 400; text-wrap: balance; } .stage.live .poster { opacity: 0; transition: opacity .6s ease; } .cover .cta .wl { position: relative; z-index: 2; } .cover .copy > p { font-size: var(--t-lede); line-height: var(--lh-lede); margin: 0 0 26px; text-wrap: pretty; } -/* what a buyer asks first, under the sentence: who makes it, how it sets up, what it costs. Each with its icon, set +/* what a buyer asks first, under the sentence: who makes it, how it sets up, where it works, what it costs. Each with its icon, set small and upright in the field's ink. */ .cover .facts { list-style: none; margin: -8px 0 24px; padding: 0; font-size: var(--t-small); line-height: var(--lh-lede); } .cover .facts li { display: flex; align-items: flex-start; gap: 10px; text-wrap: pretty; } .cover .facts li + li { margin-top: 6px; } .cover .facts .icon { margin-top: 0; color: var(--ink); } +.cover .facts .icon .solid { fill: currentColor; } /* the heart in Connecticut */ /* I. The art: a spread, the frame showing the flamingo in its left half, pinned there, centred below the running head, while the text scrolls past. */ @@ -594,7 +595,7 @@ h2 { font-weight: 400; text-wrap: balance; } --W: calc(100vw - 2 * var(--edge)); --V: calc(100svh - var(--nav)); /* under 1000px the sentence is set at the body size, so the frame has room beside it */ - --gnd: 290px; --measure: 400px; + --gnd: 292px; --measure: 400px; --F: clamp(72px, min((var(--W) - .9521 * var(--gnd)) / 5.0466, (var(--W) - var(--measure) - .9521 * var(--gnd)) / 1.2026, (var(--V) - 112px - var(--gnd)) / 1.913), 240px); --ht: calc(1.913 * var(--F) + var(--gnd)); /* the words: the headline's cap line to the note's last baseline */ @@ -620,10 +621,16 @@ h2 { font-weight: 400; text-wrap: balance; } } } @media (max-width: 820px) and (min-aspect-ratio: 1/1) { - .cover { --gnd: 286px; } + .cover { --gnd: 288px; } +} +/* a small landscape window: the facts set tighter, so the words, the headline and the frame still share the screen */ +@media (min-width: 701px) and (max-width: 999px) and (min-aspect-ratio: 1/1) { + .cover .facts { margin: -12px 0 18px; font-size: var(--t-fine); line-height: 1.3; } + .cover .facts li + li { margin-top: 2px; } + .cover .copy > p { margin-bottom: 20px; } } @media (min-width: 1000px) and (min-aspect-ratio: 1/1) { - .cover { --gnd: 311px; --measure: calc(17.7 * var(--t-lede)); } + .cover { --gnd: 341px; --measure: calc(17.7 * var(--t-lede)); } .cover .copy > p { font-size: var(--t-lede); } } @media (min-width: 1100px) and (min-aspect-ratio: 1/1) { @@ -633,7 +640,7 @@ h2 { font-weight: 400; text-wrap: balance; } } } @media (min-width: 1200px) and (min-aspect-ratio: 1/1) { - .cover { --gnd: 275px; --measure: calc(22 * var(--t-lede)); } + .cover { --gnd: 305px; --measure: calc(21 * var(--t-lede)); } } /* A portrait window or a tablet: the phone's order, the headline @@ -649,8 +656,8 @@ h2 { font-weight: 400; text-wrap: balance; } --pad: 40px; --g1: calc(.3 * var(--F)); /* the headline's last baseline to the frame's top edge */ --g2: 40px; /* the frame's foot to the sentence */ - --words: 281px; /* the sentence, the facts, the waitlist's field and the note under it (269px under 821px) */ - --fh: min(max(.38 * var(--V), var(--V) - 2 * var(--pad) - 1.513 * var(--F) - var(--g1) - var(--g2) - var(--words)), .72 * var(--W) / .843); + --words: 311px; /* the sentence, the facts, the waitlist's field and the note under it (299px under 821px) */ + --fh: min(max(.35 * var(--V), var(--V) - 2 * var(--pad) - 1.513 * var(--F) - var(--g1) - var(--g2) - var(--words)), .72 * var(--W) / .843); --hb: calc(var(--fh) / .7379); --wb: calc(var(--hb) * .78376); display: flex; flex-direction: column; justify-content: center; min-height: var(--V); padding: var(--pad) 0; @@ -670,7 +677,7 @@ h2 { font-weight: 400; text-wrap: balance; } } } @media (min-width: 701px) and (max-width: 820px) and (aspect-ratio < 1) { - .cover { --words: 269px; } + .cover { --words: 299px; } } @media (max-width: 820px) { diff --git a/site/test/site.spec.ts b/site/test/site.spec.ts index 791868ba..5934e827 100644 --- a/site/test/site.spec.ts +++ b/site/test/site.spec.ts @@ -89,7 +89,7 @@ test('every section and its key copy is there', async ({ page }) => { await expect(page.locator('.close .words > p')).toHaveText('A framed e-paper display with no subscription.'); await expect(page.locator('body')).not.toContainText('Reserve'); await expect(page.locator('.cover .copy > p')).toHaveText('A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s.'); - await expect(page.locator('.cover .facts li')).toHaveText(['Hand-assembled in New England.', 'Sets up from your phone. No extra hardware needed.', 'From $349. No subscription, no camera, no surprises.']); + await expect(page.locator('.cover .facts li')).toHaveText(['Hand-assembled in New England.', 'Sets up from your phone. No extra hardware needed.', 'Works almost anywhere: 7,000+ public bird stations worldwide.', 'From $349. No subscription, no camera, no surprises.']); await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', 'A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s. From $349, no subscription.'); await expect(page.locator('meta[property="og:description"]')).toHaveAttribute('content', 'A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s. From $349, no subscription.'); // no exploded drawing anywhere: the reservation is its headline, the frame on its kickstand, its line and button From d49edcca7255aece0f3149c30829cb49670fad60 Mon Sep 17 00:00:00 2001 From: Wells Riley <884715+wr@users.noreply.github.com> Date: Wed, 14 Oct 2026 15:41:51 -0400 Subject: [PATCH 4/6] The cover's stations are drawn as a bird singing, and the first batch is 50 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - The worldwide fact's globe becomes a songbird with its song in front of its beak, so what is heard reads as birds, not people. Its eye is filled, as Connecticut's heart is. - The note under the signup says the number: "The first batch is just 50 frames. The waitlist hears first. No spam." - A small landscape window (701–999px) sets that note at the fine size with the facts, so it keeps to one line; the height budget is refit. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V3jLSJPncDMoCx3zZsEeRR --- site/public/index.html | 4 ++-- site/public/styles.css | 7 ++++--- site/test/site.spec.ts | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/site/public/index.html b/site/public/index.html index 7458d770..6b0b6fdd 100644 --- a/site/public/index.html +++ b/site/public/index.html @@ -88,10 +88,10 @@

Let the outside in.

  • Hand-assembled in New England.
  • Sets up from your phone. No extra hardware needed.
  • -
  • Works almost anywhere: 7,000+ public bird stations worldwide.
  • +
  • Works almost anywhere: 7,000+ public bird stations worldwide.
  • From $349. No subscription, no camera, no surprises.
-

The first batch is small. The waitlist hears first. No spam.

+

The first batch is just 50 frames. The waitlist hears first. No spam.

diff --git a/site/public/styles.css b/site/public/styles.css index d21d79ec..41e5e38f 100644 --- a/site/public/styles.css +++ b/site/public/styles.css @@ -169,7 +169,7 @@ h2 { font-weight: 400; text-wrap: balance; } .cover .facts li { display: flex; align-items: flex-start; gap: 10px; text-wrap: pretty; } .cover .facts li + li { margin-top: 6px; } .cover .facts .icon { margin-top: 0; color: var(--ink); } -.cover .facts .icon .solid { fill: currentColor; } /* the heart in Connecticut */ +.cover .facts .icon .solid { fill: currentColor; } /* Connecticut's heart, the bird's eye */ /* I. The art: a spread, the frame showing the flamingo in its left half, pinned there, centred below the running head, while the text scrolls past. */ @@ -595,7 +595,7 @@ h2 { font-weight: 400; text-wrap: balance; } --W: calc(100vw - 2 * var(--edge)); --V: calc(100svh - var(--nav)); /* under 1000px the sentence is set at the body size, so the frame has room beside it */ - --gnd: 292px; --measure: 400px; + --gnd: 290px; --measure: 400px; --F: clamp(72px, min((var(--W) - .9521 * var(--gnd)) / 5.0466, (var(--W) - var(--measure) - .9521 * var(--gnd)) / 1.2026, (var(--V) - 112px - var(--gnd)) / 1.913), 240px); --ht: calc(1.913 * var(--F) + var(--gnd)); /* the words: the headline's cap line to the note's last baseline */ @@ -621,13 +621,14 @@ h2 { font-weight: 400; text-wrap: balance; } } } @media (max-width: 820px) and (min-aspect-ratio: 1/1) { - .cover { --gnd: 288px; } + .cover { --gnd: 286px; } } /* a small landscape window: the facts set tighter, so the words, the headline and the frame still share the screen */ @media (min-width: 701px) and (max-width: 999px) and (min-aspect-ratio: 1/1) { .cover .facts { margin: -12px 0 18px; font-size: var(--t-fine); line-height: 1.3; } .cover .facts li + li { margin-top: 2px; } .cover .copy > p { margin-bottom: 20px; } + .cover .cta .wl-note { font-size: var(--t-fine); } } @media (min-width: 1000px) and (min-aspect-ratio: 1/1) { .cover { --gnd: 341px; --measure: calc(17.7 * var(--t-lede)); } diff --git a/site/test/site.spec.ts b/site/test/site.spec.ts index 5934e827..a84ed219 100644 --- a/site/test/site.spec.ts +++ b/site/test/site.spec.ts @@ -188,7 +188,7 @@ test('every section and its key copy is there', async ({ page }) => { await expect(page.locator(`${sel} .field .btn`)).toHaveText('Join the waitlist'); await expect(page.locator(`${sel} .field input`)).toHaveAttribute('placeholder', 'you@example.com'); // the note under the field says the one email, upright where the status that replaces it is italic (W-1021) - await expect(page.locator(`${sel} .wl-note`)).toHaveText(sel === '.cover' ? 'The first batch is small. The waitlist hears first. No spam.' : 'One email when Featherframe is available, and nothing else.'); + await expect(page.locator(`${sel} .wl-note`)).toHaveText(sel === '.cover' ? 'The first batch is just 50 frames. The waitlist hears first. No spam.' : 'One email when Featherframe is available, and nothing else.'); await expect(page.locator(`${sel} .wl-note`)).toBeVisible(); await expect(page.locator(`${sel} .wl-note`)).toHaveCSS('font-style', 'normal'); const [field, note] = [await page.locator(`${sel} .field`).boundingBox(), await page.locator(`${sel} .wl-note`).boundingBox()]; @@ -376,7 +376,7 @@ test('the waitlist signs up, then asks which size', async ({ page }) => { await shopRoute(page, 'waitlist', { ok: true, details: 'tok-1', state: 'interest', count: 3 }, signups); await shopRoute(page, 'waitlist/details', { ok: true }, answers); await page.goto('/'); - await expect(page.locator('.cover .wl-note')).toHaveText('The first batch is small. The waitlist hears first. No spam.'); + await expect(page.locator('.cover .wl-note')).toHaveText('The first batch is just 50 frames. The waitlist hears first. No spam.'); await page.fill('#wl-hero-email', 'ada@example.com'); await page.click('.cover .wl button[type=submit]'); await expect.poll(() => signups.length).toBe(1); From 58b12e1870a206fbdd21c2bd77a957f3f0601582 Mon Sep 17 00:00:00 2001 From: Wells Riley <884715+wr@users.noreply.github.com> Date: Wed, 14 Oct 2026 15:53:41 -0400 Subject: [PATCH 5/6] The cover's singing bird is redrawn: a plain songbird with one note Wells found the last bird inelegant at its size. This one is a few clean strokes (back, breast, wing, beak, two legs, a filled eye) drawn at four fifths, with a single filled note above its beak, chosen from four drawn side by side at 24px (14 Oct 2026). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V3jLSJPncDMoCx3zZsEeRR --- site/public/index.html | 2 +- site/public/styles.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/public/index.html b/site/public/index.html index 6b0b6fdd..03e5bf95 100644 --- a/site/public/index.html +++ b/site/public/index.html @@ -88,7 +88,7 @@

Let the outside in.

  • Hand-assembled in New England.
  • Sets up from your phone. No extra hardware needed.
  • -
  • Works almost anywhere: 7,000+ public bird stations worldwide.
  • +
  • Works almost anywhere: 7,000+ public bird stations worldwide.
  • From $349. No subscription, no camera, no surprises.

The first batch is just 50 frames. The waitlist hears first. No spam.

diff --git a/site/public/styles.css b/site/public/styles.css index 41e5e38f..b2e1b2e8 100644 --- a/site/public/styles.css +++ b/site/public/styles.css @@ -169,7 +169,7 @@ h2 { font-weight: 400; text-wrap: balance; } .cover .facts li { display: flex; align-items: flex-start; gap: 10px; text-wrap: pretty; } .cover .facts li + li { margin-top: 6px; } .cover .facts .icon { margin-top: 0; color: var(--ink); } -.cover .facts .icon .solid { fill: currentColor; } /* Connecticut's heart, the bird's eye */ +.cover .facts .icon .solid { fill: currentColor; } /* Connecticut's heart; the bird's eye and its note */ /* I. The art: a spread, the frame showing the flamingo in its left half, pinned there, centred below the running head, while the text scrolls past. */ From 0fc188151dc4c1b2c114610c08694e81fd374c7e Mon Sep 17 00:00:00 2001 From: Wells Riley <884715+wr@users.noreply.github.com> Date: Wed, 14 Oct 2026 15:56:16 -0400 Subject: [PATCH 6/6] Connecticut's heart is smaller, so the state's outline leads Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V3jLSJPncDMoCx3zZsEeRR --- site/public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/public/index.html b/site/public/index.html index 03e5bf95..256d56ad 100644 --- a/site/public/index.html +++ b/site/public/index.html @@ -86,7 +86,7 @@

Let the outside in.

A picture frame that shows the birds singing near your home, each in its hand-painted portrait from the 1800s.

    -
  • Hand-assembled in New England.
  • +
  • Hand-assembled in New England.
  • Sets up from your phone. No extra hardware needed.
  • Works almost anywhere: 7,000+ public bird stations worldwide.
  • From $349. No subscription, no camera, no surprises.