buildmymcpserver/remotion/src/scenes/BuildScene.tsx

413 lines
12 KiB
TypeScript
Raw Normal View History

feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
import { interpolate } from 'remotion';
import { C } from '../lib/colors';
import { springIn, softSpring, clampLerp } from '../lib/easings';
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
// Phase 2 (frames 159261 global → localFrame 0..102): build log streams in
// line-by-line, then a server card emerges. Two pills slot into the card:
// • `code` arrives from the left (the LLM side)
// • `🔒 NOTION_API_KEY` arrives from the right (the vault side)
// This visualizes the architectural moment: code and credentials are
// injected at runtime from separate paths.
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
//
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
// Then a subtle caption appears below the card:
// "your isolated container · only you can reach it"
//
// Log lines stagger ~10 frames apart starting at localFrame 4.
// Server card emerges at localFrame ~58.
// Slot pills fly in at localFrame ~78.
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
const LOG_LINES = [
{ label: 'Generating spec', detail: '2 tools detected' },
{ label: 'Static checks', detail: 'passed' },
{ label: 'Building image', detail: '17.2s' },
{ label: 'Deploying', detail: 'live' },
];
const LINE_STAGGER = 10;
const LINE_START = 4;
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
const CARD_START = 58;
const SLOTS_START = 78;
const CAPTION_START = 92;
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
export function BuildScene({ localFrame, fps }: { localFrame: number; fps: number }) {
const panelIn = springIn(localFrame, fps, 0);
const panelOpacity = clampLerp(localFrame, 0, 12);
// Card emerges late in phase.
const cardIn = softSpring(localFrame, fps, CARD_START, 24);
// Once the card is up, the log panel slides up to make room.
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
const panelShift = interpolate(cardIn, [0, 1], [0, -160]);
// Slot pills fly in after card is settled.
const codeSlotIn = softSpring(localFrame, fps, SLOTS_START, 18);
const secretSlotIn = softSpring(localFrame, fps, SLOTS_START + 4, 18);
// Caption appears last.
const captionIn = clampLerp(localFrame, CAPTION_START, CAPTION_START + 10);
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
return (
<div
style={{
position: 'absolute',
inset: 0,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
>
{/* Build log panel */}
<div
style={{
width: 720,
backgroundColor: C.bgElevated,
border: `1px solid ${C.border}`,
borderRadius: 14,
padding: '24px 28px',
boxShadow: '0 20px 60px rgba(0,0,0,0.5)',
opacity: panelOpacity,
transform: `translateY(${interpolate(panelIn, [0, 1], [20, 0]) + panelShift}px) scale(${interpolate(panelIn, [0, 1], [0.96, 1])})`,
display: 'flex',
flexDirection: 'column',
gap: 14,
}}
>
{/* Panel header — tiny status row */}
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
paddingBottom: 12,
borderBottom: `1px solid ${C.border}`,
fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
fontSize: 14,
color: C.fgSubtle,
letterSpacing: 1,
textTransform: 'uppercase',
}}
>
<span>build · notion-search</span>
<span style={{ color: C.fgMuted }}> running</span>
</div>
{LOG_LINES.map((line, i) => (
<LogLine
key={i}
label={line.label}
detail={line.detail}
localFrame={localFrame}
startFrame={LINE_START + i * LINE_STAGGER}
fps={fps}
/>
))}
</div>
{/* Server card (emerges in second half) */}
{cardIn > 0.01 && (
<ServerCard
progress={cardIn}
localFrame={localFrame}
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
codeSlotIn={codeSlotIn}
secretSlotIn={secretSlotIn}
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
/>
)}
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
{/* Isolated container caption */}
{captionIn > 0.01 && (
<div
style={{
position: 'absolute',
left: 0,
right: 0,
bottom: 180,
textAlign: 'center',
fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
fontSize: 13,
letterSpacing: 3,
textTransform: 'uppercase',
color: C.fgSubtle,
opacity: captionIn,
transform: `translateY(${interpolate(captionIn, [0, 1], [6, 0])}px)`,
}}
>
your isolated container · only you can reach it
</div>
)}
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
</div>
);
}
function LogLine({
label,
detail,
localFrame,
startFrame,
fps,
}: {
label: string;
detail: string;
localFrame: number;
startFrame: number;
fps: number;
}) {
const spring = springIn(localFrame, fps, startFrame);
const opacity = clampLerp(localFrame, startFrame, startFrame + 10);
const x = interpolate(spring, [0, 1], [-30, 0]);
// Check fills in slightly after the line slides in.
const checkFill = clampLerp(localFrame, startFrame + 6, startFrame + 14);
return (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 14,
opacity,
transform: `translateX(${x}px)`,
fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
fontSize: 18,
}}
>
{/* Checkmark circle */}
<div
style={{
width: 22,
height: 22,
borderRadius: 11,
backgroundColor: `rgba(34, 197, 94, ${checkFill * 0.18})`,
border: `1.5px solid ${C.success}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
}}
>
<svg width="12" height="12" viewBox="0 0 12 12">
<path
d="M 2.5 6.5 L 5 9 L 9.5 3.5"
fill="none"
stroke={C.success}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray={12}
strokeDashoffset={(1 - checkFill) * 12}
/>
</svg>
</div>
<span style={{ color: C.fg, minWidth: 220 }}>{label}</span>
<span style={{ color: C.fgMuted, flex: 1 }}>{detail}</span>
</div>
);
}
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
function ServerCard({
progress,
localFrame,
codeSlotIn,
secretSlotIn,
}: {
progress: number;
localFrame: number;
codeSlotIn: number;
secretSlotIn: number;
}) {
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
const scale = interpolate(progress, [0, 1], [0.85, 1]);
const y = interpolate(progress, [0, 1], [40, 180]);
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
// Live dot pulses once the slots have arrived.
const liveOn = secretSlotIn > 0.6;
const pulsePhase = (localFrame - (SLOTS_START + 18)) / 30;
const livePulse = liveOn
? 0.6 + 0.4 * Math.sin(pulsePhase * Math.PI * 2)
: 0;
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
return (
<div
style={{
position: 'absolute',
left: '50%',
top: '50%',
transform: `translate(-50%, calc(-50% + ${y}px)) scale(${scale})`,
opacity: progress,
}}
>
<div
style={{
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
width: 540,
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
backgroundColor: C.bgElevated,
border: `1.5px solid ${C.accent}`,
borderRadius: 16,
padding: '24px 28px',
boxShadow: `0 0 0 5px ${C.accentGlow}, 0 24px 70px rgba(0,0,0,0.6)`,
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
position: 'relative',
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
}}
>
{/* Header row: title + live dot */}
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 18,
}}
>
<div
style={{
fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
fontSize: 20,
color: C.fg,
letterSpacing: 0.2,
}}
>
notion-search
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
fontSize: 12,
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
color: liveOn ? C.success : C.fgSubtle,
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
letterSpacing: 1.5,
textTransform: 'uppercase',
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
opacity: 0.4 + 0.6 * Math.min(1, secretSlotIn),
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
}}
>
<div
style={{
width: 8,
height: 8,
borderRadius: 4,
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
backgroundColor: liveOn ? C.success : C.fgSubtle,
boxShadow: liveOn ? `0 0 ${10 * livePulse}px ${C.success}` : 'none',
opacity: liveOn ? 0.5 + 0.5 * livePulse : 0.5,
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
}}
/>
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
{liveOn ? 'live' : 'starting'}
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
</div>
</div>
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
{/* Slot pills row */}
<div
style={{
display: 'flex',
gap: 12,
marginBottom: 18,
position: 'relative',
minHeight: 38,
}}
>
{/* code slot — arrives from the left */}
<SlotPill
label="code"
kind="code"
in={codeSlotIn}
fromX={-200}
/>
{/* secret slot — arrives from the right */}
<SlotPill
label="NOTION_API_KEY"
kind="secret"
in={secretSlotIn}
fromX={200}
/>
</div>
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
{/* Tool rows */}
<ToolRow name="search_pages" desc="full-text query" />
<div style={{ height: 8 }} />
<ToolRow name="get_page_content" desc="fetch by id" />
</div>
</div>
);
}
feat: particle cloud (no discrete dots) + geo-IP country preselect on login Two coordinated polish moves the owner asked for. ## 1. Hero particle field — "no white dots, just a glow that follows the mouse and is always in motion" Previous tuning (uPointSize 2.8, uBaseAlpha 0.6) gave discrete indigo dots that additively saturated to near-white in dense clusters. The owner wanted no granular dots visible at all — a continuous indigo cloud that the cursor pulls toward itself. Changes: - **Render fragment**: replaced the anti-aliased disc SDF (`smoothstep(0.5, 0.42, d)` — hard edge) with a Gaussian falloff (`exp(-d * d * 6.0)` — smooth blob, no edge). Each particle is now a soft volume that blends seamlessly with neighbours. - **Sim fragment**: replaced the outward-gradient ring push with a mouse-halo attraction. Particles drift toward an ideal radius (~0.20) around the cursor, with exp-bell falloff so they don't collapse onto the cursor or feel influenced from across the canvas. `ringField()` helper is now unused but kept for future use. - **JS uniforms**: `uPointSize` 2.8→14 (256-tier) / 3.6→20 (128-tier); `uBaseAlpha` 0.6→0.055. Individual particles are below the perception threshold for "dot" but 65k of them additively composite into a continuous cloud. With the much lower per-particle alpha, the cumulative brightness never saturates to white. - **ParticleField tick loop**: asymmetric ring-active fade — `alpha = 0.14` ramping in (fast cursor response), `0.012` decaying out (slow glow trail after the pointer moves away). Matches the brief "glow longer + attractive to mouse but always in motion". - **ParticleHero index.tsx**: added an always-on indigo radial gradient behind the WebGL canvas, so the hero never reads as visually empty between frames — the canvas additively paints the dynamic cloud on top. Removed the white-dot stipple from the static fallback (it was the most likely source of the "weisse punkte" complaint for any visitor on the fallback path). ## 2. SMS login — pre-select country picker from visitor's geo-IP The country picker on `/login` previously defaulted to `'CH'` for everyone. Visitors from DE / AT / US / etc. had to manually scroll to their dial code — small friction but it sits on the highest-stakes conversion step in the funnel. - **New API route** `apps/api/src/routes/geo.ts` → `GET /v1/geo/country` returns `{ country: 'CH' | 'DE' | … | null }` by reading Cloudflare's `CF-IPCountry` header. Public, no auth — reading a 2-letter country code from a geo-IP header isn't PII under GDPR / DSG. `'XX'` and `'T1'` (CF's "unknown" + Tor) are normalised to `null`. Outside CF (dev), header is missing → null. - **Login page** picks up the result in the existing `useEffect`, guards against codes not in our country list, and calls `setCountry` to override the `'CH'` default. Stays at `'CH'` if the detection fails or the visitor is on a Tor exit. Verified live: the endpoint returns `{"country":"DE"}` from CF's German edge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:17:20 +02:00
function SlotPill({
label,
kind,
in: progress,
fromX,
}: {
label: string;
kind: 'code' | 'secret';
in: number;
fromX: number;
}) {
const x = interpolate(progress, [0, 1], [fromX, 0]);
const opacity = clampLerp(progress, 0.05, 0.6);
const isSecret = kind === 'secret';
return (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
padding: '8px 14px',
backgroundColor: isSecret ? 'rgba(99,102,241,0.10)' : C.bgSubtle,
border: `1px solid ${isSecret ? C.accentDim : C.borderStrong}`,
borderRadius: 999,
fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
fontSize: 13,
color: isSecret ? C.accent : C.fg,
letterSpacing: 0.5,
opacity,
transform: `translateX(${x}px)`,
}}
>
{isSecret ? (
<MiniLockIcon />
) : (
<span
style={{
display: 'inline-flex',
width: 14,
height: 14,
alignItems: 'center',
justifyContent: 'center',
color: C.fgMuted,
fontSize: 13,
}}
>
{'<>'}
</span>
)}
<span>{label}</span>
</div>
);
}
function MiniLockIcon() {
return (
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">
<path d="M 5 8 L 5 6 A 3 3 0 0 1 11 6 L 11 8" stroke={C.accent} strokeWidth={1.4} fill="none" />
<rect x="3.5" y="7.5" width="9" height="7" rx="1.5" fill="rgba(99,102,241,0.20)" stroke={C.accent} strokeWidth={1.4} />
<circle cx="8" cy="11" r="1" fill={C.accent} />
</svg>
);
}
feat(web): mobile-fit hero tiles + voluminous calmer particle field + FAQ accordion Three coordinated polish items requested: 1. **Hero step-rotator tiles fit mobile without horizontal scroll.** The previous snippets contained a 50+ char `Live at https://notion-x9.mcp.buildmymcpserver.com` URL that overflowed the ~295 px text area on a 375 px viewport. Rewrote all three snippets to be naturally short — same product story, no full URLs. The <pre> drops `overflow-x-auto` and gains `whitespace-pre-wrap break-words` so any token that does exceed the column wraps gracefully instead of forcing a scrollbar. 2. **ParticleHero — more volumetric, slower, steadier at load-in.** The "stuttery / too fast" feedback came from two issues compounding: tiny dots (1.8 px on 256-tier, with 0.42 base alpha) gave the eye too few pixels to track between frames, so individual particles read as snapping rather than drifting; and the simplex-noise drift evolved at 0.08 time-scale with 0.045 velocity, fast enough that frame-to-frame deltas exceeded a tracked particle's diameter. Render uniforms tuned: - `uPointSize` 1.8 → 2.8 (256-tier), 2.4 → 3.6 (128-tier) - `uBaseAlpha` 0.42 → 0.60 Simulation shader tuned: - Drift noise time scale 0.08 → 0.045 (the most impactful single change — particles now move at half the previous speed) - Drift velocity magnitude 0.045 → 0.028 - Ring breathing noise time scale 0.35 → 0.22 - Ring polar-wave time scales 1.2 / 0.7 → 0.7 / 0.42 Net effect: same number of particles (65k) but each individually larger, brighter, and moving more slowly. The cumulative additive bloom is denser without the jitter that read as visual stutter. 3. **FAQ collapsed into a native `<details>` accordion.** Crawlers and screen readers still see every Q+A in the SSR'd HTML — `<details><summary>...</summary><p>answer</p></details>` is the standard semantic pattern for disclosure widgets. Users see one question at a time and expand on demand, which keeps the page from feeling like an endless wall of marketing text below the fold. Container narrowed `max-w-6xl` → `max-w-3xl` for accordion typography (long-form prose reads better single-column). The default WebKit disclosure-triangle marker is suppressed with `list-none` + `[&_summary::-webkit-details-marker]:hidden`, and a `lucide-react` `ChevronDown` icon rotates 180° via `group-open:rotate-180` to indicate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:35:03 +02:00
function ToolRow({ name, desc }: { name: string; desc: string }) {
return (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 12,
padding: '10px 14px',
backgroundColor: C.bgSubtle,
border: `1px solid ${C.border}`,
borderRadius: 10,
fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
}}
>
<div style={{ width: 6, height: 6, borderRadius: 3, backgroundColor: C.accent }} />
<span style={{ color: C.fg, fontSize: 16, minWidth: 200 }}>{name}</span>
<span style={{ color: C.fgSubtle, fontSize: 14, flex: 1 }}>{desc}</span>
</div>
);
}