diff --git a/AGENTS.md b/AGENTS.md index 068a539..72d129c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -287,8 +287,15 @@ landing page read as machine-generated. Keep it that way when extending it: headings or nav any more, so the case you write is the case that renders. - **No em dashes in site copy.** Use a full stop or a comma. `src/main.test.jsx` guards this against the rendered DOM. -- **Radius scale is 6-14px** for site chrome. The capsule (`99px`) radius is reserved - for progress bars and for the in-phone surfaces that mirror native iOS controls. +- **No capsule buttons anywhere.** Site chrome sits on a 6-14px radius scale; the + in-phone buttons use ~7px, which is `RoundedRectangle(cornerRadius: 18)` from the + Swift consent sheets at the stage's scale. The `99px` radius is reserved for + progress bars, slider tracks, sheet grab handles, the iOS switch and the toast, + all of which are capsule in the native app too. An e2e test enforces this. +- **`.app-screen` children carry `flex-shrink: 0`.** It is a column flex container, + so without it a screen taller than the stage squeezes the last card instead of + scrolling. That silently collapsed the "Lumen noticed" card to a 14px sliver at + common laptop sizes; an e2e test now guards it. - **One reveal per section.** `FadeIn` is opacity plus a short travel, with no delay prop and no per-card stagger. Do not wrap individual grid items in it. - **Icons come from lucide-react**, never emoji or dingbat glyphs. diff --git a/CLAUDE.md b/CLAUDE.md index 068a539..72d129c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -287,8 +287,15 @@ landing page read as machine-generated. Keep it that way when extending it: headings or nav any more, so the case you write is the case that renders. - **No em dashes in site copy.** Use a full stop or a comma. `src/main.test.jsx` guards this against the rendered DOM. -- **Radius scale is 6-14px** for site chrome. The capsule (`99px`) radius is reserved - for progress bars and for the in-phone surfaces that mirror native iOS controls. +- **No capsule buttons anywhere.** Site chrome sits on a 6-14px radius scale; the + in-phone buttons use ~7px, which is `RoundedRectangle(cornerRadius: 18)` from the + Swift consent sheets at the stage's scale. The `99px` radius is reserved for + progress bars, slider tracks, sheet grab handles, the iOS switch and the toast, + all of which are capsule in the native app too. An e2e test enforces this. +- **`.app-screen` children carry `flex-shrink: 0`.** It is a column flex container, + so without it a screen taller than the stage squeezes the last card instead of + scrolling. That silently collapsed the "Lumen noticed" card to a 14px sliver at + common laptop sizes; an e2e test now guards it. - **One reveal per section.** `FadeIn` is opacity plus a short travel, with no delay prop and no per-card stagger. Do not wrap individual grid items in it. - **Icons come from lucide-react**, never emoji or dingbat glyphs. diff --git a/Lumen/Components/EmptyStateView.swift b/Lumen/Components/EmptyStateView.swift index 548c801..764c53a 100644 --- a/Lumen/Components/EmptyStateView.swift +++ b/Lumen/Components/EmptyStateView.swift @@ -32,7 +32,7 @@ struct EmptyStateView: View { .font(.body.bold()) .padding(.horizontal, 24) .padding(.vertical, 10) - .background(Color("MuhaBrown"), in: Capsule()) + .background(Color("MuhaBrown"), in: RoundedRectangle(cornerRadius: 18)) .foregroundStyle(Color("MuhaCream")) } .padding(.top, 4) diff --git a/e2e/live-demo.e2e.js b/e2e/live-demo.e2e.js index 10a6329..2664adb 100644 --- a/e2e/live-demo.e2e.js +++ b/e2e/live-demo.e2e.js @@ -86,6 +86,49 @@ test.describe('Lumen live demo', () => { await expect(page.locator('.app-fullscreen')).toHaveCount(0); }); + test('preview rows keep their height instead of being crushed', async ({ page }) => { + // .app-screen is a column flex container, so its children shrink below + // their own content by default rather than letting the screen scroll. That + // squeezed the "Lumen noticed" card from 106px down to 14px at common + // laptop sizes. Check the short viewport, where the squeeze was worst. + await page.setViewportSize({ width: 1280, height: 720 }); + await page.goto('/'); + + const crushed = await page.evaluate(() => { + const screen = document.querySelector('.app-preview-stage .app-screen'); + return [...screen.children] + .filter(el => el.scrollHeight > el.clientHeight + 2) + .map(el => `${el.className}: ${el.scrollHeight} > ${el.clientHeight}`); + }); + + expect(crushed).toEqual([]); + }); + + test('no button in the preview or its chrome is capsule-shaped', async ({ page }) => { + // The native app uses RoundedRectangle(cornerRadius: 18) for its buttons; + // capsules there are grab handles, progress bars and the iOS switch. A + // fully-rounded button is the landing-page tell we removed, so keep it out. + await page.goto('/'); + await page.locator('#demo').getByRole('button', { name: /review evening scene/i }).click(); + await expect(page.locator('#demo').getByText(/why lumen noticed/i)).toBeVisible(); + + const pills = await page.evaluate(() => { + const out = []; + for (const el of document.querySelectorAll('button, a[href]')) { + if (el.offsetParent === null) continue; + const h = el.getBoundingClientRect().height; + const cs = getComputedStyle(el); + if (!h || cs.borderRadius.includes('%')) continue; // circular icon buttons are fine + if (parseFloat(cs.borderRadius) >= h / 2 - 0.5) { + out.push(`${el.className}: r=${cs.borderRadius} h=${Math.round(h)}`); + } + } + return out; + }); + + expect(pills).toEqual([]); + }); + test('mobile stage is tappable without a nested bezel', async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await page.goto('/'); diff --git a/src/App.css b/src/App.css index fd58074..7f791f0 100644 --- a/src/App.css +++ b/src/App.css @@ -1092,16 +1092,6 @@ p { flex-wrap: wrap; } -.chat-chip { - padding: 4px 9px; - border-radius: 99px; - border: 1px solid rgba(255,255,255,0.1); - background: rgba(255,255,255,0.04); - color: rgba(232,224,212,0.52); - font-size: 9px; - cursor: pointer; - font-family: inherit; -} .chat-chip.apply { border-color: rgba(200,184,154,0.3); @@ -1357,20 +1347,6 @@ p { justify-content: center; } -.chapter-strip button { - display: flex; - align-items: center; - gap: 6px; - padding: 7px 13px; - border-radius: 99px; - border: 1px solid rgba(255,255,255,0.07); - background: transparent; - color: rgba(232,224,212,0.38); - font-size: 11px; - font-family: inherit; - cursor: pointer; - transition: color 0.2s, border-color 0.2s, background 0.2s; -} .chapter-strip button:hover { color: rgba(232,224,212,0.7); @@ -1414,6 +1390,14 @@ p { gap: 5px; } +/* Column flex children shrink below their content by default, so a screen + taller than the stage squeezes the last card instead of scrolling: the + "Lumen noticed" card collapsed from 106px to 14px at common laptop sizes. + Hold every row at its natural height and let .app-screen scroll. */ +.app-screen > * { + flex-shrink: 0; +} + .app-screen.dimmed { opacity: 0.3; filter: blur(1.5px); @@ -1915,7 +1899,7 @@ p { width: 100%; margin-top: 6px; padding: 8px 14px; - border-radius: 99px; + border-radius: 7px; border: none; background: #C49A6C; color: #0e0819; @@ -1936,7 +1920,7 @@ p { width: 100%; margin-top: 6px; padding: 8px 14px; - border-radius: 99px; + border-radius: 7px; background: #C49A6C; color: #0e0819; font-size: 9.5px; @@ -2789,38 +2773,9 @@ p { z-index: 0; } -.try-it-pill { - display: inline-flex; - align-items: center; - padding: 3px 8px; - border-radius: 99px; - background: rgba(196,154,108,0.16); - border: 1px solid rgba(196,154,108,0.32); - color: #C49A6C; - font-size: 8px; - letter-spacing: 0.1em; - text-transform: uppercase; - margin-left: auto; - flex-shrink: 0; - animation: pulseGlow 2.2s ease-in-out infinite; -} /* ===== COMING PILL ===== */ -.coming-pill { - display: inline-flex; - align-items: center; - padding: 3px 8px; - border-radius: 99px; - background: rgba(138,180,248,0.12); - border: 1px solid rgba(160,100,255,0.18); - color: rgba(180,130,255,0.65); - font-size: 8px; - letter-spacing: 0.1em; - text-transform: uppercase; - margin-left: auto; - flex-shrink: 0; -} /* ===== RESPONSIVE (additions) ===== */ diff --git a/src/simulator.css b/src/simulator.css index ef161ad..7cbd95d 100644 --- a/src/simulator.css +++ b/src/simulator.css @@ -318,7 +318,9 @@ .app-preview-stage { width: min(390px, 100%); - height: min(80vh, 780px); + /* 82vh rather than 80: at a 900px-tall viewport, the most common laptop + size, 80vh left the suggestion card 5px short of the fold. */ + height: min(82vh, 780px); min-height: 620px; display: flex; flex-direction: column; @@ -348,8 +350,11 @@ } .app-preview-stage .app-screen { - padding: 12px 18px 14px; - gap: 10px; + /* Tight enough that the "Lumen noticed" card, which is the whole point of + the preview, clears the fold on a 900px-tall viewport. Below that the + screen scrolls, the same as the real phone would. */ + padding: 12px 18px 10px; + gap: 8px; } .app-preview-stage .app-tabbar { @@ -583,7 +588,7 @@ gap: 8px; margin-top: 4px; padding: 11px 20px; - border-radius: 99px; + border-radius: 12px; border: 1px solid rgba(196, 154, 108, 0.4); background: rgba(196, 154, 108, 0.12); color: #e8d3b6; @@ -653,7 +658,7 @@ align-items: center; gap: 5px; padding: 7px 13px; - border-radius: 99px; + border-radius: 10px; border: 1px solid rgba(255, 255, 255, 0.14); background: rgba(20, 14, 34, 0.7); backdrop-filter: blur(8px); @@ -790,7 +795,7 @@ align-self: flex-start; margin-top: 4px; padding: 7px 12px; - border-radius: 99px; + border-radius: 7px; border: 1px solid rgba(196, 154, 108, 0.35); background: rgba(196, 154, 108, 0.1); color: #e8d3b6;