/* ==========================================================================
   RUNOVA MARKETING SITE — MOTION LAYER

   Added 2026-08-16. This file does NOT redesign the site. site.css stays the
   single source of truth for colour, type, spacing and layout; every value
   below comes from its tokens. This file only adds depth and movement on top:
   a scroll progress line, a drifting warm glow behind the hero, a headline
   that arrives word by word, a product mock that tilts toward the pointer,
   a process line that draws itself as you scroll, and richer hover states.

   WHY IT IS A SEPARATE FILE
   The home page and its stylesheet are guarded by tests (see
   tests/marketing.test.js) that pin down specific rules — cards stay unboxed,
   sections keep their layout, no weight above 700. Keeping the new movement
   here means those guards keep guarding, and this layer can be deleted in one
   line if it is ever unwanted.

   THE THREE RULES THIS FILE FOLLOWS
   1. Nothing is EVER hidden waiting for JavaScript. Every entrance rides the
      existing .reveal / .is-revealed system in site.css, which is applied by
      script and safety-swept on scroll. If this file's script never runs, the
      page is simply the page.
   2. Only decorative layers move on scroll — never text, never a control.
      Parallaxed body copy causes motion sickness.
   3. Anyone who asks their system for less movement gets none of it. The
      reduced-motion block in site.css already flattens every animation with
      !important, and there is a second, explicit block at the foot of this
      file so the intent survives even if that one is ever edited.
   ========================================================================== */

:root {
  /* A slightly slower, softer curve than site.css's --ease: things settle
     into place rather than snapping. Used for anything the eye follows. */
  --mo-ease: cubic-bezier(0.22, 1, 0.28, 1);
  --mo-ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* Pointer-driven values. JavaScript writes these; CSS reads them. Doing it
     this way means the script sets two numbers and never touches style rules,
     so the look stays entirely in this file. */
  --mo-rx: 0deg;   /* product mock tilt, x axis */
  --mo-ry: 0deg;   /* product mock tilt, y axis */
  --mo-px: 50%;    /* pointer position inside the hero, for the glow */
  --mo-py: 50%;
  --mo-flow: 0;    /* 0 → 1, how far the process line has drawn */
  --mo-read: 0;    /* 0 → 1, how far down the page the visitor is */
}

/* ==========================================================================
   1. READING PROGRESS

   A 2px line across the very top of the window that fills as the page is
   read. It sits above the sticky header and is purely decorative, so it is
   hidden from screen readers — a visitor who cannot see it loses nothing.
   scaleX is used rather than width because width forces the browser to
   re-layout on every scroll frame and transform does not.
   ========================================================================== */

.mo-progress {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  height: 2px;
  z-index: 200;
  transform: scaleX(var(--mo-read));
  transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--orange), var(--gold, var(--orange)));
  pointer-events: none;
  will-change: transform;
}
body.is-arabic .mo-progress { transform-origin: 100% 50%; }

/* ==========================================================================
   2. HEADER

   The header already blurs what is behind it. Once the page has scrolled it
   also tightens up: the bar loses a little height and the logo shrinks with
   it, which hands a few pixels back to the content without the header ever
   leaving. Only transform and height animate, both cheap.
   ========================================================================== */

.site-header { transition: border-color .25s var(--ease), box-shadow .25s var(--ease), background-color .3s var(--ease); }
.site-header.is-stuck { background: rgba(253, 253, 253, .9); }
.site-header .nav { transition: height .3s var(--mo-ease); }
.site-header.is-stuck .nav { height: calc(var(--nav-h) - 6px); }
.site-header .nav-logo img { transition: transform .3s var(--mo-ease); }
.site-header.is-stuck .nav-logo img { transform: scale(.94); }

/* Navigation links grow an underline from the middle out on hover, instead of
   only changing colour. It reads as a target being acquired. */
.nav-link { position: relative; }
.nav-link::after {
  content: "";
  position: absolute;
  inset-block-end: -2px;
  inset-inline: 0;
  height: 2px;
  border-radius: 2px;
  background: var(--orange);
  transform: scaleX(0);
  transition: transform .26s var(--mo-ease);
}
.nav-link:hover::after,
.nav-link:focus-visible::after { transform: scaleX(1); }

/* ==========================================================================
   3. HERO BACKGROUND — the light behind the page

   From a reference Momtaz sent (2026-08-17): soft vertical bands of colour
   washing across a near-white page, moving so slowly you never catch them
   starting.

   READ THIS BEFORE CHANGING IT. Earlier the same day the hero carried two
   orange blobs and a glow that FOLLOWED THE CURSOR, and he had all three
   removed — "remove the thing that follows my cursor and makes it orange".
   What is here is deliberately not that: nothing tracks the pointer, the
   colour is cool and barely there, and it never rises above 6% opacity. If
   it ever reads as an orange smear again it has gone wrong.

   Two layers crossing at different speeds and different angles, so the
   pattern never repeats in a way the eye can follow. Both are painted on
   ::before / ::after of the hero itself — no elements, no script, so there
   is nothing to fail and nothing to insert.
   ========================================================================== */

.hp-hero::before,
.hp-hero::after {
  content: "";
  position: absolute;
  inset: -25% -10%;
  z-index: 0;
  pointer-events: none;
  filter: blur(60px);
  /* The hero clips its overflow, so without this the wash ends on a straight
     horizontal line partway down the page — which reads as a coloured band
     someone forgot to finish rather than as light. Fading it out means the
     colour dissolves into the white instead of being cut off.

     The stops matter: the layer is inset -25% top and bottom, so the hero's
     own bottom edge falls at about 83% of it. The fade has to be finished
     BEFORE that, or the clip cuts a mask that is still half opaque and the
     line comes back. */
  -webkit-mask-image: linear-gradient(180deg, #000 28%, transparent 74%);
          mask-image: linear-gradient(180deg, #000 28%, transparent 74%);
}

/* The cool half: the blues and lilacs the reference leads with. */
.hp-hero::before {
  background:
    radial-gradient(38% 55% at 18% 30%, rgba(150, 175, 255, .28), transparent 70%),
    radial-gradient(32% 48% at 62% 12%, rgba(190, 165, 255, .22), transparent 70%),
    radial-gradient(40% 60% at 88% 60%, rgba(140, 200, 255, .24), transparent 70%);
  animation: mo-aurora-a 34s ease-in-out infinite alternate;
}

/* And a much quieter warm one, so the page still belongs to the brand
   rather than turning blue. It is half the strength of the cool layer on
   purpose. */
.hp-hero::after {
  background:
    radial-gradient(34% 50% at 78% 22%, rgba(255, 170, 130, .16), transparent 72%),
    radial-gradient(30% 46% at 30% 78%, rgba(255, 190, 150, .13), transparent 72%);
  animation: mo-aurora-b 46s ease-in-out infinite alternate;
}

/* Everything the hero actually says sits above both. */
.hp-hero > .container { position: relative; z-index: 1; }

@keyframes mo-aurora-a {
  from { transform: translate3d(-3%, -2%, 0) scale(1); }
  to   { transform: translate3d(4%, 3%, 0) scale(1.14); }
}
@keyframes mo-aurora-b {
  from { transform: translate3d(3%, 2%, 0) scale(1.1); }
  to   { transform: translate3d(-4%, -3%, 0) scale(1); }
}

/* ==========================================================================
   3b. THE LOGO ROWS DRIFT (Record on any platform, 2026-08-17)

   Top row drifts start-ward, bottom row drifts the other way, at two
   different unhurried speeds. Sliding by exactly half the track lands on
   the duplicate group, so the loop has no visible seam (the arithmetic is
   explained where the layout lives, in site.css). Hovering a row pauses it
   so a logo can actually be looked at.
   ========================================================================== */

.mq-track--left  { animation: mq-drift-left 48s linear infinite; }
.mq-track--right { animation: mq-drift-right 34s linear infinite; }
.mq-row:hover .mq-track { animation-play-state: paused; }
@keyframes mq-drift-left {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@keyframes mq-drift-right {
  from { transform: translateX(-50%); }
  to   { transform: translateX(0); }
}

/* ==========================================================================
   3c. THE HEADLINE SHIMMER (2026-08-17, from a component Momtaz sent)

   A band of light crosses the headline every few seconds: the text itself
   is the mask (background-clip: text), so the light exists only inside the
   letters. Pure CSS, guarded by @supports — a browser that cannot clip a
   background to text keeps a plain ink headline and never a transparent
   one. This replaced the word-by-word entrance; the two cannot share the
   h1, because the split's inline-blocks break the clip.
   ========================================================================== */

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hp-hero-copy h1 {
    /* TWO layers, and the solid ink one is load-bearing: the light band
       slides right off the element between sweeps, and without a solid
       colour underneath the letters are transparent over nothing — the
       first build shipped an invisible headline. */
    background-color: var(--ink);
    /* A WIDE band with a lit white core and warm edges, not the faint grey
       wash this started as — at .55 white over black it read as a smudge
       rather than as light. The orange either side is what makes it look
       like the brand catching the letters instead of a grey highlighter. */
    background-image: linear-gradient(102deg,
      transparent 36%,
      rgba(254, 92, 36, .6) 45%,
      rgba(255, 255, 255, .95) 50%,
      rgba(254, 92, 36, .6) 55%,
      transparent 64%);
    background-repeat: no-repeat;
    background-size: 240% 100%;
    background-position-x: 240%;
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
    /* Shorter cycle and a shorter pause, so the sweep is actually caught
       rather than happening while nobody is looking. */
    animation: mo-h1-shimmer 4.2s linear infinite;
    animation-delay: .9s;
  }
  @keyframes mo-h1-shimmer {
    0%   { background-position-x: 240%; }
    62%  { background-position-x: -140%; }
    100% { background-position-x: -140%; }
  }
}

/* ==========================================================================
   4. THE HEADLINE, WORD BY WORD

   RETIRED 2026-08-17: the shimmer above owns the h1 now, and the clip they
   both need cannot survive the split's inline-blocks. The styles stay for
   the splitter's sake but motion.js no longer calls it on boot.

   The h1 is split into words by script and each rises into place. This is the
   one place on the site where text itself animates, and it earns it: it is
   the first thing seen, it is six words long, and it is above the fold.

   The words are only ever hidden by a class the script adds AFTER it has
   successfully split them, so a failed script leaves an ordinary heading.
   The spaces live between the spans as real text, not inside them: a space
   at the end of an inline-block is collapsed away, which ran the headline
   together into one word the first time this was built.
   ========================================================================== */

.mo-word {
  display: inline-block;
  will-change: transform;
}
.mo-split .mo-word {
  opacity: 0;
  transform: translateY(0.42em) rotate(1.5deg);
  animation: mo-word-in .68s var(--mo-ease-out) forwards;
  animation-delay: calc(var(--mo-i, 0) * 55ms);
}
@keyframes mo-word-in {
  to { opacity: 1; transform: none; }
}

/* ==========================================================================
   5. THE PRODUCT MOCK

   The drawing of the app in the hero tilts a few degrees toward the pointer.
   The rotation is tiny on purpose — 6 degrees at the very edge. Enough that
   the frame reads as a physical object catching light, not enough to make the
   fake interface inside it hard to read.
   ========================================================================== */

/* The tilt itself lives in site.css on .hp-stage-main (it reads --mo-rx and
   --mo-ry, which motion.js writes). What belongs here is the light: a slow
   shine that crosses the hero card every few seconds, like light moving
   across a screen. The pseudo-element keeps the card's own radius, sits
   above the fake interface and below nothing interactive — the whole card
   is a picture. */
.hp-stage-main .vg { position: relative; }
.hp-stage-main .vg::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(105deg, transparent 38%, rgba(255, 255, 255, .38) 50%, transparent 62%);
  background-size: 280% 100%;
  background-repeat: no-repeat;
  animation: mo-sheen 7.5s var(--ease) infinite;
  opacity: 0;
}
@keyframes mo-sheen {
  0%        { background-position: 150% 0; opacity: 0; }
  12%       { opacity: 1; }
  40%, 100% { background-position: -120% 0; opacity: 0; }
}

/* The step the card says is Running gets a travelling underline, so the
   preview looks like it is mid-task rather than frozen. */
.hp-stage-main .vg-step:has(.vg-pill--live) {
  position: relative;
  overflow: hidden;
}
.hp-stage-main .vg-step:has(.vg-pill--live)::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 0;
  height: 2px;
  width: 40%;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--orange), transparent);
  animation: mo-scan 2.8s linear infinite;
}
@keyframes mo-scan {
  from { transform: translateX(-100%); }
  to   { transform: translateX(350%); }
}

/* The recording chip floats very gently over the card's corner — a slow
   four-second bob of a few pixels, so the recorder reads as live rather
   than printed on. The chip is decorative and absolute; nothing reflows. */
.hp-stage-chip {
  animation: mo-bob 4.5s ease-in-out infinite;
}
@keyframes mo-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}

/* And its red dot breathes, the way every recorder's does. */
.hp-stage-chip .vg-rec i,
.hp-stage-main .vg-rec i {
  animation: mo-rec-pulse 1.6s ease-in-out infinite;
}
@keyframes mo-rec-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .45; transform: scale(.8); }
}

/* ==========================================================================
   6. HOVER STATES

   The home cards are deliberately unboxed — no panel, no border. That is a
   settled design decision and a test enforces it. So hover cannot add a box.
   Instead the icon tile lifts, turns very slightly, and throws a soft
   coloured glow, and any arrow in the card slides forward.
   ========================================================================== */

.hp-card .hp-tile,
.hp-flow-card .hp-tile {
  position: relative;
  transition: transform .34s var(--mo-ease), box-shadow .34s var(--mo-ease);
}
.hp-card:hover .hp-tile,
.hp-flow-card:hover .hp-tile {
  transform: translateY(-4px) rotate(-3deg) scale(1.06);
  box-shadow: 0 14px 26px -14px rgba(0, 0, 0, .34);
}

/* The glow is a pseudo-element behind the tile rather than a box-shadow on
   it, so it can be much larger and softer than a shadow would allow. */
.hp-card .hp-tile::before,
.hp-flow-card .hp-tile::before {
  content: "";
  position: absolute;
  inset: -30%;
  z-index: -1;
  border-radius: inherit;
  background: radial-gradient(circle, rgba(254, 92, 36, .3), transparent 70%);
  opacity: 0;
  transition: opacity .34s var(--ease);
}
.hp-card:hover .hp-tile::before,
.hp-flow-card:hover .hp-tile::before { opacity: 1; }

/* Cards whose tile is green or blue glow in their own colour, not orange. */
.hp-card .hp-tile--green::before,
.hp-flow-card .hp-tile--green::before { background: radial-gradient(circle, rgba(47, 125, 99, .24), transparent 70%); }
.hp-card .hp-tile--blue::before { background: radial-gradient(circle, rgba(43, 99, 196, .22), transparent 70%); }
.hp-card .hp-tile--gold::before,
.hp-flow-card .hp-tile--gold::before { background: radial-gradient(circle, rgba(255, 211, 117, .5), transparent 70%); }

/* Any link that ends in an arrow moves the arrow, not the text. */
.hp-sec-more a .ico,
.card-link .ico { transition: transform .24s var(--mo-ease); }
.hp-sec-more a:hover .ico { transform: translateX(4px); }
body.is-arabic .hp-sec-more a:hover .ico { transform: translateX(-4px); }

/* ==========================================================================
   7. BUTTONS

   Two additions. A light sweep crosses the primary button on hover, and both
   buttons lean a pixel or two toward the pointer — a "magnetic" pull that
   makes them feel picked up rather than pressed. The pull is capped at 4px:
   any more and the button appears to dodge the cursor.
   ========================================================================== */

.btn { position: relative; overflow: hidden; }
.btn-primary::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: -60%;
  width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .3), transparent);
  transform: skewX(-18deg);
  transition: transform .6s var(--mo-ease);
  pointer-events: none;
}
.btn-primary:hover::before { transform: translateX(340%) skewX(-18deg); }

/* A warm light under the cursor, inside the button. The sweep above crosses
   once on hover and is over; this stays and follows, so a button being
   considered looks lit rather than merely hovered.

   ::after rather than a third element because .btn already clips to its own
   pill (overflow: hidden), so the circle is cut to the button's shape for
   free. The position comes from motion.js as two pixel values; with no
   script it falls back to the middle and simply never moves. */
.btn-primary::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s var(--ease);
  background: radial-gradient(
    5.5rem 5.5rem at var(--mo-glowx, 50%) var(--mo-glowy, 50%),
    rgba(255, 255, 255, .34),
    transparent 72%
  );
}
.btn-primary:hover::after,
.btn-primary:focus-visible::after { opacity: 1; }

/* The label has to sit above both layers, or the sweep and the glow wash
   over the words instead of under them. */
.btn-primary > * { position: relative; z-index: 1; }

.btn.is-magnetic {
  transform: translate(var(--mo-bx, 0), var(--mo-by, 0));
  transition: transform .18s var(--mo-ease), box-shadow .18s var(--ease);
}

/* ==========================================================================
   8. THE PROCESS LINE DRAWS ITSELF

   site.css draws a plain grey rail between the three numbered nodes. This
   fills it in with the brand gradient as the section crosses the window, so
   the three steps read as one journey being travelled rather than three
   cards sitting next to each other.

   The fill is a separate layer over the rail — the grey line underneath is
   untouched, so if the script never sets --mo-flow the rail is simply the
   quiet grey rail the reference draws.
   ========================================================================== */

.hp-rail { position: relative; }
.hp-rail.mo-draw::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset-block-start: calc(50% - 1px);
  inset-inline: 18px;
  height: 2px;
  background: linear-gradient(90deg, var(--orange), var(--gold), var(--green-deep), var(--orange));
  transform: scaleX(var(--mo-flow));
  transform-origin: 0 50%;
  /* No transition. The scroll position already supplies the continuity, and a
     transition on top of it makes the line lag a quarter of a second behind
     the cards it is supposed to be connecting. */
  pointer-events: none;
}
body.is-arabic .hp-rail.mo-draw::after { transform-origin: 100% 50%; }

/* ==========================================================================
   9. ENTRANCES

   All of this hangs off .reveal / .is-revealed, which site.js already owns.
   Two additions only: the two comparison panels arrive from opposite sides so
   the contrast is felt before it is read, and section headings wipe in.
   ========================================================================== */

.js-anim .hp-vs-card--bad.is-revealed  { animation: mo-in-start .7s var(--mo-ease-out) both; }
.js-anim .hp-vs-card--good.is-revealed { animation: mo-in-end .7s var(--mo-ease-out) both; }
@keyframes mo-in-start {
  from { opacity: 0; transform: translateX(-22px); }
  to   { opacity: 1; transform: none; }
}
@keyframes mo-in-end {
  from { opacity: 0; transform: translateX(22px); }
  to   { opacity: 1; transform: none; }
}

/* Section titles are uncovered rather than faded: the clip runs top to bottom
   so the words appear to be written on. */
.js-anim .hp-h2.is-revealed,
.js-anim .hp-teams-head.is-revealed {
  animation: mo-wipe .8s var(--mo-ease-out) both;
}
@keyframes mo-wipe {
  from { opacity: 0; clip-path: inset(0 0 100% 0); transform: translateY(10px); }
  to   { opacity: 1; clip-path: inset(0 0 -10% 0); transform: none; }
}

/* The documentation column used to sit greyed out until pointed at. It went
   with the redesign of this section on 2026-08-17: the two sides are cards
   with coloured heads now, so which is which is obvious without dimming one
   of them — and dimmed content is the exact thing that made other sections
   read as broken. */

/* ==========================================================================
   11. BACK TO TOP

   Appears once the visitor is a screen and a half down. It is a real button
   with a real label, keyboard reachable, and 44px square so it is a legal
   touch target.
   ========================================================================== */

.mo-top {
  position: fixed;
  inset-block-end: clamp(1rem, 3vw, 1.75rem);
  inset-inline-end: clamp(1rem, 3vw, 1.75rem);
  z-index: 150;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--shadow-card);
  cursor: pointer;
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition: opacity .3s var(--ease), transform .3s var(--mo-ease), box-shadow .2s var(--ease);
}
.mo-top.is-shown { opacity: 1; transform: none; pointer-events: auto; }
.mo-top:hover { box-shadow: var(--shadow-lift); transform: translateY(-2px); }
/* The shared sprite has a chevron pointing down and no arrow pointing up.
   Turning the one we have costs nothing and keeps the sprite as it is. */
.mo-top .ico { width: 18px; height: 18px; transform: rotate(180deg); }

/* ==========================================================================
   12. THE SPOTLIGHT

   Added 2026-08-17. A soft warm light that follows the pointer across a card,
   the way the hero glow follows it across the hero. It is the one effect on
   the page that makes a whole grid feel awake rather than printed.

   It has to be a glow and NOT a panel. The home cards are deliberately
   unboxed — transparent, no border — and a test enforces that. So this is a
   large, very soft radial gradient on a pseudo-element sitting behind the
   card, big enough to read as light falling on the card rather than as a
   shape appearing under the cursor.

   Positions come from motion.js as --mo-sx / --mo-sy. If the script never
   runs, the class is never added and there is no glow — no card is ever
   waiting on JavaScript to be legible.
   ========================================================================== */

.mo-spot { position: relative; }
.mo-spot::before {
  content: "";
  position: absolute;
  inset: -18px -22px;
  z-index: -1;
  border-radius: var(--r-lg);
  opacity: 0;
  pointer-events: none;
  transition: opacity .4s var(--ease);
  background: radial-gradient(
    14rem 14rem at var(--mo-sx, 50%) var(--mo-sy, 50%),
    rgba(254, 92, 36, .13),
    transparent 68%
  );
}
.mo-spot:hover::before,
.mo-spot:focus-within::before { opacity: 1; }

/* ==========================================================================
   13. THE TEAM FAN

   The four team cards as a fanned loop: the active one upright and fully
   readable, the others tucked behind it at a slight rotation, changed by
   prev and next. Unlike the auto-cycling deck this replaced (deleted
   2026-08-17 after Momtaz called it broken), nothing here moves on its own:
   the visitor turns the deck, so content is never taken away mid-read.

   NOTHING APPLIES until motion.js adds .is-fan, and it only does that on a
   wide screen with movement allowed. Phones and reduced motion keep the
   plain two-column grid from site.css, which is the spec's own fallback
   rule: change the layout, not just the timing.

   The script writes four numbers per card (--fan-x/-r/-s/-o) plus a
   z-index; this file owns what they mean. The ease is the spec's
   cubic-bezier(.16,1,.3,1), which is already --mo-ease-out.
   ========================================================================== */

/* When the fan is armed the section becomes two halves: the header on the
   left, the deck on the right. Keyed off .has-fan, which the script sets and
   removes together with .is-fan, so a phone, a blocked script or reduced
   motion all keep the centred header over the plain stacked grid — a
   half-width column of a two-column grid was the broken alternative. */
.hp-section--teams.has-fan .team-split {
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
  gap: clamp(2rem, 4.5vw, 5rem);
  align-items: center;
}
.hp-section--teams.has-fan .team-intro { text-align: start; }
.hp-section--teams.has-fan .team-intro .eyebrow--sec,
.hp-section--teams.has-fan .team-intro .hp-h2,
.hp-section--teams.has-fan .team-intro .hp-sub {
  text-align: start;
  margin-inline: 0;
}

.team-grid.is-fan {
  position: relative;
  display: block;
  max-width: 34rem;
  margin-inline: auto;
  /* height is set inline by the script from the tallest card, because the
     cards are absolute and contribute nothing on their own */
}
.team-grid.is-fan .team-block {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: 100%;
  padding: clamp(1.5rem, 2.5vw, 2rem);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-sm);
  opacity: var(--fan-o, 1);
  transform:
    translate(-50%, -50%)
    translateX(var(--fan-x, 0px))
    rotate(var(--fan-r, 0deg))
    scale(var(--fan-s, 1));
  transition: transform .6s var(--mo-ease-out), opacity .45s var(--ease), box-shadow .3s var(--ease);
  will-change: transform;
}
.team-grid.is-fan .team-block.is-active { box-shadow: var(--shadow-lift); }
/* The tucked cards are clickable shortcuts to themselves. */
.team-grid.is-fan .team-block:not(.is-active) { cursor: pointer; }

/* Prev and next. Round, quiet, and real buttons. */
.fan-nav {
  display: flex;
  justify-content: center;
  gap: 12px;
  margin-top: 1.6rem;
}
.fan-nav[hidden] { display: none; }
.fan-nav button {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: border-color .25s var(--ease), color .25s var(--ease), transform .25s var(--mo-ease), box-shadow .25s var(--ease);
}
.fan-nav button:hover,
.fan-nav button:focus-visible {
  border-color: var(--orange);
  color: var(--orange-ink);
  transform: translateY(-1px);
  box-shadow: var(--shadow-card);
}
.fan-nav .ico { width: 18px; height: 18px; }
/* One arrow glyph, mirrored for "previous" — and mirrored the other way
   around in Arabic, where the deck advances leftward. */
.fan-nav [data-prev] .ico { transform: scaleX(-1); }
[dir="rtl"] .fan-nav [data-prev] .ico { transform: none; }
[dir="rtl"] .fan-nav [data-next] .ico { transform: scaleX(-1); }

/* The screen-reader announcement of which card is front. */
.mo-live {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ==========================================================================
   14. THE ANSWER BLURS IN

   From the text-reveal FAQ spec Momtaz sent. Opening a question reveals its
   answer word by word, each rising out of a blur.

   BY WORD, NOT BY CHARACTER, which is where this and the reference part
   company. Per-character means ~200 spans per answer, an answer a screen
   reader may spell out, text that copies with a space after every letter,
   and — fatally here — an <a> torn into nine unrelated fragments. Answer six
   contains a real link to the security page. Words keep it whole.

   Nothing is hidden waiting for a script: the animation supplies its own
   "from" state via `both`, so a word with no animation on it is simply a
   word. If motion.js never runs, every answer is ordinary text.
   ========================================================================== */

.faq-word {
  display: inline-block;
  /* The spaces live BETWEEN the spans as real text nodes; without this an
     inline-block lets the browser drop the one at a line break and run two
     words together. Same trap the headline splitter hit. */
  white-space: pre-wrap;
}
.faq-a.is-revealing .faq-word {
  animation: faq-word-in .5s var(--mo-ease-out) both;
  animation-delay: calc(var(--i, 0) * 16ms);
}
@keyframes faq-word-in {
  from { opacity: 0; filter: blur(7px); transform: translateY(5px); }
  to   { opacity: 1; filter: blur(0); transform: none; }
}

/* ==========================================================================
   14a. THE TYPED LINE'S CARET

   motion.js types "Join the pilot now." letter by letter into the hero; the
   caret is the part CSS owns. steps(1) so it blinks rather than fades.
   ========================================================================== */

.hp-typed-caret {
  display: inline-block;
  width: 3px;
  height: 1.05em;
  margin-inline-start: 3px;
  vertical-align: -0.15em;
  border-radius: 2px;
  background: var(--orange);
  animation: mo-caret 1.1s steps(1) infinite;
}
@keyframes mo-caret {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0; }
}

/* ==========================================================================
   15. LESS MOVEMENT, ON REQUEST

   site.css already flattens every animation and transition under
   prefers-reduced-motion with !important. This block is the belt to that
   braces: it removes the decorative layers outright rather than merely
   freezing them, so nothing is left mid-animation, and it makes sure the
   headline is fully visible even if its words were split.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .mo-progress,
  .mo-spot::before,
  .btn-primary::after,
  .hp-stage-main .vg::after,
  .hp-stage-main .vg-step:has(.vg-pill--live)::after { display: none !important; }

  .hp-stage-chip,
  .hp-stage-chip .vg-rec i,
  .hp-stage-main .vg-rec i { animation: none !important; }

  .mo-split .mo-word { opacity: 1 !important; transform: none !important; animation: none !important; }
  .faq-a .faq-word { animation: none !important; opacity: 1 !important; filter: none !important; transform: none !important; }
  /* The shimmer must not leave a transparent headline behind when its
     animation is frozen; plain ink is the readable resting state. */
  .hp-hero-copy h1 {
    background: none !important;
    -webkit-text-fill-color: currentColor !important;
    animation: none !important;
  }
  .mq-track { animation: none !important; }
  .hp-typed-caret { display: none !important; }
  /* The light stays, it simply stops moving — removing it outright would
     change the page's colour for one visitor and not another. */
  .hp-hero::before, .hp-hero::after { animation: none !important; }
  .hp-rail.mo-draw::after { display: none; }
  .mo-top { transition: none; }
}

@media print {
  .mo-progress, .mo-top { display: none !important; }
  .mo-split .mo-word { opacity: 1 !important; transform: none !important; }
}
