diff --git a/index.html b/index.html index b3b6103..96b7b8a 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,8 @@ - + + CAPY diff --git a/package-lock.json b/package-lock.json index 1ebc9e9..c119735 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "jest": "^30.3.0", "jest-environment-jsdom": "^30.3.0", "lint-staged": "^16.4.0", - "prettier": "^3.8.1", + "prettier": "^3.8.3", "ts-jest": "^29.4.6", "typescript": "~5.9.3", "typescript-eslint": "^8.57.0", @@ -6902,9 +6902,9 @@ } }, "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", "bin": { diff --git a/package.json b/package.json index bda6422..6bbf2da 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "jest": "^30.3.0", "jest-environment-jsdom": "^30.3.0", "lint-staged": "^16.4.0", - "prettier": "^3.8.1", + "prettier": "^3.8.3", "ts-jest": "^29.4.6", "typescript": "~5.9.3", "typescript-eslint": "^8.57.0", diff --git a/src/index.css b/src/index.css index 1bec510..10a21e1 100644 --- a/src/index.css +++ b/src/index.css @@ -15,6 +15,12 @@ body, html, body { overflow: hidden; + background: var(--c-bg); + transition: background-color 200ms ease; +} + +body.rail-active { + background: var(--c-surface-light); } body { diff --git a/src/lander/Lander.module.css b/src/lander/Lander.module.css index 99a9f8c..d76c6a3 100644 --- a/src/lander/Lander.module.css +++ b/src/lander/Lander.module.css @@ -71,3 +71,42 @@ min-height: var(--lander-panel-height); border-radius: var(--radius-card); } + +.mobileSnapScroller { + display: none; +} + +@media (max-width: 768px) { + .appRoot { + overflow: hidden; + height: 100dvh; + min-height: 100dvh; + padding: 0; + background: var(--c-bg); + } + + .mobileSnapScroller { + display: block; + width: 100%; + height: 100dvh; + overflow-y: scroll; + overflow-x: hidden; + scroll-snap-type: y mandatory; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + } + + .mobileSnapScroller::-webkit-scrollbar { + display: none; + } + + :global(.panel) { + width: 100%; + height: 100dvh; + min-height: 100dvh; + border-radius: 0; + border: none; + scroll-snap-align: start; + scroll-snap-stop: always; + } +} diff --git a/src/lander/Lander.tsx b/src/lander/Lander.tsx index fe2fcc1..951bc6c 100644 --- a/src/lander/Lander.tsx +++ b/src/lander/Lander.tsx @@ -1,4 +1,4 @@ -import { useRef } from 'react' +import { useEffect, useRef, useState } from 'react' import { motion } from 'framer-motion' import { TopNav } from '@/shared/components/TopNav' import { ExitOverlay } from '@/shared/components/ExitOverlay' @@ -12,6 +12,8 @@ import { InterfaceSection } from './sections/InterfaceSection' import { Helmet } from 'react-helmet-async' import styles from './Lander.module.css' +const MOBILE_QUERY = '(max-width: 768px)' + /** * The main Lander component. * Serves as the landing page for capy, featuring scrolling sections and product details. @@ -19,7 +21,19 @@ import styles from './Lander.module.css' */ function Lander() { const scrollerRef = useRef(null) - useHorizontalWheelScroll(scrollerRef, { endCutoffPx: 300 }) + const disabledRef = useRef(null) + const [isMobile, setIsMobile] = useState( + () => typeof window !== 'undefined' && window.matchMedia(MOBILE_QUERY).matches, + ) + + useEffect(() => { + const mq = window.matchMedia(MOBILE_QUERY) + const handler = (event: MediaQueryListEvent) => setIsMobile(event.matches) + mq.addEventListener('change', handler) + return () => mq.removeEventListener('change', handler) + }, []) + + useHorizontalWheelScroll(isMobile ? disabledRef : scrollerRef, { endCutoffPx: 300 }) usePageTransition() @@ -33,22 +47,31 @@ function Lander() { />
- - -
- + {isMobile ? ( +
- - - -
+
+ ) : ( + <> + +
+ + + + + + + +
+ + )}
diff --git a/src/lander/sections/CapyRailSection.module.css b/src/lander/sections/CapyRailSection.module.css index 87e2597..635c0a0 100644 --- a/src/lander/sections/CapyRailSection.module.css +++ b/src/lander/sections/CapyRailSection.module.css @@ -128,3 +128,50 @@ opacity: 0.28; } } + +@media (max-width: 768px) { + .capyRailPanel { + width: 100%; + height: 100dvh; + border: none; + border-radius: 0; + background: var(--c-surface-light); + } + + .railContent { + width: calc(100% - var(--space-32)); + margin: calc(var(--space-50) + env(safe-area-inset-top)) var(--space-16) + calc(var(--space-32) + env(safe-area-inset-bottom)); + height: calc( + 100dvh - var(--space-50) - var(--space-32) - env(safe-area-inset-top) - + env(safe-area-inset-bottom) + ); + } + + .railLinks { + margin-top: var(--space-24); + } + + .verticalMarkWrap { + display: block; + position: absolute; + top: 0; + bottom: 0; + left: auto; + right: 0; + width: 160px; + height: 100%; + opacity: 0.25; + pointer-events: none; + } + + .verticalMark { + position: absolute; + top: 50%; + left: 50%; + width: 100svh; + height: 160px; + transform: translate(-50%, -50%) rotate(-90deg); + transform-origin: center center; + } +} diff --git a/src/lander/sections/CapyRailSection.tsx b/src/lander/sections/CapyRailSection.tsx index 107818d..0699702 100644 --- a/src/lander/sections/CapyRailSection.tsx +++ b/src/lander/sections/CapyRailSection.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react' import { AnimatedPanel } from '@/shared/components/AnimatedPanel' import { AspectImage } from '@/shared/components/AspectImage' import { StaggerWords } from '@/shared/components/StaggerWords' @@ -15,6 +16,22 @@ const socialAssets = [ ] export function CapyRailSection() { + useEffect(() => { + const el = document.getElementById('more') + if (!el) return + const observer = new IntersectionObserver( + ([entry]) => { + document.body.classList.toggle('rail-active', entry.intersectionRatio >= 0.85) + }, + { threshold: [0, 0.85, 1] }, + ) + observer.observe(el) + return () => { + observer.disconnect() + document.body.classList.remove('rail-active') + } + }, []) + return (