/**
 * data-reveal - how a card row expresses which card is active.
 *
 * Contract: driven by data-reveal on a .hero-row. js/plugins/reveal-modes.js
 * toggles .is-active and NOTHING ELSE - which card is lit does not depend on
 * how "lit" is expressed, so mode selection is an attribute selector here and
 * four of the five modes are pure CSS. `cascade` is the exception: an accent
 * bar that slides needs the active card's geometry, and no selector hands a
 * child's offset to its parent.
 *
 * The five modes:
 *   spotlight  all cards visible, the inactive ones dimmed and blurred (default)
 *   focus      all lit; the active card takes more of the row
 *   cascade    spotlight's blur/dim + grow, plus a bar that slides beneath
 *              the active card - the deck's default hero-row treatment
 *   deal       cards arrive one at a time rather than un-dimming
 *   flip       the active card turns to a back face; add data-flip="stay"
 *              and every revealed card stays turned
 *
 * Invariant: css/cards.css's spotlight dim is scoped to
 * [data-reveal="spotlight"], [data-reveal="cascade"], and :not([data-reveal]).
 * Every mode here assumes cards arrive fully lit.
 *
 * Motion: css/motion.css's `off` state stops every transition globally, so
 * these modes need no motion guards of their own.
 */

/* ---- focus: all lit, the active card takes more of the row ---- */
/* Flex, not grid: flex needs one rule per child, whereas grid would need one
   :has(> .hero-card:nth-child(i).is-active) rule per card position - so the
   stylesheet would have to know the maximum row length. flex-grow also
   interpolates more reliably than grid-template-columns, and this is a
   transition rather than a static layout. */
.reveal .hero-row[data-reveal="focus"] {
  display: flex;
  align-items: stretch;
}
.reveal .hero-row[data-reveal="focus"] .hero-card { flex: 1 1 0; }
.reveal .hero-row[data-reveal="focus"] .hero-card.is-active {
  flex: 1.8 1 0;
  transform: none;            /* the row re-flows; the card does not also scale */
}

/* ---- cascade: a bar slides beneath the active card, which also gets the
   spotlight blur/dim + grow treatment (css/cards.css) rather than staying
   flat - the combination the deck settled on for every hero-row. Any other
   mode can borrow the bar with a bare data-underline attribute on the row
   (the flip row on the example slide does). ---- */
.reveal .hero-row[data-reveal="cascade"],
.reveal .hero-row[data-underline] {
  position: relative;
  padding-bottom: var(--space-2);
}
.reveal .hero-row[data-reveal="cascade"]::after,
.reveal .hero-row[data-underline]::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 3px;
  border-radius: 999px;
  background: var(--accent-key);
  /* Written by js/plugins/reveal-modes.js from the active card's geometry. */
  transform: translateX(var(--bar-x, 0px));
  width: var(--bar-w, 0px);
  opacity: var(--bar-o, 0);
  transition:
    transform var(--dur-slow) var(--ease),
    width var(--dur-slow) var(--ease),
    opacity var(--dur-fast) var(--ease);
}

/* ---- deal: cards arrive rather than un-dim ---- */
/* A different fragment class, not different CSS on the same one: .spotlight
   exists so cards are visible BEFORE being revealed. deal wants the opposite,
   so it keeps Reveal's default hide-until-shown and adds the entry move.
   Author a deal row's cards with `class="hero-card fragment deal"`. */
.reveal .slides section .fragment.deal {
  transform: translateY(18px);
  opacity: 0;
  transition:
    transform var(--dur-slow) var(--ease),
    opacity var(--dur-slow) var(--ease);
}
.reveal .slides section .fragment.deal.visible {
  transform: none;
  opacity: 1;
}
.reveal .hero-row[data-reveal="deal"] .hero-card.is-active { transform: none; }
/* On an arrows row, an arrow waits for the card it points at. */
.reveal .hero-row[data-reveal="deal"] > .hero-arrow {
  transition: opacity var(--dur-slow) var(--ease);
}
.reveal .hero-row[data-reveal="deal"] > .hero-arrow:not(:has(+ .hero-card.visible)) {
  opacity: 0;
}

/* ---- flip: the active card turns to a back face ---- */
/* Constrains cards to a fixed height (--flip-height, overridable per row): a
   back face cannot size a container it is rotated behind. Markup for this mode
   nests two faces per card:
     <div class="hero-card fragment spotlight">
       <div class="card-face front">...</div>
       <div class="card-face back">...</div>
     </div> */
/* Perspective lives in each card's own transform, not on the row: a row-level
   perspective has one vanishing point at the row's centre, so the outer cards
   of a four-card row swung lopsided, one edge ballooning toward the viewer.
   Every state below writes the same function list (perspective, translateY,
   scale, rotateY) so the browser interpolates each function on its own - the
   lift and the turn happen together instead of through a skewed matrix blend.
   The overshoot curve lets the card swing a little past flat and settle. */
.reveal .hero-row[data-reveal="flip"] .hero-card {
  height: var(--flip-height, 15rem);
  background: none;
  border: 0;
  padding: 0;
  backdrop-filter: none;
  transform-style: preserve-3d;
  transform: perspective(1100px) translateY(0) scale(1) rotateY(0deg);
  transition: transform calc(var(--dur-slow) * 1.6) cubic-bezier(0.34, 1.4, 0.5, 1);
  /* Reveal's own CSS sets will-change: opacity on every .fragment, and
     .hero-card is both the fragment and this preserve-3d element. That
     combination makes Chromium composite the wrong face once flipped (it
     paints the front face's content, mirrored, instead of the back face) - a
     layer-promotion / 3D-face-culling bug, not a mistake in this rule.
     Overriding will-change back to auto removes the accidental promotion;
     this element already gets its own layer from preserve-3d, so the hint
     bought nothing anyway. */
  will-change: auto;
}
.reveal .hero-row[data-reveal="flip"][data-flip="stay"] .hero-card.visible {
  transform: perspective(1100px) translateY(0) scale(1) rotateY(180deg);
}
/* .visible.is-active matches the stay rule's specificity and, coming later,
   wins - with only .is-active the stay rule silently cancelled the lift. */
.reveal .hero-row[data-reveal="flip"] .hero-card.is-active,
.reveal .hero-row[data-reveal="flip"] .hero-card.visible.is-active {
  transform: perspective(1100px) translateY(-6px) scale(1.05) rotateY(180deg);
}
/* The card itself is an empty 3D box, so the active glow goes on its faces -
   the same border, ring and cast shadow a lit cascade card wears. */
.reveal .hero-row[data-reveal="flip"] .hero-card.is-active { box-shadow: none; }
.reveal .hero-row[data-reveal="flip"] .hero-card.is-active .card-face {
  border-color: var(--accent-border);
  box-shadow:
    0 0 0 1px var(--accent-ring),
    0 24px 60px -20px var(--accent-cast);
}
.reveal .hero-row[data-reveal="flip"] .card-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  padding: var(--space-4) var(--space-3) var(--space-3);
  border: var(--stroke-w) solid var(--stroke);
  border-radius: var(--radius);
  background: var(--surface-glass);
  backdrop-filter: blur(18px);
}
.reveal .hero-row[data-reveal="flip"] .card-face.back { transform: rotateY(180deg); }

/* data-flip-axis="x": the card turns top-over-bottom instead - for wide,
   short cards (a data-direction="column" row of strips), where a sideways
   turn would swing a long edge far toward the viewer. Same function list,
   rotateX in place of rotateY; each rule is one attribute more specific than
   its rotateY counterpart above, and active still follows stay. The lift is
   smaller because strips sit close together. */
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"] .hero-card {
  transform: perspective(1100px) translateY(0) scale(1) rotateX(0deg);
}
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"][data-flip="stay"] .hero-card.visible {
  transform: perspective(1100px) translateY(0) scale(1) rotateX(180deg);
}
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"] .hero-card.is-active,
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"] .hero-card.visible.is-active {
  transform: perspective(1100px) translateY(0) scale(1.02) rotateX(180deg);
}
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"] .card-face.back { transform: rotateX(180deg); }

/* Hovering a strip singles it out to speak to: it grows and wears the active
   glow, the other strips dim (amounts: --row-hover-grow/-dim, css/tokens.css). The transforms repeat the full function list
   with the strip's current turn, and out-specify the stay/active rules above,
   or a hover would flip a turned strip back. The dim goes on the faces, not
   the card: opacity below 1 on the preserve-3d card flattens it, and Chromium
   then shows the mirrored front face instead of the back. */
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"] .hero-card:hover {
  transform: perspective(1100px) translateY(0) scale(var(--row-hover-grow)) rotateX(0deg);
  z-index: 1;
}
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"][data-flip="stay"] .hero-card.visible:hover,
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"] .hero-card.is-active:hover {
  transform: perspective(1100px) translateY(0) scale(var(--row-hover-grow)) rotateX(180deg);
}
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"] .hero-card:hover .card-face {
  border-color: var(--accent-border);
  box-shadow:
    0 0 0 1px var(--accent-ring),
    0 24px 60px -20px var(--accent-cast);
}
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"] .card-face {
  transition: opacity var(--dur-fast) var(--ease);
}
.reveal .hero-row[data-reveal="flip"][data-flip-axis="x"]:has(> .hero-card:hover) > .hero-card:not(:hover) .card-face {
  opacity: var(--row-hover-dim);
}

/* ---- data-together="cascade": a one-step row turns card by card ---- */
/* Top-down as the step reveals, bottom-up as it hides. js/plugins/
   reveal-modes.js writes --i / --i-rev (a card's place from the top / the
   bottom) and puts data-cascading on the row only for the length of the turn,
   so these delays never reach a hover grow. The longhand out-specifies the
   flip rule's `transition` shorthand, which would reset the delay to 0.
   --dur-stagger (css/tokens.css) is retimed by motion slow and zeroed by off. */
.reveal .hero-row[data-cascading="down"] > .hero-card {
  transition-delay: calc(var(--i, 0) * var(--dur-stagger));
}
.reveal .hero-row[data-cascading="up"] > .hero-card {
  transition-delay: calc(var(--i-rev, 0) * var(--dur-stagger));
}

/* data-back="skip": reveal-modes.js resets a row walked back to while it is
   off screen, and this keeps the cards from turning back on the way in. */
.reveal .hero-row[data-resetting] > .hero-card,
.reveal .hero-row[data-resetting] .card-face {
  transition: none !important;
}
