/* =========================================================================
   Carousel — a horizontal, touch-first slide track.

   The track is a real scroll container with CSS scroll-snap. That is the
   whole trick: native scrolling already gives momentum, rubber-banding,
   trackpad gestures, and a scrollbar that matches the platform — all of it
   tuned by the OS and impossible to match with a JS drag emulation. The
   script only adds arrows, dots and keyboard; it never moves the track by
   hand except through scrollTo.

   Deliberately NOT click-to-zoom. Slides are content, not thumbnails; the
   lightbox pattern lives elsewhere.

   Behaviour: assets/js/carousel.js. Markup: carousel() in scripts/study.mjs.
   ========================================================================= */

.car { margin: 0; position: relative; }

.car__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-s); margin-bottom: var(--space-xs); }
.car__title { font-size: 16px; font-weight: 600; color: var(--body-text, var(--ink)); }
.car__count {
  font-size: 12px; font-weight: 600; letter-spacing: .08em;
  color: var(--ink-faint); font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* --------------------------------- track -------------------------------- */
.car__track {
  /* The track is a <ul>. Left unreset, the UA's 40px padding-left offsets
     every slide, which puts slide one's snap position at scrollLeft 40 and
     leaves a dead 0-40 band the carousel can scroll into but never snap to —
     so the prev arrow never reaches its disabled state. */
  margin: 0; padding: 0; list-style: none;
  display: flex; gap: var(--car-gap, 20px);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* Stated rather than left to the flex default, because it is the thing that
     makes the strip one height: every slide takes the tallest one's height,
     and a shorter slide centres its artwork in the space instead of sitting
     against the top edge. Without it the track's height changes at each snap,
     which pushes everything below the carousel up and down as the reader
     swipes. */
  align-items: stretch;
  /* The track bleeds to the page edge on small screens so a slide can be
     half-visible at the margin — the affordance that says "this scrolls".
     Padding, not margin, so the snap positions stay inside the padding box. */
  padding-bottom: var(--space-3xs);
  /* platform scrollbar is redundant next to the dots, and on desktop it sits
     awkwardly under the slides */
  scrollbar-width: none;
}
.car__track::-webkit-scrollbar { display: none; }

.car__slide {
  list-style: none;
  flex: 0 0 var(--car-slide, 100%);
  min-width: 0;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  /* The slide is stretched to the tallest of its siblings, so it is a column
     that centres what it holds. A slide shorter than the strip then floats in
     the middle of its share rather than hanging from the top — which is what
     makes a mixed set of proportions read as one strip. */
  display: flex; flex-direction: column; justify-content: center;
}
/* `always` matters on touch: without it a fast flick skates past several
   slides at once, which makes a demo reel feel like it is being skipped. */

/* THE CHILD COMBINATORS ARE LOAD-BEARING. A video slide is rendered by the
   video component, so it contains a `<video>` nested inside `.vid__frame`
   which is absolutely positioned and sized by that component. A descendant
   selector here would reach in and re-size it — `height: auto` and a second
   `aspect-ratio` fighting the frame's own. These rules are for a slide's own
   direct media only; anything the video component owns, it keeps. */
.car__slide figure { margin: 0; display: block; }
/* An enlargeable slide wraps its image in the same anchor `media()` uses, so
   `> a > img` is spelled out beside `> img`. It is listed rather than relaxed
   to a descendant selector for the reason above: the video component's own
   <video> must stay out of reach. The anchor is the only element that can ever
   sit between a slide's figure and its image. */
.car__slide > figure > img, .car__slide > figure > video,
.car__slide > figure > a > img { margin: 0; display: block; }
.car__slide > figure > img, .car__slide > figure > video,
.car__slide > figure > a > img {
  width: 100%; height: auto; border-radius: var(--radius-s);
  background: var(--line);
}
/* The link is a plain pass-through box; the image inside it keeps the radius
   and the sizing, and the hover lift comes from enlarge.css.

   `position: relative` is not cosmetic. The link ends with a visually-hidden
   "(view larger)" span, and that span is `position: absolute`. With no
   positioned ancestor inside the slide it resolves against a containing block
   far outside the track, and the browser counts the result as document width:
   measured on this page, 1050px of horizontal page scroll appearing the moment
   the first carousel slide became enlargeable. Making the link the containing
   block keeps the span inside the slide it belongs to. */
.car__slide > figure > a {
  display: block; position: relative;
  border-radius: var(--radius-s);
}
/* Optional shared aspect ratio. Slides of mixed proportions make the track
   ragged and the dots feel arbitrary, because each snap lands on a different
   height. Setting `ratio` in the content crops them all to one shape — and a
   video slide gets the same ratio handed to its frame instead. */
/* `cover` is the default because it fills the slot, and most slides are close
   enough to the shared ratio that the crop costs nothing. A slide whose own
   proportions are far from it sets `"fit": "contain"` in the content and is
   shown whole instead, letterboxed onto its own `bg`. Per slide rather than
   per strip: the choice belongs to the artwork, not to the carousel. */
.car[style*="--car-ratio"] .car__slide > figure > img,
.car[style*="--car-ratio"] .car__slide > figure > video,
.car[style*="--car-ratio"] .car__slide > figure > a > img {
  aspect-ratio: var(--car-ratio); height: auto;
  object-fit: var(--car-fit, cover);
}
/* The component's own figure is the slide's media, so it carries no margin.

   It keeps the VIDEO component's radius rather than the strip's. The override
   that used to sit here set `--radius-s` on the frame alone and left the clip
   wrapper inside it at the larger value, so an 8px frame was clipping an 18px
   corner and the wrapper's curve sat outside the frame's — visible on both
   Convert Assist email slides. A video is a video wherever it is shown; the
   one radius now lives on `.vid` in video.css. */
.car__slide .vid { margin: 0; }
.car__cap {
  margin-top: var(--space-xs);
  font-size: var(--type-caption-size); line-height: var(--type-caption-line);
  color: var(--ink-faint);
}

/* --------------------------------- arrows ------------------------------- */
.car__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  z-index: 2;
  width: 44px; height: 44px; border-radius: var(--radius-round);
  display: grid; place-items: center;
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); cursor: pointer;
  box-shadow: var(--shadow-raised);
  transition: opacity .2s ease, box-shadow .2s ease;
}
.car__nav:hover { box-shadow: var(--shadow-raised-hi); }
.car__nav:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.car__nav--prev { left: -22px; }
.car__nav--next { right: -22px; }
.car__nav[disabled] { opacity: 0; pointer-events: none; }

/* Arrows are a pointer convenience. Touch has the swipe, and on a narrow
   screen they would sit on top of the slides. */
@media (hover: none), (max-width: 640px) {
  .car__nav { display: none; }
}

/* ---------------------------------- dots -------------------------------- */
.car__dots {
  display: flex; justify-content: center; gap: var(--space-2xs);
  margin: var(--space-xs) 0 0; padding: 0; list-style: none;
}
.car__dot {
  width: 8px; height: 8px; padding: 0; border: 0; border-radius: var(--radius-round);
  background: var(--ink); opacity: .22; cursor: pointer;
  transition: opacity .2s ease, transform .2s ease;
}
.car__dot:hover { opacity: .5; }
.car__dot[aria-current="true"] { opacity: 1; transform: scale(1.25); }
.car__dot:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
/* The dot itself is 8px; the hit area has to be a finger, not a pixel. */
.car__dots li { display: grid; place-items: center; min-width: 24px; min-height: 24px; }

/* Announces the current slide to a screen reader; hidden by `.u-sr`. */

@media (prefers-reduced-motion: reduce) {
  .car__track { scroll-behavior: auto; }
  .car__dot, .car__nav { transition: none; }
}
