diff --git a/next.config.ts b/next.config.ts
index 4f95361..4c5003e 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -10,7 +10,18 @@ const nextConfig: NextConfig = {
// typedRoutes: true,
allowedDevOrigins: ["tillisoftware.local"],
images: {
- remotePatterns: [new URL("https://placecats.com/**")],
+ remotePatterns: [
+ {
+ protocol: "https",
+ hostname: "placecats.com",
+ pathname: "/**",
+ },
+ {
+ protocol: "https",
+ hostname: "picsum.photos",
+ pathname: "/**",
+ },
+ ],
// dangerouslyAllowSVG: true,
},
experimental: {
diff --git a/package.json b/package.json
index c1df37e..22d2772 100644
--- a/package.json
+++ b/package.json
@@ -44,9 +44,11 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
- "embla-carousel-auto-scroll": "^8.6.0",
- "embla-carousel-autoplay": "^8.6.0",
- "embla-carousel-react": "^8.6.0",
+ "embla-carousel": "9.0.0-rc01",
+ "embla-carousel-auto-scroll": "9.0.0-rc01",
+ "embla-carousel-autoplay": "9.0.0-rc01",
+ "embla-carousel-class-names": "9.0.0-rc01",
+ "embla-carousel-react": "9.0.0-rc01",
"gsap": "^3.13.0",
"hast-util-to-jsx-runtime": "^2.3.6",
"input-otp": "^1.4.2",
@@ -80,4 +82,4 @@
"tw-animate-css": "^1.4.0",
"typescript": "^5"
}
-}
+}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5539b1b..f50a859 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -110,15 +110,21 @@ importers:
date-fns:
specifier: ^4.1.0
version: 4.1.0
+ embla-carousel:
+ specifier: 9.0.0-rc01
+ version: 9.0.0-rc01
embla-carousel-auto-scroll:
- specifier: ^8.6.0
- version: 8.6.0(embla-carousel@8.6.0)
+ specifier: 9.0.0-rc01
+ version: 9.0.0-rc01(embla-carousel@9.0.0-rc01)
embla-carousel-autoplay:
- specifier: ^8.6.0
- version: 8.6.0(embla-carousel@8.6.0)
+ specifier: 9.0.0-rc01
+ version: 9.0.0-rc01(embla-carousel@9.0.0-rc01)
+ embla-carousel-class-names:
+ specifier: 9.0.0-rc01
+ version: 9.0.0-rc01(embla-carousel@9.0.0-rc01)
embla-carousel-react:
- specifier: ^8.6.0
- version: 8.6.0(react@19.2.0)
+ specifier: 9.0.0-rc01
+ version: 9.0.0-rc01(react@19.2.0)
gsap:
specifier: ^3.13.0
version: 3.13.0
@@ -1444,28 +1450,33 @@ packages:
dom-helpers@5.2.1:
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
- embla-carousel-auto-scroll@8.6.0:
- resolution: {integrity: sha512-WT9fWhNXFpbQ6kP+aS07oF5IHYLZ1Dx4DkwgCY8Hv2ZyYd2KMCPfMV1q/cA3wFGuLO7GMgKiySLX90/pQkcOdQ==}
+ embla-carousel-auto-scroll@9.0.0-rc01:
+ resolution: {integrity: sha512-9lMYoiriEwy2foZ0i7lHG9rU8TMPxSgw3jXmZr4de/UXzqB/fv+z8deHIOORSlA/5Po+6r6+mov3g2tT3T0S4w==}
peerDependencies:
- embla-carousel: 8.6.0
+ embla-carousel: 9.0.0-rc01
- embla-carousel-autoplay@8.6.0:
- resolution: {integrity: sha512-OBu5G3nwaSXkZCo1A6LTaFMZ8EpkYbwIaH+bPqdBnDGQ2fh4+NbzjXjs2SktoPNKCtflfVMc75njaDHOYXcrsA==}
+ embla-carousel-autoplay@9.0.0-rc01:
+ resolution: {integrity: sha512-gl7jUe0X9xd5v7IiyFF2FCR+KTBesnutM0gQ3S75oo9EizZi5tJMNdVaFwvzepz6kGzDkkSbKMWitQvAbFVfdQ==}
peerDependencies:
- embla-carousel: 8.6.0
+ embla-carousel: 9.0.0-rc01
- embla-carousel-react@8.6.0:
- resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==}
+ embla-carousel-class-names@9.0.0-rc01:
+ resolution: {integrity: sha512-3REWZOQGpc5cpbusftwghFle8yvINlCIqIj0QlonWRGsrgh6W+m4bAYeF0gKY9iGbuKfCRxDTBqdWQGA/DDzpA==}
+ peerDependencies:
+ embla-carousel: 9.0.0-rc01
+
+ embla-carousel-react@9.0.0-rc01:
+ resolution: {integrity: sha512-2ik9QtVm3UXJWkVdEEm6bInmxNSmxq9Z2q5GWuJx3v2vZvujmlDzcrIE6bvh+wWgPmDn6jekJCRHm1eEl/N0SA==}
peerDependencies:
react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
- embla-carousel-reactive-utils@8.6.0:
- resolution: {integrity: sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==}
+ embla-carousel-reactive-utils@9.0.0-rc01:
+ resolution: {integrity: sha512-RnW0NMrL7wVAQb9jro+l96hLI2JairyFHS2Jv+fvXakveD/c5aD9aoNH94YRbTmi0G7PxrKSxydmCpTy5eFmrA==}
peerDependencies:
- embla-carousel: 8.6.0
+ embla-carousel: 9.0.0-rc01
- embla-carousel@8.6.0:
- resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==}
+ embla-carousel@9.0.0-rc01:
+ resolution: {integrity: sha512-4BTERU1gAXgg4Vl0m7hQ1GzePGLNNfM2j030ww8i9idiPXumyRUpaNUDfT2zx1Hv8um1Ew7QKBy/HdNPz8L30g==}
enhanced-resolve@5.18.3:
resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
@@ -3198,25 +3209,29 @@ snapshots:
'@babel/runtime': 7.28.4
csstype: 3.1.3
- embla-carousel-auto-scroll@8.6.0(embla-carousel@8.6.0):
+ embla-carousel-auto-scroll@9.0.0-rc01(embla-carousel@9.0.0-rc01):
+ dependencies:
+ embla-carousel: 9.0.0-rc01
+
+ embla-carousel-autoplay@9.0.0-rc01(embla-carousel@9.0.0-rc01):
dependencies:
- embla-carousel: 8.6.0
+ embla-carousel: 9.0.0-rc01
- embla-carousel-autoplay@8.6.0(embla-carousel@8.6.0):
+ embla-carousel-class-names@9.0.0-rc01(embla-carousel@9.0.0-rc01):
dependencies:
- embla-carousel: 8.6.0
+ embla-carousel: 9.0.0-rc01
- embla-carousel-react@8.6.0(react@19.2.0):
+ embla-carousel-react@9.0.0-rc01(react@19.2.0):
dependencies:
- embla-carousel: 8.6.0
- embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0)
+ embla-carousel: 9.0.0-rc01
+ embla-carousel-reactive-utils: 9.0.0-rc01(embla-carousel@9.0.0-rc01)
react: 19.2.0
- embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0):
+ embla-carousel-reactive-utils@9.0.0-rc01(embla-carousel@9.0.0-rc01):
dependencies:
- embla-carousel: 8.6.0
+ embla-carousel: 9.0.0-rc01
- embla-carousel@8.6.0: {}
+ embla-carousel@9.0.0-rc01: {}
enhanced-resolve@5.18.3:
dependencies:
diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx
index f8007a3..f33de0c 100644
--- a/src/app/home/page.tsx
+++ b/src/app/home/page.tsx
@@ -303,7 +303,7 @@ export default function Home() {
@@ -377,8 +377,8 @@ export default function Home() {
-
-
+ {/* TODO: make a set mx for mobile */}
+
{
- const interval = setInterval(() => {
- setActiveIndex((current) => (current + 1) % industries.length);
- }, 5000); // Rotate every 5 seconds
-
- return () => clearInterval(interval);
- }, []);
-
- const handleIndustryClick = (index: number) => {
- setActiveIndex(index);
- };
-
- // Create an extended array for continuous scrolling effect
- const getVisibleItems = () => {
- const items = [];
- // Show current and next 3 items (or more depending on viewport)
- for (let i = 0; i < industries.length + 2; i++) {
- const index = (activeIndex + i) % industries.length;
- items.push({ ...industries[index], displayIndex: i });
- }
- return items;
- };
-
- const visibleItems = getVisibleItems();
-
- return (
- <>
-
-
Solutions for
-
- {visibleItems.map((item, idx) => {
- const isActive = item.displayIndex === 0;
- const offset = item.displayIndex;
-
- // Calculate translateX based on size differences
- let translateX = 0;
- if (offset === 0) {
- translateX = 0;
- } else if (offset === 1) {
- // Position after the active text + arrow + some gap
- translateX = 550; // Adjust based on active text width
- } else if (offset === 2) {
- translateX = 900;
- } else if (offset === 3) {
- translateX = 1200;
- }
-
- const opacity = offset === 0 ? 1 : offset <= 3 ? 1 : 0;
-
- return (
-
- );
- })}
-
-
-
- {industries[activeIndex].description}
-
-
- {visibleItems.map((item, idx) => {
- const isActive = item.displayIndex === 0;
- const offset = item.displayIndex;
-
- // Position items: active at 0, others spaced to the right
- const translateX = offset * 650;
- const scale = isActive ? 1 : 0.6;
- // Show active (1.0), next 2 items (0.3), third item fading (0.15)
- const opacity =
- offset === 0
- ? 1
- : offset === 1
- ? 0.3
- : offset === 2
- ? 0.3
- : offset === 3
- ? 0.15
- : 0;
- const zIndex = isActive
- ? 10
- : offset === 1
- ? 5
- : offset === 2
- ? 4
- : 1;
-
- return (
-
-
-

-
- {isActive && (
-
- )}
-
- );
- })}
-
- >
- );
-}
diff --git a/src/app/home/solutions-carousel/TextContentSection.tsx b/src/app/home/solutions-carousel/TextContentSection.tsx
new file mode 100644
index 0000000..15515cf
--- /dev/null
+++ b/src/app/home/solutions-carousel/TextContentSection.tsx
@@ -0,0 +1,43 @@
+"use client";
+
+import { ChevronRightIcon } from "lucide-react";
+import { Button } from "@/components/ui/button";
+
+type ActiveIndustry = {
+ name: string;
+ description: string;
+};
+
+export default function TextContentSection({
+ activeIndustry,
+}: {
+ activeIndustry: ActiveIndustry;
+}) {
+ return (
+ // dont want to hardcode pixels here. just want this text width to remain the same
+
+
+
+ Solutions for
+
+
+
+
+
+
+ {activeIndustry.description}
+
+
+ );
+}
diff --git a/src/app/home/solutions-carousel/data.ts b/src/app/home/solutions-carousel/data.ts
new file mode 100644
index 0000000..15b3834
--- /dev/null
+++ b/src/app/home/solutions-carousel/data.ts
@@ -0,0 +1,82 @@
+export const industries = [
+ {
+ name: "Banking and Finance",
+ description:
+ "Transform your company's financial solutions. Improve AML and KYC compliance and reduce operating costs while increasing customer satisfaction.",
+ image: "https://picsum.photos/600/350?v=4",
+ },
+ {
+ name: "Education and Universities",
+ description:
+ "Streamline tuition payments, campus services, and student billing with automated workflows designed for educational institutions.",
+ image: "https://picsum.photos/600/350?v=3",
+ },
+ {
+ name: "Insurance",
+ description:
+ "Simplify premium collections, claims processing, and policy management with intelligent automation built for insurance providers.",
+ image: "https://picsum.photos/600/350?v=2",
+ },
+ {
+ name: "Utilities",
+ description:
+ "Modernize utility billing, meter-to-cash workflows, and customer payments with real-time tracking and automated reminders.",
+ image: "https://picsum.photos/600/350?v=1",
+ },
+ {
+ name: "Banking and Finance",
+ description:
+ "Transform your company's financial solutions. Improve AML and KYC compliance and reduce operating costs while increasing customer satisfaction.",
+ image: "https://picsum.photos/600/350?v=4",
+ },
+ {
+ name: "Education and Universities",
+ description:
+ "Streamline tuition payments, campus services, and student billing with automated workflows designed for educational institutions.",
+ image: "https://picsum.photos/600/350?v=3",
+ },
+ {
+ name: "Insurance",
+ description:
+ "Simplify premium collections, claims processing, and policy management with intelligent automation built for insurance providers.",
+ image: "https://picsum.photos/600/350?v=2",
+ },
+ {
+ name: "Utilities",
+ description:
+ "Modernize utility billing, meter-to-cash workflows, and customer payments with real-time tracking and automated reminders.",
+ image: "https://picsum.photos/600/350?v=1",
+ },
+];
+
+// TODO: delete above and uncomment this
+// const industries = [
+// {
+// name: "Banking and Finance",
+// description:
+// "Transform your company's financial solutions. Improve AML and KYC compliance and reduce operating costs while increasing customer satisfaction.",
+// image:
+// "https://www.figma.com/api/mcp/asset/d48da3a6-7db6-4d57-8d1c-8bd157cf220c",
+// },
+// {
+// name: "Education and Universities",
+// description:
+// "Streamline tuition payments, campus services, and student billing with automated workflows designed for educational institutions.",
+// image:
+// "https://www.figma.com/api/mcp/asset/f6cc94b0-8742-4fc3-8476-965bdae60183",
+// },
+// {
+// name: "Insurance",
+// description:
+// "Simplify premium collections, claims processing, and policy management with intelligent automation built for insurance providers.",
+// image:
+// "https://www.figma.com/api/mcp/asset/3204408a-bf6c-48d3-a62d-64e82699389f",
+// },
+// {
+// name: "Utilities",
+// description:
+// "Modernize utility billing, meter-to-cash workflows, and customer payments with real-time tracking and automated reminders.",
+// image:
+// "https://www.figma.com/api/mcp/asset/ae6da2ac-178b-49bb-881f-a6098a7710d9",
+// },
+// ];
diff --git a/src/app/home/solutions-carousel/index.tsx b/src/app/home/solutions-carousel/index.tsx
new file mode 100644
index 0000000..1a390c4
--- /dev/null
+++ b/src/app/home/solutions-carousel/index.tsx
@@ -0,0 +1,167 @@
+"use client";
+
+import type {
+ EmblaCarouselType,
+ EmblaEventListType,
+ EmblaEventModelType,
+ EmblaOptionsType,
+} from "embla-carousel";
+import Autoplay from "embla-carousel-autoplay";
+import ClassNames from "embla-carousel-class-names";
+import useEmblaCarousel from "embla-carousel-react";
+import Image from "next/image";
+import { useCallback, useEffect, useRef, useState } from "react";
+import { industries } from "./data";
+import TextContentSection from "./TextContentSection";
+import { useUnidirectionalEmbla } from "./useUnidirectionalEmbla";
+
+const TWEEN_FACTOR_BASE = 0.4;
+
+const clamp = (number: number, min: number, max: number): number =>
+ Math.min(Math.max(number, min), max);
+
+export function SolutionsCarousel() {
+ const [selectedIdx, setSelectedIdx] = useState(0);
+
+ const options: Partial = {
+ align: "start",
+ loop: true, // for some reason this is buggy, but we need this
+ containScroll: "trimSnaps",
+ skipSnaps: true,
+ };
+
+ const plugins = [
+ Autoplay(),
+ ClassNames({ snapped: "is-snapped", active: true }),
+ ];
+ const [emblaRef, emblaApi] = useEmblaCarousel(options, plugins);
+
+ useUnidirectionalEmbla(emblaApi);
+
+ // // sync carousel changes with selected idx
+ useEffect(() => {
+ if (!emblaApi) return;
+
+ const onSelect = () => {
+ setSelectedIdx(emblaApi.selectedSnap());
+ };
+
+ emblaApi.on("select", onSelect);
+ onSelect(); // set initial idx
+
+ return () => {
+ emblaApi.off("select", onSelect);
+ };
+ }, [emblaApi]);
+
+ const tweenFactor = useRef(0);
+ const tweenNodes = useRef([]);
+
+ const setTweenNodes = useCallback((emblaApi: EmblaCarouselType): void => {
+ tweenNodes.current = emblaApi.slideNodes().map((slideNode) => {
+ return slideNode.querySelector(".embla__slide__img") as HTMLElement;
+ });
+ console.log(tweenNodes.current);
+ }, []);
+
+ const setTweenFactor = useCallback((emblaApi: EmblaCarouselType) => {
+ tweenFactor.current = TWEEN_FACTOR_BASE * emblaApi.snapList().length;
+ }, []);
+
+ const tweenScale = useCallback(
+ (
+ emblaApi: EmblaCarouselType,
+ event?: EmblaEventModelType,
+ ) => {
+ const engine = emblaApi.internalEngine();
+ const scrollProgress = emblaApi.scrollProgress();
+ const slidesInView = emblaApi.slidesInView();
+ const isScrollEvent = event?.type === "scroll";
+
+ emblaApi.snapList().forEach((scrollSnap, snapIndex) => {
+ let diffToTarget = scrollSnap - scrollProgress;
+ const slidesInSnap = engine.scrollSnapList.slidesBySnap[snapIndex];
+
+ slidesInSnap.forEach((slideIndex) => {
+ if (isScrollEvent && !slidesInView.includes(slideIndex)) return;
+
+ if (engine.options.loop) {
+ engine.slideLooper.loopPoints.forEach((loopItem) => {
+ const target = loopItem.target();
+
+ if (slideIndex === loopItem.index && target !== 0) {
+ const sign = Math.sign(target);
+
+ if (sign === -1) {
+ diffToTarget = scrollSnap - (1 + scrollProgress);
+ }
+ if (sign === 1) {
+ diffToTarget = scrollSnap + (1 - scrollProgress);
+ }
+ }
+ });
+ }
+
+ const tweenValue = 1 - Math.abs(diffToTarget * tweenFactor.current);
+ const scale = clamp(tweenValue, 0, 1).toString();
+ const tweenNode = tweenNodes.current[slideIndex];
+ if (!tweenNode) {
+ console.log("missing tween node for slide index", slideIndex);
+ return;
+ }
+ tweenNode.style.transform = `scale(${scale})`;
+ });
+ });
+ },
+ [],
+ );
+
+ // biome-ignore lint/correctness/useExhaustiveDependencies:
+ useEffect(() => {
+ if (!emblaApi) return;
+
+ setTweenNodes(emblaApi);
+ setTweenFactor(emblaApi);
+ tweenScale(emblaApi);
+
+ emblaApi
+ .on("reinit", setTweenNodes)
+ .on("reinit", setTweenFactor)
+ .on("reinit", tweenScale)
+ .on("scroll", tweenScale)
+ .on("slidefocus", tweenScale);
+ }, [emblaApi]);
+
+ const currentIndustry = industries[selectedIdx % industries.length];
+
+ return (
+
+
+
+ {/* Carousel */}
+
+
+
+ {industries.map((industry, idx) => (
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/app/home/solutions-carousel/useUnidirectionalEmbla.ts b/src/app/home/solutions-carousel/useUnidirectionalEmbla.ts
new file mode 100644
index 0000000..636a100
--- /dev/null
+++ b/src/app/home/solutions-carousel/useUnidirectionalEmbla.ts
@@ -0,0 +1,55 @@
+import type { EmblaCarouselType } from "embla-carousel";
+import { useEffect, useRef } from "react";
+
+export function useUnidirectionalEmbla(
+ emblaApi: EmblaCarouselType | undefined,
+) {
+ const startXRef = useRef(null);
+ const startIndexRef = useRef(0);
+ const isBlockingRef = useRef(false);
+
+ useEffect(() => {
+ if (!emblaApi) return;
+
+ const viewport = emblaApi.rootNode();
+
+ const onPointerDown = (e: PointerEvent) => {
+ startXRef.current = e.clientX;
+ startIndexRef.current = emblaApi.selectedSnap();
+ isBlockingRef.current = false;
+ };
+
+ const onPointerMove = (e: PointerEvent) => {
+ if (startXRef.current === null) return;
+
+ const deltaX = e.clientX - startXRef.current;
+
+ // block backward scrolling
+ if (deltaX > 0) {
+ isBlockingRef.current = true;
+
+ e.preventDefault();
+ e.stopImmediatePropagation();
+
+ // scroll to the start index
+ // emblaApi.scrollTo(startIndexRef.current, false);
+ }
+ };
+
+ const onPointerUp = () => {
+ startXRef.current = null;
+ isBlockingRef.current = false;
+ };
+
+ viewport.addEventListener("pointerdown", onPointerDown, { passive: false });
+ viewport.addEventListener("pointermove", onPointerMove, { passive: false });
+ viewport.addEventListener("pointerup", onPointerUp);
+ viewport.addEventListener("pointercancel", onPointerUp);
+ return () => {
+ viewport.removeEventListener("pointerdown", onPointerDown);
+ viewport.removeEventListener("pointermove", onPointerMove);
+ viewport.removeEventListener("pointerup", onPointerUp);
+ viewport.removeEventListener("pointercancel", onPointerUp);
+ };
+ }, [emblaApi]);
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 186a7f3..643d198 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,3 +1,4 @@
import Home from "./home/page";
+import "../css/embla.css";
export default Home;
diff --git a/src/css/embla.css b/src/css/embla.css
new file mode 100644
index 0000000..7f1a180
--- /dev/null
+++ b/src/css/embla.css
@@ -0,0 +1,72 @@
+.embla {
+ min-height: 25rem;
+ --slide-height: 12rem;
+ --slide-spacing: 1rem;
+ --slide-size: 33%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ /* background-color: red; */
+}
+
+@media (min-width: 768px) {
+ .embla {
+ max-width: 100rem;
+ min-height: 30rem;
+ --slide-height: 16rem;
+ --slide-spacing: 2rem;
+ --slide-size: 50%;
+ }
+}
+
+@media (min-width: 1024px) {
+ .embla {
+ max-width: 100rem;
+ min-height: 40rem;
+ --slide-height: 19rem;
+ --slide-spacing: 3rem;
+ --slide-size: 30%;
+ }
+}
+
+.embla__viewport {
+ overflow: hidden;
+ width: 100%;
+}
+
+.embla__container {
+ display: flex;
+ margin-left: calc(var(--slide-spacing) * -1);
+ transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
+ touch-action: pan-y pinch-zoom;
+}
+
+.embla__slide {
+ flex: 0 0 var(--slide-size);
+ padding-left: var(--slide-spacing);
+}
+
+.embla__slide.is-snapped {
+ /* transform: translateY(-48px) !important; */
+ /* --slide-height: 25rem; */
+ /* --slide-size: 40%; */
+}
+
+.embla__slide:not(.is-snapped) {
+ opacity: 0.16;
+}
+
+.embla__slide__img {
+ height: var(--slide-height);
+ width: 100%;
+ object-fit: cover;
+ border-radius: 1.35rem;
+ user-select: none;
+}
+
+
+
+.animate-fade-slide {
+ animation: fadeSlide 300ms ease;
+}