Skip to content

feat(landing): v5 landing, back as a PR to fix before it ships again - #586

Open
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply
Open

feat(landing): v5 landing, back as a PR to fix before it ships again#586
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

The v5 marketing landing, pulled off main in #584 and off production in #585, back as an open PR so it can be fixed before it ships again.

What this is

A single re-apply commit: the revert of #584's squash (9f34454c). The tree is byte-identical to 5b1a240dmain as it stood immediately before the revert — so this carries exactly what #524 (47 commits) and #568 (the drag-field e2e journey) put on main, and nothing else:

  • src/components/landing-v5/ — Hero, IntroOverlay, Navbar, the three Acts, FeatureLab + the eight lab demos, Gallery, Journal, Faq, Closing, BetaModal, DragField
  • src/lib/landing/ — the scroll/physics engine (engine/sim.ts + tests, ambient, flip, graph, marquee, plant) and the hero3d/ WebGL panel rig
  • src/components/companion/CompanionShell.tsx + the five public pages the nav links to (/faq, /gallery, /news, /team, /wiki), and the /about port
  • globals.css — the .landing-dc token scope; lib/api.ts — the beta-signup client
  • retirement of the v4 marketing/ surfaces, bands, and graph demo
  • e2e/landing-drag-field.spec.ts (replacing landing-graph.spec.ts), the v5 public-seo.spec.ts assertions, and the matching eslint-suppressions.json baseline

The original 47-commit history stays reachable through #524's merge commit on main; this PR is the revert-of-the-revert, so main keeps a linear story.

Status

Not ready to merge as-is — this is the starting point for the landing fix. Push fixes onto this branch.

Getting it back to production

production is on the pre-#576 v4 landing again. Once the fix lands here and on main, re-cut the frontend-only cherry the way #576 did (or take it with the next make promote).

🤖 Generated with Claude Code

Reverts 9f34454 (#584), which pulled the v5 marketing landing off main.
The tree is byte-identical to 5b1a240, main's state immediately before
the revert: the landing-v5/ components, lib/landing/ engine + hero3d rig,
the five companion pages, the v4 retirement, and the #568 drag-field
e2e journey. Lives here as an open PR so the landing can be fixed before
it goes back onto main / production.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c0fb03e-5ffc-476f-a7ab-6e8ffd54b9c2


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabaseBot commented Aug 23, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 23, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging20eefd1Commit Preview URL

Branch Preview URL
Aug 28 2026, 02:23 AM

Darkest-Teddyand others added 3 commits August 27, 2026 22:20
Ports the hero from `Sapling Landing.html`, the later Claude Design export.
The wordmark lockup is unchanged; what moved:
- The 01/02/03 Ingest/Map/Recall keys are gone. The bottom band is now three
columns holding the info box (left), the scroll cue (centre) and an empty
spacer (right). The spacer is load bearing: it balances the info box so the
cue lands on the viewport centre line, not the centre of the content.
- The info box moved from the right edge to the left.
- The cue reads SCROLL TO EXPLORE, flanked by two arrows, the second offset
0.28s so the pair reads as a fall rather than a blink.
- Both CTAs grew to 16px/30px and 16px/27px at 16.5px.
- The lede is 500 weight on #2A3B32 rather than regular on #33443B.
Splits the veil into two knobs. The source packs three gradients into two
elements, but they do different jobs: the bottom band is a legibility floor
for the info box and cue and has faded out by half height, while the rim and
the two panel radials are what sit on top of the WebGL cards. Scaling them
together meant the cards could not be cleared without also thinning the text
backing. `sceneWash` is held at 0.3, which drops the heaviest veil over any
card from 0.84 to ~0.25; `wash` stays at full strength.
Drops the two `.hero-keys` rules from globals.css, whose selector no longer
exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
The three acts recomputed their progress from scroll position every frame, so
scrolling back up ran them in reverse: the graph un-assembled, chips flew back
into the document, the carousel spun backwards. `actPeak` now latches the
furthest point each act reached, so progress only advances and scrolling up
holds the played state.
That alone made a second problem obvious. Each act's extra height is nothing
but scrub runway — 360vh on act 1, 220vh on act 2, 240vh on act 3 — and once
latched it drives nothing, so getting back out meant grinding through eight
screens of frozen scenery. `foldActs` cuts a section to one viewport and takes
the same distance off the scroll position.
The fold is seamless from any position because of how sticky behaves. While
pinned, the child sits at the viewport top for every scrollY in
[top, top + h - vh], so cutting to one viewport and pulling scrollY back to
`top` leaves it pixel-identical. Past the act the shrink instead cancels the
upward shift of everything below; above it nothing on screen moves. One
expression covers all three, and clamping at zero makes the third a no-op.
The smoothed scroll passes shift too, or the next frame reads a 360vh delta
and either lerps through it or trips the `jumping` bypass.
Two triggers, and between them upward scroll is never pinned: the act
finished, or the reader is scrolling up inside one they have started. The
second strands a half-played act at whatever progress it reached, because the
runway that would finish it is exactly what gets cut — the intended trade,
since scrolling up out of a scene is a decision to leave it.
Skipped while explore mode is active: it holds the reader inside act 1 on
purpose, and resizing the section there would move the page out from under
the graph.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Replaces the `[data-reveal]` attribute scan in `useLanding` with a `FadeIn`
component under `components/landing/anim`. Same motion, same 0.12 threshold;
the difference is that a block declares its own entrance where it is written
instead of opting into a page-wide querySelectorAll on a 900ms interval.
One shared IntersectionObserver backs every instance.
Also fixes a latent flash: the scan set `opacity:0` in an effect after paint,
so a revealed block could show before hiding itself. `FadeIn` renders visible
and hides in a layout effect, before paint.
Styles are written straight to the node rather than held in React state. That
is what this component is — there is no state the rest of the tree can
observe, the hide must land before paint, and routing it through useState
means a synchronous set inside a layout effect, i.e. a cascading render on
mount for every block on the page.
Three paths never hide, because stranding real copy at `opacity:0` is a worse
failure than an un-animated heading: no IntersectionObserver, reduced motion,
and no-JS (first render is visible, so server markup stays readable).
Migrates all seven existing reveal sites and adds fades to the ActIngest and
ActTutor headings. The engine owns exactly six attributes — [data-cap],
[data-tick], [data-tcap], [data-panel], [data-ingest-tile], [data-chip] — and
no FadeIn wraps any of them; ActTutor's stops short of its [data-tcap]
captions. ActGraph gets none: both its headings ARE captions. FeatureLab gets
none either: it is a fixed modal with its own panelFade entrance, never
scrolled into view.
`data-*` passthrough is not decoration — DragField welds its cluster tracks to
[data-drag-anchor], and the FAQ column is both an anchor and a reveal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@AndresL230@Darkest-Teddy
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(landing): v5 landing, back as a PR to fix before it ships again by AndresL230 · Pull Request #586 · SaplingLearn/Sapling · GitHub
Skip to content

feat(landing): v5 landing, back as a PR to fix before it ships again - #586

Open
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply
Open

feat(landing): v5 landing, back as a PR to fix before it ships again#586
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

The v5 marketing landing, pulled off main in #584 and off production in #585, back as an open PR so it can be fixed before it ships again.

What this is

A single re-apply commit: the revert of #584's squash (9f34454c). The tree is byte-identical to 5b1a240dmain as it stood immediately before the revert — so this carries exactly what #524 (47 commits) and #568 (the drag-field e2e journey) put on main, and nothing else:

  • src/components/landing-v5/ — Hero, IntroOverlay, Navbar, the three Acts, FeatureLab + the eight lab demos, Gallery, Journal, Faq, Closing, BetaModal, DragField
  • src/lib/landing/ — the scroll/physics engine (engine/sim.ts + tests, ambient, flip, graph, marquee, plant) and the hero3d/ WebGL panel rig
  • src/components/companion/CompanionShell.tsx + the five public pages the nav links to (/faq, /gallery, /news, /team, /wiki), and the /about port
  • globals.css — the .landing-dc token scope; lib/api.ts — the beta-signup client
  • retirement of the v4 marketing/ surfaces, bands, and graph demo
  • e2e/landing-drag-field.spec.ts (replacing landing-graph.spec.ts), the v5 public-seo.spec.ts assertions, and the matching eslint-suppressions.json baseline

The original 47-commit history stays reachable through #524's merge commit on main; this PR is the revert-of-the-revert, so main keeps a linear story.

Status

Not ready to merge as-is — this is the starting point for the landing fix. Push fixes onto this branch.

Getting it back to production

production is on the pre-#576 v4 landing again. Once the fix lands here and on main, re-cut the frontend-only cherry the way #576 did (or take it with the next make promote).

🤖 Generated with Claude Code

Reverts 9f34454 (#584), which pulled the v5 marketing landing off main.
The tree is byte-identical to 5b1a240, main's state immediately before
the revert: the landing-v5/ components, lib/landing/ engine + hero3d rig,
the five companion pages, the v4 retirement, and the #568 drag-field
e2e journey. Lives here as an open PR so the landing can be fixed before
it goes back onto main / production.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c0fb03e-5ffc-476f-a7ab-6e8ffd54b9c2


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabaseBot commented Aug 23, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 23, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging20eefd1Commit Preview URL

Branch Preview URL
Aug 28 2026, 02:23 AM

Darkest-Teddyand others added 3 commits August 27, 2026 22:20
Ports the hero from `Sapling Landing.html`, the later Claude Design export.
The wordmark lockup is unchanged; what moved:
- The 01/02/03 Ingest/Map/Recall keys are gone. The bottom band is now three
columns holding the info box (left), the scroll cue (centre) and an empty
spacer (right). The spacer is load bearing: it balances the info box so the
cue lands on the viewport centre line, not the centre of the content.
- The info box moved from the right edge to the left.
- The cue reads SCROLL TO EXPLORE, flanked by two arrows, the second offset
0.28s so the pair reads as a fall rather than a blink.
- Both CTAs grew to 16px/30px and 16px/27px at 16.5px.
- The lede is 500 weight on #2A3B32 rather than regular on #33443B.
Splits the veil into two knobs. The source packs three gradients into two
elements, but they do different jobs: the bottom band is a legibility floor
for the info box and cue and has faded out by half height, while the rim and
the two panel radials are what sit on top of the WebGL cards. Scaling them
together meant the cards could not be cleared without also thinning the text
backing. `sceneWash` is held at 0.3, which drops the heaviest veil over any
card from 0.84 to ~0.25; `wash` stays at full strength.
Drops the two `.hero-keys` rules from globals.css, whose selector no longer
exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
The three acts recomputed their progress from scroll position every frame, so
scrolling back up ran them in reverse: the graph un-assembled, chips flew back
into the document, the carousel spun backwards. `actPeak` now latches the
furthest point each act reached, so progress only advances and scrolling up
holds the played state.
That alone made a second problem obvious. Each act's extra height is nothing
but scrub runway — 360vh on act 1, 220vh on act 2, 240vh on act 3 — and once
latched it drives nothing, so getting back out meant grinding through eight
screens of frozen scenery. `foldActs` cuts a section to one viewport and takes
the same distance off the scroll position.
The fold is seamless from any position because of how sticky behaves. While
pinned, the child sits at the viewport top for every scrollY in
[top, top + h - vh], so cutting to one viewport and pulling scrollY back to
`top` leaves it pixel-identical. Past the act the shrink instead cancels the
upward shift of everything below; above it nothing on screen moves. One
expression covers all three, and clamping at zero makes the third a no-op.
The smoothed scroll passes shift too, or the next frame reads a 360vh delta
and either lerps through it or trips the `jumping` bypass.
Two triggers, and between them upward scroll is never pinned: the act
finished, or the reader is scrolling up inside one they have started. The
second strands a half-played act at whatever progress it reached, because the
runway that would finish it is exactly what gets cut — the intended trade,
since scrolling up out of a scene is a decision to leave it.
Skipped while explore mode is active: it holds the reader inside act 1 on
purpose, and resizing the section there would move the page out from under
the graph.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Replaces the `[data-reveal]` attribute scan in `useLanding` with a `FadeIn`
component under `components/landing/anim`. Same motion, same 0.12 threshold;
the difference is that a block declares its own entrance where it is written
instead of opting into a page-wide querySelectorAll on a 900ms interval.
One shared IntersectionObserver backs every instance.
Also fixes a latent flash: the scan set `opacity:0` in an effect after paint,
so a revealed block could show before hiding itself. `FadeIn` renders visible
and hides in a layout effect, before paint.
Styles are written straight to the node rather than held in React state. That
is what this component is — there is no state the rest of the tree can
observe, the hide must land before paint, and routing it through useState
means a synchronous set inside a layout effect, i.e. a cascading render on
mount for every block on the page.
Three paths never hide, because stranding real copy at `opacity:0` is a worse
failure than an un-animated heading: no IntersectionObserver, reduced motion,
and no-JS (first render is visible, so server markup stays readable).
Migrates all seven existing reveal sites and adds fades to the ActIngest and
ActTutor headings. The engine owns exactly six attributes — [data-cap],
[data-tick], [data-tcap], [data-panel], [data-ingest-tile], [data-chip] — and
no FadeIn wraps any of them; ActTutor's stops short of its [data-tcap]
captions. ActGraph gets none: both its headings ARE captions. FeatureLab gets
none either: it is a fixed modal with its own panelFade entrance, never
scrolled into view.
`data-*` passthrough is not decoration — DragField welds its cluster tracks to
[data-drag-anchor], and the FAQ column is both an anchor and a reveal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@AndresL230@Darkest-Teddy
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(landing): v5 landing, back as a PR to fix before it ships again by AndresL230 · Pull Request #586 · SaplingLearn/Sapling · GitHub
Skip to content

feat(landing): v5 landing, back as a PR to fix before it ships again - #586

Open
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply
Open

feat(landing): v5 landing, back as a PR to fix before it ships again#586
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

The v5 marketing landing, pulled off main in #584 and off production in #585, back as an open PR so it can be fixed before it ships again.

What this is

A single re-apply commit: the revert of #584's squash (9f34454c). The tree is byte-identical to 5b1a240dmain as it stood immediately before the revert — so this carries exactly what #524 (47 commits) and #568 (the drag-field e2e journey) put on main, and nothing else:

  • src/components/landing-v5/ — Hero, IntroOverlay, Navbar, the three Acts, FeatureLab + the eight lab demos, Gallery, Journal, Faq, Closing, BetaModal, DragField
  • src/lib/landing/ — the scroll/physics engine (engine/sim.ts + tests, ambient, flip, graph, marquee, plant) and the hero3d/ WebGL panel rig
  • src/components/companion/CompanionShell.tsx + the five public pages the nav links to (/faq, /gallery, /news, /team, /wiki), and the /about port
  • globals.css — the .landing-dc token scope; lib/api.ts — the beta-signup client
  • retirement of the v4 marketing/ surfaces, bands, and graph demo
  • e2e/landing-drag-field.spec.ts (replacing landing-graph.spec.ts), the v5 public-seo.spec.ts assertions, and the matching eslint-suppressions.json baseline

The original 47-commit history stays reachable through #524's merge commit on main; this PR is the revert-of-the-revert, so main keeps a linear story.

Status

Not ready to merge as-is — this is the starting point for the landing fix. Push fixes onto this branch.

Getting it back to production

production is on the pre-#576 v4 landing again. Once the fix lands here and on main, re-cut the frontend-only cherry the way #576 did (or take it with the next make promote).

🤖 Generated with Claude Code

Reverts 9f34454 (#584), which pulled the v5 marketing landing off main.
The tree is byte-identical to 5b1a240, main's state immediately before
the revert: the landing-v5/ components, lib/landing/ engine + hero3d rig,
the five companion pages, the v4 retirement, and the #568 drag-field
e2e journey. Lives here as an open PR so the landing can be fixed before
it goes back onto main / production.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c0fb03e-5ffc-476f-a7ab-6e8ffd54b9c2


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabaseBot commented Aug 23, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 23, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging20eefd1Commit Preview URL

Branch Preview URL
Aug 28 2026, 02:23 AM

Darkest-Teddyand others added 3 commits August 27, 2026 22:20
Ports the hero from `Sapling Landing.html`, the later Claude Design export.
The wordmark lockup is unchanged; what moved:
- The 01/02/03 Ingest/Map/Recall keys are gone. The bottom band is now three
columns holding the info box (left), the scroll cue (centre) and an empty
spacer (right). The spacer is load bearing: it balances the info box so the
cue lands on the viewport centre line, not the centre of the content.
- The info box moved from the right edge to the left.
- The cue reads SCROLL TO EXPLORE, flanked by two arrows, the second offset
0.28s so the pair reads as a fall rather than a blink.
- Both CTAs grew to 16px/30px and 16px/27px at 16.5px.
- The lede is 500 weight on #2A3B32 rather than regular on #33443B.
Splits the veil into two knobs. The source packs three gradients into two
elements, but they do different jobs: the bottom band is a legibility floor
for the info box and cue and has faded out by half height, while the rim and
the two panel radials are what sit on top of the WebGL cards. Scaling them
together meant the cards could not be cleared without also thinning the text
backing. `sceneWash` is held at 0.3, which drops the heaviest veil over any
card from 0.84 to ~0.25; `wash` stays at full strength.
Drops the two `.hero-keys` rules from globals.css, whose selector no longer
exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
The three acts recomputed their progress from scroll position every frame, so
scrolling back up ran them in reverse: the graph un-assembled, chips flew back
into the document, the carousel spun backwards. `actPeak` now latches the
furthest point each act reached, so progress only advances and scrolling up
holds the played state.
That alone made a second problem obvious. Each act's extra height is nothing
but scrub runway — 360vh on act 1, 220vh on act 2, 240vh on act 3 — and once
latched it drives nothing, so getting back out meant grinding through eight
screens of frozen scenery. `foldActs` cuts a section to one viewport and takes
the same distance off the scroll position.
The fold is seamless from any position because of how sticky behaves. While
pinned, the child sits at the viewport top for every scrollY in
[top, top + h - vh], so cutting to one viewport and pulling scrollY back to
`top` leaves it pixel-identical. Past the act the shrink instead cancels the
upward shift of everything below; above it nothing on screen moves. One
expression covers all three, and clamping at zero makes the third a no-op.
The smoothed scroll passes shift too, or the next frame reads a 360vh delta
and either lerps through it or trips the `jumping` bypass.
Two triggers, and between them upward scroll is never pinned: the act
finished, or the reader is scrolling up inside one they have started. The
second strands a half-played act at whatever progress it reached, because the
runway that would finish it is exactly what gets cut — the intended trade,
since scrolling up out of a scene is a decision to leave it.
Skipped while explore mode is active: it holds the reader inside act 1 on
purpose, and resizing the section there would move the page out from under
the graph.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Replaces the `[data-reveal]` attribute scan in `useLanding` with a `FadeIn`
component under `components/landing/anim`. Same motion, same 0.12 threshold;
the difference is that a block declares its own entrance where it is written
instead of opting into a page-wide querySelectorAll on a 900ms interval.
One shared IntersectionObserver backs every instance.
Also fixes a latent flash: the scan set `opacity:0` in an effect after paint,
so a revealed block could show before hiding itself. `FadeIn` renders visible
and hides in a layout effect, before paint.
Styles are written straight to the node rather than held in React state. That
is what this component is — there is no state the rest of the tree can
observe, the hide must land before paint, and routing it through useState
means a synchronous set inside a layout effect, i.e. a cascading render on
mount for every block on the page.
Three paths never hide, because stranding real copy at `opacity:0` is a worse
failure than an un-animated heading: no IntersectionObserver, reduced motion,
and no-JS (first render is visible, so server markup stays readable).
Migrates all seven existing reveal sites and adds fades to the ActIngest and
ActTutor headings. The engine owns exactly six attributes — [data-cap],
[data-tick], [data-tcap], [data-panel], [data-ingest-tile], [data-chip] — and
no FadeIn wraps any of them; ActTutor's stops short of its [data-tcap]
captions. ActGraph gets none: both its headings ARE captions. FeatureLab gets
none either: it is a fixed modal with its own panelFade entrance, never
scrolled into view.
`data-*` passthrough is not decoration — DragField welds its cluster tracks to
[data-drag-anchor], and the FAQ column is both an anchor and a reveal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@AndresL230@Darkest-Teddy
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(landing): v5 landing, back as a PR to fix before it ships again by AndresL230 · Pull Request #586 · SaplingLearn/Sapling · GitHub
Skip to content

feat(landing): v5 landing, back as a PR to fix before it ships again - #586

Open
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply
Open

feat(landing): v5 landing, back as a PR to fix before it ships again#586
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

The v5 marketing landing, pulled off main in #584 and off production in #585, back as an open PR so it can be fixed before it ships again.

What this is

A single re-apply commit: the revert of #584's squash (9f34454c). The tree is byte-identical to 5b1a240dmain as it stood immediately before the revert — so this carries exactly what #524 (47 commits) and #568 (the drag-field e2e journey) put on main, and nothing else:

  • src/components/landing-v5/ — Hero, IntroOverlay, Navbar, the three Acts, FeatureLab + the eight lab demos, Gallery, Journal, Faq, Closing, BetaModal, DragField
  • src/lib/landing/ — the scroll/physics engine (engine/sim.ts + tests, ambient, flip, graph, marquee, plant) and the hero3d/ WebGL panel rig
  • src/components/companion/CompanionShell.tsx + the five public pages the nav links to (/faq, /gallery, /news, /team, /wiki), and the /about port
  • globals.css — the .landing-dc token scope; lib/api.ts — the beta-signup client
  • retirement of the v4 marketing/ surfaces, bands, and graph demo
  • e2e/landing-drag-field.spec.ts (replacing landing-graph.spec.ts), the v5 public-seo.spec.ts assertions, and the matching eslint-suppressions.json baseline

The original 47-commit history stays reachable through #524's merge commit on main; this PR is the revert-of-the-revert, so main keeps a linear story.

Status

Not ready to merge as-is — this is the starting point for the landing fix. Push fixes onto this branch.

Getting it back to production

production is on the pre-#576 v4 landing again. Once the fix lands here and on main, re-cut the frontend-only cherry the way #576 did (or take it with the next make promote).

🤖 Generated with Claude Code

Reverts 9f34454 (#584), which pulled the v5 marketing landing off main.
The tree is byte-identical to 5b1a240, main's state immediately before
the revert: the landing-v5/ components, lib/landing/ engine + hero3d rig,
the five companion pages, the v4 retirement, and the #568 drag-field
e2e journey. Lives here as an open PR so the landing can be fixed before
it goes back onto main / production.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c0fb03e-5ffc-476f-a7ab-6e8ffd54b9c2


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabaseBot commented Aug 23, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 23, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging20eefd1Commit Preview URL

Branch Preview URL
Aug 28 2026, 02:23 AM

Darkest-Teddyand others added 3 commits August 27, 2026 22:20
Ports the hero from `Sapling Landing.html`, the later Claude Design export.
The wordmark lockup is unchanged; what moved:
- The 01/02/03 Ingest/Map/Recall keys are gone. The bottom band is now three
columns holding the info box (left), the scroll cue (centre) and an empty
spacer (right). The spacer is load bearing: it balances the info box so the
cue lands on the viewport centre line, not the centre of the content.
- The info box moved from the right edge to the left.
- The cue reads SCROLL TO EXPLORE, flanked by two arrows, the second offset
0.28s so the pair reads as a fall rather than a blink.
- Both CTAs grew to 16px/30px and 16px/27px at 16.5px.
- The lede is 500 weight on #2A3B32 rather than regular on #33443B.
Splits the veil into two knobs. The source packs three gradients into two
elements, but they do different jobs: the bottom band is a legibility floor
for the info box and cue and has faded out by half height, while the rim and
the two panel radials are what sit on top of the WebGL cards. Scaling them
together meant the cards could not be cleared without also thinning the text
backing. `sceneWash` is held at 0.3, which drops the heaviest veil over any
card from 0.84 to ~0.25; `wash` stays at full strength.
Drops the two `.hero-keys` rules from globals.css, whose selector no longer
exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
The three acts recomputed their progress from scroll position every frame, so
scrolling back up ran them in reverse: the graph un-assembled, chips flew back
into the document, the carousel spun backwards. `actPeak` now latches the
furthest point each act reached, so progress only advances and scrolling up
holds the played state.
That alone made a second problem obvious. Each act's extra height is nothing
but scrub runway — 360vh on act 1, 220vh on act 2, 240vh on act 3 — and once
latched it drives nothing, so getting back out meant grinding through eight
screens of frozen scenery. `foldActs` cuts a section to one viewport and takes
the same distance off the scroll position.
The fold is seamless from any position because of how sticky behaves. While
pinned, the child sits at the viewport top for every scrollY in
[top, top + h - vh], so cutting to one viewport and pulling scrollY back to
`top` leaves it pixel-identical. Past the act the shrink instead cancels the
upward shift of everything below; above it nothing on screen moves. One
expression covers all three, and clamping at zero makes the third a no-op.
The smoothed scroll passes shift too, or the next frame reads a 360vh delta
and either lerps through it or trips the `jumping` bypass.
Two triggers, and between them upward scroll is never pinned: the act
finished, or the reader is scrolling up inside one they have started. The
second strands a half-played act at whatever progress it reached, because the
runway that would finish it is exactly what gets cut — the intended trade,
since scrolling up out of a scene is a decision to leave it.
Skipped while explore mode is active: it holds the reader inside act 1 on
purpose, and resizing the section there would move the page out from under
the graph.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Replaces the `[data-reveal]` attribute scan in `useLanding` with a `FadeIn`
component under `components/landing/anim`. Same motion, same 0.12 threshold;
the difference is that a block declares its own entrance where it is written
instead of opting into a page-wide querySelectorAll on a 900ms interval.
One shared IntersectionObserver backs every instance.
Also fixes a latent flash: the scan set `opacity:0` in an effect after paint,
so a revealed block could show before hiding itself. `FadeIn` renders visible
and hides in a layout effect, before paint.
Styles are written straight to the node rather than held in React state. That
is what this component is — there is no state the rest of the tree can
observe, the hide must land before paint, and routing it through useState
means a synchronous set inside a layout effect, i.e. a cascading render on
mount for every block on the page.
Three paths never hide, because stranding real copy at `opacity:0` is a worse
failure than an un-animated heading: no IntersectionObserver, reduced motion,
and no-JS (first render is visible, so server markup stays readable).
Migrates all seven existing reveal sites and adds fades to the ActIngest and
ActTutor headings. The engine owns exactly six attributes — [data-cap],
[data-tick], [data-tcap], [data-panel], [data-ingest-tile], [data-chip] — and
no FadeIn wraps any of them; ActTutor's stops short of its [data-tcap]
captions. ActGraph gets none: both its headings ARE captions. FeatureLab gets
none either: it is a fixed modal with its own panelFade entrance, never
scrolled into view.
`data-*` passthrough is not decoration — DragField welds its cluster tracks to
[data-drag-anchor], and the FAQ column is both an anchor and a reveal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@AndresL230@Darkest-Teddy
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(landing): v5 landing, back as a PR to fix before it ships again by AndresL230 · Pull Request #586 · SaplingLearn/Sapling · GitHub
Skip to content

feat(landing): v5 landing, back as a PR to fix before it ships again - #586

Open
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply
Open

feat(landing): v5 landing, back as a PR to fix before it ships again#586
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

The v5 marketing landing, pulled off main in #584 and off production in #585, back as an open PR so it can be fixed before it ships again.

What this is

A single re-apply commit: the revert of #584's squash (9f34454c). The tree is byte-identical to 5b1a240dmain as it stood immediately before the revert — so this carries exactly what #524 (47 commits) and #568 (the drag-field e2e journey) put on main, and nothing else:

  • src/components/landing-v5/ — Hero, IntroOverlay, Navbar, the three Acts, FeatureLab + the eight lab demos, Gallery, Journal, Faq, Closing, BetaModal, DragField
  • src/lib/landing/ — the scroll/physics engine (engine/sim.ts + tests, ambient, flip, graph, marquee, plant) and the hero3d/ WebGL panel rig
  • src/components/companion/CompanionShell.tsx + the five public pages the nav links to (/faq, /gallery, /news, /team, /wiki), and the /about port
  • globals.css — the .landing-dc token scope; lib/api.ts — the beta-signup client
  • retirement of the v4 marketing/ surfaces, bands, and graph demo
  • e2e/landing-drag-field.spec.ts (replacing landing-graph.spec.ts), the v5 public-seo.spec.ts assertions, and the matching eslint-suppressions.json baseline

The original 47-commit history stays reachable through #524's merge commit on main; this PR is the revert-of-the-revert, so main keeps a linear story.

Status

Not ready to merge as-is — this is the starting point for the landing fix. Push fixes onto this branch.

Getting it back to production

production is on the pre-#576 v4 landing again. Once the fix lands here and on main, re-cut the frontend-only cherry the way #576 did (or take it with the next make promote).

🤖 Generated with Claude Code

Reverts 9f34454 (#584), which pulled the v5 marketing landing off main.
The tree is byte-identical to 5b1a240, main's state immediately before
the revert: the landing-v5/ components, lib/landing/ engine + hero3d rig,
the five companion pages, the v4 retirement, and the #568 drag-field
e2e journey. Lives here as an open PR so the landing can be fixed before
it goes back onto main / production.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c0fb03e-5ffc-476f-a7ab-6e8ffd54b9c2


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabaseBot commented Aug 23, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 23, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging20eefd1Commit Preview URL

Branch Preview URL
Aug 28 2026, 02:23 AM

Darkest-Teddyand others added 3 commits August 27, 2026 22:20
Ports the hero from `Sapling Landing.html`, the later Claude Design export.
The wordmark lockup is unchanged; what moved:
- The 01/02/03 Ingest/Map/Recall keys are gone. The bottom band is now three
columns holding the info box (left), the scroll cue (centre) and an empty
spacer (right). The spacer is load bearing: it balances the info box so the
cue lands on the viewport centre line, not the centre of the content.
- The info box moved from the right edge to the left.
- The cue reads SCROLL TO EXPLORE, flanked by two arrows, the second offset
0.28s so the pair reads as a fall rather than a blink.
- Both CTAs grew to 16px/30px and 16px/27px at 16.5px.
- The lede is 500 weight on #2A3B32 rather than regular on #33443B.
Splits the veil into two knobs. The source packs three gradients into two
elements, but they do different jobs: the bottom band is a legibility floor
for the info box and cue and has faded out by half height, while the rim and
the two panel radials are what sit on top of the WebGL cards. Scaling them
together meant the cards could not be cleared without also thinning the text
backing. `sceneWash` is held at 0.3, which drops the heaviest veil over any
card from 0.84 to ~0.25; `wash` stays at full strength.
Drops the two `.hero-keys` rules from globals.css, whose selector no longer
exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
The three acts recomputed their progress from scroll position every frame, so
scrolling back up ran them in reverse: the graph un-assembled, chips flew back
into the document, the carousel spun backwards. `actPeak` now latches the
furthest point each act reached, so progress only advances and scrolling up
holds the played state.
That alone made a second problem obvious. Each act's extra height is nothing
but scrub runway — 360vh on act 1, 220vh on act 2, 240vh on act 3 — and once
latched it drives nothing, so getting back out meant grinding through eight
screens of frozen scenery. `foldActs` cuts a section to one viewport and takes
the same distance off the scroll position.
The fold is seamless from any position because of how sticky behaves. While
pinned, the child sits at the viewport top for every scrollY in
[top, top + h - vh], so cutting to one viewport and pulling scrollY back to
`top` leaves it pixel-identical. Past the act the shrink instead cancels the
upward shift of everything below; above it nothing on screen moves. One
expression covers all three, and clamping at zero makes the third a no-op.
The smoothed scroll passes shift too, or the next frame reads a 360vh delta
and either lerps through it or trips the `jumping` bypass.
Two triggers, and between them upward scroll is never pinned: the act
finished, or the reader is scrolling up inside one they have started. The
second strands a half-played act at whatever progress it reached, because the
runway that would finish it is exactly what gets cut — the intended trade,
since scrolling up out of a scene is a decision to leave it.
Skipped while explore mode is active: it holds the reader inside act 1 on
purpose, and resizing the section there would move the page out from under
the graph.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Replaces the `[data-reveal]` attribute scan in `useLanding` with a `FadeIn`
component under `components/landing/anim`. Same motion, same 0.12 threshold;
the difference is that a block declares its own entrance where it is written
instead of opting into a page-wide querySelectorAll on a 900ms interval.
One shared IntersectionObserver backs every instance.
Also fixes a latent flash: the scan set `opacity:0` in an effect after paint,
so a revealed block could show before hiding itself. `FadeIn` renders visible
and hides in a layout effect, before paint.
Styles are written straight to the node rather than held in React state. That
is what this component is — there is no state the rest of the tree can
observe, the hide must land before paint, and routing it through useState
means a synchronous set inside a layout effect, i.e. a cascading render on
mount for every block on the page.
Three paths never hide, because stranding real copy at `opacity:0` is a worse
failure than an un-animated heading: no IntersectionObserver, reduced motion,
and no-JS (first render is visible, so server markup stays readable).
Migrates all seven existing reveal sites and adds fades to the ActIngest and
ActTutor headings. The engine owns exactly six attributes — [data-cap],
[data-tick], [data-tcap], [data-panel], [data-ingest-tile], [data-chip] — and
no FadeIn wraps any of them; ActTutor's stops short of its [data-tcap]
captions. ActGraph gets none: both its headings ARE captions. FeatureLab gets
none either: it is a fixed modal with its own panelFade entrance, never
scrolled into view.
`data-*` passthrough is not decoration — DragField welds its cluster tracks to
[data-drag-anchor], and the FAQ column is both an anchor and a reveal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@AndresL230@Darkest-Teddy
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(landing): v5 landing, back as a PR to fix before it ships again by AndresL230 · Pull Request #586 · SaplingLearn/Sapling · GitHub
Skip to content

feat(landing): v5 landing, back as a PR to fix before it ships again - #586

Open
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply
Open

feat(landing): v5 landing, back as a PR to fix before it ships again#586
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

The v5 marketing landing, pulled off main in #584 and off production in #585, back as an open PR so it can be fixed before it ships again.

What this is

A single re-apply commit: the revert of #584's squash (9f34454c). The tree is byte-identical to 5b1a240dmain as it stood immediately before the revert — so this carries exactly what #524 (47 commits) and #568 (the drag-field e2e journey) put on main, and nothing else:

  • src/components/landing-v5/ — Hero, IntroOverlay, Navbar, the three Acts, FeatureLab + the eight lab demos, Gallery, Journal, Faq, Closing, BetaModal, DragField
  • src/lib/landing/ — the scroll/physics engine (engine/sim.ts + tests, ambient, flip, graph, marquee, plant) and the hero3d/ WebGL panel rig
  • src/components/companion/CompanionShell.tsx + the five public pages the nav links to (/faq, /gallery, /news, /team, /wiki), and the /about port
  • globals.css — the .landing-dc token scope; lib/api.ts — the beta-signup client
  • retirement of the v4 marketing/ surfaces, bands, and graph demo
  • e2e/landing-drag-field.spec.ts (replacing landing-graph.spec.ts), the v5 public-seo.spec.ts assertions, and the matching eslint-suppressions.json baseline

The original 47-commit history stays reachable through #524's merge commit on main; this PR is the revert-of-the-revert, so main keeps a linear story.

Status

Not ready to merge as-is — this is the starting point for the landing fix. Push fixes onto this branch.

Getting it back to production

production is on the pre-#576 v4 landing again. Once the fix lands here and on main, re-cut the frontend-only cherry the way #576 did (or take it with the next make promote).

🤖 Generated with Claude Code

Reverts 9f34454 (#584), which pulled the v5 marketing landing off main.
The tree is byte-identical to 5b1a240, main's state immediately before
the revert: the landing-v5/ components, lib/landing/ engine + hero3d rig,
the five companion pages, the v4 retirement, and the #568 drag-field
e2e journey. Lives here as an open PR so the landing can be fixed before
it goes back onto main / production.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c0fb03e-5ffc-476f-a7ab-6e8ffd54b9c2


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabaseBot commented Aug 23, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 23, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging20eefd1Commit Preview URL

Branch Preview URL
Aug 28 2026, 02:23 AM

Darkest-Teddyand others added 3 commits August 27, 2026 22:20
Ports the hero from `Sapling Landing.html`, the later Claude Design export.
The wordmark lockup is unchanged; what moved:
- The 01/02/03 Ingest/Map/Recall keys are gone. The bottom band is now three
columns holding the info box (left), the scroll cue (centre) and an empty
spacer (right). The spacer is load bearing: it balances the info box so the
cue lands on the viewport centre line, not the centre of the content.
- The info box moved from the right edge to the left.
- The cue reads SCROLL TO EXPLORE, flanked by two arrows, the second offset
0.28s so the pair reads as a fall rather than a blink.
- Both CTAs grew to 16px/30px and 16px/27px at 16.5px.
- The lede is 500 weight on #2A3B32 rather than regular on #33443B.
Splits the veil into two knobs. The source packs three gradients into two
elements, but they do different jobs: the bottom band is a legibility floor
for the info box and cue and has faded out by half height, while the rim and
the two panel radials are what sit on top of the WebGL cards. Scaling them
together meant the cards could not be cleared without also thinning the text
backing. `sceneWash` is held at 0.3, which drops the heaviest veil over any
card from 0.84 to ~0.25; `wash` stays at full strength.
Drops the two `.hero-keys` rules from globals.css, whose selector no longer
exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
The three acts recomputed their progress from scroll position every frame, so
scrolling back up ran them in reverse: the graph un-assembled, chips flew back
into the document, the carousel spun backwards. `actPeak` now latches the
furthest point each act reached, so progress only advances and scrolling up
holds the played state.
That alone made a second problem obvious. Each act's extra height is nothing
but scrub runway — 360vh on act 1, 220vh on act 2, 240vh on act 3 — and once
latched it drives nothing, so getting back out meant grinding through eight
screens of frozen scenery. `foldActs` cuts a section to one viewport and takes
the same distance off the scroll position.
The fold is seamless from any position because of how sticky behaves. While
pinned, the child sits at the viewport top for every scrollY in
[top, top + h - vh], so cutting to one viewport and pulling scrollY back to
`top` leaves it pixel-identical. Past the act the shrink instead cancels the
upward shift of everything below; above it nothing on screen moves. One
expression covers all three, and clamping at zero makes the third a no-op.
The smoothed scroll passes shift too, or the next frame reads a 360vh delta
and either lerps through it or trips the `jumping` bypass.
Two triggers, and between them upward scroll is never pinned: the act
finished, or the reader is scrolling up inside one they have started. The
second strands a half-played act at whatever progress it reached, because the
runway that would finish it is exactly what gets cut — the intended trade,
since scrolling up out of a scene is a decision to leave it.
Skipped while explore mode is active: it holds the reader inside act 1 on
purpose, and resizing the section there would move the page out from under
the graph.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Replaces the `[data-reveal]` attribute scan in `useLanding` with a `FadeIn`
component under `components/landing/anim`. Same motion, same 0.12 threshold;
the difference is that a block declares its own entrance where it is written
instead of opting into a page-wide querySelectorAll on a 900ms interval.
One shared IntersectionObserver backs every instance.
Also fixes a latent flash: the scan set `opacity:0` in an effect after paint,
so a revealed block could show before hiding itself. `FadeIn` renders visible
and hides in a layout effect, before paint.
Styles are written straight to the node rather than held in React state. That
is what this component is — there is no state the rest of the tree can
observe, the hide must land before paint, and routing it through useState
means a synchronous set inside a layout effect, i.e. a cascading render on
mount for every block on the page.
Three paths never hide, because stranding real copy at `opacity:0` is a worse
failure than an un-animated heading: no IntersectionObserver, reduced motion,
and no-JS (first render is visible, so server markup stays readable).
Migrates all seven existing reveal sites and adds fades to the ActIngest and
ActTutor headings. The engine owns exactly six attributes — [data-cap],
[data-tick], [data-tcap], [data-panel], [data-ingest-tile], [data-chip] — and
no FadeIn wraps any of them; ActTutor's stops short of its [data-tcap]
captions. ActGraph gets none: both its headings ARE captions. FeatureLab gets
none either: it is a fixed modal with its own panelFade entrance, never
scrolled into view.
`data-*` passthrough is not decoration — DragField welds its cluster tracks to
[data-drag-anchor], and the FAQ column is both an anchor and a reveal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@AndresL230@Darkest-Teddy
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(landing): v5 landing, back as a PR to fix before it ships again by AndresL230 · Pull Request #586 · SaplingLearn/Sapling · GitHub
Skip to content

feat(landing): v5 landing, back as a PR to fix before it ships again - #586

Open
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply
Open

feat(landing): v5 landing, back as a PR to fix before it ships again#586
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

The v5 marketing landing, pulled off main in #584 and off production in #585, back as an open PR so it can be fixed before it ships again.

What this is

A single re-apply commit: the revert of #584's squash (9f34454c). The tree is byte-identical to 5b1a240dmain as it stood immediately before the revert — so this carries exactly what #524 (47 commits) and #568 (the drag-field e2e journey) put on main, and nothing else:

  • src/components/landing-v5/ — Hero, IntroOverlay, Navbar, the three Acts, FeatureLab + the eight lab demos, Gallery, Journal, Faq, Closing, BetaModal, DragField
  • src/lib/landing/ — the scroll/physics engine (engine/sim.ts + tests, ambient, flip, graph, marquee, plant) and the hero3d/ WebGL panel rig
  • src/components/companion/CompanionShell.tsx + the five public pages the nav links to (/faq, /gallery, /news, /team, /wiki), and the /about port
  • globals.css — the .landing-dc token scope; lib/api.ts — the beta-signup client
  • retirement of the v4 marketing/ surfaces, bands, and graph demo
  • e2e/landing-drag-field.spec.ts (replacing landing-graph.spec.ts), the v5 public-seo.spec.ts assertions, and the matching eslint-suppressions.json baseline

The original 47-commit history stays reachable through #524's merge commit on main; this PR is the revert-of-the-revert, so main keeps a linear story.

Status

Not ready to merge as-is — this is the starting point for the landing fix. Push fixes onto this branch.

Getting it back to production

production is on the pre-#576 v4 landing again. Once the fix lands here and on main, re-cut the frontend-only cherry the way #576 did (or take it with the next make promote).

🤖 Generated with Claude Code

Reverts 9f34454 (#584), which pulled the v5 marketing landing off main.
The tree is byte-identical to 5b1a240, main's state immediately before
the revert: the landing-v5/ components, lib/landing/ engine + hero3d rig,
the five companion pages, the v4 retirement, and the #568 drag-field
e2e journey. Lives here as an open PR so the landing can be fixed before
it goes back onto main / production.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c0fb03e-5ffc-476f-a7ab-6e8ffd54b9c2


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabaseBot commented Aug 23, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 23, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging20eefd1Commit Preview URL

Branch Preview URL
Aug 28 2026, 02:23 AM

Darkest-Teddyand others added 3 commits August 27, 2026 22:20
Ports the hero from `Sapling Landing.html`, the later Claude Design export.
The wordmark lockup is unchanged; what moved:
- The 01/02/03 Ingest/Map/Recall keys are gone. The bottom band is now three
columns holding the info box (left), the scroll cue (centre) and an empty
spacer (right). The spacer is load bearing: it balances the info box so the
cue lands on the viewport centre line, not the centre of the content.
- The info box moved from the right edge to the left.
- The cue reads SCROLL TO EXPLORE, flanked by two arrows, the second offset
0.28s so the pair reads as a fall rather than a blink.
- Both CTAs grew to 16px/30px and 16px/27px at 16.5px.
- The lede is 500 weight on #2A3B32 rather than regular on #33443B.
Splits the veil into two knobs. The source packs three gradients into two
elements, but they do different jobs: the bottom band is a legibility floor
for the info box and cue and has faded out by half height, while the rim and
the two panel radials are what sit on top of the WebGL cards. Scaling them
together meant the cards could not be cleared without also thinning the text
backing. `sceneWash` is held at 0.3, which drops the heaviest veil over any
card from 0.84 to ~0.25; `wash` stays at full strength.
Drops the two `.hero-keys` rules from globals.css, whose selector no longer
exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
The three acts recomputed their progress from scroll position every frame, so
scrolling back up ran them in reverse: the graph un-assembled, chips flew back
into the document, the carousel spun backwards. `actPeak` now latches the
furthest point each act reached, so progress only advances and scrolling up
holds the played state.
That alone made a second problem obvious. Each act's extra height is nothing
but scrub runway — 360vh on act 1, 220vh on act 2, 240vh on act 3 — and once
latched it drives nothing, so getting back out meant grinding through eight
screens of frozen scenery. `foldActs` cuts a section to one viewport and takes
the same distance off the scroll position.
The fold is seamless from any position because of how sticky behaves. While
pinned, the child sits at the viewport top for every scrollY in
[top, top + h - vh], so cutting to one viewport and pulling scrollY back to
`top` leaves it pixel-identical. Past the act the shrink instead cancels the
upward shift of everything below; above it nothing on screen moves. One
expression covers all three, and clamping at zero makes the third a no-op.
The smoothed scroll passes shift too, or the next frame reads a 360vh delta
and either lerps through it or trips the `jumping` bypass.
Two triggers, and between them upward scroll is never pinned: the act
finished, or the reader is scrolling up inside one they have started. The
second strands a half-played act at whatever progress it reached, because the
runway that would finish it is exactly what gets cut — the intended trade,
since scrolling up out of a scene is a decision to leave it.
Skipped while explore mode is active: it holds the reader inside act 1 on
purpose, and resizing the section there would move the page out from under
the graph.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Replaces the `[data-reveal]` attribute scan in `useLanding` with a `FadeIn`
component under `components/landing/anim`. Same motion, same 0.12 threshold;
the difference is that a block declares its own entrance where it is written
instead of opting into a page-wide querySelectorAll on a 900ms interval.
One shared IntersectionObserver backs every instance.
Also fixes a latent flash: the scan set `opacity:0` in an effect after paint,
so a revealed block could show before hiding itself. `FadeIn` renders visible
and hides in a layout effect, before paint.
Styles are written straight to the node rather than held in React state. That
is what this component is — there is no state the rest of the tree can
observe, the hide must land before paint, and routing it through useState
means a synchronous set inside a layout effect, i.e. a cascading render on
mount for every block on the page.
Three paths never hide, because stranding real copy at `opacity:0` is a worse
failure than an un-animated heading: no IntersectionObserver, reduced motion,
and no-JS (first render is visible, so server markup stays readable).
Migrates all seven existing reveal sites and adds fades to the ActIngest and
ActTutor headings. The engine owns exactly six attributes — [data-cap],
[data-tick], [data-tcap], [data-panel], [data-ingest-tile], [data-chip] — and
no FadeIn wraps any of them; ActTutor's stops short of its [data-tcap]
captions. ActGraph gets none: both its headings ARE captions. FeatureLab gets
none either: it is a fixed modal with its own panelFade entrance, never
scrolled into view.
`data-*` passthrough is not decoration — DragField welds its cluster tracks to
[data-drag-anchor], and the FAQ column is both an anchor and a reveal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@AndresL230@Darkest-Teddy
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(landing): v5 landing, back as a PR to fix before it ships again by AndresL230 · Pull Request #586 · SaplingLearn/Sapling · GitHub
Skip to content

feat(landing): v5 landing, back as a PR to fix before it ships again - #586

Open
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply
Open

feat(landing): v5 landing, back as a PR to fix before it ships again#586
AndresL230 wants to merge 4 commits into
mainfrom
feat/landing-v5-reapply

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

The v5 marketing landing, pulled off main in #584 and off production in #585, back as an open PR so it can be fixed before it ships again.

What this is

A single re-apply commit: the revert of #584's squash (9f34454c). The tree is byte-identical to 5b1a240dmain as it stood immediately before the revert — so this carries exactly what #524 (47 commits) and #568 (the drag-field e2e journey) put on main, and nothing else:

  • src/components/landing-v5/ — Hero, IntroOverlay, Navbar, the three Acts, FeatureLab + the eight lab demos, Gallery, Journal, Faq, Closing, BetaModal, DragField
  • src/lib/landing/ — the scroll/physics engine (engine/sim.ts + tests, ambient, flip, graph, marquee, plant) and the hero3d/ WebGL panel rig
  • src/components/companion/CompanionShell.tsx + the five public pages the nav links to (/faq, /gallery, /news, /team, /wiki), and the /about port
  • globals.css — the .landing-dc token scope; lib/api.ts — the beta-signup client
  • retirement of the v4 marketing/ surfaces, bands, and graph demo
  • e2e/landing-drag-field.spec.ts (replacing landing-graph.spec.ts), the v5 public-seo.spec.ts assertions, and the matching eslint-suppressions.json baseline

The original 47-commit history stays reachable through #524's merge commit on main; this PR is the revert-of-the-revert, so main keeps a linear story.

Status

Not ready to merge as-is — this is the starting point for the landing fix. Push fixes onto this branch.

Getting it back to production

production is on the pre-#576 v4 landing again. Once the fix lands here and on main, re-cut the frontend-only cherry the way #576 did (or take it with the next make promote).

🤖 Generated with Claude Code

Reverts 9f34454 (#584), which pulled the v5 marketing landing off main.
The tree is byte-identical to 5b1a240, main's state immediately before
the revert: the landing-v5/ components, lib/landing/ engine + hero3d rig,
the five companion pages, the v4 retirement, and the #568 drag-field
e2e journey. Lives here as an open PR so the landing can be fixed before
it goes back onto main / production.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c0fb03e-5ffc-476f-a7ab-6e8ffd54b9c2


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabaseBot commented Aug 23, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 23, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging20eefd1Commit Preview URL

Branch Preview URL
Aug 28 2026, 02:23 AM

Darkest-Teddyand others added 3 commits August 27, 2026 22:20
Ports the hero from `Sapling Landing.html`, the later Claude Design export.
The wordmark lockup is unchanged; what moved:
- The 01/02/03 Ingest/Map/Recall keys are gone. The bottom band is now three
columns holding the info box (left), the scroll cue (centre) and an empty
spacer (right). The spacer is load bearing: it balances the info box so the
cue lands on the viewport centre line, not the centre of the content.
- The info box moved from the right edge to the left.
- The cue reads SCROLL TO EXPLORE, flanked by two arrows, the second offset
0.28s so the pair reads as a fall rather than a blink.
- Both CTAs grew to 16px/30px and 16px/27px at 16.5px.
- The lede is 500 weight on #2A3B32 rather than regular on #33443B.
Splits the veil into two knobs. The source packs three gradients into two
elements, but they do different jobs: the bottom band is a legibility floor
for the info box and cue and has faded out by half height, while the rim and
the two panel radials are what sit on top of the WebGL cards. Scaling them
together meant the cards could not be cleared without also thinning the text
backing. `sceneWash` is held at 0.3, which drops the heaviest veil over any
card from 0.84 to ~0.25; `wash` stays at full strength.
Drops the two `.hero-keys` rules from globals.css, whose selector no longer
exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
The three acts recomputed their progress from scroll position every frame, so
scrolling back up ran them in reverse: the graph un-assembled, chips flew back
into the document, the carousel spun backwards. `actPeak` now latches the
furthest point each act reached, so progress only advances and scrolling up
holds the played state.
That alone made a second problem obvious. Each act's extra height is nothing
but scrub runway — 360vh on act 1, 220vh on act 2, 240vh on act 3 — and once
latched it drives nothing, so getting back out meant grinding through eight
screens of frozen scenery. `foldActs` cuts a section to one viewport and takes
the same distance off the scroll position.
The fold is seamless from any position because of how sticky behaves. While
pinned, the child sits at the viewport top for every scrollY in
[top, top + h - vh], so cutting to one viewport and pulling scrollY back to
`top` leaves it pixel-identical. Past the act the shrink instead cancels the
upward shift of everything below; above it nothing on screen moves. One
expression covers all three, and clamping at zero makes the third a no-op.
The smoothed scroll passes shift too, or the next frame reads a 360vh delta
and either lerps through it or trips the `jumping` bypass.
Two triggers, and between them upward scroll is never pinned: the act
finished, or the reader is scrolling up inside one they have started. The
second strands a half-played act at whatever progress it reached, because the
runway that would finish it is exactly what gets cut — the intended trade,
since scrolling up out of a scene is a decision to leave it.
Skipped while explore mode is active: it holds the reader inside act 1 on
purpose, and resizing the section there would move the page out from under
the graph.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Replaces the `[data-reveal]` attribute scan in `useLanding` with a `FadeIn`
component under `components/landing/anim`. Same motion, same 0.12 threshold;
the difference is that a block declares its own entrance where it is written
instead of opting into a page-wide querySelectorAll on a 900ms interval.
One shared IntersectionObserver backs every instance.
Also fixes a latent flash: the scan set `opacity:0` in an effect after paint,
so a revealed block could show before hiding itself. `FadeIn` renders visible
and hides in a layout effect, before paint.
Styles are written straight to the node rather than held in React state. That
is what this component is — there is no state the rest of the tree can
observe, the hide must land before paint, and routing it through useState
means a synchronous set inside a layout effect, i.e. a cascading render on
mount for every block on the page.
Three paths never hide, because stranding real copy at `opacity:0` is a worse
failure than an un-animated heading: no IntersectionObserver, reduced motion,
and no-JS (first render is visible, so server markup stays readable).
Migrates all seven existing reveal sites and adds fades to the ActIngest and
ActTutor headings. The engine owns exactly six attributes — [data-cap],
[data-tick], [data-tcap], [data-panel], [data-ingest-tile], [data-chip] — and
no FadeIn wraps any of them; ActTutor's stops short of its [data-tcap]
captions. ActGraph gets none: both its headings ARE captions. FeatureLab gets
none either: it is a fixed modal with its own panelFade entrance, never
scrolled into view.
`data-*` passthrough is not decoration — DragField welds its cluster tracks to
[data-drag-anchor], and the FAQ column is both an anchor and a reveal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3wrhrFAfwkY1Zd13GL5QM
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@AndresL230@Darkest-Teddy