/* ==========================================================================
   Deck — fanned card presentation
   ==========================================================================

   Turns an ordinary card grid into a hand-dealt, overlapping deck: cards fly in
   from below at random angles, and pointing at one raises it upright while its
   neighbours slide their content out from underneath so it stays readable.

   PROGRESSIVE ENHANCEMENT. Nothing here applies until assets/js/deck.js adds
   `.deck--fan` or `.deck--carousel` to the container. A visitor with no
   JavaScript, or with `prefers-reduced-motion: reduce`, keeps the host grid
   exactly as it was — which is why none of these rules touch the bare
   `[data-deck]` selector beyond declaring custom properties.

   HOST-AGNOSTIC. The component knows only three hooks — `[data-deck]` on the
   container, `[data-deck-card]` on each card, `.deck-num` on the ordinal — so
   the same file drives the platform homepage's `.features-grid` and a generated
   instance's `.lp-features` without naming either. A host that wants to tune the
   footprint overrides `--deck-w`; everything else it needs, the component sets.

   TRANSFORM BUDGET. Card motion is written with the independent `translate`,
   `rotate` and `scale` longhands, never `transform`. Both host card classes
   already animate `transform` on hover (a lift in main.css, a lift plus an icon
   flourish in index.php's section style), and the longhands compose with that
   instead of fighting it. The same reasoning is why `.lp-reveal` in landing.css
   uses `translate`. The one exception is the hover lift itself, which IS
   neutralised in deck modes: a card that is already scaled and rotated must not
   also jump 8px, and the deck's own `.is-active` state replaces it.

   SPECIFICITY. Selectors are deliberately doubled up as `[data-deck].deck--fan`
   rather than the plainer `.deck--fan`. index.php carries its features CSS in an
   inline <style> inside the section, which parses AFTER this stylesheet's
   <link>, so an equal-specificity rule there would win on order. Two compound
   selectors clear that without resorting to !important.
   ========================================================================== */

[data-deck] {
    /* Card footprint. Width is fixed — the fan needs every card the same size,
       and six at the 296px ceiling with the overlap below span ~1154px, inside
       the 1300px content column.
       
       HEIGHT IS NOT. It was originally a fixed `aspect-ratio`, which is what put
       a band of dead space under the copy on every card: the ratio has to be
       generous enough for the longest card in the longest language, so every
       shorter card pays for it. Tuning the number cannot win — Arabic copy runs
       longer AND is set larger, so a ratio tight enough to suit English clipped
       Arabic, and one that suited Arabic left English half empty.

       Instead the fan row stretches its cards (see `align-items` below), so they
       all take the height of the tallest, and that height comes from content.
       Each language now gets a card sized to its own copy with no per-language
       rule anywhere. This is only a FLOOR, to stop a set of very short cards
       collapsing into squares and losing the playing-card proportion. */
    --deck-w: clamp(215px, 19.5vw, 296px);
    --deck-min-ratio: 0.95;

    /* How much of a card its right-hand neighbour covers. 0.42 is the point
       where six cards still read as one deck rather than a row with gaps. */
    --deck-overlap: 0.42;

    --deck-dur: 720ms;
    /* Overshoots slightly past the resting value, which is what sells the
       "snapped upright" feel. A pure ease-out reads mechanical here. */
    --deck-ease: cubic-bezier(0.34, 1.56, 0.64, 1);
    --deck-ease-out: cubic-bezier(0.22, 1, 0.36, 1);

}

/* The ordinal is deck furniture: it only earns its place once cards overlap and
   the set needs to read as finite. In the grid fallback it stays out of the way,
   so that layout is byte-identical to what shipped before. */
.deck-num {
    display: none;
}

/* ==========================================================================
   Fan — pointer-driven, desktop
   ========================================================================== */

[data-deck].deck--fan {
    display: flex;
    flex-wrap: nowrap;
    justify-content: center;
    gap: 0;

    /* The load-bearing line for card height. `stretch` gives every card the
       cross size of the flex line, and that line is as tall as the tallest
       card's content — so the cards stay equal (which the fan requires) without
       anyone declaring a height (which is what created the dead space).
       `center` here previously combined with a fixed container min-height to
       pin all six to a size nothing had asked for. */
    align-items: stretch;

    /* Headroom for the raised card's 1.08 scale and for rotation at the ends,
       now that no container min-height reserves it. Transforms do not affect
       layout, so without this the outer cards would lean into whatever sits
       above and below the deck. ~40px is the measured overhang at these sizes. */
    padding-block: 2rem;
}

[data-deck].deck--fan > [data-deck-card] {
    inline-size: var(--deck-w);
    /* Proportion floor only. Real height comes from the row's `stretch`, which
       resolves to the tallest card's content — see --deck-min-ratio. */
    min-block-size: calc(var(--deck-w) / var(--deck-min-ratio));
    flex: 0 0 auto;
    margin: 0;

    /* Column so the ordinal can be pinned to the bottom with an auto margin. */
    display: flex;
    flex-direction: column;
    position: relative;
    overflow: hidden;

    /* Logical, so a card reads from the right in Arabic with no second rule.
       Centred text looks accidental in a tall narrow card. */
    text-align: start;
    padding: clamp(1.15rem, 1.5vw, 1.5rem);

    translate: var(--deck-x, 0px) var(--deck-y, 0px);
    rotate: var(--deck-r, 0deg);
    scale: var(--deck-s, 1);
    will-change: translate, rotate, scale;
}

/* Motion is deliberately withheld until `.is-primed`, which deck.js adds a
   frame after the mode class. Transitions that were live on the frame the fan
   layout lands would animate every card FROM the position it held in the grid,
   which reads as the section coming apart rather than a deck being dealt. */
[data-deck].deck--fan.is-primed > [data-deck-card] {
    transition:
        translate var(--deck-dur) var(--deck-ease),
        rotate var(--deck-dur) var(--deck-ease),
        scale var(--deck-dur) var(--deck-ease),
        opacity 520ms var(--deck-ease-out);
}

/* The overlap itself. `margin-inline-start` means the deck fans from the right
   in RTL with no extra rule — the only direction-aware work left is in the JS,
   which has to map a physical cursor position onto a logical card order. */
[data-deck].deck--fan > [data-deck-card] + [data-deck-card] {
    margin-inline-start: calc(var(--deck-w) * var(--deck-overlap) * -1);
}

/* Content push. Each direct child slides sideways by the same amount, which is
   indistinguishable from moving a wrapper and spares every host a markup
   change. Pseudo-elements are untouched, so .feature-card::before (the gradient
   hairline along the top edge) stays pinned to the card. */
[data-deck].deck--fan > [data-deck-card] > * {
    translate: var(--deck-push, 0px);
}

[data-deck].deck--fan.is-primed > [data-deck-card] > * {
    transition: translate var(--deck-dur) var(--deck-ease);
}

/* There is deliberately NO bottom fade here. An earlier revision faded the last
   few rems of each card into its own background, so that copy clipped by the
   fixed aspect-ratio dissolved rather than ending mid-letter. Height follows
   content now, so nothing clips — the tallest card fits exactly and the rest
   carry slack — and the gradient had nothing left to hide. It just washed over
   the blank space above the ordinal, which is the space this revision set out to
   reduce.

   `overflow: hidden` on the card stays, doing a different job: containing the
   sideways content push, which shifts copy by up to 45% of the card and would
   otherwise spill into its neighbours. */

/* Raised: square to the baseline, scaled up, content recentred. z-index is set
   inline by deck.js, which owns the paint order it has to restore afterwards. */
[data-deck].deck--fan > [data-deck-card].is-active {
    --deck-x: 0px;
    --deck-y: 0px;
    --deck-r: 0deg;
    --deck-s: 1.08;
    --deck-push: 0px;
}

/* Suppress the host's hover lift — see the TRANSFORM BUDGET note above. The
   icon flourish in index.php is a `transform` on a CHILD, so it survives this
   and still fires. */
[data-deck].deck--fan > [data-deck-card]:hover {
    transform: none;
}

/* Deal-in. Cards are held invisible until deck.js has both measured the deck
   and seen it approach the viewport, then released in sequence. The stagger
   lives behind `.is-dealing` and is stripped once the deal finishes, otherwise
   every later hover would inherit up to 350ms of dead delay. */
[data-deck].deck--fan:not(.is-dealt) > [data-deck-card] {
    opacity: 0;
}

[data-deck].deck--fan.is-dealing > [data-deck-card] {
    transition-delay: calc(var(--deck-i, 0) * 70ms);
}

/* ==========================================================================
   Carousel — touch, below the fan breakpoint
   ========================================================================== */

[data-deck].deck--carousel {
    /* Wider and shallower than the fan: one card is centred at a time, so it
       can afford to be near-full-bleed. */
    --deck-w: min(74vw, 280px);

    display: flex;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 0;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-block: 1rem;
    /* Centres the first and last card rather than pinning them to the edges. */
    padding-inline: calc(50% - (var(--deck-w) / 2));
}

[data-deck].deck--carousel::-webkit-scrollbar {
    display: none;
}

[data-deck].deck--carousel > [data-deck-card] {
    inline-size: var(--deck-w);
    flex: 0 0 auto;
    margin: 0;
    display: flex;
    flex-direction: column;
    scroll-snap-align: center;
    text-align: start;

    /* No fixed ratio and no clipping here. Cards stretch to the tallest of the
       set, so the centred card shows its copy in full — the mode a touch
       visitor spends all their time in must not be the truncated one. */
    rotate: var(--deck-r, 0deg);
    scale: var(--deck-s, 0.94);
    will-change: rotate, scale;
}

/* Same one-frame hold as the fan, for the same reason. */
[data-deck].deck--carousel.is-primed > [data-deck-card] {
    transition:
        rotate 480ms var(--deck-ease),
        scale 480ms var(--deck-ease);
}

[data-deck].deck--carousel > [data-deck-card] + [data-deck-card] {
    margin-inline-start: calc(var(--deck-w) * -0.24);
}

[data-deck].deck--carousel > [data-deck-card].is-active {
    --deck-r: 0deg;
    --deck-s: 1;
}

[data-deck].deck--carousel > [data-deck-card]:hover {
    transform: none;
}

/* ==========================================================================
   Shared card furniture
   ========================================================================== */

[data-deck].deck--fan .deck-num,
[data-deck].deck--carousel .deck-num {
    display: block;
    /* Pins the ordinal to the bottom of the column. Collapses harmlessly to 0
       if the copy overruns the card, in which case the fade covers it. */
    margin-block-start: auto;
    padding-block-start: 0.5rem;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    color: var(--text-secondary);
    opacity: 0.75;
    /* Keeps 01–06 from shifting width as the active card changes. */
    font-variant-numeric: tabular-nums;
}

/* Cards become focusable in deck modes (deck.js adds tabindex), because focus
   is what gives a keyboard visitor the same reveal a pointer gets. */
[data-deck].deck--fan > [data-deck-card]:focus-visible,
[data-deck].deck--carousel > [data-deck-card]:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 3px;
}

/* deck.js declines to run at all under reduced motion, so this is defence in
   depth for a mode class left behind by a resize across the breakpoint. */
@media (prefers-reduced-motion: reduce) {

    [data-deck].deck--fan.is-primed > [data-deck-card],
    [data-deck].deck--fan.is-primed > [data-deck-card] > *,
    [data-deck].deck--carousel.is-primed > [data-deck-card] {
        transition: none;
    }
}
