/* ============================================================
   CASE STUDIES — single + archive.
   Cinematic motion, theme tokens, light + dark automatic.
   ============================================================ */

/* ---------- Generic frame ---------- */
.cs-section {
  position: relative;
  padding: clamp(56px, 9vw, 130px) 0;
  background: var(--c-bg);
  color: var(--c-ink);
  overflow: hidden;
  isolation: isolate;
}
.cs-section__inner {
  max-width: var(--container-width);
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 3vw, 2.5rem);
  position: relative;
}
.cs-section__title {
  font-size: clamp(2rem, 4.6vw, 3.6rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.04;
  margin: 0 0 0.8em;
  color: var(--c-ink);
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.32em;
}
.cs-section__title--center {
  justify-content: center;
  text-align: center;
}

/* Masked word-reveal (used by every headline) */
.cs-word {
  display: inline-block;
  overflow: hidden;
  vertical-align: baseline;
  line-height: 1.04;
  /* Slight perspective so the entrance feels cinematic, not flat. */
  perspective: 600px;
}
.cs-word__inner {
  display: inline-block;
  transform: translateY(110%) rotate(4deg);
  transform-origin: 0% 100%;
  will-change: transform;
}
[data-cs-reveal-lines].is-in .cs-word__inner {
  transform: translateY(0) rotate(0);
  transition: transform 1.05s cubic-bezier(0.2, 0.85, 0.2, 1);
}
[data-cs-reveal-lines].is-in .cs-word:nth-child(2) .cs-word__inner {
  transition-delay: 0.06s;
}
[data-cs-reveal-lines].is-in .cs-word:nth-child(3) .cs-word__inner {
  transition-delay: 0.12s;
}
[data-cs-reveal-lines].is-in .cs-word:nth-child(4) .cs-word__inner {
  transition-delay: 0.18s;
}
[data-cs-reveal-lines].is-in .cs-word:nth-child(5) .cs-word__inner {
  transition-delay: 0.24s;
}
[data-cs-reveal-lines].is-in .cs-word:nth-child(6) .cs-word__inner {
  transition-delay: 0.3s;
}
[data-cs-reveal-lines].is-in .cs-word:nth-child(7) .cs-word__inner {
  transition-delay: 0.36s;
}
[data-cs-reveal-lines].is-in .cs-word:nth-child(8) .cs-word__inner {
  transition-delay: 0.42s;
}
[data-cs-reveal-lines].is-in .cs-word:nth-child(9) .cs-word__inner {
  transition-delay: 0.48s;
}
[data-cs-reveal-lines].is-in .cs-word:nth-child(n + 10) .cs-word__inner {
  transition-delay: 0.54s;
}

/* Faded reveal for prose / chips */
[data-cs-reveal-fade] {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity 0.9s ease,
    transform 0.9s cubic-bezier(0.2, 0.85, 0.2, 1);
}
[data-cs-reveal-fade].is-in {
  opacity: 1;
  transform: translateY(0);
}

/* Prose */
.cs-prose {
  color: var(--c-ink-soft);
  font-size: clamp(1rem, 1.3vw, 1.13rem);
  line-height: 1.78;
}
.cs-prose p {
  margin: 0 0 1em;
}
.cs-prose strong {
  color: var(--c-ink);
}
.cs-prose ul {
  margin: 0 0 1em;
  padding: 0;
  list-style: none;
}
.cs-prose ul li {
  position: relative;
  padding-left: 26px;
  margin-bottom: 0.55em;
}
.cs-prose ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7em;
  width: 12px;
  height: 2px;
  background: var(--c-primary);
}
.cs-prose ol {
  padding-left: 1.4em;
  margin: 0 0 1em;
}

/* ============================================================
   1. HERO
   ============================================================ */
.cs-hero {
  position: relative;
  min-height: clamp(540px, 85vh, 920px);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  isolation: isolate;
  background: var(--c-bg-feature);
  color: var(--c-on-feature);
  padding: clamp(72px, 11vw, 150px) 0 clamp(48px, 8vw, 100px);
}
.cs-hero__bg {
  position: absolute;
  inset: -8% -2%;
  background: var(--cs-hero-img) center / cover no-repeat;
  filter: brightness(0.78) saturate(1.1);
  z-index: -3;
  transform: scale(1.06);
  will-change: transform;
}
.cs-hero__veil {
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(
      60% 80% at 25% 20%,
      rgba(138, 0, 190, 0.42) 0%,
      transparent 65%
    ),
    radial-gradient(
      50% 60% at 80% 70%,
      rgba(235, 148, 207, 0.22) 0%,
      transparent 60%
    ),
    linear-gradient(180deg, rgba(10, 6, 20, 0.3) 0%, rgba(10, 6, 20, 0.85) 100%);
}
.cs-hero__grain {
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0.25;
  background-image: radial-gradient(
    rgba(255, 255, 255, 0.6) 0.5px,
    transparent 0.5px
  );
  background-size: 3px 3px;
  mix-blend-mode: overlay;
  pointer-events: none;
}
.cs-hero__inner {
  max-width: 1240px;
  margin: 0 auto;
  padding: 0 clamp(20px, 4vw, 56px);
  width: 100%;
}
.cs-hero__eyebrow {
  display: inline-block;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--c-accent);
  background: rgba(255, 255, 255, 0.06);
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid rgba(235, 148, 207, 0.45);
  margin-bottom: 22px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.cs-hero__title {
  font-size: clamp(2.4rem, 7vw, 6rem);
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -0.025em;
  margin: 0 0 0.4em;
  color: #fff;
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.32em;
}
.cs-hero__tagline {
  font-size: clamp(1.1rem, 1.7vw, 1.45rem);
  max-width: 56ch;
  color: rgba(255, 255, 255, 0.86);
  margin: 0 0 28px;
  line-height: 1.55;
}
.cs-hero__client {
  display: inline-flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 22px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 14px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.cs-hero__client-name {
  color: #fff;
  font-size: 1.08rem;
  font-weight: 700;
}
.cs-hero__client-role {
  color: var(--c-accent);
  font-size: 0.88rem;
}

.cs-hero__scroll {
  position: absolute;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  color: rgba(255, 255, 255, 0.6);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.3em;
}
.cs-hero__scroll-line {
  width: 1px;
  height: 48px;
  background: linear-gradient(180deg, transparent 0%, var(--c-accent) 100%);
  position: relative;
  overflow: hidden;
}
.cs-hero__scroll-line::after {
  content: "";
  position: absolute;
  top: -100%;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(180deg, transparent 0%, #fff 100%);
  animation: cs-scroll-pulse 2.4s ease-in-out infinite;
}
@keyframes cs-scroll-pulse {
  0% {
    transform: translateY(0);
  }
  100% {
    transform: translateY(200%);
  }
}

/* ============================================================
   2. BACKGROUND  (text + optional image, side-toggleable)
   ============================================================ */
.cs-bg__inner {
  display: grid;
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
  grid-template-columns: 1fr;
}
.cs-bg--has-image .cs-bg__inner {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
}
.cs-bg--side-left.cs-bg--has-image .cs-bg__media {
  order: -1;
}

.cs-bg__media {
  margin: 0;
  position: relative;
  border-radius: 22px;
  overflow: hidden;
  background: var(--c-bg-soft);
  transform-style: preserve-3d;
  transition: transform 0.6s cubic-bezier(0.2, 0.85, 0.2, 1);
  box-shadow: 0 30px 60px -34px rgba(var(--rgb-primary) / 0.45);
}
.cs-bg__media::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(138, 0, 190, 0.3), transparent 50%);
  z-index: 1;
  mix-blend-mode: overlay;
  pointer-events: none;
}
.cs-bg__media img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  /* aspect-ratio: 4 / 5; */
  transition: transform 0.9s cubic-bezier(0.2, 0.85, 0.2, 1);
}
.cs-bg__media:hover img {
  transform: scale(1.04);
}

/* ============================================================
   3. CHALLENGE — asymmetric: aside rail + body, with giant numeral
   ============================================================ */
.cs-challenge {
  background:
    radial-gradient(
      80% 60% at 90% 0%,
      rgba(var(--rgb-primary) / 0.08),
      transparent 70%
    ),
    radial-gradient(
      50% 60% at 0% 100%,
      rgba(var(--rgb-accent) / 0.05),
      transparent 60%
    ),
    var(--c-bg);
  overflow: hidden;
}
/* Decorative oversized numeral pinned to the right side */
.cs-challenge__numeral {
  position: absolute;
  right: -2vw;
  top: 50%;
  transform: translateY(-50%);
  font-size: clamp(14rem, 32vw, 30rem);
  font-weight: 900;
  line-height: 0.85;
  letter-spacing: -0.06em;
  color: transparent;
  -webkit-text-stroke: 2px rgba(var(--rgb-primary) / 0.12);
  pointer-events: none;
  z-index: 0;
  user-select: none;
  font-family: ui-sans-serif, system-ui, sans-serif;
}
.cs-challenge__inner {
  display: grid;
  grid-template-columns: minmax(260px, 0.8fr) minmax(0, 1.4fr);
  gap: clamp(28px, 5vw, 80px);
  align-items: start;
  position: relative;
  z-index: 1;
}
.cs-challenge__aside {
  position: sticky;
  top: 100px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.cs-challenge__chip {
  display: inline-flex;
  align-self: flex-start;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--c-primary);
  background: rgba(var(--rgb-primary) / 0.08);
  border: 1px solid rgba(var(--rgb-primary) / 0.22);
  padding: 7px 14px;
  border-radius: 999px;
  font-weight: 600;
}
.cs-challenge__title {
  margin: 0;
  font-size: clamp(2rem, 4.4vw, 3.4rem);
  line-height: 1;
}
.cs-challenge__rule {
  display: block;
  width: 80px;
  height: 4px;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--c-primary), var(--c-accent));
  margin-top: 8px;
  position: relative;
  overflow: hidden;
}
.cs-challenge__rule::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.6),
    transparent
  );
  transform: translateX(-100%);
  animation: cs-rule-shimmer 3s ease-in-out infinite;
}
@keyframes cs-rule-shimmer {
  0% {
    transform: translateX(-100%);
  }
  60% {
    transform: translateX(200%);
  }
  100% {
    transform: translateX(200%);
  }
}
.cs-challenge__body {
  position: relative;
  padding: clamp(24px, 3vw, 40px) clamp(24px, 3vw, 44px);
  background: var(--c-bg-elev);
  border: 1px solid var(--c-border);
  border-radius: 22px;
  box-shadow: 0 30px 60px -34px rgba(var(--rgb-primary) / 0.35);
  max-width: none;
  margin: 0;
}
.cs-challenge__body::before {
  content: "";
  position: absolute;
  left: 0;
  top: 24px;
  bottom: 24px;
  width: 4px;
  border-radius: 4px;
  background: linear-gradient(180deg, var(--c-primary), var(--c-accent));
}
.cs-challenge__body p {
  font-size: clamp(1.02rem, 1.35vw, 1.16rem);
  line-height: 1.78;
  color: var(--c-ink-soft);
  margin: 0 0 1em;
}
.cs-challenge__body p:last-child {
  margin-bottom: 0;
}
.cs-challenge__body p strong {
  color: var(--c-ink);
}
.cs-challenge__body ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.cs-challenge__body ul li {
  position: relative;
  padding: 14px 16px 14px 56px;
  background: linear-gradient(
    135deg,
    rgba(var(--rgb-primary) / 0.05),
    rgba(var(--rgb-accent) / 0.03)
  );
  border-radius: 12px;
  border-left: 2px solid rgba(var(--rgb-primary) / 0.5);
  font-size: clamp(0.98rem, 1.3vw, 1.08rem);
  color: var(--c-ink-soft);
  line-height: 1.55;
  counter-increment: cs-li;
  transition:
    transform 0.4s cubic-bezier(0.2, 0.85, 0.2, 1),
    background 0.3s ease;
}
.cs-challenge__body ul {
  counter-reset: cs-li;
}
.cs-challenge__body ul li::before {
  content: counter(cs-li, decimal-leading-zero);
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  background: linear-gradient(135deg, var(--c-primary), var(--c-secondary));
  color: #fff;
  font-size: 0.72rem;
  font-weight: 800;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  letter-spacing: 0;
}
.cs-challenge__body ul li:hover {
  transform: translateX(6px);
  background: linear-gradient(
    135deg,
    rgba(var(--rgb-primary) / 0.1),
    rgba(var(--rgb-accent) / 0.06)
  );
}
@media (max-width: 900px) {
  .cs-challenge__inner {
    grid-template-columns: 1fr;
  }
  .cs-challenge__aside {
    position: static;
  }
  .cs-challenge__numeral {
    font-size: 18rem;
    right: -10vw;
    opacity: 0.6;
  }
}

/* ============================================================
   4. CONCEPT
   ============================================================ */
.cs-concept {
  background: linear-gradient(180deg, var(--c-bg) 0%, var(--c-bg-soft) 100%);
}
.cs-concept__inner {
  text-align: center;
}
.cs-concept__tagline {
  max-width: 48ch;
  margin: 0 auto 28px;
  font-size: clamp(1.05rem, 1.5vw, 1.25rem);
  color: var(--c-muted);
  line-height: 1.6;
}
/* The body lives inside a centred section but its own copy reads
 * left-to-right (or right-to-left under RTL). Use logical `start` so
 * Arabic flows correctly without a separate override.
 *
 * The section now adopts the same surface treatment as the Challenge
 * block: prose paragraphs sit on a soft elevated card; lists become
 * counter-numbered chips (matching .cs-challenge__body ul). This keeps
 * Concept visually consistent with the rest of the case-study system
 * instead of reading as raw text. */
.cs-concept__body {
  max-width: min(960px, 100%);
  margin: 0 auto 40px;
  text-align: start;
  position: relative;
  padding: clamp(22px, 3vw, 38px) clamp(22px, 3vw, 40px);
  background: var(--c-bg-elev);
  border: 1px solid var(--c-border);
  border-radius: 22px;
  box-shadow: 0 30px 60px -34px rgba(var(--rgb-primary) / 0.28);
}
.cs-concept__body::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 24px;
  bottom: 24px;
  width: 4px;
  border-radius: 4px;
  background: linear-gradient(180deg, var(--c-primary), var(--c-accent));
}
.cs-concept__body p {
  margin: 0 0 1em;
  font-size: clamp(1rem, 1.32vw, 1.13rem);
  line-height: 1.78;
  color: var(--c-ink-soft);
}
.cs-concept__body p:last-child {
  margin-bottom: 0;
}
.cs-concept__body strong {
  color: var(--c-ink);
}
/* Numbered chips for both UL and OL — editors writing the Concept body
 * in WordPress's editor often hit "list" once without thinking about
 * which kind, so unifying both produces consistent visuals. */
.cs-concept__body ul,
.cs-concept__body ol {
  list-style: none;
  padding: 0;
  margin: 0 0 1em;
  display: flex;
  flex-direction: column;
  gap: 10px;
  counter-reset: cs-concept-li;
}
.cs-concept__body ul:last-child,
.cs-concept__body ol:last-child {
  margin-bottom: 0;
}
.cs-concept__body ul li,
.cs-concept__body ol li {
  position: relative;
  padding: 14px 16px 14px 56px;
  background: linear-gradient(
    135deg,
    rgba(var(--rgb-primary) / 0.05),
    rgba(var(--rgb-accent) / 0.03)
  );
  border-radius: 12px;
  border-inline-start: 2px solid rgba(var(--rgb-primary) / 0.5);
  font-size: clamp(0.98rem, 1.3vw, 1.08rem);
  line-height: 1.55;
  color: var(--c-ink-soft);
  counter-increment: cs-concept-li;
  transition:
    transform 0.4s cubic-bezier(0.2, 0.85, 0.2, 1),
    background 0.3s ease;
}
.cs-concept__body ul li::before,
.cs-concept__body ol li::before {
  content: counter(cs-concept-li, decimal-leading-zero);
  position: absolute;
  inset-inline-start: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  background: linear-gradient(135deg, var(--c-primary), var(--c-secondary));
  color: #fff;
  font-size: 0.72rem;
  font-weight: 800;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  letter-spacing: 0;
}
.cs-concept__body ul li:hover,
.cs-concept__body ol li:hover {
  transform: translateX(6px);
  background: linear-gradient(
    135deg,
    rgba(var(--rgb-primary) / 0.08),
    rgba(var(--rgb-accent) / 0.05)
  );
}
html[dir="rtl"] .cs-concept__body ul li:hover,
html[dir="rtl"] .cs-concept__body ol li:hover {
  transform: translateX(-6px);
}
/* Padding flips for RTL because `padding` is physical here (number chip
 * sits at the inline-start, but the long-hand needs explicit logical
 * forms — the shorthand above uses physical left padding). */
html[dir="rtl"] .cs-concept__body ul li,
html[dir="rtl"] .cs-concept__body ol li {
  padding: 14px 56px 14px 16px;
}
.cs-concept__image {
  margin: 0 auto;
  max-width: 1000px;
  border-radius: 20px;
  overflow: hidden;
  background: var(--c-bg-elev);
  box-shadow: 0 40px 80px -40px rgba(var(--rgb-primary) / 0.4);
  transform-style: preserve-3d;
}
.cs-concept__image img {
  width: 100%;
  height: auto;
  display: block;
}

/* ============================================================
   5. STAGES — section header + offset stage cards with ghost numerals
   ============================================================ */
.cs-stages {
  background:
    radial-gradient(
      60% 50% at 50% 0%,
      rgba(var(--rgb-primary) / 0.08),
      transparent 70%
    ),
    var(--c-bg);
}

/* ---- Section header ---- */
.cs-stages__head {
  text-align: center;
  max-width: 880px;
  margin: 0 auto clamp(48px, 7vw, 88px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
.cs-stages__chip {
  display: inline-flex;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--c-primary);
  background: rgba(var(--rgb-primary) / 0.08);
  border: 1px solid rgba(var(--rgb-primary) / 0.22);
  padding: 7px 14px;
  border-radius: 999px;
  font-weight: 600;
}
.cs-stages__title {
  justify-content: center;
  margin: 0;
}
.cs-stages__desc {
  max-width: 64ch;
  color: var(--c-muted);
  font-size: clamp(1rem, 1.3vw, 1.15rem);
}

/* ---- Stage card ---- */
.cs-stages__list {
  display: flex;
  flex-direction: column;
  gap: clamp(56px, 8vw, 110px);
}
.cs-stage {
  position: relative;
  padding: clamp(28px, 4vw, 56px) clamp(20px, 3vw, 44px);
  border-radius: 24px;
  background:
    linear-gradient(
      180deg,
      rgba(var(--rgb-bg-elev) / 0.7),
      rgba(var(--rgb-bg-elev) / 0.4)
    ),
    var(--c-bg-elev);
  border: 1px solid var(--c-border);
  overflow: hidden;
  isolation: isolate;
  box-shadow: 0 30px 60px -38px rgba(var(--rgb-primary) / 0.35);
  transition:
    transform 0.6s cubic-bezier(0.2, 0.85, 0.2, 1),
    box-shadow 0.6s ease,
    border-color 0.4s ease;
}
.cs-stage:hover {
  transform: translateY(-4px);
  box-shadow: 0 50px 90px -40px rgba(var(--rgb-primary) / 0.45);
  border-color: rgba(var(--rgb-primary) / 0.4);
}
/* Animated diagonal stripe at the top edge */
.cs-stage::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 4px;
  background: linear-gradient(
    90deg,
    var(--c-primary) 0%,
    var(--c-accent) 50%,
    var(--c-cyan) 100%
  );
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1s cubic-bezier(0.2, 0.85, 0.2, 1) 0.2s;
}
.cs-stage.is-in::before {
  transform: scaleX(1);
}

/* Ghost numeral — huge, half off the card, behind everything */
.cs-stage__big-num {
  position: absolute;
  top: -20px;
  right: clamp(-30px, -2vw, -10px);
  font-size: clamp(10rem, 22vw, 22rem);
  font-weight: 900;
  letter-spacing: -0.06em;
  line-height: 0.85;
  color: transparent;
  -webkit-text-stroke: 2px rgba(var(--rgb-primary) / 0.1);
  pointer-events: none;
  user-select: none;
  z-index: 0;
  font-family: ui-sans-serif, system-ui, sans-serif;
  transform: translateX(20px);
  opacity: 0;
  transition:
    transform 1.2s cubic-bezier(0.2, 0.85, 0.2, 1),
    opacity 1.2s ease;
}
.cs-stage.is-in .cs-stage__big-num {
  transform: translateX(0);
  opacity: 1;
}

/* Header row: rail + chip number + title */
.cs-stage__head {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(18px, 2.5vw, 32px);
  align-items: center;
  padding-bottom: clamp(20px, 3vw, 36px);
  border-bottom: 1px dashed var(--c-border);
  margin-bottom: clamp(20px, 3vw, 36px);
}
.cs-stage__head-rail {
  position: absolute;
  left: -28px;
  top: 8px;
  bottom: 0;
  width: 3px;
  background: linear-gradient(180deg, var(--c-primary), var(--c-accent));
  border-radius: 3px;
  transform: scaleY(0);
  transform-origin: top;
  transition: transform 1.1s cubic-bezier(0.2, 0.85, 0.2, 1) 0.3s;
}
.cs-stage.is-in .cs-stage__head-rail {
  transform: scaleY(1);
}

.cs-stage__num {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: 92px;
  padding: 14px 16px;
  background: linear-gradient(135deg, var(--c-primary), var(--c-secondary));
  color: #fff;
  border-radius: 18px;
  box-shadow: 0 18px 40px -18px rgba(var(--rgb-primary) / 0.7);
  transform: rotate(-3deg) scale(0.7);
  opacity: 0;
  transition:
    transform 0.9s cubic-bezier(0.34, 1.56, 0.64, 1),
    opacity 0.6s ease;
  flex-shrink: 0;
}
.cs-stage.is-in .cs-stage__num {
  transform: rotate(-3deg) scale(1);
  opacity: 1;
}
.cs-stage__num-label {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  opacity: 0.85;
  font-weight: 600;
}
.cs-stage__num-value {
  font-size: clamp(1.6rem, 2.4vw, 2.2rem);
  font-weight: 900;
  letter-spacing: -0.02em;
  line-height: 1;
}

.cs-stage__title {
  font-size: clamp(1.6rem, 3vw, 2.4rem);
  font-weight: 800;
  letter-spacing: -0.015em;
  color: var(--c-ink);
  margin: 0;
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0 0.32em;
  line-height: 1.04;
}
.cs-stage__title .cs-word__inner {
  transform: translateY(110%);
  transition: transform 1s cubic-bezier(0.2, 0.85, 0.2, 1);
}
.cs-stage.is-in .cs-stage__title .cs-word__inner {
  transform: translateY(0);
}
.cs-stage.is-in .cs-stage__title .cs-word:nth-child(2) .cs-word__inner {
  transition-delay: 0.08s;
}
.cs-stage.is-in .cs-stage__title .cs-word:nth-child(3) .cs-word__inner {
  transition-delay: 0.16s;
}
.cs-stage.is-in .cs-stage__title .cs-word:nth-child(4) .cs-word__inner {
  transition-delay: 0.24s;
}

.cs-stage__intro {
  position: relative;
  z-index: 1;
  max-width: 70ch;
  font-size: clamp(1rem, 1.3vw, 1.15rem);
  color: var(--c-muted);
  margin: 0 0 clamp(24px, 3vw, 40px);
  opacity: 0;
  transform: translateY(20px);
  transition:
    opacity 0.9s ease 0.3s,
    transform 0.9s cubic-bezier(0.2, 0.85, 0.2, 1) 0.3s;
}
.cs-stage.is-in .cs-stage__intro {
  opacity: 1;
  transform: translateY(0);
}

/* Stage body — mobile-first: blocks stack vertically. */
.cs-stage__blocks {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: clamp(28px, 4vw, 48px);
}

@media (max-width: 760px) {
  .cs-stage__head {
    grid-template-columns: 1fr;
    gap: 14px;
  }
  .cs-stage__head-rail {
    left: -16px;
  }
  .cs-stage__num {
    align-self: flex-start;
  }
  .cs-stage__big-num {
    font-size: 11rem;
    right: -20px;
  }
}

/* ============================================================
   MEDIA BLOCKS
   ============================================================ */
.cs-block {
  opacity: 0;
  transform: translateY(40px);
  transition:
    opacity 0.9s ease,
    transform 0.9s cubic-bezier(0.2, 0.85, 0.2, 1);
}
.cs-stage.is-in .cs-block {
  opacity: 1;
  transform: translateY(0);
}
.cs-stage.is-in .cs-block:nth-of-type(2) {
  transition-delay: 0.1s;
}
.cs-stage.is-in .cs-block:nth-of-type(3) {
  transition-delay: 0.2s;
}
.cs-stage.is-in .cs-block:nth-of-type(4) {
  transition-delay: 0.3s;
}
.cs-stage.is-in .cs-block:nth-of-type(5) {
  transition-delay: 0.4s;
}

/* Text-only block */
.cs-block--text {
  max-width: 70ch;
}

/* Image-only block */
.cs-block--image .cs-block__media--image {
  margin: 0 auto;
  max-width: 1100px;
  border-radius: 18px;
  overflow: hidden;
  background: var(--c-bg-soft);
  position: relative;
  box-shadow: 0 30px 70px -36px rgba(var(--rgb-primary) / 0.45);
}
.cs-block__media--image img {
  width: 100%;
  height: auto;
  display: block;
  transition: transform 0.9s cubic-bezier(0.2, 0.85, 0.2, 1);
}
.cs-block__media--image:hover img {
  transform: scale(1.03);
}

/* Text + Image (side by side) */
.cs-block--text-image {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(24px, 4vw, 56px);
  align-items: center;
}
.cs-block--text-image.cs-block--side-left .cs-block__media {
  order: -1;
}
.cs-block--text-image .cs-block__media--image {
  border-radius: 18px;
  overflow: hidden;
  background: var(--c-bg-soft);
  position: relative;
  box-shadow: 0 28px 60px -32px rgba(var(--rgb-primary) / 0.4);
  transform: perspective(1200px) rotateY(-3deg) rotateX(2deg);
  transition: transform 0.9s cubic-bezier(0.2, 0.85, 0.2, 1);
}
.cs-block--text-image.cs-block--side-left .cs-block__media--image {
  transform: perspective(1200px) rotateY(3deg) rotateX(2deg);
}
.cs-block--text-image .cs-block__media--image:hover {
  transform: perspective(1200px) rotateY(0) rotateX(0) translateY(-4px);
}
.cs-block--text-image .cs-block__media--image::after {
  content: "";
  position: absolute;
  inset: auto -20px -20px auto;
  width: 80px;
  height: 80px;
  border: 2px solid var(--c-accent);
  border-radius: 18px;
  z-index: -1;
  opacity: 0.6;
}
.cs-block--text-image.cs-block--side-left .cs-block__media--image::after {
  inset: auto auto -20px -20px;
}
.cs-block--text-image .cs-block__copy {
  align-self: center;
}

/* Gallery grid — see canonical rules in the GALLERY section near the
   bottom of this file. Local overrides here apply only to image hover. */
.cs-block__grid-item:hover img {
  transform: scale(1.04);
}
.cs-block__grid-item img {
  transition: transform 0.7s cubic-bezier(0.2, 0.85, 0.2, 1);
}

/* Carousel */
.cs-block__media--carousel {
  width: 100%;
}
.cs-swiper {
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 30px 60px -28px rgba(var(--rgb-primary) / 0.4);
}
.cs-swiper .swiper-slide {
  aspect-ratio: 16 / 10;
  background: var(--c-bg-soft);
}
.cs-swiper .swiper-slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.cs-swiper .swiper-button-prev,
.cs-swiper .swiper-button-next {
  color: #fff;
  background: rgba(var(--rgb-ink) / 0.6);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: background 0.3s ease;
}
.cs-swiper .swiper-button-prev:hover,
.cs-swiper .swiper-button-next:hover {
  background: var(--c-primary);
}
.cs-swiper .swiper-button-prev::after,
.cs-swiper .swiper-button-next::after {
  font-size: 16px;
  font-weight: 800;
}
.cs-swiper .swiper-pagination-bullet {
  background: #fff;
  opacity: 0.55;
}
.cs-swiper .swiper-pagination-bullet-active {
  background: var(--c-accent);
  opacity: 1;
  width: 22px;
  border-radius: 4px;
}

/* Video block */
.cs-block__media--video {
  position: relative;
  border-radius: 18px;
  overflow: hidden;
  background: #000;
  box-shadow: 0 30px 60px -28px rgba(var(--rgb-primary) / 0.5);
}
.cs-block__media--video iframe,
.cs-block__media--video video {
  width: 100%;
  height: 100%;
  display: block;
  border: 0;
  position: absolute;
  inset: 0;
  object-fit: cover;
}
.cs-block--ratio-16-9 .cs-block__media--video {
  aspect-ratio: 16 / 9;
  width: 100%;
}
.cs-block--ratio-1-1 .cs-block__media--video {
  aspect-ratio: 1 / 1;
  max-width: 560px;
  margin: 0 auto;
  width: 100%;
}
.cs-block--ratio-9-16 .cs-block__media--video {
  aspect-ratio: 9 / 16;
  max-width: 360px;
  margin: 0 auto;
  width: 100%;
}

/* Text + Video */
.cs-block--text-video {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 56px);
  align-items: center;
}
.cs-block--text-video.cs-block--side-left .cs-block__media {
  order: -1;
}

/* Mixed: feature + thumbs */
.cs-block__media--mixed {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: 16px;
  align-items: stretch;
}
.cs-block__feature {
  display: block;
  border-radius: 16px;
  overflow: hidden;
  background: var(--c-bg-soft);
  aspect-ratio: 4 / 3;
  box-shadow: 0 24px 48px -22px rgba(var(--rgb-primary) / 0.4);
}
.cs-block__feature img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.8s cubic-bezier(0.2, 0.85, 0.2, 1);
}
.cs-block__feature:hover img {
  transform: scale(1.06);
}
.cs-block__thumbs {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}
.cs-block__thumbs a {
  border-radius: 12px;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  background: var(--c-bg-soft);
  transition: transform 0.4s ease;
}
.cs-block__thumbs a:hover {
  transform: translateY(-4px);
}
.cs-block__thumbs img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s ease;
}
.cs-block__thumbs a:hover img {
  transform: scale(1.08);
}

/* ============================================================
   6. RESULTS
   ============================================================ */
.cs-results {
  background:
    radial-gradient(
      60% 80% at 0% 0%,
      rgba(var(--rgb-primary) / 0.08),
      transparent 60%
    ),
    radial-gradient(
      60% 80% at 100% 100%,
      rgba(var(--rgb-cyan) / 0.05),
      transparent 60%
    ),
    var(--c-bg);
}
.cs-results__intro {
  text-align: center;
  max-width: 70ch;
  margin: 0 auto clamp(28px, 4vw, 48px);
}
.cs-results__table {
  max-width: 980px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: clamp(12px, 1.4vw, 18px);
}
/* Dashboard-card row: icon · label · sparkline · value.
   Mobile-first: icon + stacked label/value, wave hidden under 720px. */
.cs-results__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "icon label value";
  align-items: center;
  gap: 16px;
  padding: clamp(16px, 1.8vw, 22px) clamp(18px, 2.2vw, 28px);
  background: var(--c-bg-elev);
  border: 1px solid var(--c-border);
  border-radius: 18px;
  box-shadow: 0 12px 32px -28px rgba(var(--rgb-primary) / 0.4);
  transition:
    transform 0.5s cubic-bezier(0.2, 0.85, 0.2, 1),
    border-color 0.4s ease,
    box-shadow 0.5s ease;
  opacity: 0;
  transform: translateX(-20px);
}
.cs-results.is-in .cs-results__row {
  opacity: 1;
  transform: translateX(0);
  transition:
    opacity 0.7s ease calc(var(--i) * 0.08s),
    transform 0.7s cubic-bezier(0.2, 0.85, 0.2, 1) calc(var(--i) * 0.08s);
}
.cs-results__row:hover {
  transform: translateY(-3px);
  border-color: rgba(var(--rgb-primary) / 0.4);
  box-shadow: 0 24px 50px -28px rgba(var(--rgb-primary) / 0.45);
}

/* Icon tile — gradient square with the SVG inside in white. */
.cs-results__icon {
  grid-area: icon;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: clamp(44px, 4.2vw, 56px);
  height: clamp(44px, 4.2vw, 56px);
  border-radius: 14px;
  background: linear-gradient(135deg, var(--c-primary), var(--c-secondary));
  color: #fff;
  box-shadow: 0 14px 30px -16px rgba(var(--rgb-primary) / 0.55);
  flex-shrink: 0;
}
.cs-results__icon svg {
  width: 55%;
  height: 55%;
}

.cs-results__label {
  grid-area: label;
  font-size: clamp(0.95rem, 1.3vw, 1.1rem);
  color: var(--c-ink);
  font-weight: 700;
  letter-spacing: -0.005em;
  min-width: 0;
}

/* Decorative sparkline — sits centered inside its grid track. */
.cs-results__wave {
  grid-area: wave;
  width: clamp(120px, 18vw, 220px);
  height: clamp(28px, 3vw, 40px);
  display: none;
  flex-shrink: 0;
  justify-self: center;
}

.cs-results__value {
  grid-area: value;
  font-size: clamp(1.4rem, 2.4vw, 2.1rem);
  font-weight: 800;
  text-align: right;
  background: linear-gradient(135deg, var(--c-primary), var(--c-accent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
[data-theme="dark"] .cs-results__value {
  background: linear-gradient(135deg, var(--c-accent), var(--c-cyan));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Tablet+: bring the wave into the row between label and value.
   Fixed-width wave + fixed-min value column = perfect vertical alignment
   across every row, no matter the label length. */
@media (min-width: 720px) {
  .cs-results__row {
    grid-template-columns:
      auto /* icon */
      minmax(0, 1fr) /* label fills */
      clamp(120px, 18vw, 220px) /* wave — fixed track */
      minmax(110px, max-content); /* value — right-aligned, stable */
    grid-template-areas: "icon label wave value";
    gap: 20px;
  }
  .cs-results__wave {
    display: block;
    width: 100%;
  }
}

/* ============================================================
   7. TESTIMONIAL — split layout: aside meta + offset quote card
   ============================================================ */
/* ============================================================
   TESTIMONIAL — portrait + magenta rule, light background.
   Replaces the old dark "feature" gradient block with oversized
   serif quote-marks. The new design uses a centered eyebrow + title
   above a single quote card with a subtle SVG quote glyph, the
   author's portrait (or initials avatar fallback), and a magenta
   vertical rule that anchors the attribution.
   ============================================================ */
.cs-testimonial {
  background: var(--c-bg);
  color: var(--c-ink);
}
.cs-testimonial__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(24px, 4vw, 48px);
}
.cs-testimonial__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
  max-width: 720px;
}
.cs-testimonial__title {
  margin: 0;
}

/* Quote card — centered, max-width capped so long quotes don't span
 * the whole 1280px container. Soft elev background, subtle border,
 * SVG quote-mark in the corner. */
.cs-testimonial__card {
  position: relative;
  width: 100%;
  max-width: 880px;
  margin: 0;
  padding: clamp(32px, 4.5vw, 56px) clamp(28px, 4vw, 56px) clamp(28px, 4vw, 44px);
  background: var(--c-bg-elev);
  border: 1px solid var(--c-border);
  border-radius: 26px;
  box-shadow: 0 40px 80px -50px rgb(var(--rgb-primary) / 0.4);
  overflow: hidden;
  isolation: isolate;
}
/* Magenta gradient rule on the leading edge — same brand cue used by
 * .cs-stage and .cs-campaign__card hover. Always-on here because the
 * card is the visual anchor of the section. */
.cs-testimonial__card::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 4px;
  background: linear-gradient(
    180deg,
    var(--c-primary) 0%,
    var(--c-accent) 100%
  );
}
/* Decorative SVG quote-mark in the top-right corner. Faint enough not
 * to compete with the body text; strong enough to signal "this is a
 * quote" without needing serif glyphs. */
.cs-testimonial__quote-mark {
  position: absolute;
  top: clamp(18px, 2.5vw, 28px);
  right: clamp(18px, 2.5vw, 28px);
  width: clamp(44px, 5vw, 64px);
  height: auto;
  color: var(--c-primary);
  opacity: 0.14;
  pointer-events: none;
}

.cs-testimonial__quote {
  margin: 0;
  padding: 0;
}
.cs-testimonial__quote p {
  margin: 0;
  font-size: clamp(1.1rem, 1.6vw, 1.45rem);
  line-height: 1.6;
  color: var(--c-ink);
  font-weight: 500;
  letter-spacing: -0.005em;
}

/* Attribution row — avatar + name/role. Top border separates it
 * cleanly from the quote. */
.cs-testimonial__attr {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: clamp(20px, 2.5vw, 28px) 0 0;
  padding-top: clamp(18px, 2vw, 24px);
  border-top: 1px solid var(--c-border);
}
.cs-testimonial__avatar {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  overflow: hidden;
  background: rgb(var(--rgb-primary) / 0.1);
  color: var(--c-primary);
  border: 2px solid var(--c-bg-elev);
  box-shadow: 0 0 0 1px rgb(var(--rgb-primary) / 0.2);
}
.cs-testimonial__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Initials fallback when no portrait was uploaded — same magenta tint
 * + bold serif-ish display, mirrors the icon-tile pattern from
 * .cs-campaign__icon. */
.cs-testimonial__initials {
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--c-primary);
}
.cs-testimonial__attr-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  line-height: 1.35;
}
.cs-testimonial__author {
  color: var(--c-ink);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -0.005em;
}
.cs-testimonial__role {
  color: var(--c-muted);
  font-size: 0.82rem;
  letter-spacing: 0.02em;
}

/* RTL: rule moves to the right edge, quote-mark to the left corner */
html[dir="rtl"] .cs-testimonial__card::before {
  inset: 0 0 0 auto;
}
html[dir="rtl"] .cs-testimonial__quote-mark {
  right: auto;
  left: clamp(18px, 2.5vw, 28px);
  transform: scaleX(-1);
}

/* ============================================================
   8. CTA
   ============================================================ */
.cs-cta {
  background: linear-gradient(
    135deg,
    var(--c-primary) 0%,
    var(--c-secondary) 100%
  );
  color: #fff;
  text-align: center;
  isolation: isolate;
}
.cs-cta__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(
      40% 60% at 20% 100%,
      rgba(255, 210, 0, 0.18),
      transparent 60%
    ),
    radial-gradient(
      50% 60% at 80% 0%,
      rgba(235, 148, 207, 0.3),
      transparent 60%
    );
  animation: cs-cta-pulse 8s ease-in-out infinite alternate;
}
@keyframes cs-cta-pulse {
  0% {
    transform: scale(1) rotate(0);
  }
  100% {
    transform: scale(1.1) rotate(2deg);
  }
}
.cs-cta__heading {
  font-size: clamp(1.8rem, 4vw, 3rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  max-width: 24ch;
  margin: 0 auto 32px;
  line-height: 1.15;
  color: #fff;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 0.32em;
}
.cs-cta__btn {
  display: inline-block;
  padding: 18px 44px;
  background: #fff;
  color: var(--c-primary);
  font-weight: 700;
  font-size: 1.05rem;
  border-radius: 999px;
  text-decoration: none;
  transition:
    transform 0.25s ease,
    box-shadow 0.25s ease;
  box-shadow: 0 14px 38px -12px rgba(0, 0, 0, 0.45);
  will-change: transform;
}
.cs-cta__btn:hover {
  box-shadow: 0 20px 48px -14px rgba(0, 0, 0, 0.55);
}
.cs-cta__btn span {
  display: inline-block;
  transition: transform 0.3s ease;
}
.cs-cta__btn:hover span {
  transform: translateX(2px);
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1100px) {
  .cs-bg--has-image .cs-bg__inner,
  .cs-block--text-image,
  .cs-block--text-video {
    grid-template-columns: 1fr;
  }
  .cs-bg--side-left.cs-bg--has-image .cs-bg__media,
  .cs-block--text-image.cs-block--side-left .cs-block__media,
  .cs-block--text-video.cs-block--side-left .cs-block__media {
    order: unset;
  }
  .cs-block--text-image .cs-block__media--image {
    transform: none;
  }
}

@media (max-width: 760px) {
  .cs-block__media--mixed {
    grid-template-columns: 1fr;
  }
  .cs-block__thumbs {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: 4px;
    gap: 10px;
    grid-template-columns: none;
  }
  .cs-block__thumbs a {
    flex: 0 0 48%;
    aspect-ratio: 1 / 1;
    scroll-snap-align: start;
  }
}

@media (max-width: 600px) {
  .cs-hero {
    min-height: 70vh;
    padding-top: 100px;
  }
  .cs-section {
    padding: 60px 0;
  }
  .cs-results__row {
    padding: 14px 16px;
    gap: 12px;
  }
  .cs-results__value {
    font-size: 1.35rem;
  }
  .cs-testimonial__quote {
    padding: 36px 22px;
  }
}

/* Reduce motion preference */
@media (prefers-reduced-motion: reduce) {
  .cs-word__inner,
  [data-cs-reveal-fade],
  .cs-stage__num,
  .cs-block,
  .cs-results__row {
    transition: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
  .cs-hero__scroll-line::after {
    animation: none;
  }
  .cs-cta__bg {
    animation: none;
  }
}

/* ============================================================
   BLOCK HEAD — optional title + description shown above any media block.
   Inside a stage, each block reads as a sub-step of its parent stage:
   left accent rule + auto-numbered marker (A, B, C…) tie it visually
   to the stage card it lives in.
   ============================================================ */
.cs-stage__blocks {
  counter-reset: cs-substep;
}
.cs-block__head {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: clamp(14px, 2.2vw, 22px);
  padding-left: clamp(14px, 1.8vw, 22px);
  border-left: 3px solid rgba(var(--rgb-primary) / 0.35);
}
.cs-stage__blocks .cs-block__head {
  counter-increment: cs-substep;
  position: relative;
}
.cs-stage__blocks .cs-block__head::before {
  content: counter(cs-substep, upper-alpha);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  margin-bottom: 4px;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--c-primary), var(--c-secondary));
  color: #fff;
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  box-shadow: 0 8px 18px -10px rgba(var(--rgb-primary) / 0.6);
  align-self: flex-start;
}
.cs-block__title {
  margin: 0;
  font-size: clamp(1.15rem, 2.1vw, 1.6rem);
  line-height: 1.25;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--c-fg, inherit);
}
.cs-block__desc {
  font-size: 0.98rem;
  line-height: 1.55;
  color: rgba(var(--rgb-fg, 17 17 17) / 0.78);
}
.cs-block__desc p:last-child {
  margin-bottom: 0;
}

/* ============================================================
   GALLERY — mobile-first responsive grid that adapts to item count.
   Mobile: single column. Tablet+: 2 columns. Desktop tunes the
   pattern by item count, and a vertical/portrait item promotes
   itself to "feature" so it spans both rows alongside stacked thumbs.
   ============================================================ */
.cs-block__media--gallery {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
  width: 100%;
  max-width: 100%;
}
@media (min-width: 640px) {
  .cs-block__media--gallery {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px;
  }
}

/* Tablet-range has-vertical (640px–1023px) — matches the desktop
 * "anchor + stack" treatment but with a smaller fixed row height so
 * the Short/Reel doesn't dominate the viewport. Without this, the
 * default 2-col grid above would render the image at 16:10 (short)
 * and the vertical at 9:16 (tall) side-by-side — heights don't match
 * and the image looks orphaned. */
@media (min-width: 640px) and (max-width: 1023.98px) {
  .cs-block__media--gallery:has(.is-vertical) {
    --cs-bento-h: clamp(360px, 52vw, 460px);
    display: grid;
    grid-template-columns: minmax(0, 1fr) calc(var(--cs-bento-h) * 9 / 16);
    grid-template-rows: var(--cs-bento-h);
    grid-auto-flow: row;
    height: var(--cs-bento-h);
    align-items: stretch;
    gap: 14px;
  }
  html[dir="rtl"] .cs-block__media--gallery:has(.is-vertical) {
    grid-template-columns: calc(var(--cs-bento-h) * 9 / 16) minmax(0, 1fr);
  }
  .cs-block__media--gallery:has(.is-vertical) > .cs-block__grid-item.is-vertical {
    grid-column: 2;
    grid-row: 1 / -1;
    aspect-ratio: auto;
    width: 100%;
    height: 100%;
    max-height: none;
    align-self: stretch;
  }
  html[dir="rtl"] .cs-block__media--gallery:has(.is-vertical) > .cs-block__grid-item.is-vertical {
    grid-column: 1;
  }
  .cs-block__media--gallery:has(.is-vertical) > .cs-block__grid-item:not(.is-vertical) {
    grid-column: 1;
    aspect-ratio: auto;
    height: 100%;
    min-height: 0;
    align-self: stretch;
  }
  html[dir="rtl"] .cs-block__media--gallery:has(.is-vertical) > .cs-block__grid-item:not(.is-vertical) {
    grid-column: 2;
  }
  .cs-block__media--gallery[data-cols="2"]:has(.is-vertical) {
    grid-template-rows: 1fr;
  }
  .cs-block__media--gallery[data-cols="3"]:has(.is-vertical) {
    grid-template-rows: 1fr 1fr;
  }
  .cs-block__media--gallery[data-cols="4"]:has(.is-vertical) {
    grid-template-rows: 1fr 1fr 1fr;
  }
  /* Two verticals → flatten to side-by-side at native 9:16 (no fixed
   * height) so neither gets stretched. */
  .cs-block__media--gallery[data-cols="2"]:has(.is-vertical + .is-vertical) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto;
    height: auto;
  }
  .cs-block__media--gallery[data-cols="2"]:has(.is-vertical + .is-vertical)
    > .cs-block__grid-item.is-vertical {
    grid-column: auto;
    grid-row: auto;
    aspect-ratio: 9 / 16;
    height: auto;
    align-self: start;
  }
}
/* Desktop layout for galleries lives in the BENTO block further down
 * (~line 2545). The previous "uniform grid" rules at this location were
 * removed because they competed with the bento rules in the same media
 * query and produced inconsistent results. Bento is now the single
 * source of truth for ≥1024px gallery layout. */

.cs-block__grid-item {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 16px;
  background: var(--c-bg-soft, #f4f4f6);
  box-shadow: 0 18px 40px -28px rgba(var(--rgb-primary, 138 0 190) / 0.4);
  aspect-ratio: 16 / 10;
}
.cs-block__grid-item img,
.cs-block__grid-item video,
.cs-block__grid-item iframe {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: 0;
}
/* Orientation modifiers — applied at every breakpoint as the BASE
 * intrinsic ratio for each cell. Bento rules at ≥1024px override these
 * for hero/feature roles, but solo cells (and the 2-item layout) read
 * directly from these values.
 *
 * `is-vertical` is 9/16 because the dominant vertical content type is
 * a YouTube Short / Instagram Reel — using 4/5 here meant Shorts
 * letterboxed inside the cell with `object-fit: contain` on the iframe.
 * Auto-detected portrait images (ratio < 0.9) still fit fine in 9/16. */
.cs-block__grid-item.is-normal {
  aspect-ratio: 16 / 10;
}
.cs-block__grid-item.is-square {
  aspect-ratio: 1 / 1;
}
.cs-block__grid-item.is-vertical {
  aspect-ratio: 9 / 16;
}
.cs-block__grid-item--video {
  cursor: default;
}
.cs-block__grid-item--video iframe,
.cs-block__grid-item--video video {
  background: #000;
  object-fit: contain;
}

/* ============================================================
   PER-BLOCK SPLIT — copy left, media right.
   When a block has both a head (title+desc) and a media grid,
   show them side-by-side on desktop. Mobile stacks naturally.
   Applies to gallery, image, video, and carousel layouts.
   ============================================================ */
@media (min-width: 960px) {
  .cs-block--gallery,
  .cs-block--image,
  .cs-block--video {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    column-gap: clamp(28px, 3.5vw, 56px);
    align-items: start;
  }
  .cs-block--gallery > .cs-block__head,
  .cs-block--image > .cs-block__head,
  .cs-block--video > .cs-block__head {
    grid-column: 1;
    margin-bottom: 0;
    padding-top: clamp(8px, 1.5vw, 18px);
  }
  .cs-block--gallery > .cs-block__media,
  .cs-block--image > .cs-block__media,
  .cs-block--video > .cs-block__media {
    grid-column: 2;
    min-width: 0;
  }
  /* Without a head, media should fill the row — fall back to single column. */
  .cs-block--gallery:not(:has(> .cs-block__head)),
  .cs-block--image:not(:has(> .cs-block__head)),
  .cs-block--video:not(:has(> .cs-block__head)) {
    display: block;
  }
}

/* ============================================================
   SUB-STEP OVERLAP — successive media blocks within a stage tuck
   under the previous one with alternating left/right shift, so
   sub-steps feel layered. Desktop only — mobile stacks linearly.
   ============================================================ */
@media (min-width: 1024px) {
  .cs-stage__blocks > .cs-block + .cs-block {
    /* margin-top: clamp(-100px, -6vw, -48px); */
    position: relative;
    z-index: 2;
  }
  /* .cs-stage__blocks > .cs-block:nth-child(even) {
    transform: translateX(clamp(8px, 1.6vw, 24px));
  } */
  /* .cs-stage__blocks > .cs-block:nth-child(odd) ~ .cs-block:nth-child(odd) {
    transform: translateX(clamp(-24px, -1.6vw, -8px));
  } */
}

/* ============================================================
   CAMPAIGN DETAILS — single client per case study, mobile-first
   vertical responsive table
   ============================================================ */
.cs-campaign {
  background: var(--c-bg, #fff);
  padding: clamp(40px, 5.5vw, 80px) 0;
}
.cs-campaign .cs-section__title {
  margin-bottom: clamp(12px, 1.8vw, 20px);
}
.cs-campaign__desc {
  max-width: 760px;
  margin: 0 auto clamp(16px, 2vw, 24px);
  text-align: center;
}
/* ---------- Campaign Brief — clean info cards ----------
 * Mirrors the .ct-card system used on the contact page so this section
 * speaks the same dialect as the rest of the site: elev background,
 * faint magenta-tinted icon tile, primary-color SVG, icon flips to
 * filled magenta on hover. NO gradient tiles, NO pink card wash.
 *
 * Editors can write multi-value fields with separators (`,` `|` `;`
 * `/` `–` `—` newline) and each token becomes a chip; single values
 * render as a clean bold value. */
.cs-campaign__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  max-width: 1100px;
  margin: 0 auto;
}
@media (min-width: 720px) {
  .cs-campaign__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 20px;
  }
}
/* On wide screens we keep 2 columns rather than forcing 3 — wider cards
 * give multi-chip lists (Platforms, CTAs) room to breathe without
 * wrapping every chip onto its own line. The page already establishes
 * its rhythm via the eyebrow + heading; the grid doesn't need to be
 * the visual anchor. */
@media (min-width: 1100px) {
  .cs-campaign__grid {
    gap: 24px;
  }
}

.cs-campaign__card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: clamp(1.4rem, 2.4vw, 1.8rem);
  background: var(--c-bg-elev);
  border: 1px solid var(--c-border);
  color: var(--c-ink);
  border-radius: 22px;
  overflow: hidden;
  isolation: isolate;
  transition:
    transform 320ms ease,
    border-color 320ms ease,
    box-shadow 320ms ease;
  will-change: transform;
}
/* Subtle radial primary tint on hover — same trick .ct-card uses. No
 * always-on background wash. */
.cs-campaign__card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    135deg,
    rgb(var(--rgb-primary) / 0.08),
    transparent 60%
  );
  opacity: 0;
  transition: opacity 320ms ease;
  z-index: -1;
}
.cs-campaign__card:hover {
  transform: translateY(-4px);
  border-color: rgb(var(--rgb-primary) / 0.4);
  box-shadow: 0 28px 60px -28px rgb(var(--rgb-primary) / 0.45);
}
.cs-campaign__card:hover::before {
  opacity: 1;
}

.cs-campaign__card-head {
  display: flex;
  align-items: center;
  gap: 14px;
}
.cs-campaign__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  border-radius: 14px;
  background: rgb(var(--rgb-primary) / 0.1);
  color: var(--c-primary);
  transition:
    transform 320ms ease,
    background-color 320ms ease,
    color 320ms ease;
}
.cs-campaign__icon svg {
  width: 22px;
  height: 22px;
}
.cs-campaign__card:hover .cs-campaign__icon {
  transform: rotate(-6deg) scale(1.05);
  background: var(--c-primary);
  color: #fff;
}
.cs-campaign__label {
  display: block;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--c-muted);
}

.cs-campaign__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.cs-campaign__chip {
  display: inline-flex;
  align-items: center;
  padding: 6px 12px;
  background: transparent;
  color: var(--c-ink);
  border: 1px solid var(--c-border);
  border-radius: 999px;
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.3;
  transition:
    background-color 0.28s ease,
    color 0.28s ease,
    border-color 0.28s ease,
    transform 0.28s ease;
}
.cs-campaign__chip:hover {
  background: var(--c-primary);
  color: #fff;
  border-color: var(--c-primary);
  transform: translateY(-1px);
}
[data-theme="dark"] .cs-campaign__chip:hover {
  background: var(--c-accent);
  color: var(--c-bg);
  border-color: var(--c-accent);
}

/* Single-value cards (Client Name, single goal) — render as one bold
 * value, not a chip with a border. The chip class is still used so
 * editors can add separators later without changing markup. */
.cs-campaign__chips:has(> .cs-campaign__chip:only-child) {
  gap: 0;
}
.cs-campaign__chips:has(> .cs-campaign__chip:only-child) .cs-campaign__chip {
  background: transparent;
  border-color: transparent;
  padding: 0;
  font-size: clamp(1.15rem, 1.8vw, 1.4rem);
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--c-ink);
}
.cs-campaign__chips:has(> .cs-campaign__chip:only-child) .cs-campaign__chip:hover {
  background: transparent;
  color: var(--c-primary);
  transform: none;
}

/* ============================================================
   PHASE 1 — Brand-voice alignment
   Accent word + magenta-dot eyebrow chips, applied across every
   section so the case study speaks the same dialect as the rest of
   the site (contact, team, services, single-project).
   ============================================================ */

/* Accent word — italic + brand color on the LAST word of any masked
 * headline. Mirrors `.word.accent` rules in contact.css/about.css/etc. */
.cs-single .cs-word.accent .cs-word__inner {
  color: var(--c-primary);
  font-style: italic;
}
[data-theme="dark"] .cs-single .cs-word.accent .cs-word__inner {
  color: var(--c-accent);
}

/* Italic glyphs (and trailing punctuation like "!") lean past the
 * .cs-word box, and the `overflow: hidden` mask used for the entrance
 * animation clips them. The mask only needs to clip during the reveal
 * — once `.is-in` fires the inner sits at translateY(0), so we can
 * safely release the clip and let italics breathe. Padding-right gives
 * a small safety margin so the next word's gap isn't visually crushed. */
.cs-single [data-cs-reveal-lines].is-in .cs-word,
.cs-single .cs-stage.is-in .cs-word {
  overflow: visible;
}
.cs-single .cs-word.accent .cs-word__inner {
  padding-right: 0.08em;
}

/* Eyebrow chip — magenta dot + caps label, sits above section titles.
 * Visual matches .lilac-pcta__eyebrow / .related-projects__eyebrow. */
.cs-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.4rem 0.85rem;
  margin-bottom: 1rem;
  border: 1px solid rgb(var(--rgb-ink) / 0.12);
  border-radius: 999px;
  background: rgb(var(--rgb-ink) / 0.04);
  color: rgb(var(--rgb-ink) / 0.72);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  width: fit-content;
}
.cs-eyebrow__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--c-primary);
  box-shadow: 0 0 0 3px rgb(var(--rgb-primary) / 0.18);
}
[data-theme="dark"] .cs-eyebrow__dot {
  background: var(--c-accent);
  box-shadow: 0 0 0 3px rgb(var(--rgb-accent) / 0.22);
}

/* Centered sections (Concept, Campaign, Results) — align the eyebrow
 * with the centered title rather than left-edge of the column. */
.cs-concept .cs-eyebrow,
.cs-campaign .cs-eyebrow,
.cs-results .cs-eyebrow {
  margin-left: auto;
  margin-right: auto;
}

/* Stages eyebrow inherits the cs-stages__head's text-align; keep
 * left-aligned. The legacy .cs-stages__chip rule no longer renders
 * any markup but is left untouched for safety. */

/* CTA — the section now uses the .lilac-pcta block (defined in
 * project.css). The .cs-pcta--cs modifier is reserved for future
 * case-study-specific tweaks; nothing custom needed today. */

/* ============================================================
   PHASE 2 — Stages density & rhythm
   - Quick-stats strip below the hero
   - Sticky stage progress rail
   - Alternating stage layout (odd/even numeral side)
   - First-letter / vertical-rule treatment for stage prose
   ============================================================ */

/* ---------- Quick-stats strip ---------- */
.cs-quickstats {
  position: relative;
  padding: clamp(20px, 3vw, 32px) 0;
  background: var(--c-bg);
  border-bottom: 1px solid rgb(var(--rgb-ink) / 0.08);
}
.cs-quickstats__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(12px, 2vw, 28px) clamp(20px, 3vw, 44px);
  align-items: center;
  justify-content: flex-start;
}
.cs-quickstats__item {
  display: inline-flex;
  align-items: baseline;
  gap: 0.6rem;
  font-size: clamp(0.86rem, 1vw, 0.98rem);
}
.cs-quickstats__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--c-primary);
  box-shadow: 0 0 0 3px rgb(var(--rgb-primary) / 0.18);
  transform: translateY(-1px);
  flex: 0 0 auto;
}
[data-theme="dark"] .cs-quickstats__dot {
  background: var(--c-accent);
  box-shadow: 0 0 0 3px rgb(var(--rgb-accent) / 0.22);
}
.cs-quickstats__label {
  color: rgb(var(--rgb-ink) / 0.55);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 0.72rem;
}
.cs-quickstats__value {
  color: var(--c-ink);
  font-weight: 700;
  letter-spacing: -0.005em;
}

/* ---------- Stages shell + rail ----------
 * The rail is a sticky overlay that floats in the LEFT GUTTER of
 * .cs-section__inner — it does NOT take a grid/flex track of its own.
 * That avoids the "dead column" of empty pink/lavender space that
 * appears when the rail is short and the stages are tall. The cards
 * keep their natural full width.
 *
 * Sticky works because the rail's containing block is .cs-stages__shell
 * which is as tall as all the stages combined — sticky pins inside it.
 *
 * Below 1280px the rail is hidden (the big-num watermark + per-stage
 * chip already orient the reader). */
.cs-stages__shell {
  position: relative;
}

.cs-stages__rail {
  display: none;
}

@media (min-width: 1280px) {
  /* Default state — rail sits in flow at the top of the stages shell.
   * `position: sticky` is intentionally NOT used here because several
   * ancestors of this section have `overflow: hidden`, which silently
   * clips/disables sticky. We use a JS-driven `position: fixed` switch
   * instead (see .is-pinned below). */
  .cs-stages__rail {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 200px;
  }
  .cs-stages__rail-inner {
    /* Padding lives inside .rail-inner (not the .rail wrapper) so the
     * glass-pill background applied in the pinned state has visual
     * breathing room without nudging the unpinned layout. */
    padding: 18px 16px 18px 18px;
    border-radius: 18px;
    border: 1px solid transparent;
    /* No transition on background/border/shadow — those properties
     * change when .is-pinned toggles, and a fade animation chained
     * with sub-pixel scroll updates from Lenis produces a visible
     * shimmer at the pin/unpin threshold. Snap-on, snap-off feels
     * cleaner anyway. */
  }
  /* Reserve the rail's gutter so cards don't underlap it. */
  .cs-stages__shell.has-rail {
    padding-left: 232px;
  }

  /* Sentinel is a 1px probe placed at the top of the rail. When it
   * scrolls offscreen, JS computes the rail's pinned position and
   * toggles .is-pinned on the rail wrapper. */
  .cs-stages__rail-sentinel {
    position: absolute;
    top: 0;
    height: 1px;
    width: 1px;
  }

  /* Pinned state — `position: fixed` so the rail is guaranteed visible
   * regardless of ancestor `overflow: hidden`. JS sets --cs-rail-left
   * to the rail's correct viewport-x (since the centered container's
   * left edge varies with viewport width). */
  .cs-stages__rail.is-pinned {
    position: fixed;
    /* Sit below the floating site header. Same offset pattern used by
     * .pin-text-wrapper / .lilac-sticky-top in project.css so the rail
     * lines up with other sticky elements site-wide. */
    top: calc(var(--header-h, 88px) + 1.25rem);
    left: var(--cs-rail-left, 0);
    /* `position: absolute` height was implied 100%; with fixed we
     * size to content so the pill hugs its items. */
    height: auto;
    z-index: 5;
    /* Force a compositing layer so the browser doesn't repaint the
     * rail on every scroll frame — and so sub-pixel rounding stays
     * consistent. Without this, `position: fixed` elements adjacent to
     * tall scrolled content can visibly shimmer. */
    will-change: transform;
    transform: translateZ(0);
  }
  /* Pinned background is OPAQUE (no backdrop-filter). Combining
   * backdrop-filter with `position: fixed` while Lenis smooth-scrolls
   * the page causes a visible per-frame flicker on Chromium — the
   * filter recomputes against sub-pixel-shifted content beneath it.
   * A solid pill with primary tint reads identically and is rock-
   * stable regardless of scroll speed. */
  .cs-stages__rail.is-pinned .cs-stages__rail-inner {
    background:
      linear-gradient(
        180deg,
        rgb(var(--rgb-primary) / 0.06),
        rgb(var(--rgb-primary) / 0.03)
      ),
      var(--c-bg);
    border-color: rgb(var(--rgb-primary) / 0.22);
    box-shadow:
      0 1px 0 rgb(var(--rgb-ink) / 0.04),
      0 18px 50px -20px rgb(var(--rgb-primary) / 0.35);
  }
  [data-theme="dark"] .cs-stages__rail.is-pinned .cs-stages__rail-inner {
    background: var(--c-bg-elev);
    border-color: rgb(var(--rgb-ink) / 0.18);
  }
}

/* Below 1280px the rail is hidden entirely — the sentinel must not
 * leak any layout space. */
@media (max-width: 1279.98px) {
  .cs-stages__rail-sentinel { display: none; }
}
.cs-stages__rail-inner {
  position: relative;
  padding-left: 18px;
}
/* Vertical rail line — anchors all rail items visually. */
.cs-stages__rail-inner::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: linear-gradient(
    to bottom,
    rgb(var(--rgb-ink) / 0) 0%,
    rgb(var(--rgb-ink) / 0.18) 12%,
    rgb(var(--rgb-ink) / 0.18) 88%,
    rgb(var(--rgb-ink) / 0) 100%
  );
}
.cs-stages__rail-eyebrow {
  display: block;
  margin: 0 0 14px;
  padding-left: 4px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgb(var(--rgb-ink) / 0.55);
}
.cs-stages__rail-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.cs-stages__rail-item {
  position: relative;
}
/* Bullet on the rail line — fills with primary on .is-active. */
.cs-stages__rail-item::before {
  content: "";
  position: absolute;
  left: -18px;
  top: 0.55em;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--c-bg);
  border: 1px solid rgb(var(--rgb-ink) / 0.32);
  transition:
    background-color 0.32s ease,
    border-color 0.32s ease,
    box-shadow 0.32s ease,
    transform 0.32s ease;
}
.cs-stages__rail-item.is-active::before {
  background: var(--c-primary);
  border-color: var(--c-primary);
  box-shadow: 0 0 0 4px rgb(var(--rgb-primary) / 0.18);
  transform: scale(1.15);
}
[data-theme="dark"] .cs-stages__rail-item.is-active::before {
  background: var(--c-accent);
  border-color: var(--c-accent);
  box-shadow: 0 0 0 4px rgb(var(--rgb-accent) / 0.22);
}
.cs-stages__rail-link {
  display: flex;
  align-items: baseline;
  gap: 10px;
  text-decoration: none;
  color: rgb(var(--rgb-ink) / 0.55);
  font-size: 0.92rem;
  line-height: 1.35;
  transition: color 0.28s ease;
}
.cs-stages__rail-link:hover { color: var(--c-ink); }
.cs-stages__rail-item.is-active .cs-stages__rail-link { color: var(--c-ink); }
.cs-stages__rail-num {
  font-family: var(--font-display, "Bunday Clean", sans-serif);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: rgb(var(--rgb-ink) / 0.4);
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}
.cs-stages__rail-item.is-active .cs-stages__rail-num {
  color: var(--c-primary);
}
[data-theme="dark"] .cs-stages__rail-item.is-active .cs-stages__rail-num {
  color: var(--c-accent);
}
.cs-stages__rail-title {
  font-weight: 600;
  letter-spacing: -0.005em;
  /* Truncate so a long title doesn't break the column width. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---------- Alternating stage layout ----------
 * Even-index stages keep the default visual side; odd-index stages flip
 * the big numeral to the right edge so the page reads with rhythm
 * instead of stamped-out cards. We only flip the watermark numeral,
 * not the heading column — keeping the title left-aligned preserves
 * scanability for English & RTL alike. */
.cs-stages__list .cs-stage:nth-child(even) .cs-stage__big-num {
  left: auto;
  right: clamp(-12px, -1.5vw, -4px);
  text-align: right;
}

/* ---------- Stage prose treatment ----------
 * `.cs-stage__intro` and `.cs-block--text` were pure paragraph blocks
 * — the wall-of-text problem when a case study has 5+ stages. Add a
 * vertical magenta rule + drop-cap-style first letter so they read
 * editorially. */
.cs-stage__intro {
  position: relative;
  padding-left: clamp(18px, 2.2vw, 28px);
}
.cs-stage__intro::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.45em;
  bottom: 0.45em;
  width: 3px;
  border-radius: 2px;
  background: linear-gradient(
    to bottom,
    var(--c-primary) 0%,
    var(--c-accent) 100%
  );
}
.cs-stage__intro > p:first-child::first-letter {
  font-family: var(--font-display, "Bunday Clean", sans-serif);
  font-weight: 800;
  font-size: 2.4em;
  line-height: 0.9;
  float: left;
  margin: 0.06em 0.18em 0 0;
  color: var(--c-primary);
  letter-spacing: -0.02em;
}
[data-theme="dark"] .cs-stage__intro > p:first-child::first-letter {
  color: var(--c-accent);
}

/* Plain text-only blocks: same vertical rule, no drop-cap (drop-cap is
 * a once-per-stage moment — overusing it kills the effect). */
.cs-block--text {
  position: relative;
  padding-left: clamp(18px, 2.2vw, 28px);
}
.cs-block--text::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.45em;
  bottom: 0.45em;
  width: 2px;
  border-radius: 2px;
  background: rgb(var(--rgb-primary) / 0.55);
}
.cs-block--text > .cs-block__head + .cs-prose,
.cs-block--text > .cs-prose {
  margin: 0;
}

/* RTL: rail moves to the right gutter, prose-rule and drop-cap flip. */
@media (min-width: 1280px) {
  html[dir="rtl"] .cs-stages__shell.has-rail {
    padding-left: 0;
    padding-right: 232px;
  }
  /* Unpinned rail anchors to the right of the shell. */
  html[dir="rtl"] .cs-stages__rail {
    left: auto;
    right: 0;
  }
  /* Mirror the rail-inner padding so the gutter that holds the bullets
     stays on the inside (= the side facing the section content), not
     against the page edge. The base LTR rule reserves 18px on the left
     for the bullet column and 16px on the right; in RTL we swap those. */
  html[dir="rtl"] .cs-stages__rail-inner {
    padding: 18px 18px 18px 16px;
  }
  /* Pinned rail in RTL — needs to explicitly re-set `left` and `right`
     because `html[dir="rtl"] .cs-stages__rail` (specificity 0,2,1) has
     higher specificity than the base `.cs-stages__rail.is-pinned` rule
     (specificity 0,2,0). Without these, `left: auto; right: 0` leaks
     through into the pinned state — the rail sticks to the viewport's
     right edge instead of using the JS-computed --cs-rail-left, which
     visually drifts the rail off the centered shell. */
  html[dir="rtl"] .cs-stages__rail.is-pinned {
    left: var(--cs-rail-left, 0);
    right: auto;
  }
}

/* RTL — vertical rail line and bullet markers flip to the inner side.
 *
 * In LTR the bullets sit at left:-18px of each item, which is x=0 of the
 * pill (its left edge) — partly underneath the rounded corner. That
 * works visually because the pill's left side is the outer/page-edge
 * side and visitors don't focus on it. In RTL the rail flips to the
 * right edge of the section, so the bullets land near the right
 * rounded corner, which reads visually messy because that side is
 * now the page-content-facing edge that the eye lands on first.
 *
 * Pull the bullets and the rail line a bit further inside (away from
 * the rounded corner) on RTL so they sit cleanly in the gutter. The
 * LTR side keeps its existing values. */
html[dir="rtl"] .cs-stages__rail-inner::before {
  left: auto;
  right: 9px;
}
html[dir="rtl"] .cs-stages__rail-item::before {
  left: auto;
  right: -14px;
}
/* Eyebrow nudge — was `padding-left: 4px` to align the eyebrow text
   visually with the bulleted item titles below it; mirror to padding-right. */
html[dir="rtl"] .cs-stages__rail-eyebrow {
  padding-left: 0;
  padding-right: 4px;
}
html[dir="rtl"] .cs-stage__intro,
html[dir="rtl"] .cs-block--text {
  padding-left: 0;
  padding-right: clamp(18px, 2.2vw, 28px);
}
html[dir="rtl"] .cs-stage__intro::before,
html[dir="rtl"] .cs-block--text::before {
  left: auto;
  right: 0;
}
html[dir="rtl"] .cs-stage__intro > p:first-child::first-letter {
  float: right;
  margin: 0.06em 0 0 0.18em;
}
html[dir="rtl"] .cs-stages__list .cs-stage:nth-child(even) .cs-stage__big-num {
  right: auto;
  left: clamp(-12px, -1.5vw, -4px);
  text-align: left;
}

/* ============================================================
   PHASE 3 — Media block variety
   - Bento grid for galleries (uses is-vertical/square/normal)
   - Mobile slider for media-heavy blocks below lg
   ============================================================ */

/* ---------- Bento gallery (≥1024px) ----------
 * Replaces the older uniform grid for galleries with 3+ items. Items
 * with is-vertical span two rows and create the "feature" anchor;
 * is-square/is-normal slot into the remaining cells via grid auto-
 * placement. Below 1024px we keep the existing simple stacking grid
 * because narrow screens don't have room for asymmetric bento.
 *
 * Strategy: switch to dense auto-flow with explicit row-span on tall
 * items. Cell aspect ratios are SET BY ORIENT, not by data-cols, so
 * the grid feels organic regardless of count. */
@media (min-width: 1024px) {
  /* ───────────────────────────────────────────────────────────────────
   * Bento gallery — desktop ≥1024px.
   *
   * Two distinct layout strategies depending on whether the gallery
   * contains a vertical (Shorts/Reels) item:
   *
   *  A) HAS-VERTICAL → "anchor + stack". The first vertical becomes
   *     the visual anchor on one side at 9:16. Every other item stacks
   *     in the remaining column at a uniform landscape ratio. This
   *     guarantees no orphan rows, no overlapping cells, no
   *     dense-flow surprises — heights are deterministic.
   *
   *  B) NO-VERTICAL → "hero bento". First item becomes a wide hero,
   *     siblings tile beneath. Standard 6-col grid; only used when
   *     there's no vertical to anchor against.
   *
   * Note on data-cols: capped to 4 in the template, so [data-cols="4"]
   * matches "4 OR MORE items". The "stack column" approach handles
   * counts naturally — the same rules work for 3, 4, 5+.
   * ─────────────────────────────────────────────────────────────────── */

  /* ─── A) HAS-VERTICAL — fixed-height row, equal visual weight ──
   *
   * The gallery is a flex row with a fixed height (--cs-bento-h).
   * Inside, the vertical (Shorts/Reels) takes a narrow column equal
   * to its 9:16 width at the row height — naturally narrower than
   * the rest. Every other item shares the remaining width as flex
   * children inside their own implicit "stack" — but since we have
   * no markup wrapper, we use `flex-direction: row-reverse` with the
   * vertical pinned to the end via `order` and use a wrapper-less
   * trick: the gallery becomes a flex row, the vertical sits at one
   * end at fixed width, and the landscapes wrap to a column on the
   * other end via `flex-direction: row` + `flex-wrap` is awkward —
   * so we switch to GRID with explicit row counts driven by item
   * count.
   *
   * Flex won't help without markup. Use grid: column 1 = stack
   * (1fr), column 2 = vertical (auto-sized to row height × 9/16).
   * Set grid-template-rows and gallery height based on landscape
   * count via :has() with class-based nth selectors so the rows
   * always partition evenly. */
  .cs-block__media--gallery:has(.is-vertical) {
    --cs-bento-h: clamp(420px, 56vh, 620px);
    display: grid;
    grid-template-columns: minmax(0, 1fr) calc(var(--cs-bento-h) * 9 / 16);
    grid-auto-flow: row;
    grid-template-rows: var(--cs-bento-h);
    height: var(--cs-bento-h);
    align-items: stretch;
    gap: 18px;
  }
  html[dir="rtl"] .cs-block__media--gallery:has(.is-vertical) {
    grid-template-columns: calc(var(--cs-bento-h) * 9 / 16) minmax(0, 1fr);
  }
  /* Vertical anchor — fills the narrow column at the row's full
   * height. */
  .cs-block__media--gallery:has(.is-vertical) > .cs-block__grid-item.is-vertical {
    grid-column: 2;
    grid-row: 1 / -1;
    aspect-ratio: auto;
    width: 100%;
    height: 100%;
    max-height: none;
    align-self: stretch;
  }
  html[dir="rtl"] .cs-block__media--gallery:has(.is-vertical) > .cs-block__grid-item.is-vertical {
    grid-column: 1;
  }
  /* Landscape siblings — share the wide column. They stretch to
   * their row height; object-fit:cover on the inner img/video keeps
   * media from distorting. */
  .cs-block__media--gallery:has(.is-vertical) > .cs-block__grid-item:not(.is-vertical) {
    grid-column: 1;
    aspect-ratio: auto;
    height: 100%;
    min-height: 0;
    align-self: stretch;
  }
  html[dir="rtl"] .cs-block__media--gallery:has(.is-vertical) > .cs-block__grid-item:not(.is-vertical) {
    grid-column: 2;
  }
  /* Partition the stack column into N equal rows for N landscape
   * siblings. data-cols on the gallery counts ALL items (capped to 4
   * by the template), so the count of landscapes = data-cols - 1
   * when exactly one vertical exists, and we cap at 3 stacked
   * landscapes (data-cols=4).
   *
   * One vertical + 1 landscape → stack column = 1 row.
   * One vertical + 2 landscapes → 2 rows.
   * One vertical + 3 landscapes → 3 rows.
   *
   * data-cols includes the vertical, so subtract 1. */
  .cs-block__media--gallery[data-cols="2"]:has(.is-vertical) {
    grid-template-rows: 1fr;
  }
  .cs-block__media--gallery[data-cols="3"]:has(.is-vertical) {
    grid-template-rows: 1fr 1fr;
  }
  .cs-block__media--gallery[data-cols="4"]:has(.is-vertical) {
    grid-template-rows: 1fr 1fr 1fr;
  }
  /* When TWO+ verticals exist, the rules above pin the first to the
   * narrow column and stretch additional ones in the stack column —
   * which would distort them. Override: extra verticals stay 9:16
   * inside their stack row, centered, with object-fit handling. */
  .cs-block__media--gallery:has(.is-vertical) > .cs-block__grid-item.is-vertical ~ .cs-block__grid-item.is-vertical {
    grid-column: 1;
    grid-row: auto;
    aspect-ratio: 9 / 16;
    height: auto;
    max-height: 100%;
    align-self: center;
    justify-self: center;
    width: auto;
  }
  html[dir="rtl"] .cs-block__media--gallery:has(.is-vertical) > .cs-block__grid-item.is-vertical ~ .cs-block__grid-item.is-vertical {
    grid-column: 2;
  }
  /* When TWO verticals are present (e.g. two Shorts side-by-side), the
   * anchor rule above pins only one of them — flatten back to a simple
   * 2-up grid at native 9:16. Override the fixed height + row template
   * from the anchor rules. */
  .cs-block__media--gallery[data-cols="2"]:has(.is-vertical + .is-vertical),
  .cs-block__media--gallery[data-cols="2"]:has(.is-vertical:nth-child(2)) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto;
    height: auto;
  }
  .cs-block__media--gallery[data-cols="2"]:has(.is-vertical + .is-vertical)
    > .cs-block__grid-item.is-vertical,
  .cs-block__media--gallery[data-cols="2"]:has(.is-vertical:nth-child(2))
    > .cs-block__grid-item.is-vertical {
    grid-column: auto;
    grid-row: auto;
    aspect-ratio: 9 / 16;
    height: auto;
    max-height: 70vh;
    align-self: start;
  }

  /* ─── B) NO-VERTICAL — hero bento ───────────────────────────── */
  .cs-block__media--gallery:not(:has(.is-vertical)) {
    grid-template-columns: repeat(6, minmax(0, 1fr));
    grid-auto-flow: row;
    grid-auto-rows: auto;
    column-gap: 18px;
    row-gap: 28px;
  }
  /* Default cell: 16:10. */
  .cs-block__media--gallery:not(:has(.is-vertical)) > .cs-block__grid-item {
    grid-column: span 2;
    grid-row: auto;
    aspect-ratio: 16 / 10;
    height: auto;
  }
  .cs-block__media--gallery:not(:has(.is-vertical)) > .cs-block__grid-item.is-square {
    grid-column: span 2;
    aspect-ratio: 1 / 1;
  }
  /* 2 items, no vertical: equal halves at 16:10. */
  .cs-block__media--gallery[data-cols="2"]:not(:has(.is-vertical)) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
  .cs-block__media--gallery[data-cols="2"]:not(:has(.is-vertical)) > .cs-block__grid-item {
    grid-column: auto;
    aspect-ratio: 16 / 10;
  }
  /* 3+ items, no vertical: first item is hero spanning full width. */
  .cs-block__media--gallery:not([data-cols="2"]):not(:has(.is-vertical))
    > .cs-block__grid-item:first-child {
    grid-column: span 6;
    aspect-ratio: 16 / 9;
  }
  /* 3 items, no vertical: two siblings span 3 each at 16:9. */
  .cs-block__media--gallery[data-cols="3"]:not(:has(.is-vertical))
    > .cs-block__grid-item:not(:first-child) {
    grid-column: span 3;
    aspect-ratio: 16 / 9;
  }
  /* 4+ items, no vertical: siblings span 2 each (3 per row) at 16:10. */
  .cs-block__media--gallery[data-cols="4"]:not(:has(.is-vertical))
    > .cs-block__grid-item:not(:first-child) {
    grid-column: span 2;
    aspect-ratio: 16 / 10;
  }
}

/* ---------- Mobile slider for media-heavy blocks (<1024px) ----------
 * On narrow screens, a 5-image gallery becomes a tall stacked column
 * that's exhausting to scroll. Replace the .cs-block__media--gallery
 * grid with a horizontal swiper for 3+ items. Markup stays the same;
 * we just toggle the layout via CSS + a helper class added by JS at
 * runtime (so the bento desktop view doesn't pay the swiper init cost
 * unnecessarily). */
@media (max-width: 1023.98px) {
  .cs-block__media--gallery.cs-block__media--mobile-slider {
    display: block;
    padding-bottom: 28px;
    overflow: visible;
  }
  .cs-block__media--gallery.cs-block__media--mobile-slider .swiper-wrapper {
    align-items: stretch;
  }

  /* Uniform aspect ratio across ALL slides — including videos. Mixed-
   * orientation galleries (which is the common case in case studies)
   * read as one cohesive set instead of a parade of variable-height
   * cards that bounce the layout on swipe. We pick 4:3 because it sits
   * comfortably between 1:1 (squares) and 16:9 (landscapes), so most
   * content fits without ugly cropping.
   *
   * The orient classes (is-vertical / is-square) are intentionally
   * IGNORED here — letting them through would re-introduce the
   * variable-height swipe rhythm we're trying to eliminate. Tap-to-
   * lightbox still shows full-resolution dimensions for any image. */
  .cs-block__media--gallery.cs-block__media--mobile-slider .cs-block__grid-item,
  .cs-block__media--gallery.cs-block__media--mobile-slider .cs-block__grid-item.is-vertical,
  .cs-block__media--gallery.cs-block__media--mobile-slider .cs-block__grid-item.is-square {
    width: auto;
    aspect-ratio: 4 / 3;
    height: auto;
  }

  /* Videos (YouTube/Vimeo iframes + native <video>) get cover/center
   * fill instead of `contain`, so they fill the slide cleanly. Native
   * videos crop edges; iframe embeds aren't affected by object-fit
   * (they have no intrinsic content), but the iframe itself is sized
   * 100% × 100% by .cs-block__grid-item img/video/iframe at line 1683
   * so it fills the slide regardless. */
  .cs-block__media--gallery.cs-block__media--mobile-slider
    .cs-block__grid-item--video video {
    object-fit: cover;
  }
  .cs-block__media--gallery.cs-block__media--mobile-slider
    .swiper-pagination {
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 6px;
  }
  .cs-block__media--gallery.cs-block__media--mobile-slider
    .swiper-pagination-bullet {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: rgb(var(--rgb-ink) / 0.2);
    transition: background-color 0.3s ease, width 0.3s ease;
  }
  .cs-block__media--gallery.cs-block__media--mobile-slider
    .swiper-pagination-bullet-active {
    background: var(--c-primary);
    width: 22px;
    border-radius: 999px;
  }
}

/* ============================================================
   PHASE 4 — Related case studies cross-link
   Card-style swiper before the CTA. Mirrors the .ct-card minimal
   pattern (elev background, faint border, primary on hover) so it
   reads as part of the same design system.
   ============================================================ */
.cs-related {
  background: var(--c-bg);
  /* Tight bottom padding because the .lilac-pcta CTA below has its
   * own generous top padding — avoids stacking two big air gaps. */
  padding-bottom: clamp(40px, 6vw, 80px);
}
.cs-related__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: clamp(28px, 4vw, 48px);
  flex-wrap: wrap;
}
.cs-related__title {
  margin: 0;
  text-align: left;
  justify-content: flex-start;
}
.cs-related__nav {
  display: flex;
  gap: 10px;
  flex: 0 0 auto;
}
.cs-related__navbtn {
  width: 46px;
  height: 46px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--c-bg-elev);
  border: 1px solid var(--c-border);
  color: var(--c-ink);
  cursor: pointer;
  transition:
    background-color 0.28s ease,
    border-color 0.28s ease,
    color 0.28s ease,
    transform 0.28s ease;
}
.cs-related__navbtn:hover {
  background: var(--c-primary);
  border-color: var(--c-primary);
  color: #fff;
  transform: translateY(-2px);
}
.cs-related__navbtn svg {
  width: 18px;
  height: 18px;
}
/* Swiper disabled-state (when at the start/end of the slider) */
.cs-related__navbtn.swiper-button-disabled {
  opacity: 0.35;
  cursor: not-allowed;
  pointer-events: none;
}

/* The swiper container — overflow visible so the next slide peeks
 * cleanly past the edge of .cs-section__inner. */
.cs-related__slider {
  overflow: visible;
}
.cs-related__slide {
  height: auto;
}

.cs-related__card {
  display: flex;
  flex-direction: column;
  height: 100%;
  text-decoration: none;
  background: var(--c-bg-elev);
  border: 1px solid var(--c-border);
  border-radius: 22px;
  overflow: hidden;
  isolation: isolate;
  color: var(--c-ink);
  transition:
    transform 0.36s cubic-bezier(0.2, 0.85, 0.2, 1),
    border-color 0.36s ease,
    box-shadow 0.36s ease;
  will-change: transform;
}
.cs-related__card:hover {
  transform: translateY(-6px);
  border-color: rgb(var(--rgb-primary) / 0.4);
  box-shadow: 0 30px 60px -28px rgb(var(--rgb-primary) / 0.45);
}
.cs-related__media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: rgb(var(--rgb-ink) / 0.04);
}
.cs-related__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.7s cubic-bezier(0.2, 0.85, 0.2, 1);
}
.cs-related__card:hover .cs-related__media img {
  transform: scale(1.05);
}
.cs-related__badge {
  position: absolute;
  top: 12px;
  left: 12px;
  display: inline-flex;
  align-items: center;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgb(var(--rgb-bg) / 0.92);
  border: 1px solid var(--c-border);
  color: var(--c-ink);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.cs-related__copy {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: clamp(16px, 2vw, 22px);
  flex: 1;
}
.cs-related__name {
  margin: 0;
  font-size: clamp(1.05rem, 1.5vw, 1.25rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--c-ink);
  transition: color 0.28s ease;
}
.cs-related__card:hover .cs-related__name {
  color: var(--c-primary);
}
.cs-related__client {
  font-size: 0.82rem;
  color: var(--c-muted);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.cs-related__cta {
  margin-top: auto;
  padding-top: 14px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--c-primary);
  transition: gap 0.28s ease;
}
.cs-related__cta svg {
  width: 16px;
  height: 16px;
}
.cs-related__card:hover .cs-related__cta {
  gap: 12px;
}

/* RTL: flip the cta arrow and badge position */
html[dir="rtl"] .cs-related__title {
  text-align: right;
  justify-content: flex-end;
}
html[dir="rtl"] .cs-related__nav {
  flex-direction: row-reverse;
}
html[dir="rtl"] .cs-related__cta svg {
  transform: scaleX(-1);
}
html[dir="rtl"] .cs-related__badge {
  left: auto;
  right: 12px;
}

/* Below 600px the head wraps; nav buttons drop below the title */
@media (max-width: 599.98px) {
  .cs-related__head {
    flex-direction: column;
    align-items: flex-start;
  }
  .cs-related__nav {
    align-self: flex-end;
  }
}

/* Static fallback — used when there's only ONE related case study, so
 * a swiper would be overkill (a 1-slide carousel feels broken). The
 * single card stays centered and capped at a comfortable width. */
.cs-related__static {
  display: flex;
  justify-content: flex-start;
  max-width: 480px;
}
.cs-related--single .cs-related__head {
  margin-bottom: clamp(20px, 3vw, 32px);
}

/* ============================================================
   PHASE 4 — RTL audit pass
   Sweeps the Phase 1-4 additions that defaulted to LTR-only flex/
   spacing rules so they read correctly under html[dir="rtl"].
   ============================================================ */

/* ---------- Eyebrow ---------- */
/* The eyebrow chip uses `gap` + `align-items`, which are direction-
 * neutral, so the dot+label order naturally reverses in RTL. Nothing
 * to override. The centered variant (Concept/Campaign/Results) also
 * stays centered — no change needed. */

/* ---------- Quick-stats strip ---------- */
/* `.cs-quickstats__list` is `flex` row with `justify-content: flex-start`.
 * In RTL the start is the right edge, which is exactly what we want. The
 * dot-label-value order inside each item remains correct because each
 * stat is a baseline-aligned mini-flexbox of 3 spans. */

/* ---------- Campaign cards ---------- */
/* The card head puts icon → label in row order. In RTL we want
 * label → icon visually but the SOURCE order should stay (icon, label)
 * for reading order. Reverse only the visual flow. */
html[dir="rtl"] .cs-campaign__card-head {
  flex-direction: row-reverse;
}
/* Single-value chip styling is direction-neutral; multi-chip wrapping
 * uses `flex-wrap` with `gap`, which mirrors automatically. */

/* ---------- Bento gallery ---------- */
/* CSS Grid auto-flow respects writing-mode. The :has(.is-vertical)
 * promotion rules use `grid-column: span N`, which is direction-
 * relative — first item in RTL = right edge. No override needed. */

/* ---------- Mobile gallery slider ---------- */
/* Swiper handles RTL internally when `dir="rtl"` is set on the
 * .swiper element. Our pagination uses `left: 50%; transform:
 * translateX(-50%)` which centers regardless of direction. The
 * pagination bullet active-state uses `width: 22px` with no
 * directional border-radius, so it reads identically in RTL. */

/* ---------- Testimonial ---------- */
/* Card edge-rule and quote-mark already RTL-flipped above (lines
 * 1369-1374). The .cs-testimonial__attr row uses `display: flex`
 * with `gap` — naturally mirrors. Avatar order (avatar → text)
 * becomes (text ← avatar) in RTL, which is correct. */

/* ---------- Related case studies ---------- */
/* Head + nav direction-flip already covered above (lines 2711-2724).
 * One more thing — the `.cs-related__cta` span uses gap which mirrors,
 * but the arrow SVG ALSO needs to face left in RTL. Already handled
 * (line 2718). The card scale/hover effects are direction-neutral. */


