fix scroll smooth
This commit is contained in:
@@ -123,7 +123,10 @@ function useLabelFade(i: number, progress: React.RefObject<number>, awake: boole
|
||||
useFrame(() => {
|
||||
if (!awake) return;
|
||||
const idx = (progress.current ?? 0) * (N - 1);
|
||||
const op = THREE.MathUtils.clamp(1 - (Math.abs(idx - i) - 0.4) / 0.45, 0, 1);
|
||||
// Full within ±0.2 of the district centre, fading to 0 by ±0.5 — i.e. exactly
|
||||
// at the stage boundary where `focused` flips and the labels unmount, so the
|
||||
// mount/unmount is never visible (labels are already at 0 opacity by then).
|
||||
const op = THREE.MathUtils.clamp(1 - (Math.abs(idx - i) - 0.2) / 0.3, 0, 1);
|
||||
for (const el of labels.current) el.style.opacity = String(op);
|
||||
});
|
||||
return register;
|
||||
@@ -321,7 +324,7 @@ function OrderPacket({ curve, offset }: { curve: THREE.Curve<THREE.Vector3>; off
|
||||
}
|
||||
|
||||
/** A vehicle parked on its own glowing route node (flat on the floor, no white pad). */
|
||||
function RiderAvatar({ rider, register, awake }: { rider: typeof RIDERS[number]; register: (el: HTMLElement | null) => void; awake: boolean }) {
|
||||
function RiderAvatar({ rider, register, awake, focused }: { rider: typeof RIDERS[number]; register: (el: HTMLElement | null) => void; awake: boolean; focused: boolean }) {
|
||||
return (
|
||||
<group position={[rider.x, 0, ROW_Z]}>
|
||||
<mesh position={[0, 0.014, 0]} rotation={[-Math.PI / 2, 0, 0]}><ringGeometry args={[0.52, 0.64, 36]} /><meshBasicMaterial color={GREEN} transparent opacity={0.6} side={THREE.DoubleSide} toneMapped={false} /></mesh>
|
||||
@@ -331,7 +334,7 @@ function RiderAvatar({ rider, register, awake }: { rider: typeof RIDERS[number];
|
||||
<GLBVehicle kind={rider.kind} awake={awake} />
|
||||
</Suspense>
|
||||
</group>
|
||||
{awake && (
|
||||
{focused && (
|
||||
<Html center distanceFactor={9} position={[0, 1.5, 0]} zIndexRange={[20, 0]} pointerEvents="none">
|
||||
<div className="dm-st3d-chip" ref={register}>
|
||||
<span className="dm-st3d-chip__ico">{rider.icon}</span>
|
||||
@@ -346,7 +349,7 @@ function RiderAvatar({ rider, register, awake }: { rider: typeof RIDERS[number];
|
||||
const ORDERS_SRC: [number, number, number] = [3.3, 0.45, -0.7];
|
||||
const HUB_CORE: [number, number, number] = [0, 0.55, -1.4];
|
||||
|
||||
const IntakeHub = React.memo(function IntakeHub({ i, progress, reduced, awake }: { i: number; progress: React.RefObject<number>; reduced: boolean; awake: boolean }) {
|
||||
const IntakeHub = React.memo(function IntakeHub({ i, progress, reduced, awake, focused }: { i: number; progress: React.RefObject<number>; reduced: boolean; awake: boolean; focused: boolean }) {
|
||||
const register = useLabelFade(i, progress, awake);
|
||||
const counter = useRef<HTMLSpanElement>(null);
|
||||
const halo = useRef<THREE.Mesh>(null);
|
||||
@@ -389,9 +392,9 @@ const IntakeHub = React.memo(function IntakeHub({ i, progress, reduced, awake }:
|
||||
{/* soft contact shadow grounding the fleet (baked once for performance) */}
|
||||
<ContactShadows position={[0, 0.02, ROW_Z]} scale={[7.5, 2.6]} resolution={512} blur={2.6} far={1.2} opacity={0.4} frames={1} color="#1e293b" />
|
||||
|
||||
{RIDERS.map((r) => <RiderAvatar key={r.id} rider={r} register={register} awake={awake} />)}
|
||||
{RIDERS.map((r) => <RiderAvatar key={r.id} rider={r} register={register} awake={awake} focused={focused} />)}
|
||||
|
||||
{awake && (
|
||||
{focused && (
|
||||
<>
|
||||
<Html center distanceFactor={9} position={[ORDERS_SRC[0], ORDERS_SRC[1] + 0.5, ORDERS_SRC[2]]} zIndexRange={[20, 0]} pointerEvents="none">
|
||||
<div className="dm-st3d-tag" style={{ ["--tc" as string]: GREEN }} ref={register}>📄 orders.csv</div>
|
||||
@@ -422,7 +425,7 @@ const STRATS = [
|
||||
];
|
||||
const CORE: [number, number, number] = [0, 1.45, -2.7];
|
||||
|
||||
const StrategyNetwork = React.memo(function StrategyNetwork({ i, progress, reduced, awake }: { i: number; progress: React.RefObject<number>; reduced: boolean; awake: boolean }) {
|
||||
const StrategyNetwork = React.memo(function StrategyNetwork({ i, progress, reduced, awake, focused }: { i: number; progress: React.RefObject<number>; reduced: boolean; awake: boolean; focused: boolean }) {
|
||||
const register = useLabelFade(i, progress, awake);
|
||||
const ring = useRef<THREE.Mesh>(null);
|
||||
const coreMesh = useRef<THREE.Mesh>(null);
|
||||
@@ -450,7 +453,7 @@ const StrategyNetwork = React.memo(function StrategyNetwork({ i, progress, reduc
|
||||
<mesh position={[0, 0.65, -2.7]}><cylinderGeometry args={[0.4, 0.62, 1.2, 24]} /><meshStandardMaterial color="#2a3350" metalness={0.5} roughness={0.4} /></mesh>
|
||||
<mesh ref={coreMesh} position={CORE}><icosahedronGeometry args={[0.6, 1]} /><meshStandardMaterial color={PURPLE} emissive={PURPLE} emissiveIntensity={1.3} toneMapped={false} flatShading /></mesh>
|
||||
<mesh ref={ring} position={CORE} rotation={[Math.PI / 2.4, 0, 0]}><torusGeometry args={[0.98, 0.03, 10, 44]} /><meshStandardMaterial color={PURPLE} emissive={PURPLE} emissiveIntensity={1.1} toneMapped={false} /></mesh>
|
||||
{awake && (
|
||||
{focused && (
|
||||
<Html center distanceFactor={9} position={[0, 2.5, -2.7]} zIndexRange={[20, 0]} pointerEvents="none">
|
||||
<div className="dm-st3d-tag" style={{ ["--tc" as string]: PURPLE }} ref={register}>🤖 AI Engine</div>
|
||||
</Html>
|
||||
@@ -465,7 +468,7 @@ const StrategyNetwork = React.memo(function StrategyNetwork({ i, progress, reduc
|
||||
{/* flat glowing route node (consistent with the dispatch network) */}
|
||||
<mesh position={[l.end[0], 0.135, l.end[2]]} rotation={[-Math.PI / 2, 0, 0]}><ringGeometry args={[0.34, 0.44, 28]} /><meshBasicMaterial color={col} transparent opacity={l.unified ? 0.6 : 0.4} side={THREE.DoubleSide} toneMapped={false} /></mesh>
|
||||
<GlowNode position={l.end} color={col} size={l.unified ? 0.15 : 0.11} />
|
||||
{awake && (
|
||||
{focused && (
|
||||
<Html center distanceFactor={9} position={[l.end[0], l.end[1] + 0.62, l.end[2]]} zIndexRange={[20, 0]} pointerEvents="none">
|
||||
<div className={`dm-st3d-tag ${l.unified ? "is-u" : "is-muted"}`} style={{ ["--tc" as string]: l.unified ? PURPLE : "#94a3b8" }} ref={register}>{l.name}</div>
|
||||
</Html>
|
||||
@@ -532,7 +535,7 @@ function DeliveryBadge({ pos, i, progress }: { pos: [number, number, number]; i:
|
||||
);
|
||||
}
|
||||
|
||||
const CityRouteMap = React.memo(function CityRouteMap({ i, progress, reduced, awake }: { i: number; progress: React.RefObject<number>; reduced: boolean; awake: boolean }) {
|
||||
const CityRouteMap = React.memo(function CityRouteMap({ i, progress, reduced, awake, focused }: { i: number; progress: React.RefObject<number>; reduced: boolean; awake: boolean; focused: boolean }) {
|
||||
const register = useLabelFade(i, progress, awake);
|
||||
// OPEN delivery path (Dispatch Hub → … → Delivery). Static "road"; only the truck moves.
|
||||
const route = useMemo(() => {
|
||||
@@ -571,7 +574,7 @@ const CityRouteMap = React.memo(function CityRouteMap({ i, progress, reduced, aw
|
||||
{/* delivery arrival pulse (only mounted/animated while this district is active) */}
|
||||
{awake && <DeliveryPulse pos={[delivery[0], 0.02, delivery[1]]} i={i} progress={progress} />}
|
||||
|
||||
{awake && (
|
||||
{focused && (
|
||||
<>
|
||||
<Html center distanceFactor={9} position={[DEPOT3[0], 1.05, DEPOT3[1]]} zIndexRange={[20, 0]} pointerEvents="none">
|
||||
<div className="dm-st3d-tag" style={{ ["--tc" as string]: BLUE }} ref={register}>🏢 Dispatch Hub</div>
|
||||
@@ -610,7 +613,7 @@ const KPIS = [
|
||||
{ n: "Battery", v: 100, a: 0.66 },
|
||||
];
|
||||
|
||||
const CommandCenter = React.memo(function CommandCenter({ i, progress, awake }: { i: number; progress: React.RefObject<number>; awake: boolean }) {
|
||||
const CommandCenter = React.memo(function CommandCenter({ i, progress, awake, focused }: { i: number; progress: React.RefObject<number>; awake: boolean; focused: boolean }) {
|
||||
const register = useLabelFade(i, progress, awake);
|
||||
const screens = useMemo(
|
||||
() =>
|
||||
@@ -631,7 +634,7 @@ const CommandCenter = React.memo(function CommandCenter({ i, progress, awake }:
|
||||
<RoundedBox args={[1.7, 1.15, 0.06]} radius={0.06} smoothness={2}>
|
||||
<meshStandardMaterial color="#ffffff" emissive={ORANGE} emissiveIntensity={0.18} metalness={0.1} roughness={0.4} transparent opacity={0.92} />
|
||||
</RoundedBox>
|
||||
{awake && (
|
||||
{focused && (
|
||||
<Html center distanceFactor={8} position={[0, 0, 0.05]} zIndexRange={[20, 0]} pointerEvents="none">
|
||||
<div className="dm-st3d-kpi" style={{ ["--tc" as string]: ORANGE }} ref={register}>
|
||||
<span className="dm-st3d-kpi__n">{s.n}</span>
|
||||
@@ -643,7 +646,7 @@ const CommandCenter = React.memo(function CommandCenter({ i, progress, awake }:
|
||||
</group>
|
||||
))}
|
||||
|
||||
{awake && (
|
||||
{focused && (
|
||||
<Html center distanceFactor={9} position={[0, 3.1, -0.4]} zIndexRange={[20, 0]} pointerEvents="none">
|
||||
<div className="dm-st3d-score" style={{ ["--tc" as string]: ORANGE }} ref={register}>Performance Grade <b>A</b> · 4.5 / 5</div>
|
||||
</Html>
|
||||
@@ -663,7 +666,7 @@ const PODIUM = [
|
||||
{ n: "Proximity", v: 64, x: 2.4, win: false },
|
||||
];
|
||||
|
||||
function Pillar({ p, register, awake }: { p: typeof PODIUM[number]; register: (el: HTMLElement | null) => void; awake: boolean }) {
|
||||
function Pillar({ p, register, focused }: { p: typeof PODIUM[number]; register: (el: HTMLElement | null) => void; focused: boolean }) {
|
||||
const h = 0.6 + (p.v / 100) * 2.2;
|
||||
const col = p.win ? RED : "#94a3b8";
|
||||
return (
|
||||
@@ -672,7 +675,7 @@ function Pillar({ p, register, awake }: { p: typeof PODIUM[number]; register: (e
|
||||
<boxGeometry args={[1.0, h, 1.0]} />
|
||||
<meshStandardMaterial color={col} emissive={col} emissiveIntensity={p.win ? 0.7 : 0.15} metalness={0.2} roughness={0.4} transparent opacity={p.win ? 0.96 : 0.7} toneMapped={false} />
|
||||
</mesh>
|
||||
{awake && (
|
||||
{focused && (
|
||||
<Html center distanceFactor={9} position={[0, h + 0.45, 0]} zIndexRange={[20, 0]} pointerEvents="none">
|
||||
<div className={`dm-st3d-tag ${p.win ? "is-win" : "is-muted"}`} style={{ ["--tc" as string]: col }} ref={register}><b>{p.v}%</b> {p.n}</div>
|
||||
</Html>
|
||||
@@ -681,7 +684,7 @@ function Pillar({ p, register, awake }: { p: typeof PODIUM[number]; register: (e
|
||||
);
|
||||
}
|
||||
|
||||
const WinnerPodium = React.memo(function WinnerPodium({ i, progress, reduced, isMobile, awake }: { i: number; progress: React.RefObject<number>; reduced: boolean; isMobile: boolean; awake: boolean }) {
|
||||
const WinnerPodium = React.memo(function WinnerPodium({ i, progress, reduced, isMobile, awake, focused }: { i: number; progress: React.RefObject<number>; reduced: boolean; isMobile: boolean; awake: boolean; focused: boolean }) {
|
||||
const register = useLabelFade(i, progress, awake);
|
||||
const trophy = useRef<THREE.Group>(null);
|
||||
useFrame((state) => {
|
||||
@@ -697,7 +700,7 @@ const WinnerPodium = React.memo(function WinnerPodium({ i, progress, reduced, is
|
||||
<mesh position={[0, 0.12, -0.6]} rotation={[-Math.PI / 2, 0, 0]}><ringGeometry args={[4.1, 4.32, 56]} /><meshBasicMaterial color={RED} transparent opacity={0.35} side={THREE.DoubleSide} toneMapped={false} /></mesh>
|
||||
<mesh position={[-2.4, 0.14, -0.6]}><cylinderGeometry args={[0.85, 0.9, 0.14, 28]} /><meshStandardMaterial color={RED} emissive={RED} emissiveIntensity={0.9} toneMapped={false} /></mesh>
|
||||
|
||||
{PODIUM.map((p) => <Pillar key={p.n} p={p} register={register} awake={awake} />)}
|
||||
{PODIUM.map((p) => <Pillar key={p.n} p={p} register={register} focused={focused} />)}
|
||||
|
||||
<mesh position={[-2.4, 3.0, -0.6]}><coneGeometry args={[1.0, 2.6, 28, 1, true]} /><meshBasicMaterial color={RED} transparent opacity={0.07} side={THREE.DoubleSide} toneMapped={false} /></mesh>
|
||||
<group ref={trophy} position={[-2.4, 3.5, -0.6]}>
|
||||
@@ -706,9 +709,9 @@ const WinnerPodium = React.memo(function WinnerPodium({ i, progress, reduced, is
|
||||
<mesh position={[0, -0.22, 0]}><cylinderGeometry args={[0.18, 0.22, 0.1, 16]} /><meshStandardMaterial color="#FFD45A" emissive="#FFB020" emissiveIntensity={1} toneMapped={false} /></mesh>
|
||||
</group>
|
||||
|
||||
{awake && !reduced && <Sparkles count={isMobile ? 16 : 28} scale={[5, 4, 4]} position={[-2.4, 2.6, -0.6]} size={3.2} speed={0.5} opacity={0.9} color="#ff9aa9" />}
|
||||
{awake && !reduced && <Sparkles count={isMobile ? 12 : 16} scale={[5, 4, 4]} position={[-2.4, 2.6, -0.6]} size={3.2} speed={0.5} opacity={0.9} color="#ff9aa9" />}
|
||||
|
||||
{awake && (
|
||||
{focused && (
|
||||
<Html center distanceFactor={9} position={[0.7, 3.0, -0.6]} zIndexRange={[20, 0]} pointerEvents="none">
|
||||
<div className="dm-st3d-winner3d" ref={register}>
|
||||
<span className="dm-st3d-winner3d__top">🏆 Best Strategy</span>
|
||||
@@ -818,14 +821,18 @@ function Scene({ progress, reduced, isMobile, stage, active, perf }: { progress:
|
||||
<Floor />
|
||||
<DataRoad />
|
||||
|
||||
<IntakeHub i={0} progress={progress} reduced={reduced} awake={near(0)} />
|
||||
<StrategyNetwork i={1} progress={progress} reduced={reduced} awake={near(1)} />
|
||||
<CityRouteMap i={2} progress={progress} reduced={reduced} awake={near(2)} />
|
||||
<CommandCenter i={3} progress={progress} awake={near(3)} />
|
||||
<WinnerPodium i={4} progress={progress} reduced={reduced} isMobile={isMobile} awake={near(4)} />
|
||||
{/* `awake` (±1) keeps geometry/shaders/animation warm for a pop-free
|
||||
transition; `focused` (current district only) gates the expensive
|
||||
in-canvas <Html> labels so only ~one district's worth project + write
|
||||
to the DOM each frame instead of three. */}
|
||||
<IntakeHub i={0} progress={progress} reduced={reduced} awake={near(0)} focused={stage === 0} />
|
||||
<StrategyNetwork i={1} progress={progress} reduced={reduced} awake={near(1)} focused={stage === 1} />
|
||||
<CityRouteMap i={2} progress={progress} reduced={reduced} awake={near(2)} focused={stage === 2} />
|
||||
<CommandCenter i={3} progress={progress} awake={near(3)} focused={stage === 3} />
|
||||
<WinnerPodium i={4} progress={progress} reduced={reduced} isMobile={isMobile} awake={near(4)} focused={stage === 4} />
|
||||
|
||||
{heavyFx && (
|
||||
<Sparkles count={30} scale={[18, 7, N * 13]} position={[0, 3, (-(N - 1) * 13) / 2]} size={2} speed={0.22} opacity={0.35} color="#9aa6c4" />
|
||||
<Sparkles count={16} scale={[18, 7, N * 13]} position={[0, 3, (-(N - 1) * 13) / 2]} size={2} speed={0.22} opacity={0.35} color="#9aa6c4" />
|
||||
)}
|
||||
|
||||
{heavyFx && (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useEffect, useRef, useState, useSyncExternalStore } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { motion, useMotionValue, useTransform, type MotionValue } from "framer-motion";
|
||||
import gsap from "gsap";
|
||||
@@ -12,6 +12,23 @@ const StrategyCanvas = dynamic(() => import("./StrategyCanvas"), { ssr: false })
|
||||
/** Center of each stage's scroll window (0…1). */
|
||||
const CENTER = (i: number) => i / (N - 1);
|
||||
|
||||
/** True only while a card's own opacity window is open (tiny buffer). Keeps
|
||||
* not-yet-reached stage cards out of the DOM / off the compositor until their
|
||||
* stage is on screen — no future workflow state is rendered before activation.
|
||||
* Visually identical: a card outside its window is opacity:0 regardless. */
|
||||
function useInWindow(mv: MotionValue<number>, threshold = 0.01): boolean {
|
||||
// `mv` is an external mutable store (a MotionValue). useTransform `.set()`s its
|
||||
// output synchronously while the PARENT renders, so a plain `.on("change") -> setState`
|
||||
// updates this component during the parent's render (React warns). useSyncExternalStore
|
||||
// is built for exactly this: it reads a snapshot and reconciles store-changes-during-
|
||||
// render safely. The snapshot is a primitive boolean, so it never re-renders needlessly.
|
||||
return useSyncExternalStore(
|
||||
(onStoreChange) => mv.on("change", onStoreChange),
|
||||
() => mv.get() > threshold,
|
||||
() => mv.get() > threshold,
|
||||
);
|
||||
}
|
||||
|
||||
/** Persistent top rail: the 5 stages, current one highlighted. */
|
||||
function StageRail({ active }: { active: number }) {
|
||||
return (
|
||||
@@ -45,9 +62,13 @@ function StageCard({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const c = CENTER(i);
|
||||
const opacity = useTransform(scroll, [c - 0.14, c - 0.06, c + 0.06, c + 0.14], [0, 1, 1, 0]);
|
||||
const y = useTransform(scroll, [c - 0.14, c - 0.05], [34, 0]);
|
||||
// Window kept < the 0.25 per-stage span (5 stages) so two adjacent stage cards
|
||||
// never overlap — one stage's card is fully out before the next fades in.
|
||||
const opacity = useTransform(scroll, [c - 0.1, c - 0.05, c + 0.05, c + 0.1], [0, 1, 1, 0]);
|
||||
const y = useTransform(scroll, [c - 0.1, c - 0.05], [34, 0]);
|
||||
const s = STAGES[i];
|
||||
// Only mount the card while its stage's cross-fade window is open.
|
||||
if (!useInWindow(opacity)) return null;
|
||||
return (
|
||||
<motion.div
|
||||
className={`dm-st-card-story is-${side}`}
|
||||
@@ -123,7 +144,9 @@ export default function StrategySection({ connected = false }: { connected?: boo
|
||||
trigger: el,
|
||||
start: "top top",
|
||||
end: "bottom bottom",
|
||||
scrub: 0.5,
|
||||
// Match Workflow 1's responsiveness (0.4) for consistent pacing across all
|
||||
// three workflows — 0.5 made the dolly feel slower / disconnected here.
|
||||
scrub: 0.4,
|
||||
invalidateOnRefresh: true,
|
||||
onUpdate: (self) => {
|
||||
const p = self.progress;
|
||||
@@ -135,7 +158,7 @@ export default function StrategySection({ connected = false }: { connected?: boo
|
||||
if (na !== lastActive) { lastActive = na; setActive(na); }
|
||||
},
|
||||
});
|
||||
const refresh = setTimeout(() => ScrollTrigger.refresh(), 300);
|
||||
const refresh = setTimeout(() => ScrollTrigger.refresh(), 120);
|
||||
return () => { clearTimeout(refresh); st.kill(); };
|
||||
}, [scroll]);
|
||||
|
||||
@@ -154,10 +177,14 @@ export default function StrategySection({ connected = false }: { connected?: boo
|
||||
<div className="dm-st-card">
|
||||
{mountScene && (
|
||||
<div className="dm-st-canvas">
|
||||
<StrategyCanvas progress={progressRef} reduced={reduced} isMobile={isMobile} active={sceneActive} stage={active} />
|
||||
<StrategyCanvas progress={progressRef} reduced={reduced} isMobile={isMobile} active={sceneActive && pinState === "pinned"} stage={active} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Overlay mounts only once the section is pinned/activated — its content
|
||||
can never be seen during the approach ("before"), where the sticky sits
|
||||
at the top of the tall section near the previous workflow's seam. */}
|
||||
{pinState !== "before" && (
|
||||
<div className="dm-st-ui">
|
||||
{/* Persistent header */}
|
||||
<motion.div className="dm-st-top" style={{ opacity: headerOpacity }}>
|
||||
@@ -229,6 +256,7 @@ export default function StrategySection({ connected = false }: { connected?: boo
|
||||
</div>
|
||||
</StageCard>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<style>{styles}</style>
|
||||
@@ -237,8 +265,12 @@ export default function StrategySection({ connected = false }: { connected?: boo
|
||||
}
|
||||
|
||||
const styles = `
|
||||
.dm-st { position: relative; height: 720vh; background: transparent; }
|
||||
.dm-st-sticky { position: absolute; top: 0; left: 0; width: 100%; height: 100vh; overflow: hidden; }
|
||||
/* Scroll length tuned for pacing: ~100vh per stage (was 144vh) so the 5 stages
|
||||
complete in noticeably less scrolling and the workflow feels tighter / faster.
|
||||
Stage cross-fade windows are progress-based (0…1), so they stay aligned. */
|
||||
.dm-st { position: relative; height: 500vh; background: transparent; }
|
||||
.dm-st-sticky { position: absolute; top: 0; left: 0; width: 100%; height: 100vh; overflow: hidden;
|
||||
will-change: transform; transform: translateZ(0); backface-visibility: hidden; }
|
||||
.dm-st.is-pinned .dm-st-sticky { position: fixed; top: 0; left: 0; }
|
||||
.dm-st.is-after .dm-st-sticky { position: absolute; top: auto; bottom: 0; }
|
||||
|
||||
@@ -257,6 +289,9 @@ const styles = `
|
||||
.dm-st.is-connected .dm-st-card {
|
||||
top: 20px !important; left: 20px !important; right: 20px !important; bottom: 0 !important;
|
||||
border-radius: 28px 28px 0 0 !important; border-bottom: none !important;
|
||||
/* Flush against the Strategy card below — drop the heavy downward shadow so it
|
||||
doesn't cast a dark band onto that card's top edge (the two read as one container). */
|
||||
box-shadow: none !important;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.dm-st.is-connected .dm-st-card {
|
||||
@@ -397,7 +432,7 @@ const styles = `
|
||||
.dm-st-rail__line { width: 9px; }
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.dm-st { height: 640vh; }
|
||||
.dm-st { height: 420vh; }
|
||||
.dm-st-card-story { left: 0 !important; right: 0 !important; margin: 0 auto; width: calc(100% - 28px);
|
||||
bottom: clamp(18px, 4vh, 40px); padding: 15px 16px; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user