/* ==========================================================================
   carousel.css — leaf 08's reviews, one at a time.

   THE NO-JS CONTRACT. Every rule in this file is scoped under `html.js` AND
   under a `.carousel*` class that only js/carousel.js ever creates. With
   scripting off, or if the module graph fails, NOTHING here matches and the
   `<ul class="reviews">` keeps exactly the horizontally-snapped scroller
   compose.css gives it. That fallback is already readable and already reaches
   all ten reviews, so it is left completely alone.

   THE SIGNATURE IS BLUR-INTO-FOCUS (motion.css §header). The slide change is a
   crossfade with `filter: blur(N) -> blur(0)` and ZERO TRANSLATE. There is no
   slide, no carousel-track transform, and no horizontal travel of any kind —
   a sliding testimonial would be the one move this whole motion system exists
   to refuse. Out uses --d-leaf-out / --e-fall, in uses --d-leaf-in / --e-lay,
   the same pair the leaf change itself uses, because this IS a leaf change in
   miniature.

   THE BORDER DOES NOT MOVE. compose.css puts a 1px rule around each .review.
   Under the carousel that rule is lifted onto `.carousel__window`, so the
   frame stays crisp and only the quote inside it blurs. Blurring a hairline
   border reads as a smudge, not as focus.

   NO TIMER. There is no auto-advance in this file and none in the JS. The
   client's source video auto-advanced; a testimonial that moves while it is
   being read is hostile, and it would also mean motion no user asked for.
   ========================================================================== */

/* -- 1 ── the region ----------------------------------------------------- */

html.js .about__reviews .carousel{
  display: grid;
  gap: var(--s-4);
  min-width: 0;
  max-width: min(100%, 44rem);
}

/* The frame. One rule, one radius, for the whole carousel — see the header. */
html.js .carousel__window{
  position: relative;
  display: grid;
  min-width: 0;
  padding: clamp(0.875rem, 1.8cqw, 1.5rem);
  border: 1px solid var(--on-rule);
  border-radius: 2px;
}

/* -- 2 ── the list becomes a single cell --------------------------------- */

/* Undo the scroller. Every override here answers a specific declaration in
   compose.css `.reviews`, so nothing is left half-converted. */
html.js .carousel .reviews{
  display: grid;
  grid-auto-flow: row;
  grid-template-columns: minmax(0, 1fr);
  grid-auto-columns: auto;
  gap: 0;
  margin: 0;
  padding: 0;
  overflow: hidden;           /* was overflow-x:auto — nothing scrolls now */
  scroll-snap-type: none;
}

/* All ten stack in the same grid cell, so the frame is always as tall as the
   LONGEST review and changing slide shifts no layout at all. */
html.js .carousel .review{
  --blur: 7px;
  grid-area: 1 / 1;
  border: 0;                  /* the rule moved to .carousel__window */
  padding: 0;
  scroll-snap-align: none;
  gap: var(--s-4);
  opacity: 0;
  visibility: hidden;         /* out of the a11y tree, un-selectable, no tab */
  filter: blur(var(--blur));
  transition:
    opacity    var(--d-leaf-out) var(--e-fall),
    filter     var(--d-leaf-out) var(--e-fall),
    visibility 0s linear var(--d-leaf-out);
}

html.js .carousel .review[data-on]{
  opacity: 1;
  visibility: visible;
  filter: blur(0);
  transition:
    opacity    var(--d-leaf-in) var(--e-lay),
    filter     var(--d-leaf-in) var(--e-lay),
    visibility 0s linear 0s;
}

/* THE LEAF STAYS AUTHORITATIVE. `visibility: visible` on a descendant undoes an
   ancestor's `visibility: hidden`, so without this the active slide would climb
   back into the a11y tree and the tab order while leaf 08 is NOT the live leaf —
   defeating the one mechanism leaf.css uses to hide seven eighths of the page.
   The flip is delayed by leaf.css's own --d-leaf-out so the quote does not pop
   out before the leaf has finished fading. */
html.js .leaf__body:not([data-live]) .carousel .review[data-on]{
  visibility: hidden;
  transition: visibility 0s linear var(--d-leaf-out);
}

/* One quote at a time can carry more size than one of ten in a row could. */
html.js .carousel .review blockquote p{
  font-size: clamp(1.05rem, 1.75cqw, 1.45rem);
  line-height: 1.32;
  text-wrap: pretty;
}

/* -- 3 ── the control bar ------------------------------------------------ */

html.js .carousel__bar{
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}

html.js .carousel__nav{
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

html.js .carousel__arrow{
  appearance: none;
  -webkit-appearance: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--on-rule);
  border-radius: 999px;
  background-color: transparent;
  color: var(--on-plate);
  cursor: pointer;
  transition:
    border-color var(--d-onplate) var(--e-roll),
    background-color var(--d-onplate) var(--e-roll),
    color var(--d-onplate) var(--e-roll);
}

html.js .carousel__arrow svg{
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: square;
  stroke-linejoin: miter;
}
/* One chevron path, mirrored. */
html.js .carousel__arrow--next svg{ transform: scaleX(-1) }

/* --seal-blue is the ONE interactive colour on the site (tokens.css §2.1). */
html.js .carousel__arrow:hover{
  border-color: var(--seal-blue);
  color: var(--seal-blue);
}
html.js .carousel__arrow:active{
  border-color: var(--seal-blue);
  background-color: var(--seal-blue);
  color: var(--plate-foam);
}
/* Leaf 08 is kraft, but the component must survive being reused on carbon,
   where seal-blue is invisible. */
html.js [data-plate="carbon"] .carousel__arrow:hover{
  border-color: var(--focus-dark);
  color: var(--focus-dark);
}

/* -- 4 ── the ten dots --------------------------------------------------- */

html.js .carousel__dots{
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0;                     /* the 24px targets already carry the rhythm */
  margin: 0;
  padding: 0;
}

/* 24 x 24 is the WCAG 2.2 (2.5.8) minimum target; the visible dot is 7px. */
html.js .carousel__dot{
  appearance: none;
  -webkit-appearance: none;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background-color: transparent;
  color: inherit;
  cursor: pointer;
}

html.js .carousel__dot > i{
  display: block;
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background-color: transparent;
  box-shadow: inset 0 0 0 1px var(--on-plate);
  opacity: 0.45;
  transition:
    opacity var(--d-onplate) var(--e-roll),
    background-color var(--d-onplate) var(--e-roll),
    transform var(--d-onplate) var(--e-roll);
}

html.js .carousel__dot:hover > i{ opacity: 1 }

html.js .carousel__dot[aria-current="true"] > i{
  background-color: var(--on-plate);
  opacity: 1;
  transform: scale(1.3);      /* monotone, zero overshoot */
}

/* -- 5 ── reduced motion: instant swap, no blur -------------------------- */

@media (prefers-reduced-motion: reduce){
  html.js .carousel .review,
  html.js .carousel .review[data-on]{
    transition: none !important;
    filter: none !important;
  }
  html.js .carousel__arrow,
  html.js .carousel__dot > i{
    transition: none !important;
    transform: none !important;
  }
  /* The active dot must still be identifiable without the scale. */
  html.js .carousel__dot[aria-current="true"] > i{
    background-color: var(--on-plate);
    opacity: 1;
  }
}

/* -- 6 ── forced colours ------------------------------------------------- */

@media (forced-colors: active){
  html.js .carousel__window,
  html.js .carousel__arrow{ border-color: CanvasText }
  html.js .carousel__dot > i{
    box-shadow: inset 0 0 0 1px CanvasText;
    opacity: 1;
  }
  html.js .carousel__dot[aria-current="true"] > i{ background-color: Highlight }
}
