Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion apps/marketing/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const newsreader = Newsreader({
subsets: ["latin"],
weight: "variable",
axes: ["opsz"],
style: ["normal", "italic"],
display: "swap",
});

Expand Down
45 changes: 31 additions & 14 deletions apps/marketing/src/components/GoogleOneTap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,44 @@ export function GoogleOneTap() {
window.google.accounts.id.prompt();
};

// Load or reuse the GSI script
const existing = document.querySelector(
`script[src="${GOOGLE_GSI_SCRIPT_URL}"]`
);
// Load or reuse the GSI script. The 98 KB script is ~75% unused on a
// marketing page and competes with the hero for bandwidth, so wait until
// the browser is idle (or shortly after load where idle callbacks are
// unsupported) before injecting it.
const load = () => {
const existing = document.querySelector(
`script[src="${GOOGLE_GSI_SCRIPT_URL}"]`
);

if (existing) {
if (window.google?.accounts?.id) {
init();
if (existing) {
if (window.google?.accounts?.id) {
init();
} else {
existing.addEventListener("load", init);
}
} else {
existing.addEventListener("load", init);
const script = document.createElement("script");
script.src = GOOGLE_GSI_SCRIPT_URL;
script.async = true;
script.defer = true;
script.onload = init;
document.head.appendChild(script);
}
};

let idleId: number | undefined;
let timeoutId: number | undefined;
// typeof, not `in`: with lib.dom declaring requestIdleCallback the `in`
// guard narrows `window` to `never` in the else branch (TS2339 in CI).
if (typeof window.requestIdleCallback === "function") {
idleId = window.requestIdleCallback(load, { timeout: 4000 });
} else {
const script = document.createElement("script");
script.src = GOOGLE_GSI_SCRIPT_URL;
script.async = true;
script.defer = true;
script.onload = init;
document.head.appendChild(script);
timeoutId = window.setTimeout(load, 2500);
}

return () => {
if (idleId !== undefined) window.cancelIdleCallback(idleId);
if (timeoutId !== undefined) window.clearTimeout(timeoutId);
if (window.google?.accounts?.id) {
window.google.accounts.id.cancel();
}
Expand Down
2 changes: 1 addition & 1 deletion apps/marketing/src/components/SiteFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function SiteFooter() {
<Link href="/" className="flex items-center gap-2">
<Image
src="/android-chrome-192x192.png"
alt="PageSpace"
alt=""
width={20}
height={20}
className="rounded"
Expand Down
2 changes: 1 addition & 1 deletion apps/marketing/src/components/SiteNavbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function SiteNavbar() {
>
<Image
src="/android-chrome-192x192.png"
alt="PageSpace"
alt=""
width={20}
height={20}
className="rounded"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export function PageTypeCarouselSection() {
>
<CarouselContent className="carousel-track -ml-6">
{PAGE_TYPES.map((t, i) => (
<CarouselItem key={t.id} className="slide basis-[86%] max-w-[720px] pl-6" data-active={i === current} aria-label={t.label}>
<CarouselItem key={t.id} className="slide basis-[86%] max-w-[720px] pl-6" data-active={i === current} aria-hidden={i !== current} aria-label={t.label}>
<ScaledFrame designWidth={CARD_W} designHeight={CARD_H}>
<MockCard type={t} />
</ScaledFrame>
Expand Down
13 changes: 12 additions & 1 deletion apps/marketing/src/components/sections/landing/landing.css
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,9 @@
/* Emphasis via opacity only — scaling inactive slides made the edge-to-edge
gaps look uneven (a shrunk neighbor reads as a wider gap). Uniform size keeps
every gap identical. */
& .slide { transition: opacity .3s ease; opacity: 0.5; }
/* 0.90 is the lowest opacity at which muted 12px text inside a dimmed slide
still clears 4.5:1 on the card (axe blends slide opacity into the text). */
& .slide { transition: opacity .3s ease; opacity: 0.90; }
& .slide[data-active="true"] { opacity: 1; }
/* Reserve two lines so captions of different lengths don't shift the page. */
& .caption { text-align: center; margin-top: 12px; color: var(--muted-foreground); font-size: var(--t-body); line-height: 1.5; max-width: 600px; margin-left: auto; margin-right: auto; min-height: 48px; }
Expand Down Expand Up @@ -340,6 +342,7 @@
& .doc .h3 { font-size: 22px; font-weight: 700; margin: 0 0 10px; letter-spacing: -0.01em; }
& .doc p { font-size: 14px; line-height: 1.65; color: var(--muted-foreground); margin: 0 0 10px; }
& .doc p .fg { color: var(--foreground); }
& .doc ul { margin: 0; padding: 0; list-style: disc; }
& .doc li { font-size: 14px; color: var(--muted-foreground); margin-left: 20px; line-height: 1.6; }
& .stream { display: flex; flex-direction: column; gap: 14px; height: 100%; padding: 18px 20px; }
& .msg { display: flex; gap: 11px; align-items: flex-start; }
Expand Down Expand Up @@ -487,6 +490,14 @@

@media (max-width: 820px) {
.lp .aw-body { height: 500px; }
/* Below 820px the hero window always lays out at ScaledAppWindow's 760px
design width, which with the 500px body above makes it 547px tall, and is
then scaled to fit. Reserve exactly that scaled box (width x 547/760) in
CSS so the server HTML is already the right height; the client effect only
adds the transform. Without this the window shrank ~270px on hydration
(mobile CLS 0.20). Keep the ratio in sync with the mock's chrome height. */
.lp .appwin-outer { aspect-ratio: 760 / 547; overflow: hidden; }
.lp .appwin-inner { width: 760px; transform-origin: top left; }
}

/* The hero app window keeps all three panes on every screen; below 900px the
Expand Down
6 changes: 4 additions & 2 deletions apps/marketing/src/components/sections/landing/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ function DocMock() {
<div className="h3">Q1 Planning</div>
<p>A rich-text editor built on TipTap with markdown shortcuts and code blocks. Write naturally — the toolbar stays out of your way.</p>
<p><span className="fg">AI edits your document directly.</span> Ask the sidebar chat to rewrite a paragraph and changes appear inline.</p>
<li>Real-time collaboration &amp; version history</li>
<ul>
<li>Real-time collaboration &amp; version history</li>
</ul>
</div>
</>
);
Expand Down Expand Up @@ -159,7 +161,7 @@ function TaskMock() {
<table className="tl">
<thead>
<tr>
<th style={{ width: 34 }} />
<th style={{ width: 34 }}><span className="sr-only">Done</span></th>
<th>Task</th>
<th style={{ width: 104 }}>Status</th>
<th style={{ width: 88 }}>Priority</th>
Expand Down