diff --git a/Public/landing.css b/Public/landing.css index ba64949..1cd9c7e 100644 --- a/Public/landing.css +++ b/Public/landing.css @@ -267,6 +267,21 @@ a { color: inherit; } .cards-3 .card:hover { box-shadow: var(--shadow-lg); } .cards-3 .card-icon { margin-left: 0; } .card h3 { margin: 0 0 10px; font-size: 17.5px; letter-spacing: -.015em; font-weight: 700; } +/* A card inside a group: the group owns the h3, so the card's title is an h4 + and has to keep the same look. Styled together so the two cannot drift. */ +.card h4 { margin: 0 0 10px; font-size: 17.5px; letter-spacing: -.015em; font-weight: 700; } +/* Groups exist so the page can gain features without gaining top-level + sections (relay#44). Quieter than an h2 band head, louder than a card. */ +.group-head { + margin: 46px 0 22px; + font-size: 15px; + font-weight: 700; + letter-spacing: .06em; + text-transform: uppercase; + color: var(--ink-2); + text-align: center; +} +.group-head:first-of-type { margin-top: 30px; } .card p { margin: 0; color: var(--ink-2); font-size: 15px; line-height: 1.6; } .card-icon { display: flex; align-items: center; justify-content: center; diff --git a/Resources/Views/messages.leaf b/Resources/Views/messages.leaf index 31f9d63..0ce5d7c 100644 --- a/Resources/Views/messages.leaf +++ b/Resources/Views/messages.leaf @@ -26,10 +26,11 @@ accidentally end up in.

+

Saying something

-

One-to-one and groups

+

One-to-one and groups

Text, photos and voice notes, on your own or with everybody. Mention someone and they'll hear about it even if they've muted the group — @@ -38,7 +39,7 @@

-

Voice calls

+

Voice calls

Ring someone directly. The audio is encrypted between the two phones, and the check that you're talking to the right person rides inside the @@ -47,60 +48,28 @@

-

Stories

+

Stories

Post a picture, a few words or a link, for as long as you choose — an hour, or a week. Only your contacts see it, and the server is never told how long it's meant to last.

-
- -

Chats that clear themselves

-

- Set a conversation to tidy up after an hour, a day or a week. It - applies to both phones, and the server isn't told a - conversation is timed, let alone for how long. -

-
-
- -

No phone number

-

- Sign up with a username and an email. People find you by the name they - already call you, and your number stays out of it entirely. -

-
-
- -

You'll know if a key changes

-

- If someone's phone is replaced — or somebody tries to stand in for - them — the app stops and tells you, with one thing you can do about - it. There's no way to wave it away, because there shouldn't be. -

-
-

Fix the typo

+

Fix the typo

Change a message after you've sent it. It turns up on the other phone as the same message, marked edited — not as a second one nobody can tell apart from the first.

-
- -

Find what you said

-

- Search across every conversation you have. It happens on your phone, - because the server has never been able to read a word of it — which - is also why nobody can search it but you. -

-
+
+

On your own time

+
-

Write now, send later

+

Write now, send later

Say it while you're thinking of it and let it arrive at a decent hour. The message waits on your phone, not on the @@ -109,7 +78,7 @@

-

Nudge yourself, quiet a thread

+

Nudge yourself, quiet a thread

Ask to be reminded to reply, or put a conversation to sleep for the afternoon. Both are kept on your phone — the server has no idea a @@ -117,17 +86,46 @@

- -

Link previews, made at your end

+ +

Chats that clear themselves

- When you share a link, your phone builds the little - card — so the site never hears from the person you sent it to. It - does hear from you, which is why there's a switch to turn it off. + Set a conversation to tidy up after an hour, a day or a week. It + applies to both phones, and the server isn't told a + conversation is timed, let alone for how long. +

+
+
+ +

Find what you said

+

+ Search across every conversation you have. It happens on your phone, + because the server has never been able to read a word of it — which + is also why nobody can search it but you. +

+
+
+

Knowing it stays yours

+
+
+ +

No phone number

+

+ Sign up with a username and an email. People find you by the name they + already call you, and your number stays out of it entirely. +

+
+
+ +

You'll know if a key changes

+

+ If someone's phone is replaced — or somebody tries to stand in for + them — the app stops and tells you, with one thing you can do about + it. There's no way to wave it away, because there shouldn't be.

-

Photos don't say where you were

+

Photos don't say where you were

Pictures and video you send from the photo button are rebuilt on the way out, so the coordinates your camera wrote in don't travel with @@ -136,9 +134,18 @@ which is rather the point of a file, so check what's in it first.

+
+ +

Link previews, made at your end

+

+ When you share a link, your phone builds the little + card — so the site never hears from the person you sent it to. It + does hear from you, which is why there's a switch to turn it off. +

+
-

Lock it behind your face

+

Lock it behind your face

Your phone is the one place your messages are readable, so it's the one place worth locking. Face, fingerprint or your usual PIN, and it @@ -147,6 +154,7 @@

+ diff --git a/Tests/FlamenetRelayTests/PublicPagesTests.swift b/Tests/FlamenetRelayTests/PublicPagesTests.swift index 0fbdb41..30474dd 100644 --- a/Tests/FlamenetRelayTests/PublicPagesTests.swift +++ b/Tests/FlamenetRelayTests/PublicPagesTests.swift @@ -228,6 +228,45 @@ final class PublicPagesTests: XCTestCase { "the page no longer states that the size it keeps is exact") } + /// 🔴 **The shape has to survive the next fifty features (relay#44).** + /// + /// The landing page says what the product IS in three cards and sends the + /// detail to `/messages`, `/files`, `/wallet`. That split already happened. + /// What it did not come with was anything that KEEPS it — and `/messages` + /// had quietly reached thirteen cards in one flat list, with 52 unstarted + /// ideas still to land. A flat list of a hundred is a specification, and + /// nobody reads it. + /// + /// So features group by what a person is trying to do: the count inside a + /// group grows freely, the number of groups does not. This test fails when + /// a page goes back to a flat list long enough to stop being skimmable, + /// which is the moment to add a group rather than a fourteenth card. + func testFeaturePagesStayGroupedRatherThanFlat() throws { + // Below this, a flat list is still fine — grouping four cards is noise. + let flatCeiling = 8 + + for name in ["landing", "messages", "files", "wallet"] { + let page = try String(contentsOf: views.appendingPathComponent("\(name).leaf"), + encoding: .utf8) + let cards = page.components(separatedBy: "
").count - 1 + let groups = page.components(separatedBy: "class=\"group-head\"").count - 1 + if cards > flatCeiling { + XCTAssertGreaterThan( + groups, 0, + "\(name).leaf has \(cards) cards in a flat list. Group them by what " + + "somebody is trying to do — see relay#44 — rather than adding " + + "another card to the pile.") + } + // A group per card is the same problem wearing a hat. + if groups > 0 { + XCTAssertLessThanOrEqual( + groups, 5, + "\(name).leaf has \(groups) groups. The point is that the TOP level " + + "stays small; put the feature inside an existing group.") + } + } + } + func testTheSafetyPageCarriesAReachableContact() throws { let page = try String(contentsOf: views.appendingPathComponent("safety.leaf"), encoding: .utf8)