/* Layout */

.cs-hero-bg {
  background: var(--color-bg-secondary);
}

.container-inner.cs-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--grid-gutter);
}

.cs-content {
  grid-column: 1 / span 6;
}

/* Project body section width per tokens.md: columns 4-9 (6 columns,
   centered). Scoped to .cs-section (and .cs-final-outer, which uses the
   same body-content column) so the hero row (which keeps its own
   left-text/right-image split) is unaffected. */
.cs-section .cs-content,
.cs-final-outer .cs-content,
.cs-other-projects-outer .cs-content {
  grid-column: 4 / span 6;
}

/* Hero */

.cs-hero {
  padding-top: var(--space-hero);
  padding-bottom: var(--space-section);
  align-items: center;
}

.cs-hero .cs-content > * + * {
  margin-top: var(--space-small);
}

.cs-hero .subheading {
  max-width: 640px;
}

/* align-self:center (not the grid's default stretch): when the image's
   own rendered height is shorter than the row (driven by .cs-content's
   text), it should center in that extra space rather than sit stretched
   with the actual picture pinned to the top and empty space below it. */
.cs-hero-image {
  grid-column: 7 / span 6;
  align-self: center;
}

.cs-hero-image img {
  width: 100%;
  height: auto;
  display: block;
}

.cs-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-medium);
  margin-top: var(--space-medium) !important;
}

.cs-stat-number {
  font-size: var(--h2-size);
  font-weight: var(--h2-weight);
  line-height: var(--h2-line-height);
}

.cs-stat-label {
  font-size: var(--body-small-size);
  color: var(--color-text-secondary);
}

/* Section rhythm */

.cs-section {
  padding-top: var(--space-section);
  padding-bottom: var(--space-section);
  border-top: var(--border-default);
}

.cs-section:first-of-type {
  border-top: none;
}

.cs-section h2 {
  margin-bottom: var(--space-small);
}

.cs-body p,
.cs-body ul {
  color: var(--color-text-secondary);
  max-width: 623px;
}

.cs-body > * + * {
  margin-top: var(--space-small);
}

.cs-body ul {
  padding-left: 20px;
}

.cs-body strong {
  color: var(--color-text-primary);
}

.text-danger {
  color: var(--color-text-danger);
}

/* Accordion */

.accordion {
  background: var(--color-bg-secondary);
  border: var(--border-default);
  border-radius: var(--radius-card);
}

.accordion + .accordion {
  margin-top: var(--space-small);
}

.accordion-summary {
  display: flex;
  align-items: center;
  gap: var(--space-xsmall);
  padding: var(--space-small);
  cursor: pointer;
  list-style: none;
}

.accordion-summary::-webkit-details-marker {
  display: none;
}

.accordion-icon {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  display: block;
}

.accordion-title {
  flex: 1;
  font-size: var(--body-size);
  font-weight: var(--body-strong-weight);
  line-height: var(--body-line-height);
}

.accordion-chevron {
  flex-shrink: 0;
  transition: transform 0.3s ease-out;
}

/* Driven by .is-open (set by case-study.js), not [open] directly: closing
   a native <details> removes the open attribute synchronously, which
   hides the content before a transition keyed off [open] can play (it
   animates open fine, then snaps shut instantly on close, and that
   broken close leaves the next open broken too). Keeping `open` true
   until the collapse transition actually finishes -- see case-study.js
   -- avoids that; the chevron is keyed off the same class so both
   animate in sync. */
.accordion.is-open .accordion-chevron {
  transform: rotate(180deg);
}

.accordion-content {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s ease-out;
}

.accordion.is-open .accordion-content {
  grid-template-rows: 1fr;
}

.accordion-content-inner {
  overflow: hidden;
  padding: 0 var(--space-small) var(--space-small);
}

.accordion-content-inner p + p {
  margin-top: var(--space-small);
}

.accordion-content-inner ol {
  margin: var(--space-small) 0 0;
  padding-left: 20px;
  color: var(--color-text-secondary);
}

.accordion-content-inner video,
.accordion-content-inner img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  margin-top: var(--space-small);
}

/* My activities */

.my-activities {
  margin-top: var(--space-large);
}

.my-activities-label {
  margin-bottom: var(--space-xsmall);
}

.my-activities ul {
  margin: 0;
  padding-left: 20px;
  color: var(--color-text-secondary);
}

/* Journey stepper */

.journey-label {
  color: var(--color-text-secondary);
  margin-bottom: var(--space-xsmall);
}

/* For a label immediately followed by an .annotated-image: that wrapper
   always carries its own margin-top: var(--space-small), and the PNGs in
   this pattern bake in their own ~var(--space-small) of transparent drop-
   shadow padding above the "real" screenshot content -- two separate
   mechanisms (a CSS margin and actual image pixels) that don't collapse
   with each other, so they stack and roughly double the gap. Rather than
   touching .annotated-image's margin-top (shared by every annotated
   image, not just labeled ones), this cancels the label side: a negative
   margin-bottom equal to -space-small collapses against the wrapper's
   +space-small margin-top to net ~0 CSS-side gap, leaving the image's own
   baked-in padding to read as the sole (and correctly-sized) breathing
   room. Reusable for the same label-before-shadowed-image pattern in
   future case studies. */
.label--tight {
  margin-bottom: calc(-1 * var(--space-small));
}

.journey-stepper {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.journey-step {
  padding: 6px 10px;
  border-radius: var(--radius-chip);
  background: var(--color-bg-secondary);
  font-size: var(--body-small-size);
  color: var(--color-text-secondary);
  white-space: nowrap;
}

.journey-step.is-active {
  background: var(--color-bg-light-blue);
  color: var(--color-text-primary);
  font-weight: var(--body-small-strong-weight);
}

/* Annotated images (arrow baked into the asset; overflow left open
   deliberately so any bleed isn't clipped) */

.annotated-image {
  overflow: visible;
  margin-top: var(--space-small);
}

.annotated-image img {
  max-width: 100%;
  display: block;
}

/* Per-image target width + left offset, measured directly from Figma --
   do not recalculate the values themselves. These images are
   intentionally wider than their column at desktop; the arrow bleeds
   past the content edge by design (confirmed: images must be able to
   exceed the 6-column width, not just fill it).

   Because the bleed target (Npx) exceeds the .cs-content column's own
   width, min(Npx, 100%) on the <img> alone can't work -- 100% resolves
   against the column, which never reaches Npx even at full desktop
   width. The WRAPPER instead gets a width that can legitimately exceed
   the column: min(Npx, <the true safe max given the column's actual
   left position>). That safe max is derived directly from the grid
   math (content left-edge = 106px + 0.25 * viewport width, valid once
   container-inner is unclamped, i.e. viewport < 1920px), solved so the
   wrapper's right edge never passes the viewport's right edge:
     max safe width = viewport - contentLeft = 0.75 * viewport - 106px
   Using 75vw - 130px (a 24px safety margin folded in) keeps this
   continuous with no arbitrary threshold -- it's the exact geometry,
   not an approximated breakpoint.

   This only applies at desktop (>=901px) because .cs-content's own
   column geometry is structurally different at mobile (full width,
   grid-column: 1/-1) -- reusing the site's one foundational breakpoint,
   not introducing a new image-specific one; below it, the wrapper needs
   no override since the column already is the full available width.

   Offset uses transform: translateX(-X%), where X% = offset-px / the
   Figma reference width the offset was measured against -- NOT the
   source PNG's raw pixel dimensions (these are 2x exports; e.g.
   graphic-cor-4.png is 1662px wide raw for an 831px target, so using
   naturalWidth would halve the intended offset). translateX(%) is
   relative to the element's own rendered width, so this scales down
   proportionally as the image shrinks, automatically. */

.annotated-image--cor-4 img,
.annotated-image--cor-6 img,
.annotated-image--cor-7 img {
  width: 100%;
  height: auto;
}

.annotated-image--cor-4 img {
  transform: translateX(-3.369%); /* 28 / 831 */
}

.annotated-image--cor-6 img {
  transform: translateX(-3.483%); /* 28 / 804 */
}

.annotated-image--cor-7 img {
  transform: translateX(-10.907%); /* 95 / 871 */
}

@media (min-width: 901px) {
  .annotated-image--cor-4 {
    width: min(831px, calc(75vw - 130px));
  }

  .annotated-image--cor-6 {
    width: min(804px, calc(75vw - 130px));
  }

  .annotated-image--cor-7 {
    width: min(871px, calc(75vw - 130px));
  }
}

/* graphic-cor-8.png: deliberate full-bleed exception, not a pattern to
   reuse. At 1586px it's wider than the site's own 1520px max container,
   so it can never sit "in column" the way the other three do -- instead
   it breaks out of .cs-content entirely and centers on the viewport,
   using the same min()-based width logic within that breakout
   container. Desktop only (>=901px): mobile swaps to a dedicated,
   nearly-square (682x684) export via <picture>/<source> in the markup,
   which sits in-column like the other annotated images instead -- this
   breakout formula is sized for the wide desktop asset and doesn't apply
   to it. */

.annotated-image--cor-8 img {
  display: block;
  margin: 0 auto;
  width: 100%;
  height: auto;
}

@media (min-width: 901px) {
  .annotated-image--cor-8 {
    width: calc(100vw - 48px);
    margin-left: calc(50% - 50vw + 24px);
  }

  .annotated-image--cor-8 img {
    width: min(1586px, 100%);
  }
}

/* Reusable caption class for future annotated images that do NOT
   bake the caption text into the asset (unused on this page for now --
   both graphic-cor-4.png and graphic-cor-6.png include the caption). */

.annotation-caption {
  font-family: var(--font-display);
  font-size: var(--display-size);
  font-weight: var(--display-weight);
  line-height: var(--display-line-height);
  color: var(--color-text-primary);
}

/* Tag pill variants (case-study specific; base .tag-pill is in components.css) */

.tag-pill--default {
  border: none;
  background: var(--color-bg-secondary);
  color: var(--color-text-secondary);
}

.tag-pill--filled {
  border: none;
  background: var(--color-bg-light-blue);
  color: var(--color-text-primary);
}

/* Opportunity */

.cs-opportunity-image img {
  width: 100%;
  height: auto;
  display: block;
  margin-top: var(--space-small);
}

/* Initiative */

.cs-label-strong {
  margin-bottom: var(--space-xsmall);
}

.cs-initiative-block + .cs-initiative-block {
  margin-top: var(--space-large);
}

/* Test results */

.test-results {
  display: flex;
  gap: var(--space-large);
  flex-wrap: wrap;
  margin-top: var(--space-small);
}

/* Matches the height of the stat number's line-box (h2-size * h2-line-height)
   and top-aligns the icon within it, so the icon and the number sit in
   equal-height boxes above the label -- keeping labels aligned across
   columns regardless of type-scale changes, instead of a hardcoded
   margin-bottom guessed to match. */
.test-result-icon-frame {
  height: calc(var(--h2-size) * var(--h2-line-height));
  display: flex;
  align-items: flex-start;
}

.test-result-icon {
  width: 38px;
  height: 38px;
  display: block;
}

.test-result-label {
  font-size: var(--body-small-size);
  color: var(--color-text-secondary);
  max-width: 170px;
}

/* Risk-level tabs -- reuses the tokens.md Tab card spec directly */

.risk-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-small);
  margin-top: var(--space-small);
}

/* Below this width, 4 single-row tabs no longer fit (measured crossover:
   fits at 1335px, wraps at 1334px) and flex-wrap would pack an uneven
   3-1 split. A 2-column grid replaces the row entirely at that point
   (with a small safety margin above the measured crossover, since the
   exact wrap point is font-metric dependent and can shift a few px
   across engines) and stays a clean 2x2 all the way down through
   mobile, instead of flex-wrap re-balancing unpredictably at each width. */
@media (max-width: 1360px) {
  .risk-tabs {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }
}

.tab-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xsmall);
  flex: 1;
  background: var(--color-bg-primary);
  border: var(--border-default);
  border-radius: var(--radius-card);
  padding: var(--space-small);
  font-family: inherit;
  font-size: var(--body-size);
  color: var(--color-text-primary);
  cursor: pointer;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

.tab-card:hover {
  background: var(--color-bg-secondary);
}

.tab-card.is-selected {
  background: var(--color-bg-secondary-active);
  border: var(--border-active);
}

.tab-card.is-selected:hover {
  background: var(--color-bg-secondary-active);
}

.tab-card-icon {
  width: 64px;
  height: 64px;
  object-fit: contain;
  display: block;
}

.risk-tab-panels {
  margin-top: var(--space-small);
}

.risk-tab-panel img {
  width: 100%;
  height: auto;
  display: block;
}

.risk-tab-panel {
  transition: opacity 0.3s ease-out;
}

.risk-tab-panel.is-entering {
  opacity: 0;
}

.risk-tab-panel[hidden] {
  display: none;
}

/* Pull-quote / signature -- reuses the existing Display token (Caveat,
   24px) directly, same as annotation captions; no new type spec. */

.cs-quote {
  font-family: var(--font-display);
  font-size: var(--display-size);
  font-weight: var(--display-weight);
  line-height: var(--display-line-height);
  color: var(--color-text-primary);
  margin-top: var(--space-small);
}

/* Final takeaway */

.cs-final-outer {
  background: var(--color-bg-secondary);
  padding-top: var(--space-section);
  padding-bottom: var(--space-section);
}

.cs-final-outer h2 {
  margin-bottom: var(--space-medium);
}

.cs-final-block + .cs-final-block {
  margin-top: var(--space-small);
}

/* A block that starts a new sub-section (its own heading, e.g. "Next
   steps") gets the wider space-large gap instead of the space-small
   rhythm used between takeaway points within the same sub-section. */
.cs-final-block + .cs-final-block--subsection {
  margin-top: var(--space-large);
}

.cs-final-block .cs-label-strong {
  margin-bottom: var(--space-xxsmall);
}

/* Scoped to .cs-final-block so this doesn't leak into .cs-body's other
   uses elsewhere on the page. */
.cs-final-block .cs-body > * + * {
  margin-top: var(--space-small);
}

/* Other projects */

.cs-other-projects-outer {
  padding-top: var(--space-section);
  padding-bottom: var(--space-section);
}

.cs-other-projects-outer h2 {
  margin-bottom: var(--space-medium);
}

/* Wrapper, not a single hardcoded card: lets a third card (e.g. Bumble)
   be added later as another .project-card--compact child with no
   restructuring. */
.other-projects-list > .project-card + .project-card {
  margin-top: var(--space-medium);
}

/* Title + CTA only, CTA right-aligned instead of stacked below --
   overrides the homepage card's 12-col grid (media/body split) with a
   simple row layout. The accent-strip ::before and card shell (padding,
   radius, shadow) are inherited unchanged from .project-card. */
.project-card--compact {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-medium);
}

.other-projects-contact {
  color: var(--color-text-secondary);
  margin-top: var(--space-medium);
}

.other-projects-contact a {
  font-weight: var(--body-strong-weight);
  color: var(--color-text-primary);
}

@media (max-width: 900px) {
  .cs-hero {
    padding-top: var(--space-large);
    padding-bottom: var(--space-large);
  }

  .cs-body p,
  .cs-body ul {
    max-width: none;
  }

  /* minmax(0, 1fr), not a bare 1fr: an fr track's implicit minimum is
     "auto" (its items' min-content size), not 0 -- if any item's
     min-content exceeds the available width (e.g. a heading at its
     un-wrapped/longest-word width), the whole single-column track grows
     to fit it and overflows the viewport instead of letting that item's
     own text wrap within the constrained width. minmax(0, 1fr)
     overrides that implicit minimum so the track (and everything in it)
     stays pinned to the true available width -- a real, independent bug
     unrelated to any specific grid item, found while debugging the
     Glovo hero's now-abandoned full-bleed image (its wrapper was
     overflowing mobile by ~40px even with its own explicit
     min-width:0, which traced back to this grid-level cause). */
  .container-inner.cs-grid {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--space-medium);
  }

  .cs-content,
  .cs-section .cs-content,
  .cs-final-outer .cs-content,
  .cs-other-projects-outer .cs-content,
  .cs-hero-image {
    grid-column: 1 / -1;
  }

  .cs-hero-image {
    order: -1;
  }

  .cs-stats {
    gap: var(--space-small) var(--space-medium);
  }

  .cs-hero .subheading {
    max-width: none;
  }

  /* The desktop icon-frame height matches the stat-number's line-box so
     both sit flush with a neighboring stat in the same row (see
     .test-result-icon-frame). At mobile, .test-results items stack into
     their own row instead (flex-wrap), so there's no neighboring stat to
     align against -- that alignment height just reads as an oversized,
     accidental gap. Collapse the frame to the icon's own size and use an
     explicit, deliberate space-small (24px) gap instead. */
  .test-result-icon-frame {
    height: auto;
    margin-bottom: var(--space-small);
  }

  /* .test-results items no longer fit side by side at mobile widths, so
     flex-wrap stacks them into their own rows -- the shared gap then
     doubles as the vertical space between those stacked rows. The base
     rule's space-large (60px) was sized for the horizontal gap between
     side-by-side desktop items; stacked vertically at mobile it read as
     too much. Drop to the next step down the tokens.md spacing scale,
     space-medium (40px), rather than a new hardcoded value. */
  .test-results {
    gap: var(--space-medium);
  }

  .business-objectives {
    grid-template-columns: 1fr;
  }

  /* Same fixed 3-per-row layout as desktop -- the reference doesn't
     restack this at mobile, and 3 columns of ~100px each comfortably fit
     down to the mobile breakpoint. */

  .cs-breakout-8col {
    width: 100%;
    margin-left: 0;
  }

  /* 5bb + 5c stack instead of sitting side by side, per confirmed spec. */
  .usability-tab-media--pair {
    flex-direction: column;
    gap: var(--space-xsmall);
  }

  .usability-tab-toggle {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }

  /* Stack title above CTA (matching the homepage card's own mobile
     button-stacking pattern) instead of cramming both into a row too
     narrow for long titles. */
  .project-card--compact {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-small);
  }

  .project-card--compact .btn-primary {
    width: 100%;
  }
}

/* ===== Glovo case study ===== */

/* Generic one-off 2-column split (e.g. Problem/Solution) -- not a
   tokens.md component, just a reusable layout utility to avoid repeating
   inline flex styles. Stacks at mobile like everything else on the page. */
.cs-two-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-small);
}

.cs-two-col p:not(.body-strong) {
  color: var(--color-text-secondary);
}

@media (min-width: 901px) {
  .cs-two-col {
    flex-direction: row;
    gap: var(--space-medium);
  }

  .cs-two-col > * {
    flex: 1;
  }
}

/* Business objective cards */

.business-objectives {
  display: grid;
  gap: var(--space-small);
  margin-top: var(--space-small);
}

@media (min-width: 901px) {
  .business-objectives {
    grid-template-columns: repeat(3, 1fr);
  }
}

.business-objective-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xsmall);
  text-align: center;
  background: var(--color-bg-secondary);
  border-radius: var(--radius-card);
  padding: var(--space-small);
}

.business-objective-icon {
  width: 40px;
  height: 40px;
  display: block;
}

.business-objective-title {
  font-size: var(--body-size);
  font-weight: var(--body-strong-weight);
  line-height: var(--body-line-height);
  color: var(--color-text-primary);
}

/* Timeline (month bullets) */

.cs-timeline {
  margin-top: var(--space-medium);
}

.cs-timeline-label {
  color: var(--color-text-secondary);
  margin-bottom: var(--space-small);
}

.cs-timeline-row {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-small) var(--space-medium);
}

.cs-timeline-row + .cs-timeline-row {
  margin-top: var(--space-medium);
}

/* Dashed connector: a full-width line behind every bullet at the
   circles' vertical center (20px = half the 40px circle, since the
   circle is always each bullet's first/top child regardless of how many
   lines its description wraps to). The circles' own opaque background
   sits on top (z-index) and masks the segment behind them, so only the
   gaps between circles read as a dashed line -- no per-column math
   needed, and it never carries across a row break since each row gets
   its own independent connector. */
.cs-timeline-row::before {
  content: '';
  position: absolute;
  top: 20px;
  left: 0;
  right: 0;
  border-top: 1px dashed var(--color-border-subtle);
  z-index: 0;
}

.timeline-bullet {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-xsmall);
}

.timeline-bullet-circle {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  background: var(--color-bg-light-green);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--body-size);
  font-weight: var(--body-strong-weight);
  color: var(--color-text-primary);
}

.timeline-bullet-desc {
  font-size: var(--body-small-size);
  font-weight: var(--body-small-weight);
  line-height: var(--body-small-line-height);
  color: var(--color-text-primary);
}

/* Findings (one-off icon-plus-heading list, not a tokens.md component
   per the confirmed scope -- specific to this section only) */

.findings-list {
  margin-top: var(--space-small);
}

.findings-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-xsmall);
}

.findings-item + .findings-item {
  margin-top: var(--space-small);
}

.findings-icon {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  margin-top: 2px;
}

.findings-item-title {
  font-size: var(--body-size);
  font-weight: var(--body-strong-weight);
  line-height: var(--body-line-height);
  color: var(--color-text-primary);
}

.findings-item-text {
  color: var(--color-text-secondary);
  margin-top: var(--space-smallest);
}

/* 8-column breakout (lo-fi mockup, graphic-glovo-2, the 5bb+5c pair
   wrapper): escapes .cs-content's 6-column box to occupy 8 of the
   page's 12 columns instead, centered -- same technique as the Deel
   case study's cor-4/6/7 annotated images (derived from the identical
   container math; verified the constant term against that rule's own
   documented formula, content left-edge = 106px + 0.25vw), generalized
   from a Figma-measured px target to a pure column-count fraction since
   these images just need "8 columns," not a specific offset. Capped via
   min()/max() at each formula's value when viewport hits 1920px
   (container-inner's own max-width point), matching cor-4/6/7's cap. */
.cs-breakout-8col {
  margin-top: var(--space-small);
}

@media (min-width: 901px) {
  .cs-breakout-8col {
    width: min(1005.333px, calc(66.667vw - 274.667px));
    margin-left: max(-128.667px, calc(-8.333vw + 31.333px));
  }
}

/* Framed image: CSS-applied border/shadow/radius for the Glovo mockups
   exported flat (no baked-in effects) -- graphic-glovo-2/5a/5bb/5c/7 and
   3a/3b/4a/4b. Sized to its container's width, not native file
   dimensions, since several of these source PNGs are exported larger
   than their intended render size. */
.framed-image {
  display: block;
  width: 100%;
  height: auto;
  border: var(--border-default);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* Lo-fi mockup (graphic-glovo-1.png) and hi-fi dashboard
   (graphic-glovo-2.png): both sit below an intro paragraph and both use
   the same space-large (60px) gap as every other "next distinct block"
   transition on this page (Business objectives, Timeline, My activities
   all use space-large the same way -- confirmed against those for
   consistency). graphic-glovo-1 alone bakes in its own ~18px of
   transparent shadow padding above the "real" screenshot content (same
   mechanism as Deel's .label--tight, just applied directly to this
   wrapper's margin instead of a preceding label, since a body paragraph
   precedes it here, not a tight label): cancel exactly that amount out
   of the space-large target so the EFFECTIVE visual gap matches
   graphic-glovo-2's plain space-large gap (which has no baked-in padding
   to account for). */
.annotated-image--glovo-1 {
  margin-top: calc(var(--space-large) - 18px);
}

.annotated-image--glovo-2 {
  margin-top: var(--space-large);
}

.annotated-image--glovo-1 img {
  width: 100%;
  height: auto;
  display: block;
}

/* Usability testing tabs (reusable component, instantiated 3x) */

.usability-tabs {
  margin-top: var(--space-small);
}

.usability-tab-toggle {
  gap: var(--space-xsmall);
}

@media (min-width: 901px) {
  .usability-tab-toggle {
    display: flex;
  }

  /* Combined, the two tabs span 100% of the body content column width. */
  .usability-tab-btn {
    flex: 1;
  }
}

.usability-tab-btn {
  padding: var(--space-xsmall) var(--space-medium);
  border-radius: var(--radius-card);
  background: var(--color-bg-secondary);
  border: 1.5px solid transparent;
  font-family: inherit;
  font-size: var(--body-size);
  font-weight: var(--body-weight);
  color: var(--color-text-primary);
  cursor: pointer;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

/* Selected state reuses the Deel case study's Tab card selected style
   exactly (tokens.md "Tab cards" spec: Background secondary-active +
   Border active), rather than the white/black-border combination this
   had before. */
.usability-tab-btn.is-selected {
  background: var(--color-bg-secondary-active);
  border: var(--border-active);
}

.usability-tab-panels {
  margin-top: var(--space-small);
}

.usability-tab-panel {
  transition: opacity 0.3s ease-out;
}

.usability-tab-panel.is-entering {
  opacity: 0;
}

.usability-tab-panel[hidden] {
  display: none;
}

.usability-tab-media {
  display: flex;
}

/* The Product insights pair (5bb+5c) sits side by side within the same
   8-column breakout the standalone mockups use (see .cs-breakout-8col),
   confirmed explicitly -- both images together occupy 8 columns, not
   8 columns each. */
@media (min-width: 901px) {
  .usability-tab-media--pair {
    gap: var(--space-small);
  }
}

/* min-width:0 overrides the flex item default (min-width:auto), which
   for a replaced element like <img> resolves to its natural intrinsic
   width -- without this, flex-shrink can't shrink either image below
   ~670px and they overflow the row instead of splitting it evenly. */
.usability-tab-media--pair .framed-image {
  width: auto;
  min-width: 0;
  flex: 1;
}

.usability-tab-caption {
  margin: var(--space-small) 0 0;
  padding-left: 20px;
  color: var(--color-text-secondary);
}

.usability-tab-caption li + li {
  margin-top: var(--space-xsmall);
}

.usability-tab-caption strong {
  color: var(--color-text-primary);
}

/* Responsive design screenshot (graphic-glovo-7.png): phone-shaped, so
   it stays narrow and centered rather than filling the full content
   column like the desktop mockups above. */
.cs-responsive-frame {
  max-width: 320px;
  margin: var(--space-small) auto 0;
}

/* Impact section: the two stats span the full content column width
   combined, each taking 3 of the page's OWN 12 columns -- since
   .cs-content itself is only 6 of those 12 columns, 3+3 = 6 means each
   stat takes exactly half of .cs-content's width, filling it completely
   combined (not a nested 12-column subdivision of .cs-content, which
   would make each stat a quarter-width instead). Rather than sizing to
   their own content like the default .test-results flex-wrap layout
   used elsewhere (Deel's initiatives, which have 2-3 items of varying
   content width and shouldn't force an even split), this modifier scopes
   the change so Deel's own .test-results usage is untouched. */
.test-results--grid {
  display: grid;
  gap: var(--grid-gutter);
}

@media (min-width: 901px) {
  .test-results--grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.test-results--grid .test-result-label {
  max-width: none;
}

/* Impact section mascot illustration (graphic-glovo-6.png): decorative,
   not tied to any stat. Spans 4 of the page's 12 columns at desktop
   (narrower than .cs-content's own 6-column box, so it fits without
   needing a breakout -- same column-width formula as .cs-breakout-8col,
   just for a 4-column span instead of 8, and capped the same way at
   viewport=1920px). Mobile keeps a flat fallback width. */
.cs-mascot {
  width: 220px;
  height: auto;
  display: block;
  margin-top: var(--space-large);
  margin-inline: auto;
}

@media (min-width: 901px) {
  .cs-mascot {
    width: min(490.667px, calc(33.333vw - 149.333px));
  }
}
