/* ==========================================================================
   Carousel

   The track is a native scroll-snap row. It is swipeable and keyboard
   scrollable with no JavaScript at all; carousel.js only adds the arrows,
   the progress bar and mouse dragging. Nothing here depends on script having
   run, so a failed asset degrades to a plain horizontal scroller rather than
   an empty section.
   ========================================================================== */

.carousel {
  position: relative;
  display: grid;
  gap: var(--space-4);
}

/* Flex, not grid.
   `grid-auto-columns: minmax(0, …)` looked right but a grid track may shrink
   below its minmax minimum to fit the container, so all thirteen specialties
   compressed into one screen instead of overflowing — the carousel had
   nothing to scroll. Flex items with `flex: 0 0 <width>` refuse to shrink,
   which is exactly the behaviour a track needs. */
.carousel__track {
  display: flex;
  gap: var(--space-4);
  padding: 4px;
  margin: 0;
  list-style: none;

  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 4px;

  /* The native bar is redundant next to the progress indicator and adds a
     stripe under every carousel on Windows. */
  scrollbar-width: none;
}

.carousel__track::-webkit-scrollbar {
  display: none;
}

.carousel__track:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 4px;
  border-radius: var(--radius-lg);
}

.carousel__slide {
  flex: 0 0 min(78vw, 300px);
  scroll-snap-align: start;
  min-inline-size: 0;
}

@media (min-width: 700px) {
  .carousel__slide {
    flex-basis: calc((100% - var(--space-4) * 2) / 3);
  }
}

@media (min-width: 1100px) {
  .carousel--wide .carousel__slide {
    flex-basis: calc((100% - var(--space-4) * 3) / 4);
  }

  .carousel--portrait .carousel__slide {
    flex-basis: calc((100% - var(--space-4) * 4) / 5);
  }
}

/* --- Reviews ---------------------------------------------------------------
   WHOLE CARDS ONLY. Three across on a desktop, two on a tablet, and never a
   card cut by the edge of the row.

   This used to size the cards at 36% so the next one always peeked, on the
   reasoning that a visible sliver advertises that the row scrolls. A
   percentage does not stay a sliver, though: the wider the screen the wider
   the overflow, and on a 1920px viewport three 36% cards plus two gaps
   overshot the container by roughly 200px — the third card was cut in half.
   The client reported it twice, first as cards being cut and then as a
   decision: three cards, no partial fourth.

   Nothing is lost by dropping the peek. The arrows and the progress bar
   appear whenever the track overflows, which it does with more cards than
   fit; and when everything already fits, a carousel that advertises hidden
   content it does not have is worse than one that sits still.

   Mobile keeps its 82vw card. That is a single-column layout where the
   next card starting at the screen edge is the normal swipe affordance, not
   a card clipped inside a row of three.
   ------------------------------------------------------------------------ */

.carousel--reviews .carousel__slide {
  flex-basis: min(82vw, 340px);
}

@media (min-width: 700px) {
  .carousel--reviews .carousel__slide {
    flex-basis: calc((100% - var(--space-4)) / 2);
  }
}

@media (min-width: 1100px) {
  .carousel--reviews .carousel__slide {
    flex-basis: calc((100% - var(--space-4) * 2) / 3);
  }
}

/* Dragging: suppress the text-selection ghost and show the grabbing cursor. */
.carousel.is-dragging .carousel__track {
  cursor: grabbing;
  user-select: none;
  scroll-snap-type: none;
}

/* --- Controls -------------------------------------------------------------- */
/* Hidden until the track actually overflows — arrows that cannot move are
   noise, and disabled-looking chrome reads as broken. */

.carousel__controls {
  display: none;
  align-items: center;
  gap: var(--space-3);
}

.carousel.is-scrollable .carousel__controls {
  display: flex;
}

.carousel__progress {
  flex: 1;
  block-size: 3px;
  border-radius: 999px;
  background: var(--color-border);
  overflow: hidden;
}

.carousel__progress-bar {
  display: block;
  block-size: 100%;
  inline-size: 100%;
  border-radius: inherit;
  background: var(--color-accent);
  transform-origin: left center;
  transform: scaleX(0.08);
  transition: transform var(--transition-fast);
}

.carousel__button {
  inline-size: 44px;
  block-size: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast),
    transform var(--transition-fast), opacity var(--transition-fast);
}

.carousel__button:hover:not(:disabled) {
  border-color: var(--color-accent);
  background: var(--color-accent);
  color: var(--color-text-invert);
  transform: translateY(-1px);
}

.carousel__button:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Arrows drawn in CSS: no icon font, no SVG request, and they inherit
   colour from the button. */
.carousel__button::before {
  content: '';
  inline-size: 9px;
  block-size: 9px;
  border-block-start: 2px solid currentColor;
  border-inline-end: 2px solid currentColor;
}

.carousel__button--prev::before {
  transform: rotate(-135deg);
  margin-inline-start: 3px;
}

.carousel__button--next::before {
  transform: rotate(45deg);
  margin-inline-end: 3px;
}

/* On an inverted section the controls have to invert too, or they vanish. */
.section--ink .carousel__button,
.carousel--invert .carousel__button {
  border-color: rgb(255 255 255 / 0.28);
  background: transparent;
  color: var(--color-text-invert);
}

.section--ink .carousel__progress,
.carousel--invert .carousel__progress {
  background: rgb(255 255 255 / 0.18);
}

@media (prefers-reduced-motion: reduce) {
  .carousel__progress-bar,
  .carousel__button {
    transition: none;
  }
}
