.herobuilder-carousel .hb-stage {
    width: 100%;
    overflow: hidden;
    /* Own stacking context so the layers' z-indexes stay contained and can't paint over
       the Bootstrap carousel controls/indicators (which live in the carousel context). */
    position: relative;
    z-index: 0;
}

/* Keep the carousel navigation above the slide layers. */
.herobuilder-carousel .hb-carousel-nav {
    z-index: 5;
}

/* Positioning + geometry come from the per-slide scoped CSS. */
.hb-layer {
    position: absolute;
}

/* Art direction wraps the <img> in a <picture>; it must not become a box of its own. */
.hb-layer picture {
    display: contents;
}

/* Image layer classes sit on the layer box; a rounding (rounded-*) has to reach the image itself.
   The <picture> passes it on too — inheritance still runs through a display:contents element. */
.hb-layer picture,
.hb-layer .hb-layer-img {
    border-radius: inherit;
}

.hb-layer .hb-layer-img,
.hb-layer.hb-layer-img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: fill;
}

/* Text layers: fill their geometry box, vertically centred; box-sizing keeps the
   readability scrim padding inside the placed box.

   `line-height` is reset here on purpose: `.herobuilder-carousel .carousel-item`
   below sets `line-height: 0` to kill the inline-image baseline gap under the
   image layer, and that value is inherited. Without this reset every text layer
   that has no explicit `lh-*` class collapses — all wrapped lines land on the
   same baseline and the text renders as an unreadable overlay of itself.
   Bootstrap's `lh-*` utilities ship with !important, so they still win. */
.hb-layer-text {
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    justify-content: center;
    line-height: normal;
}

/* A text layer's HTML element (h1–h6, p) is semantics only — the look comes from the layer's
   classes. Reset what browsers and Bootstrap give headings/paragraphs (size, weight, margin, …).
   `html … :is(h1, …)` weighs 0,0,2: above Bootstrap's element rules (0,0,1), below any class
   (0,1,0), so `display-*`, `fw-*` or a site's own layer classes still win.
   .hb-layer-tag is the element inside a linked text layer (classes sit on the <a>). */
html :where(.hb-layer-text):is(h1, h2, h3, h4, h5, h6, p),
html .hb-layer-tag {
    margin: 0;
    font-family: inherit;
    font-size: inherit;
    font-weight: inherit;
    font-style: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
}

html .hb-layer-tag {
    line-height: inherit;
}

/* Box properties: same weight as the reset above (0,0,2), so a layer class still wins. */
html :where(.hb-stage) :is(h1, h2, h3, h4, h5, h6, p) {
    padding: 0;
    background-color: transparent;
}

/* t3bootstrap styles headings in its hero area: `.hero h1, .hero p { padding; background-color }`
   (0,1,1). Only there the reset needs more weight — 0,1,2, which still loses to any two-class
   rule. A layer class that sets padding or a background inside the hero area therefore needs
   two classes, e.g. `.hb-layer.my-class` (it would against the theme rule anyway). The text
   scrim is emitted at 0,2,0 (CompositionProcessor) and wins; Bootstrap's utilities are !important. */
html .hero :where(.hb-stage) :is(h1, h2, h3, h4, h5, h6, p) {
    padding: 0;
    background-color: transparent;
}

/* Button layers fill their placed box; the label is centred and btn classes style it. */
.hb-layer-button {
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    text-decoration: none;
}

.herobuilder-carousel .carousel-item {
    /* Ensure the slide fills the stage without extra whitespace. */
    line-height: 0;
}

/* ---------------------------------------------------------------------------
   Bootstrap 5 carousel fallback.

   The CE emits Bootstrap carousel markup, but a site theme may compile only a
   subset of Bootstrap's components (a theme that curates its own SCSS component
   list may leave `bootstrap/carousel` out, e.g. when its legacy hero slider used
   a different library). Without the component CSS every slide stays
   `display: block` and the slides stack vertically instead of sliding.

   These rules mirror Bootstrap's own carousel component, scoped to
   `.herobuilder-carousel` so they only ever affect this CE. On a theme that
   *does* ship the component they resolve to the same values, just at a higher
   specificity — so keeping them is harmless either way.
   --------------------------------------------------------------------------- */
.herobuilder-carousel {
    position: relative;
}

.herobuilder-carousel.pointer-event {
    touch-action: pan-y;
}

.herobuilder-carousel .carousel-inner {
    position: relative;
    width: 100%;
    overflow: hidden;
}

.herobuilder-carousel .carousel-inner::after {
    display: block;
    clear: both;
    content: "";
}

.herobuilder-carousel .carousel-item {
    position: relative;
    display: none;
    float: left;
    width: 100%;
    margin-right: -100%;
    backface-visibility: hidden;
    transition: transform 0.6s ease-in-out;
}

.herobuilder-carousel .carousel-item.active,
.herobuilder-carousel .carousel-item-next,
.herobuilder-carousel .carousel-item-prev {
    display: block;
}

.herobuilder-carousel .carousel-item-next:not(.carousel-item-start),
.herobuilder-carousel .active.carousel-item-end {
    transform: translateX(100%);
}

.herobuilder-carousel .carousel-item-prev:not(.carousel-item-end),
.herobuilder-carousel .active.carousel-item-start {
    transform: translateX(-100%);
}

/* Cross-fade instead of sliding (slider_effect = fade). */
.herobuilder-carousel.carousel-fade .carousel-item {
    opacity: 0;
    transition-property: opacity;
    transform: none;
}

.herobuilder-carousel.carousel-fade .carousel-item.active,
.herobuilder-carousel.carousel-fade .carousel-item-next.carousel-item-start,
.herobuilder-carousel.carousel-fade .carousel-item-prev.carousel-item-end {
    z-index: 1;
    opacity: 1;
}

.herobuilder-carousel.carousel-fade .active.carousel-item-start,
.herobuilder-carousel.carousel-fade .active.carousel-item-end {
    z-index: 0;
    opacity: 0;
    transition: opacity 0s 0.6s;
}

/* ---------------------------------------------------------------------------
   Navigation: prev · indicators · next as one group, bottom centre.

   The slides are free compositions — text can sit anywhere, and on light product
   shots white controls vanish. So the controls do not sit on the artwork at the
   sides (where they overlapped text) but in their own dark pill, which keeps them
   at >= 3:1 (WCAG 1.4.11) on any background, a white one included:
   pill 55 % black, glyphs white, inactive indicators 70 % white. The active
   indicator is also twice as long, so the state does not rest on brightness alone.
   Every target is at least 24 x 24 px (WCAG 2.5.8).
   --------------------------------------------------------------------------- */
.herobuilder-carousel .hb-carousel-nav {
    position: absolute;
    left: 50%;
    bottom: 1rem;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.25rem;
    background: rgba(0, 0, 0, 0.55);
    border-radius: 999px;
    transform: translateX(-50%);
}

.herobuilder-carousel .hb-carousel-nav .carousel-control-prev,
.herobuilder-carousel .hb-carousel-nav .carousel-control-next {
    position: static;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    color: #fff;
    background: none;
    border: 0;
    border-radius: 50%;
    opacity: 1;
    transition: background-color 0.15s ease;
}

.herobuilder-carousel .hb-carousel-nav .carousel-control-prev:hover,
.herobuilder-carousel .hb-carousel-nav .carousel-control-next:hover {
    background-color: rgba(255, 255, 255, 0.2);
}

.herobuilder-carousel .carousel-control-prev-icon,
.herobuilder-carousel .carousel-control-next-icon {
    display: inline-block;
    width: 1rem;
    height: 1rem;
    background-repeat: no-repeat;
    background-position: 50%;
    background-size: 100% 100%;
    /* Bootstrap's dark-variant filter must not turn the white glyph dark. */
    filter: none;
}

.herobuilder-carousel .carousel-control-prev-icon {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e");
}

.herobuilder-carousel .carousel-control-next-icon {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
}

.herobuilder-carousel .hb-carousel-nav .carousel-indicators {
    position: static;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 0.25rem;
    margin: 0;
    list-style: none;
}

/* The visible bar is the content box; padding widens the target to 24 x 24 px. */
.herobuilder-carousel .hb-carousel-nav .carousel-indicators [data-bs-target] {
    box-sizing: content-box;
    flex: 0 0 auto;
    width: 1rem;
    height: 4px;
    padding: 10px 4px;
    margin: 0;
    text-indent: -999px;
    cursor: pointer;
    background-color: rgba(255, 255, 255, 0.7);
    background-clip: content-box;
    border: 0;
    border-radius: 999px;
    opacity: 1;
    transition: width 0.3s ease, background-color 0.3s ease;
}

.herobuilder-carousel .hb-carousel-nav .carousel-indicators .active {
    width: 2rem;
    background-color: #fff;
}

/* Keyboard focus must stay visible on the dark pill and on any slide. */
.herobuilder-carousel .carousel-control-prev:focus-visible,
.herobuilder-carousel .carousel-control-next:focus-visible,
.herobuilder-carousel .carousel-indicators [data-bs-target]:focus-visible,
.herobuilder-carousel .hb-carousel-toggle:focus-visible {
    outline: 2px solid #fff;
    outline-offset: -2px;
    box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.6);
}

/* Pause/play for autoplay (WCAG 2.2.2), bottom left — same backdrop as the nav pill,
   vertically centred on it (pill: 2.25rem + 2 x 0.25rem padding). */
.herobuilder-carousel .hb-carousel-toggle {
    position: absolute;
    left: 1rem;
    bottom: 1.25rem;
    z-index: 5;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    color: #fff;
    background: rgba(0, 0, 0, 0.55);
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    transition: background-color 0.15s ease;
}

.herobuilder-carousel .hb-carousel-toggle:hover {
    background-color: rgba(0, 0, 0, 0.75);
}

.herobuilder-carousel .hb-carousel-toggle[hidden] {
    display: none;
}

/* Two bars = pause; a triangle = play (drawn with borders, no icon font needed). */
.herobuilder-carousel .hb-carousel-toggle-icon {
    display: block;
    width: 0.75rem;
    height: 0.875rem;
    margin: auto;
    border-left: 0.25rem solid currentColor;
    border-right: 0.25rem solid currentColor;
}

.herobuilder-carousel .hb-carousel-toggle.is-paused .hb-carousel-toggle-icon {
    width: 0;
    height: 0;
    margin-left: calc(50% - 0.25rem);
    border-left: 0.75rem solid currentColor;
    border-right: 0;
    border-top: 0.45rem solid transparent;
    border-bottom: 0.45rem solid transparent;
}

@media (prefers-reduced-motion: reduce) {
    .herobuilder-carousel .carousel-item,
    .herobuilder-carousel .carousel-control-prev,
    .herobuilder-carousel .carousel-control-next,
    .herobuilder-carousel .carousel-indicators [data-bs-target] {
        transition: none;
    }
}

/* ---------------------------------------------------------------------------
   Transition: "layers" (slider_effect = layers).

   The slide itself cross-fades (Bootstrap's .carousel-fade), which is what carries the
   background image: the incoming background is already visible while the outgoing one
   fades, so the stage is never empty. Every other layer is animated on its own —
   fading out front-most first, and building back up rearmost-first on the incoming
   slide, staggered by depth.

   Only `opacity` is animated on purpose. `transform` on .hb-layer belongs to the AOS
   entrance effects (layer rotation uses the individual `rotate` property) and on
   .hb-layer-img to the image flip, so a transform here would silently drop those.

   --hb-depth (0 = rearmost) and --hb-depth-rev (0 = front-most) come from the
   composition's z-order; the rearmost layer carries .hb-layer-bg and is exempt.

   Layers with an entrance animation (non-empty data-aos) are exempt too, here and in
   "parallax": these rules out-rank AOS and replaced its movement, duration and delay with
   their own quick fade. Such a layer plays its own effect once the slide has arrived
   (hero.js). With reduced motion AOS strips data-aos, so the reduced-motion rules below
   cover every layer again.
   --------------------------------------------------------------------------- */
.herobuilder-carousel.hb-fx-layers .carousel-item .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])) {
    opacity: 0;
    transition: opacity 260ms ease;
}

/* Resting and incoming slide: rearmost layer first, so the composition builds up in depth
   order. The incoming item only gains .active once Bootstrap's 600ms item fade is done,
   which is exactly when the new background has arrived. */
.herobuilder-carousel.hb-fx-layers .carousel-item.active .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])) {
    opacity: 1;
    transition-delay: calc(var(--hb-depth, 1) * 70ms);
}

/* Outgoing slide: front-most layer first and quicker than the background cross-fade, so the
   composition comes apart before the background underneath it changes. */
.herobuilder-carousel.hb-fx-layers .carousel-item.active.carousel-item-start .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])),
.herobuilder-carousel.hb-fx-layers .carousel-item.active.carousel-item-end .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])) {
    opacity: 0;
    transition-duration: 180ms;
    transition-delay: calc(var(--hb-depth-rev, 0) * 70ms);
}

/* Reduced motion: keep every layer up, no staggering — the slide change itself stays a plain
   cross-fade. Repeating the outgoing selector here keeps it from winning on specificity. */
@media (prefers-reduced-motion: reduce) {
    .herobuilder-carousel.hb-fx-layers .carousel-item .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])),
    .herobuilder-carousel.hb-fx-layers .carousel-item.active.carousel-item-start .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])),
    .herobuilder-carousel.hb-fx-layers .carousel-item.active.carousel-item-end .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])) {
        opacity: 1;
        transition: none;
    }
}

/* ---------------------------------------------------------------------------
   Transition: "parallax" (slider_effect = parallax).

   Same choreography as "layers", but the elements also drift horizontally, further the
   further forward they sit — so the slide comes apart and reassembles with depth. The
   background layer never moves: it only cross-fades with the slide, which keeps the
   stage covered at all times.

   Movement uses the individual `translate` property, not `transform`: `transform` on
   .hb-layer belongs to the AOS entrance effects and the layer rotation is the individual
   `rotate` property — individual transform properties compose (translate → rotate → scale
   → transform) instead of replacing each other.

   The offset is a viewport-relative length, not a percentage: `translate` percentages
   resolve against the element's own box, and layer boxes here range from a few percent of
   the stage to over 200% of it (corner clusters), which would scramble the depth order.
   Override --hb-parallax-step per site to dial the travel up or down.
   --------------------------------------------------------------------------- */
.herobuilder-carousel.hb-fx-parallax .carousel-item .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])) {
    --hb-parallax-step: clamp(8px, 1.8vw, 34px);
    --hb-off: calc(var(--hb-depth, 0) * var(--hb-parallax-step));
    opacity: 0;
    translate: var(--hb-off) 0;
    transition: opacity 300ms ease, translate 380ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Backward navigation: the incoming slide waits on the other side, so the travel direction
   always matches the direction the user asked for. */
.herobuilder-carousel.hb-fx-parallax .carousel-item.carousel-item-prev .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])) {
    translate: calc(-1 * var(--hb-off)) 0;
}

/* Resting and incoming slide: settle into place, rearmost layer first. */
.herobuilder-carousel.hb-fx-parallax .carousel-item.active .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])) {
    opacity: 1;
    translate: 0 0;
    transition-delay: calc(var(--hb-depth, 1) * 70ms);
}

/* Outgoing slide, forward: drift on to the left, front-most layer leaving first. */
.herobuilder-carousel.hb-fx-parallax .carousel-item.active.carousel-item-start .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])) {
    opacity: 0;
    translate: calc(-1 * var(--hb-off)) 0;
    transition-duration: 260ms;
    transition-delay: calc(var(--hb-depth-rev, 0) * 70ms);
}

/* Outgoing slide, backward: same, mirrored. */
.herobuilder-carousel.hb-fx-parallax .carousel-item.active.carousel-item-end .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])) {
    opacity: 0;
    translate: var(--hb-off) 0;
    transition-duration: 260ms;
    transition-delay: calc(var(--hb-depth-rev, 0) * 70ms);
}

@media (prefers-reduced-motion: reduce) {
    .herobuilder-carousel.hb-fx-parallax .carousel-item .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])),
    .herobuilder-carousel.hb-fx-parallax .carousel-item.carousel-item-prev .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])),
    .herobuilder-carousel.hb-fx-parallax .carousel-item.active.carousel-item-start .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])),
    .herobuilder-carousel.hb-fx-parallax .carousel-item.active.carousel-item-end .hb-layer:not(.hb-layer-bg):not([data-aos]:not([data-aos=""])) {
        opacity: 1;
        translate: 0 0;
        transition: none;
    }
}
