Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4ad9d8d
feat(nav): add "Get updates" newsletter entry to the header
RisingOrange Aug 9, 2026
08caefe
feat(newsletter): route the homepage subscribe box to the /join movem…
RisingOrange Aug 9, 2026
b588aa5
copy(newsletter): relabel the homepage box for the /join handoff
RisingOrange Aug 9, 2026
bb17cb2
feat: dedicated /subscribe landing for the newsletter entry points
RisingOrange Aug 9, 2026
ef31caa
copy: /subscribe h1 -> "Get PauseAI updates"
RisingOrange Aug 9, 2026
cda0dc9
feat(subscribe): dedicated newsletter form with local-group + Substac…
RisingOrange Aug 12, 2026
067e214
fix(subscribe): address review findings on the do-more continuation
RisingOrange Aug 12, 2026
3ffaca5
copy: align homepage newsletter box heading to Subscribe
RisingOrange Aug 12, 2026
2cf203a
fix(subscribe): stop form inputs overflowing the card on mobile
RisingOrange Aug 12, 2026
ca0e7f8
fix(subscribe): reframe the do-more step as its own "Get involved" flow
RisingOrange Aug 12, 2026
bda7688
fix(subscribe): put opt-in labels beside the checkbox, not below
RisingOrange Aug 12, 2026
7eb4242
fix(subscribe): use 'chapter' wording and mark the required email field
RisingOrange Aug 12, 2026
be5d55e
feat(subscribe): require name, country and city like the /join form
RisingOrange Aug 12, 2026
d2ead9d
fix(nav): render Subscribe as a normal nav item to stop the header wr…
RisingOrange Aug 12, 2026
6e00712
fix(nav): give the header its own width so all six top-level items fi…
RisingOrange Aug 12, 2026
5449218
fix(newsletter): make the handoff work without JS, and latch the /joi…
RisingOrange Aug 13, 2026
dc27fde
fix(subscribe): review round 2 — hover contrast, consent restore, upd…
RisingOrange Aug 13, 2026
1ed9d65
style(subscribe): match the /join form width
RisingOrange Aug 16, 2026
54eb6cf
fix(subscribe): match /join's heading rhythm, and drop confirmations …
RisingOrange Aug 16, 2026
8d914d4
copy(subscribe): label the do-more step 'Continue', not 'Submit'
RisingOrange Aug 16, 2026
d50eb45
fix(subscribe): hide the second thank-you on the no-intent path too
RisingOrange Aug 16, 2026
8a8b663
fix(subscribe): restart the flow when Subscribe is clicked from the p…
RisingOrange Aug 16, 2026
07f4ca9
merge: bring in main, and put the subscribe form behind Turnstile
RisingOrange Aug 16, 2026
1739350
refactor(subscribe): keep the form fields in one object
RisingOrange Aug 16, 2026
cfdadf7
feat(subscribe): record which form a signup came from
RisingOrange Aug 16, 2026
d523039
fix(onboarding): don't let the anti-spam check block deploy previews
RisingOrange Aug 16, 2026
9bcbf2b
review: make the liveness invariant structural, and document the flip
RisingOrange Aug 16, 2026
35b3760
merge: take the deploy-preview anti-spam fix (#1046)
RisingOrange Aug 16, 2026
750646e
fix(onboarding): don't render the widget when no token is required
RisingOrange Aug 16, 2026
c60bb1c
merge: take the widget-only-when-required change, and apply it to Sub…
RisingOrange Aug 16, 2026
ecdea37
merge: main, now that the anti-spam preview fix has landed
RisingOrange Aug 16, 2026
839df02
copy(subscribe): Jonathan's wording for the Substack opt-in and the h…
RisingOrange Aug 17, 2026
867bdde
Merge remote-tracking branch 'origin/main' into prototype/newsletter-…
RisingOrange Aug 17, 2026
db0f2d1
copy(subscribe): drop the chapter opt-in's sub-line
RisingOrange Aug 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
"header_about": "About",
"header_community": "Community",
"header_get_involved": "Get involved",
"header_subscribe": "Subscribe",
"home_newsletter_heading": "Subscribe",
"home_newsletter_button": "Sign up",
"home_newsletter_description": "Sign up to our newsletter and receive updates about how to support our movement.",
"header_our_strategy": "Our strategy",
"header_why": "Why this matters",
"header_find_group": "Find your local group",
Expand Down
7 changes: 6 additions & 1 deletion src/lib/components/Home.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@
</Block>
</section>

<NewsletterSignup />
<NewsletterSignup
handoffHref="/subscribe"
headingText={m.home_newsletter_heading()}
descriptionText={m.home_newsletter_description()}
buttonText={m.home_newsletter_button()}
/>

<style>
.divider {
Expand Down
33 changes: 26 additions & 7 deletions src/lib/components/NewsletterSignup.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<script lang="ts">
import * as m from '$lib/paraglide/messages.js'
import { tick } from 'svelte'
import { goto } from '$app/navigation'
import { localizeHref } from '$lib/paraglide/runtime'

interface Props {
// Localizable text
Expand All @@ -10,14 +12,17 @@
descriptionText?: m.LocalizedString
// State variable for email binding (for external use)
email?: string
/** When set, submit routes to this signup path with the email prefilled, instead of Substack. */
handoffHref?: string
}

let {
placeholderText = m.newsletter_email_placeholder(),
buttonText = m.newsletter_button(),
headingText = m.newsletter_heading(),
descriptionText = m.newsletter_description(),
email = $bindable('')
email = $bindable(''),
handoffHref = undefined
}: Props = $props()

// State for showing success message
Expand All @@ -27,6 +32,15 @@
const handleSubmit = async (e: Event) => {
e.preventDefault()

// Newsletter entry that feeds the CRM: hand off to the signup flow with the
// email prefilled, rather than subscribing to Substack directly.
if (handoffHref) {
const dest = `${localizeHref(handoffHref)}?subscribe-email=${encodeURIComponent(email)}`
email = ''
void goto(dest)
return
}

// Show success message immediately
showSuccess = true

Expand All @@ -46,25 +60,30 @@
<h3 class="toc-exclude">{headingText}</h3>
<p>{descriptionText}</p>

<!-- Direct POST to Substack API in new tab -->
<!-- Direct POST to Substack API in new tab, unless handing off to the signup
flow — then the native target mirrors the hydrated goto(), so submitting
before hydration (or without JS) still lands on the flow rather than
subscribing straight to Substack. -->
<form
bind:this={formElement}
action="https://pauseai.substack.com/api/v1/free"
method="POST"
target="_blank"
action={handoffHref ? localizeHref(handoffHref) : 'https://pauseai.substack.com/api/v1/free'}
method={handoffHref ? 'GET' : 'POST'}
target={handoffHref ? undefined : '_blank'}
onsubmit={handleSubmit}
>
<div class="input-group">
<input
type="email"
name="email"
name={handoffHref ? 'subscribe-email' : 'email'}
bind:value={email}
placeholder={placeholderText}
aria-label={placeholderText}
required
enterkeyhint="done"
/>
<input type="hidden" name="source" value="pauseai_website" />
{#if !handoffHref}
<input type="hidden" name="source" value="pauseai_website" />
{/if}
<button type="submit">{buttonText}</button>
</div>
</form>
Expand Down
16 changes: 16 additions & 0 deletions src/lib/components/navbar/narrow/NarrowNavbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import Logo from '$lib/components/logo.svelte'
import { localizeHref } from '$lib/paraglide/runtime'
import Menu from '@lucide/svelte/icons/menu'
import Mail from '@lucide/svelte/icons/mail'
import X from '@lucide/svelte/icons/x'
import ChevronDown from '@lucide/svelte/icons/chevron-down'
import { onMount } from 'svelte'
Expand Down Expand Up @@ -82,6 +83,15 @@
{/each}
</div>
</details>
{:else if item.mail}
<LinkWithoutIcon
href={item.href}
class="panel-link get-updates"
aria-current={isCurrent(item) ? 'page' : undefined}
>
<span>{item.label}</span>
<Mail size="1em" />
</LinkWithoutIcon>
{:else}
<LinkWithoutIcon
href={item.href}
Expand Down Expand Up @@ -154,6 +164,12 @@
border-radius: 4px;
}

.panel :global(.panel-link.get-updates) {
display: flex;
align-items: center;
gap: 0.5rem;
}

.panel :global(.panel-link.c2a) {
color: var(--brand);
}
Expand Down
3 changes: 3 additions & 0 deletions src/lib/components/navbar/navItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export interface NavItem {
external?: boolean
/** Render as a call-to-action (brand-coloured) link. */
c2a?: boolean
/** Show a leading mail icon; the newsletter / updates entry. */
mail?: boolean
/** Sub-items shown in a dropdown (desktop) / accordion (mobile). */
children?: NavItem[]
}
Expand Down Expand Up @@ -43,6 +45,7 @@ export function getNavItems(): NavItem[] {
{ label: m.header_start_group(), href: '/national-groups' }
]
},
{ label: m.header_subscribe(), href: '/subscribe', mail: true },
{ label: m.header_donate(), href: '/donate', c2a: true }
]
}
38 changes: 38 additions & 0 deletions src/lib/components/navbar/universal/UniversalNavbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,43 @@
text-transform: uppercase;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}

/* The "Get updates" nav item (WideNavbar) is a bare link, not a Navlink, so it
needs its own colour/spacing here where .nav-links is in scope: matches the
.navlink rhythm and overrides the global `a` colour. */
.nav-links :global(.get-updates) {
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-family: var(--font-heading);
font-weight: 700;
font-size: 1.1rem;
color: var(--text);
text-decoration: none;
white-space: nowrap;
padding: 0 0.5rem;
margin-left: 0.5rem;
margin-right: -0.5rem;
}

.nav-links :global(.get-updates.inverted) {
color: white;
}

.nav-links :global(.get-updates:hover) {
color: var(--brand);
text-decoration: underline;
}

/* On the hero's orange band the brand-coloured hover would be invisible.
Matches .navlink.inverted:hover. */
.nav-links :global(.get-updates.inverted:hover) {
color: black;
}

.nav-links :global(.get-updates.active) {
color: var(--brand-subtle);
}
</style>
13 changes: 13 additions & 0 deletions src/lib/components/navbar/wide/WideNavbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
import UniversalNavbar from '../universal/UniversalNavbar.svelte'
import Navlink from '../Navlink.svelte'
import NavDropdown from '../NavDropdown.svelte'
import MailIcon from '@lucide/svelte/icons/mail'
import LinkWithoutIcon from '$lib/components/LinkWithoutIcon.svelte'
import { page } from '$app/state'
import { localizeHref } from '$lib/paraglide/runtime'
import type { NavItem } from '../navItems'

interface Props {
Expand All @@ -17,6 +21,15 @@
{#each items as item, i (item.label)}
{#if item.children}
<NavDropdown {item} {inverted} />
{:else if item.mail}
{@const active = !!item.href && localizeHref(page.url.pathname) === localizeHref(item.href)}
<LinkWithoutIcon
href={item.href}
class="get-updates{inverted ? ' inverted' : ''}{active ? ' active' : ''}"
>
<MailIcon size="0.85em" />
<span>{item.label}</span>
</LinkWithoutIcon>
{:else}
<Navlink {inverted} first={i === 0} href={item.href} c2a={item.c2a} external={item.external}>
{item.label}
Expand Down
Loading
Loading