/* ============================================
   CUBE PHOTO CAROUSEL MODULE (Swiper-based)
   Single display mode: cube (3D rotating cube).
   Runtime values come from CSS custom properties
   (--pc-radius, --pc-main-height) set inline by
   the renderer. The carousel mechanics are driven
   by the Swiper library's cube effect; this
   stylesheet only styles the module shell, slides,
   captions, and controls.
   ============================================ */

.cube-photo-carousel-viewport {
    /* Clip wrapper. The Swiper root inside is overflow:visible so the rotating
       cube faces and their face shadows read in full while the cube turns; this
       wrapper clips the 3D spill exactly at the module boundary so the carousel
       never reads wider than its module-wrapper (matching every other module).
       box-sizing: border-box keeps the padding inside the 100% width. This
       wrapper is also the containing block for the nav arrows and pagination
       dots, which are rendered as SIBLINGS of the Swiper root (not inside it).
       position:relative makes the absolute arrows anchor to the module box; the
       --swiper-navigation-* tokens live here so Swiper's own
       .swiper-button-prev/next rules size and place the arrows against this
       wrapper. */
    box-sizing: border-box;
    position: relative;
    width: 100%;
    overflow: hidden;
    --swiper-navigation-size: 44px;
    --swiper-navigation-sides-offset: 8px;
    /* The arrows anchor against this viewport (their containing block). Swiper
       applies `top: var(--swiper-navigation-top-offset, 50%)` to its prev/next
       buttons (with a negative margin-top to vertically center the button on
       that top), so this token IS the button's absolute top — it must keep its
       50% base. The viewport height = padding-top + cube height + padding-bottom
       (the bottom padding reserves the dots band). A plain 50% centers against
       the whole box and drifts down as the bottom padding grows, so half the
       (top − bottom) padding difference is subtracted from 50% to land the
       arrows on the CUBE's vertical center instead. The extra -4px nudges the
       arrows slightly above the geometric center (the aesthetic buffer kept
       from the fixed-padding layout). */
    --swiper-navigation-top-offset: calc(50% - 4px);
    --swiper-navigation-color: var(--text-ink);
    /* Viewport padding — two jobs at once, both shadow-aware:
       (1) Base spill room. The Swiper root is overflow:visible (see below), so
           the rotating cube faces and their face shadows spill past the square
           stage on every side — most visibly along the diagonals while a face
           turns ~45°. The top pad keeps the upper corner faces clear of the
           viewport top; the base bottom pad clears the lower spill and the dots
           band; the base side pad (0.12 × --pc-main-height) gives the diagonal
           face spill room and tracks the cube size.
       (2) Floor-shadow headroom. When the floor shadow is on
           (--pc-shadow-on:1, set by the renderer from the admin shadow toggle),
           Swiper paints a shadow plane below the cube at shadow_offset distance
           and shadow_scale width. The bottom and side padding grow with those two
           knobs so the plane is never clipped at the viewport edge — a large
           shadow_offset would otherwise push it past the bottom. The headroom
           term is multiplied by --pc-shadow-on so it collapses to 0 (no extra
           padding) when the shadow is off, and the side term also scales with
           shadow_scale because a near-1.0 scale widens the plane past the cube
           edge. All pure calc() in CSS — no JS involvement. */
    --pc-viewport-pad-top: 16px;
    --pc-viewport-pad-bottom: 48px;
    padding:
        var(--pc-viewport-pad-top, 16px)
        calc(var(--pc-main-height, 320px) * 0.12 + var(--pc-shadow-offset, 20px) * var(--pc-shadow-scale, 0.94) * 0.5 * var(--pc-shadow-on, 0))
        calc(var(--pc-viewport-pad-bottom, 48px) + var(--pc-shadow-offset, 20px) * var(--pc-shadow-on, 0));
}

.cube-photo-carousel {
    --pc-radius: 18px;
    /* The cube is a square: width and height both track --pc-main-height. The
       renderer overrides it from the admin cube_height setting (inline on the
       root). A non-square box would distort the 3D cube faces. */
    --pc-main-height: 320px;
    /* Card elevation. Tinted toward Ink (green-grey) per the design system's
       shadow vocabulary rather than pure black. A distinct dark-mode value is
       layered under the dark-theme block below so the shadow stays visible
       against the charcoal-teal canvas instead of dissolving into it. */
    --pc-shadow: 0 8px 20px rgba(26, 36, 33, 0.16);
    --pc-shadow-active: 0 12px 28px rgba(26, 36, 33, 0.22);
    box-sizing: border-box;
    position: relative;
    /* Square stage sized from --pc-main-height so the centered cube fills it.
       margin:auto centers the stage inside the clip viewport. */
    width: var(--pc-main-height, 320px);
    height: var(--pc-main-height, 320px);
    max-width: 100%;
    margin: 0 auto;
}

.cube-photo-carousel *,
.cube-photo-carousel *::before,
.cube-photo-carousel *::after {
    box-sizing: border-box;
}

/* ============================================
   SWIPER SHELL OVERRIDE
   The root is the Swiper viewport. Do NOT set
   transform / perspective / transform-style on
   .swiper or .swiper-wrapper here — Swiper's cube
   effect applies the 3D perspective itself, and
   overriding it breaks the cube.
   ============================================ */

/* Cube viewport. overflow:visible lets the rotating cube faces AND their
   box-shadow lifts spill past the root edge so the cube reads in full while it
   turns — the corner faces (turned ~45° in transit) and the face shadows would
   otherwise be clipped at the square stage boundary and the cube would look
   "trapped" in its box with invisible corners and no depth shadow. This mirrors
   the coverflow and stacked-cards modules, which both keep the Swiper root
   visible for the same 3D-spill reason. The outer .cube-photo-carousel-viewport
   clips that spill at the module boundary so the carousel still respects its
   module-wrapper width. */
.cube-photo-carousel--cube.swiper {
    overflow: visible;
}

.cube-photo-carousel__wrapper.swiper-wrapper {
    align-items: center;
}

/* ============================================
   CUBE FLOOR SHADOW
   Swiper injects a .swiper-cube-shadow element inside the wrapper (horizontal
   mode) when cubeEffect.shadow is on, sizing it from the container width and
   rotating it as a floor plane. Style only its position/z-index/opacity here —
   do NOT override the transform Swiper sets, or the floor plane breaks.
   ============================================ */

.cube-photo-carousel--cube .swiper-cube-shadow {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 0;
    pointer-events: none;
    background: rgba(0, 0, 0, 0.25);
    /* Soften the floor shadow so its hard edge reads less like a sharp cutout and
       more like a diffuse projected shadow. The blur is scaled to the cube size
       (--pc-main-height) so the softening stays in proportion across cube_height
       settings and the narrow-screen shrink: a bigger cube gets a proportionally
       softer shadow, a smaller one a proportionally tighter one. CSS renders
       filter before transform, so Swiper's own floor-plane rotateX/scale
       transform is applied on top of the already-blurred layer without conflict.
       The blur bleeds slightly past the element edge, but the viewport's
       shadow-aware padding (see .cube-photo-carousel-viewport above) already
       reserves room for the floor shadow, so the spill stays inside the module. */
    filter: blur(calc(var(--pc-main-height, 320px) * 0.035));
}

/* ============================================
   SLIDE BASE
   ============================================ */

.cube-photo-carousel__item.swiper-slide {
    position: relative;
    display: block;
    /* Square tile. Both axes track --pc-main-height so each cube face reads as
       a uniform square regardless of the source aspect ratio (object-fit: cover
       crops the image to fill the face). */
    width: var(--pc-main-height, 320px);
    height: var(--pc-main-height, 320px);
    border-radius: var(--pc-radius);
    overflow: hidden;
    background: transparent;
    text-decoration: none;
    color: inherit;
}

.cube-photo-carousel__image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ============================================
   CAPTION OVERLAY (title + description)
   A bottom gradient band over the image, painted onto the slide face itself.
   The caption rides the face it belongs to: while a face is in view its caption
   stays visible the whole time, and as the cube turns a face away its caption
   leaves together with the image — there is no separate show/hide. This mirrors
   the image itself, so the caption never appears or disappears independently of
   its slide.
   ============================================ */

.cube-photo-carousel__caption {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 40px 15px 15px;
    background: linear-gradient(to top, rgba(0, 0, 0, 1), transparent);
    color: #ffffff;
    z-index: 2;
    /* pointer-events:none lets the whole band sit over the image without
       blocking drag; the title link re-enables its own events. */
    pointer-events: none;
}

.cube-photo-carousel__title {
    display: block;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-weight: 600;
    color: #ffffff;
    line-height: 1.4;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Title as a link: only this element is clickable inside the overlay. */
.cube-photo-carousel__title--link {
    text-decoration: none;
    transition: color 0.2s ease;
    pointer-events: auto;
    cursor: pointer;
}

.cube-photo-carousel__title--link:hover {
    color: rgba(255, 255, 255, 0.85);
    text-decoration: underline;
}

.cube-photo-carousel__desc {
    font-weight: 600;
    text-decoration: none;
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    padding-bottom: 15px;
    margin-top: 6px;
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.9);
}

/* ============================================
   CUBE MODE SHADOWS
   The slideShadows toggle (cubeEffect.slideShadows) gates the inner face
   depth shadow Swiper paints on the cube faces; the base face keeps its
   box-shadow lift so each tile reads as a solid framed surface. The outer
   floor shadow (cubeEffect.shadow) is driven separately and styled via the
   .swiper-cube-shadow rule above.
   ============================================ */

/* Face elevation is gated on the --has-shadows modifier (set by the renderer
   from the admin "slide shadows" toggle). With shadows off, tiles rest on a
   hairline edge alone. The active face earns a slightly stronger lift to read
   as the focal point. */
.cube-photo-carousel--cube.cube-photo-carousel--has-shadows .cube-photo-carousel__item {
    box-shadow: var(--pc-shadow);
}

.cube-photo-carousel--cube.cube-photo-carousel--has-shadows .cube-photo-carousel__item.swiper-slide-active {
    box-shadow: var(--pc-shadow-active);
}

/* Dark mode: the light-mode shadow tint (green-grey on a near-black field)
   dissolves and the faces look flat. Lift the opacity and shift the tint
   toward true black so each face separates from the charcoal-teal canvas
   instead of disappearing into it. Active stays a step stronger than rest. */
[data-bs-theme="dark"] {
    --pc-shadow: 0 8px 22px rgba(0, 0, 0, 0.55);
    --pc-shadow-active: 0 12px 30px rgba(0, 0, 0, 0.65);
}

/* ============================================
   NAVIGATION ARROWS
   Mirrors the other photo carousels' arrows so all
   share an identical control style.
   ============================================ */

/* The arrows carry Swiper's .swiper-button-prev/next classes too, so Swiper
   already provides position/top/size/placement via the --swiper-navigation-*
   tokens set on the clip viewport above (the arrows' containing block, since
   they live as siblings of the Swiper root). Only the visual layer (face,
   color, reveal-on-hover) is layered on top — no left/right/margin overrides
   needed, which avoids any specificity clash with Swiper's selectors. */
.cube-photo-carousel__nav {
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.9);
    box-shadow: var(--shadow-sm);
    opacity: 0;
    transition: background 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}

/* Reveal arrows on hover over the whole module. The arrows are siblings of the
   Swiper root (inside the viewport), so the :hover is scoped to the viewport —
   the element that contains both the carousel and the arrows. */
.cube-photo-carousel-viewport:hover .cube-photo-carousel__nav {
    opacity: 1;
}

.cube-photo-carousel__nav:hover {
    background: #fff;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

.cube-photo-carousel__nav:focus-visible {
    outline: 3px solid var(--accent-color, #c2683f);
    outline-offset: 3px;
}

.cube-photo-carousel__nav svg {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Disabled arrow: a dimmed-but-visible control (0.3 opacity) rather than fully
   hidden, so the user reads it as "no more slides in that direction" instead of
   the control vanishing. Swiper's own .35 opacity is overridden here. */
.cube-photo-carousel__nav.swiper-button-disabled {
    opacity: 0.3;
    cursor: not-allowed;
    pointer-events: none;
}

.cube-photo-carousel-viewport:hover .cube-photo-carousel__nav.swiper-button-disabled {
    opacity: 0.3;
}

/* Dark mode: dark button face with a light icon. */
[data-bs-theme="dark"] .cube-photo-carousel__nav {
    background: rgba(30, 30, 30, 0.9);
    color: #d7dedb;
}

[data-bs-theme="dark"] .cube-photo-carousel__nav:hover {
    background: #161c1a;
}

/* ============================================
   DOT INDICATORS
   Swiper renders .swiper-pagination-bullet inside
   the .cube-photo-carousel__dots container. Styled
   to mirror the other photo carousels' indicator look.
   ============================================ */

.cube-photo-carousel__dots.swiper-pagination {
    /* Absolutely positioned at the bottom of the clip viewport so the dots row
       does NOT add to the viewport's flow height. This keeps the viewport height
       equal to the Swiper root height, which is what the nav arrows' top:50%
       centers against — otherwise the dots (~36px) push the vertical midpoint
       down and the arrows sit below the cube center. */
    position: absolute;
    left: 0;
    right: 0;
    bottom: 14px;
    display: flex;
    justify-content: center;
    gap: 8px;
    padding: 0;
    --swiper-pagination-bullet-horizontal-gap: 0;
}

.cube-photo-carousel__dots .swiper-pagination-bullet {
    width: 10px;
    height: 10px;
    margin: 0;
    border: none;
    border-radius: 50%;
    background: var(--border-strong, #c4ccc8);
    opacity: 1;
    /* Swiper defaults inactive bullets to transform:scale(.33); reset to none so
       the dots read as full-size circles. */
    transform: none;
    transition: background 0.2s ease, transform 0.2s ease;
}

.cube-photo-carousel__dots .swiper-pagination-bullet:hover {
    background: var(--text-muted);
}

.cube-photo-carousel__dots .swiper-pagination-bullet-active {
    background: var(--cc-dot-active-color, var(--primary-color));
    transform: scale(1.2);
}

[data-bs-theme="dark"] .cube-photo-carousel__dots .swiper-pagination-bullet {
    background: var(--border-strong);
}

[data-bs-theme="dark"] .cube-photo-carousel__dots .swiper-pagination-bullet:hover {
    background: var(--text-muted);
}

[data-bs-theme="dark"] .cube-photo-carousel__dots .swiper-pagination-bullet-active {
    background: var(--cc-dot-active-color, var(--primary-color));
}

/* ============================================
   REDUCED MOTION
   Strip transitions for users who prefer reduced
   motion; behavior stays intact (Swiper speed is
   set to 0 by the engine).
   ============================================ */

@media (prefers-reduced-motion: reduce) {
    .cube-photo-carousel__nav,
    .cube-photo-carousel__dots .swiper-pagination-bullet {
        transition: none !important;
    }

    .cube-photo-carousel__dots .swiper-pagination-bullet-active {
        transform: none;
    }
}

/* ============================================
   RESPONSIVE — narrow screens.
   Breakpoint matches the other photo carousels
   (767px) so the arrow icon size shift is
   identical across all of them.
   ============================================ */

@media (max-width: 767px) {
    .cube-photo-carousel {
        /* Smaller cube on narrow screens so the cube still fits within the
           viewport without dominating it. !important is required because the
           renderer sets --pc-main-height inline on the root from the admin
           cube_height setting; inline custom-property declarations win over a
           plain media-query rule, so without !important the mobile shrink would
           be ignored and a large cube would overflow narrow viewports. The
           220px here is the mobile square size. */
        --pc-main-height: 220px !important;
    }

    .cube-photo-carousel__nav svg {
        width: 16px;
        height: 16px;
    }

    .cube-photo-carousel__caption {
        padding: 30px 12px 12px;
    }

    .cube-photo-carousel__title {
        font-size: 0.85rem;
    }

}
