/**
 * Carousel component — visual styling only. The drag/snap/status-bar
 * *behaviour* lives in assets/js/carousel.component.js; see that file's
 * header comment and NOTES.md for the design rationale.
 *
 * Root: .carousel
 *   `-- .carousel__container      flex row: prev arrow, viewport, next arrow
 *        `-- .carousel__content   the native scroll container (viewport)
 *             `-- .carousel__track      flex row of cards + overpull spacers
 *                  `-- .carousel__item[]     the cards themselves
 *   `-- .carousel__page-control   wraps the status bar
 *        `-- .carousel__status-bar        the draggable "scrollbar"
 *             `-- .carousel__status-bar-slider   its indicator
 *
 * All per-component tuning lives in the custom properties below — that's the
 * only block you should need to touch to restyle the carousel.
*/
.carousel {
    display: block;
    /* `.container` is a CSS grid (`.d-grid`) with no explicit column track,
       so the implicit column is auto-sized: without this, the track grows to
       this element's content-based minimum instead of the screen width, and
       everything downstream — clientWidth, maxScroll, snap offsets, drag
       math — gets measured against that oversized box instead of what's
       actually visible. Mirrors the same override on .carousel__content
       one level down, for its own flex context. */
    min-width: 0;

    /* ---- component variables — the knobs, one place, per breakpoint ---- */
    --carousel-card-width: 100%;
    --carousel-arrow-size: 44px;

    /* Base duration for the slide back to a card, tuned for a 200px trip. The
       JS reads it and scales it by distance — short slides run shorter, long
       ones longer — then runs the motion itself, since scrollLeft can't be
       animated by CSS. */
    --carousel-settle-duration: 420ms;
}

@media screen and (min-width: 576px) { /* --breakpoint-sm, see essentials.css */
    .carousel { --carousel-card-width: 60%; }
}

@media screen and (min-width: 768px) { /* --breakpoint-md, see essentials.css */
    .carousel {
        --carousel-card-width: 40%;
        --carousel-arrow-size: 60px;
    }
}

@media screen and (min-width: 1200px) { /* --breakpoint-xl, see essentials.css */
    .carousel { --carousel-card-width: 30%; }
}

/** Lays the prev arrow, viewport and next arrow out side by side. */
.carousel__container {
    display: flex;
    flex-direction: row;
    position: relative;
    width: 100%;
    gap: var(--card-gap);
}

/**
 * Navigation arrows
*/
.carousel__navigation-arrow-left,
.carousel__navigation-arrow-right {
    flex: 0 0 auto;
    width: var(--carousel-arrow-size);
    height: var(--carousel-arrow-size);
    padding: 0;
    border-radius: 100%;
    border: 1px solid currentColor;
    color: light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.3));
    background-color: light-dark(var(--background-light), var(--background-dark));
    align-self: center;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    font: inherit;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    transition: color 150ms ease, opacity 150ms ease;
}

.carousel__navigation-arrow-left:hover,
.carousel__navigation-arrow-right:hover {
    color: light-dark(rgba(0, 0, 0, 0.6), rgba(255, 255, 255, 0.6));
}

.carousel__navigation-arrow-left:focus-visible,
.carousel__navigation-arrow-right:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

/* aria-disabled instead of [disabled]: the button keeps focus at the ends. */
.carousel__navigation-arrow-left[aria-disabled="true"],
.carousel__navigation-arrow-right[aria-disabled="true"] {
    opacity: 0.35;
    cursor: default;
}

/**
 * On the narrowest screens the arrows and their gaps cost 120px of a 288px
 * container — nearly half the width, leaving cards too small to read. Floating
 * them over the strip gives that width back to the cards; touch users drag
 * anyway, so the arrows are the redundant control here, not the cards.
*/
@media screen and (max-width: 575px) { /* one px under --breakpoint-sm (576px), see essentials.css */
    .carousel__navigation-arrow-left,
    .carousel__navigation-arrow-right {
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        z-index: 1;
    }

    .carousel__navigation-arrow-left {
        left: 4px;
    }

    .carousel__navigation-arrow-right {
        right: 4px;
    }
}

/**
 * Viewport + track
 * .carousel__content is the native scroll container. .carousel__track holds the
 * cards plus the two overpull spacers that make the elastic ends possible.
 *
 * Note there is no `scroll-snap-type` here. Snapping is done in JS instead —
 * see scheduleSettle(). CSS snapping re-resolves on the engine's own schedule,
 * against snap positions that can't always be made reachable, and it moves the
 * strip when nothing is dragging it; that produced a run of jumps at the end of
 * the track, each engine-specific. One rule in one place behaves the same
 * everywhere.
*/
.carousel__content {
    flex: 1 1 auto;
    min-width: 0;
    position: relative;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain; /* no browser back-swipe out of the carousel */
    /* Horizontal touch panning belongs to the drag handler — native touch
       scrolling is clamped at the ends and can't show the elastic pull.
       `none` rather than `pan-y`: this element is natively scrollable
       (overflow-x: auto), and on iOS Safari `pan-y` doesn't fully hand the
       horizontal axis to script the way it does elsewhere — the browser's
       own scroll/rubber-band engine stays partly active underneath the
       drag handler and fights it for scrollLeft, which looks like the drag
       freezing after a first small nudge and springing back to where it
       started. `none` gives the whole gesture to the pointer handlers,
       full stop. Trade-off: vertical page scrolling doesn't work while a
       finger is down directly over the carousel — acceptable here since
       touch users are there to drag anyway. Wheel and trackpad are
       unaffected either way and still scroll natively. */
    touch-action: none;
    /* The spacers change size mid-gesture. Scroll anchoring would "helpfully"
       adjust scrollLeft to keep the visual position, which fights the offsets
       the script is setting deliberately. */
    overflow-anchor: none;
    scrollbar-width: none;          /* Firefox */
}

.carousel__content::-webkit-scrollbar {
    display: none;                  /* Chrome, Safari */
}

.carousel__content:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 4px;
}

.carousel__content.is-dragging {
    cursor: grabbing;
    user-select: none;
}

/* Snapping must be off while the script is driving scrollLeft — during the
   drag itself and through the settle spring, which lands on a snap point
   anyway. */
.carousel__track {
    display: flex;
    flex-direction: row;
    gap: var(--card-gap);
}

/**
 * Overpull spacers — one at each end, inserted by the script and zero-width at
 * rest. A drag opens them so pulling past a card is ordinary scrolling into
 * room that already exists; see openOverpull() for why nothing is transformed.
 * The negative margins cancel the flex gap so a closed spacer contributes
 * exactly nothing to the scroll width.
*/
.carousel__overpull {
    flex: 0 0 0px;
    pointer-events: none;
    overflow-anchor: none;
}

.carousel__overpull:first-child {
    margin-right: calc(-1 * var(--card-gap));
}

.carousel__overpull:last-child {
    margin-left: calc(-1 * var(--card-gap));
}

/** One card. Width is the per-breakpoint --carousel-card-width variable — the JS measures whatever this resolves to, it never assumes a size. */
.carousel__item {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: var(--carousel-card-width);
    height: var(--card-height);
    background-color: var(--card-background);
}

/**
 * Slide status bar
*/
.carousel__page-control {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    padding: 16px;
}

.carousel__status-bar {
    position: relative;
    height: 12px;
    width: min(400px, 100%);
    background-color: var(--card-background);
    cursor: pointer;
    touch-action: none;      /* the bar handles its own horizontal dragging */
    overflow-anchor: none;
    user-select: none;       /* dragging shouldn't start a text selection/caret */
}

/* Widens the press target vertically without changing how the bar looks —
   12px is a fine visual weight but a poor thing to hit with a thumb. */
.carousel__status-bar::before {
    content: '';
    position: absolute;
    inset: -10px 0;
}

.carousel__status-bar:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 4px;
}

.carousel__status-bar.is-scrubbing {
    cursor: grabbing;
}

.carousel__status-bar-slider {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    /* width and left are written by JS: width = 100 / slideCount */
    width: 100%;
    min-width: 24px;         /* stays grabbable with a lot of cards */
    background-color: light-dark(rgba(0, 0, 0, 0.35), rgba(255, 255, 255, 0.35));
    cursor: grab;
}

.carousel__status-bar.is-scrubbing .carousel__status-bar-slider {
    cursor: grabbing;
}

/* Reduced motion is handled in the script: animateTo() jumps straight to the
   destination instead of running the spring. */
