diff --git a/components/QuotationCarousel.module.css b/components/QuotationCarousel.module.css
new file mode 100644
index 0000000..40241da
--- /dev/null
+++ b/components/QuotationCarousel.module.css
@@ -0,0 +1,124 @@
+.carousel {
+ position: relative;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: calc(8rem / 16);
+}
+
+.circle {
+ position: absolute;
+ top: 30%;
+ right: 52%;
+ z-index: -1;
+
+ background-color: var(--tertiary-background);
+ clip-path: circle();
+}
+
+.right.circle {
+ top: unset;
+ right: unset;
+ bottom: 30%;
+ left: 52%;
+}
+
+.carouselButton {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
+ padding: calc(16rem / 16);
+ height: min-content;
+
+ background: none;
+ border: none;
+
+ cursor: pointer;
+}
+
+.arrow {
+ position: relative;
+ width: calc(20rem / 16);
+ height: calc(40rem / 16);
+
+ transition: 0.2s;
+}
+
+.previous.arrow {
+ transform: rotate(180deg);
+}
+
+.carouselButton:hover > .arrow {
+ translate: calc(4rem / 16);
+}
+
+.carouselButton:hover > .previous.arrow {
+ translate: calc(-4rem / 16);
+}
+
+.card {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ align-items: stretch;
+ gap: calc(16rem / 16);
+
+ min-height: inherit;
+ height: 100%;
+ width: 100%;
+ padding: calc(30rem / 16);
+
+ background-color: var(--translucent-accent);
+ border: calc(2rem / 16) solid var(--primary-text);
+ border-radius: calc(12rem / 16);
+ box-shadow: 0 calc(1rem / 16) calc(10rem / 16) var(--primary-accent);
+}
+
+.card ul {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
+ position: relative;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ flex-grow: 1;
+}
+
+.card li {
+ position: absolute;
+ left: 0;
+ right: 0;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+
+ visibility: visible;
+ opacity: 1;
+ transition: 0.1s;
+}
+
+.card li.hidden {
+ visibility: hidden;
+ opacity: 0;
+}
+
+.card p {
+ margin: 0 calc(16rem / 16);
+ font-weight: bold;
+ text-align: center;
+}
+
+.quotationMark {
+ width: calc(20rem / 16);
+ height: calc(20rem / 16);
+}
+
+.right.quotationMark {
+ transform: rotate(180deg);
+ align-self: end;
+}
diff --git a/components/QuotationCarousel.tsx b/components/QuotationCarousel.tsx
new file mode 100644
index 0000000..fed81a7
--- /dev/null
+++ b/components/QuotationCarousel.tsx
@@ -0,0 +1,130 @@
+import React, { useState } from "react";
+import { Color } from "utils/Color";
+
+import styles from "./QuotationCarousel.module.css";
+
+interface QuotationCarouselProps {
+ data: string[];
+ /** Width of the entire carousel including the buttons, in px. */
+ width?: number;
+ /** Minimum height of the carousel, in px. */
+ height?: number;
+ /** Diameter of the background circles, in px. Set to 0 for no circles. */
+ circleDiameter?: number;
+ className?: string;
+}
+
+interface CarouselButtonProps {
+ onClick: () => void;
+ isPrevious?: boolean;
+}
+
+export function QuotationCarousel(props: QuotationCarouselProps) {
+ const {
+ data,
+ width = 600,
+ height = 100,
+ circleDiameter = 120,
+ className,
+ } = props;
+
+ const [activeIdx, setActiveIdx] = useState(0);
+
+ function showNextCard() {
+ setActiveIdx((activeIdx + 1) % data.length);
+ }
+
+ function showPreviousCard() {
+ setActiveIdx((activeIdx - 1 + data.length) % data.length);
+ }
+
+ return (
+ {quote}
+ {data.map((quote, idx) => (
+
+
{" "}
+