/* ==========================================================================
   Teachmint X homepage revamp — scoped styles
   All selectors are namespaced under .rv-* so they never collide with the
   existing tm-* / Tailwind layers. Section 1: Hero.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Shared type scale. Every section references these so sizes/weights cannot
   drift apart again:
     --rv-fs-h2     section heading      --rv-fs-lede   section sub-copy
     --rv-fs-title  card / item heading  --rv-fs-body   card / item description
   -------------------------------------------------------------------------- */
:where(.rv-hero, .rv-trust, .rv-agents, .rv-wb, .rv-cc, .rv-vx, .rv-hw, .rv-gc, .rv-tm, .rv-sp) {
  --rv-fs-h2: clamp(1.9rem, 3.6vw, 2.9rem);
  --rv-fw-h2: 500;
  --rv-fs-lede: clamp(1rem, 1.35vw, 1.15rem);
  --rv-fs-title: clamp(1.15rem, 1.75vw, 1.6rem);
  --rv-fw-title: 600;
  --rv-fs-body: clamp(0.9rem, 1.1vw, 1rem);
  /* one card radius everywhere, and one (smaller) radius for media nested
     inside a card so it reads as contained rather than competing */
  --rv-radius-card: 18px;
  --rv-radius-media: 12px;
}

/* Phones: one flat size for every section heading. Overriding the token hits all
   eight headings at once; the hero H1 is untouched because it has its own size
   (.rv-hero__title), not this token. Must sit after the block above — both use
   :where() so specificity is 0 and source order decides. */
@media (max-width: 640px) {
  :where(.rv-hero, .rv-trust, .rv-agents, .rv-wb, .rv-cc, .rv-vx, .rv-hw, .rv-gc, .rv-tm, .rv-sp) {
    --rv-fs-h2: 24px;
  }
}

/* --------------------------------------------------------------------------
   Text reveal. Headings and sub-copy rise a little and fade in as their
   section comes into view, once each.

   The hidden state lives on .rv-reveal, which is added by JS (initReveals) —
   never in the markup — so if scripts fail or are blocked the copy renders
   normally instead of being stuck at opacity 0. Only the text nodes are
   transformed, never a sticky container (.rv-wb__pin / .rv-cc__intro /
   .rv-cc__card), because a transform on those would break the pinning.
   -------------------------------------------------------------------------- */
.rv-reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.95s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.95s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: var(--rv-reveal-delay, 0ms);
}
.rv-reveal.is-revealed {
  opacity: 1;
  transform: none;
}
/* Hero heading: a slow word-by-word fade. The word spans are wrapped by JS
   (initHeroTitleReveal), and the accent span is kept whole so its gradient still
   runs across "Agentic AI" instead of restarting on each word.
   Plain inline spans, opacity only — no transform, no inline-block — so line
   breaking and `text-wrap: balance` are completely unaffected. */
.rv-hero__title .rv-word {
  opacity: 0;
  /* Short fade on a tight stagger: consecutive words overlap heavily, so the
     reveal reads as one continuous wave instead of seven discrete words
     appearing in turn. A long fade on a wide stagger looked choppy. */
  transition: opacity 0.75s cubic-bezier(0.33, 1, 0.68, 1);
  transition-delay: var(--rv-word-delay, 0ms);
}
/* Promoted only while animating; initHeroTitleReveal clears it on completion so
   the layers are not held for the life of the page. */
.rv-hero__title.is-words-animating .rv-word {
  will-change: opacity;
}
.rv-hero__title.is-words-revealed .rv-word {
  opacity: 1;
}

/* Belt and braces: if the class is ever applied under reduced motion, the copy
   still shows. initReveals() also declines to add it in the first place. The
   hero selector is listed too so its higher specificity cannot reinstate the
   transition here. */
@media (prefers-reduced-motion: reduce) {
  .rv-reveal,
  .rv-reveal.is-revealed,
  .rv-hero__title .rv-word {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   Homepage header spacer.
   headerTMX on the "/" template is `position: fixed`, so the page needs a
   spacer equal to its height or the hero slides underneath it. Measured from
   the rendered header: 80px at >=1280px, 62px below that (the header's own
   layout switches at Tailwind's xl breakpoint). Re-measure if headerTMX changes.
   -------------------------------------------------------------------------- */
.rv-header-spacer {
  height: 62px;
}
@media (min-width: 1280px) {
  .rv-header-spacer {
    height: 80px;
  }
}

.rv-hero {
  --rv-navy: #14284d;
  --rv-navy-700: #1c3b6e;
  --rv-blue: #5491fc;
  --rv-violet: #7567fc;
  --rv-sky: #34bcfb;
  --rv-muted: #4c6389;
  --rv-border: #c9d6ee;

  position: relative;
  overflow: hidden;
  text-align: center;

  /* ----- Photo-backed hero geometry -----
     The classroom photo is anchored to the BOTTOM of the section at its
     natural aspect (height = 75.76% of its rendered width), so the panel's
     position is a fixed fraction of that width. Measured off the asset:
     the panel's top edge sits 58.34% of the photo's height up from its
     bottom = 58.34% x 75.76% = 44.2% of the photo's width.

     Reserving exactly that much padding-bottom therefore parks the panel's
     top edge right at the end of the content, and the extra px in the calc()
     is the visible gap below the CTAs — the same number at every width, with
     no per-breakpoint nudging. Whatever of the photo doesn't fit above is
     cropped off the top (overflow: hidden), which is what removes the ceiling
     on wide screens. Should the content ever be tall enough to out-run the
     photo, background-color is the photo's own top-row colour, so the join
     is invisible. */
  padding: 56px 24px calc(44.2% + 44px);
  background-color: #e2d2c6;
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
}

.rv-hero__photo {
  position: absolute;
  z-index: 0;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  /* Only `width` changes per breakpoint — see the responsive block. Keep it in
     step with the padding-bottom above: padding = 44.2% x (width / 100%). */
  width: 100%;
  height: auto;
  /* The global `img { max-width: 100% }` would silently clamp the zoomed
     widths below back to 100% and break the padding/photo pairing. */
  max-width: none;
}

.rv-hero__inner {
  position: relative;
  z-index: 1;
  margin: 0 auto;
  max-width: 1200px; /* shared content width — see note at top of Section 2 */
  /* tm-tfi-max-width carries a 30px bottom padding. Dropped here so the px
     term in the section's padding-bottom IS the gap above the panel. */
  padding-bottom: 0;
}

/* ----- Eyebrow chip ----- */
.rv-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 18px;
  border-radius: 999px;
  border: 1px solid rgba(117, 103, 252, 0.25);
  background: linear-gradient(
    90deg,
    rgba(52, 188, 251, 0.3) 0%,
    rgba(84, 145, 252, 0.3) 50%,
    rgba(117, 103, 252, 0.3) 100%
  );
  color: var(--rv-navy);
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

.rv-chip__icon {
  width: 24px;
  height: 24px;
  flex: none;
}

/* Vertical roll: the visible window is one line tall; the track holds all
   phrases stacked and translates up so the current phrase exits at the top
   while the next rises into place. Width = widest phrase (constant). */
.rv-chip__text {
  display: block;
  overflow: hidden;
  height: 1.4em;
}
.rv-chip__track {
  display: flex;
  flex-direction: column;
  will-change: transform;
}
.rv-chip__phrase {
  display: flex;
  align-items: center;
  justify-content: center; /* center each phrase within the fixed-width window */
  height: 1.4em;
  line-height: 1.4em;
  white-space: nowrap;
}

/* ----- Headline ----- */
.rv-hero__title {
  margin: 22px auto 0;
  max-width: 60rem;
  font-weight: 600; /* semi-bold */
  font-style: normal; /* upright — no italics */
  font-size: clamp(2rem, 5.2vw, 3.5rem);
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--rv-navy);
  text-wrap: balance;
}

.rv-hero__accent {
  /* Block so the headline always breaks after "AI-Powered", instead of
     letting the line fill and wrap wherever the width happens to run out. */
  display: block;
  background: linear-gradient(90deg, #5491fc 0%, #7567fc 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

/* ----- Optional subhead (off by default) ----- */
.rv-hero__subhead {
  margin: 16px auto 0;
  max-width: 60ch;
  font-size: clamp(1rem, 1.6vw, 1.15rem);
  line-height: 1.6;
  color: var(--rv-muted);
}

/* ----- CTAs ----- */
/* Layout only — the buttons themselves are the site's shared component
   (tm-* utilities + .hero-cta-w), not styled here. */
.rv-hero__cta {
  flex-wrap: wrap;
  margin-top: 28px;
}

/* ==========================================================================
   Responsive
   ========================================================================== */

/* Tablet — zoom the photo 1.3x so the panel keeps a usable share of the
   viewport as the room narrows; padding-bottom tracks it (44.2% x 1.3). */
@media (max-width: 1024px) {
  .rv-hero {
    padding-top: 48px;
    padding-bottom: calc(57.5% + 36px);
  }
  .rv-hero__photo {
    width: 130%;
  }
}

/* Mobile */
@media (max-width: 640px) {
  .rv-hero {
    /* 2.2x: at phone widths an un-zoomed room leaves the panel ~40% of the
       viewport, which is unreadable. This crops the room to the wall around
       the panel instead. padding-bottom = 44.2% x 2.2. */
    padding: 32px 16px calc(97.2% + 26px);
  }
  .rv-hero__photo {
    width: 220%;
  }
  .rv-chip {
    font-size: 13px;
    padding: 7px 14px;
  }
  .rv-chip__icon {
    width: 20px;
    height: 20px;
  }
  .rv-hero__title {
    /* Phones keep the natural two-line split ("AI-Powered Connected" /
       "Classroom Device"). Forcing the break after "AI-Powered" as the desktop
       does needs a 350px line, and the column here is only 322px, so it would
       spill to three lines. 20ch + balance is what holds it at two. */
    font-size: clamp(1.35rem, 6.4vw, 2rem);
    max-width: 20ch;
  }
  .rv-hero__accent {
    /* Back to inline on phones — the forced break is a desktop-only rule. */
    display: inline;
  }
  .rv-hero__cta {
    margin-top: 24px;
  }
}

/* Respect reduced motion — chip stays on the first phrase, no crossfade */
@media (prefers-reduced-motion: reduce) {
  .rv-chip__track {
    transition: none;
  }
}

/* ==========================================================================
   Section 2 — Trust strip (auto-scrolling logo marquee)
   ========================================================================== */
.rv-trust {
  --rv-navy: #14284d;
  --rv-muted: #4c6389;
  --rv-border: #c9d6ee;
  background: #ffffff;
  /* 48px top: the hero used to fade out into a white panel, so 8px was enough
     air under it. It now ends on the photo's hard bottom edge, and the heading
     needs clearance from it. */
  padding: 48px 0 64px;
  overflow: hidden;
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.rv-trust__title {
  margin: 0 auto 44px;
  padding: 0 16px;
  text-align: center;
  color: var(--rv-navy);
  font-weight: 600;
  font-style: normal;
  font-size: clamp(1.375rem, 3vw, 2rem);
  line-height: 1.25;
}
/* Viewport clips the track and fades both edges */
.rv-trust__viewport {
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(
    to right, transparent, #000 8%, #000 92%, transparent
  );
  mask-image: linear-gradient(
    to right, transparent, #000 8%, #000 92%, transparent
  );
}
.rv-trust__track {
  display: flex;
  width: max-content;
  animation: rv-trust-scroll 45s linear infinite;
}
.rv-trust__viewport:hover .rv-trust__track {
  animation-play-state: paused;
}
.rv-trust__item {
  flex: 0 0 auto;
  width: 168px;
  padding: 0 12px;
  text-align: center;
}
.rv-trust__logo {
  width: 76px;
  height: 76px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
}
.rv-trust__logo img,
.rv-trust__logo svg {
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* Monochrome, per the approved reference: the crests arrive in a dozen
     different palettes, and desaturating them keeps the strip reading as one
     calm row instead of competing with the sections around it. The captions
     stay navy. */
  filter: grayscale(1);
}
.rv-trust__name {
  margin-top: 14px;
  color: var(--rv-navy);
  font-size: 15px;
  font-weight: 400; /* regular — the logo carries the emphasis, not the caption */
  line-height: 1.35;
}
.rv-trust__country {
  margin-top: 2px;
  color: var(--rv-muted);
  font-size: 0.85em;
  line-height: 1.3;
}
/* Track holds two identical copies; shifting by -50% loops seamlessly. */
@keyframes rv-trust-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
@media (max-width: 640px) {
  .rv-trust { padding-top: 32px; padding-bottom: 44px; }
  .rv-trust__item { width: 138px; }
  .rv-trust__logo { width: 62px; height: 62px; }
  .rv-trust__name { font-size: 13px; margin-top: 12px; }
  .rv-trust__country { font-size: 11px; }
}
@media (prefers-reduced-motion: reduce) {
  .rv-trust__track { animation: none; }
  .rv-trust__viewport { overflow-x: auto; } /* allow manual scroll instead */
}

/* ==========================================================================
   Section 3 — EduAI agents (center-focus carousel)
   ========================================================================== */
.rv-agents {
  --rv-navy: #14284d;
  --rv-muted: #4c6389;
  --rv-card-w: 340px;
  background: #f4f8fd; /* shared section tint */
  padding: 64px 24px 72px;
  overflow: hidden;
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
/* Intro: left heading + right paragraph */
.rv-agents__head {
  max-width: 1200px;
  margin: 0 auto 52px;
  display: flex;
  gap: 40px;
  justify-content: space-between;
  align-items: flex-start;
}
.rv-agents__heading {
  color: var(--rv-navy);
  font-weight: var(--rv-fw-h2); /* medium */
  font-style: normal;
  font-size: var(--rv-fs-h2);
  line-height: 1.14;
  /* Narrow enough that "Classroom" cannot sit on line 1, so the heading breaks
     as "EduAI: The Agentic" / "Classroom Intelligence". */
  max-width: 22ch;
}
.rv-agents__intro {
  flex: 0 1 470px;
  color: var(--rv-muted);
  font-size: var(--rv-fs-lede);
  line-height: 1.55;
  padding-top: 6px;
}

/* Carousel */
.rv-agents__carousel {
  position: relative;
  max-width: 1200px;
  margin: 0 auto;
}
.rv-agents__stage {
  position: relative;
} /* height set by JS (cards are absolutely positioned) */
.rv-agents__card {
  position: absolute;
  top: 0;
  left: 50%;
  width: var(--rv-card-w);
  transform: translateX(-50%);
  transform-origin: center center;
  transition: transform 0.55s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.45s ease;
  will-change: transform, opacity;
}
/* Gradient border via double background (padding-box white + border-box gradient) */
.rv-agents__card-inner {
  height: 100%;
  border: 2px solid transparent;
  border-radius: var(--rv-radius-card);
  padding: 14px 14px 22px;
  background:
    linear-gradient(#ffffff, #ffffff) padding-box,
    linear-gradient(155deg, #7b07ff 0%, #07acff 100%) border-box;
}
.rv-agents__card.is-active .rv-agents__card-inner {
  box-shadow: 0 24px 55px rgba(84, 73, 240, 0.22);
}
.rv-agents__img {
  border-radius: var(--rv-radius-media);
  overflow: hidden;
}
.rv-agents__img img {
  display: block;
  width: 100%;
  height: auto;
}
.rv-agents__title {
  margin: 20px 0 0;
  text-align: center;
  color: var(--rv-navy);
  font-weight: var(--rv-fw-title);
  font-style: normal;
  font-size: var(--rv-fs-title);
}
.rv-agents__desc {
  margin: 10px auto 0;
  max-width: 30ch;
  text-align: center;
  color: var(--rv-muted);
  font-size: var(--rv-fs-body);
  line-height: 1.45;
}

/* Arrows */
.rv-agents__arrow {
  position: absolute;
  top: 38%;
  transform: translateY(-50%);
  width: 52px;
  height: 52px;
  padding: 0;
  border: 1px solid #dbe4f3;
  border-radius: 50%;
  background: #ffffff;
  color: #14284d;
  box-shadow: 0 6px 18px rgba(20, 40, 77, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 40;
  transition: background-color 0.2s ease, transform 0.2s ease;
}
.rv-agents__arrow:hover {
  background: #f2f6ff;
}
.rv-agents__arrow:active {
  transform: translateY(-50%) scale(0.94);
}
.rv-agents__arrow:focus-visible {
  outline: 2px solid #07acff;
  outline-offset: 3px;
}
.rv-agents__arrow svg {
  width: 22px;
  height: 22px;
}
.rv-agents__arrow--prev {
  left: 4px;
}
.rv-agents__arrow--next {
  right: 4px;
}

@media (max-width: 860px) {
  .rv-agents__head {
    flex-direction: column;
    align-items: center;
    text-align: center; /* centre-align title + copy on mobile/tablet */
    gap: 14px;
    margin-bottom: 40px;
  }
  .rv-agents__heading {
    max-width: 22ch;
  }
  .rv-agents__intro {
    flex-basis: auto;
  }
}
@media (max-width: 640px) {
  .rv-agents {
    --rv-card-w: 268px;
    padding: 40px 16px 48px;
  }
  /* no font-size override here — --rv-fs-title already scales down */
  .rv-agents__arrow {
    width: 44px;
    height: 44px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .rv-agents__card {
    transition: none;
  }
}

/* ==========================================================================
   Section 4 — Interactive whiteboard features (pinned scroll-scrub)
   ========================================================================== */
.rv-wb {
  --rv-navy: #14284d;
  --rv-muted: #4c6389;
  position: relative;
  height: 300vh; /* scroll track */
  /* Dotted backdrop on the SECTION so it covers the whole track, and
     attachment: fixed so it stays locked to the viewport rather than scrolling.
     Both together are what let the pin be shorter than the viewport (see the
     640px block) without showing an undotted band under it. */
  background-color: #ffffff;
  background-image: radial-gradient(circle, #e4e8f0 1.5px, transparent 1.6px);
  background-size: 38px 38px;
  background-attachment: fixed;
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.rv-wb__pin {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: 96px 24px 32px;
  /* Light dotted backdrop (pure CSS). Lives on the pinned element so the
     pattern stays put while the section is pinned. */
  background: transparent; /* the dots live on the section — see .rv-wb */
}
.rv-wb__intro {
  flex: 0 0 auto;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  display: flex;
  gap: 40px;
  justify-content: space-between;
  align-items: flex-start;
}
.rv-wb__heading {
  color: var(--rv-navy);
  font-weight: var(--rv-fw-h2); /* medium — matches Section 3 */
  font-style: normal;
  font-size: var(--rv-fs-h2);
  line-height: 1.14;
  max-width: 22ch; /* wraps to 2 lines, like the other section headings */
  text-wrap: balance;
}
.rv-wb__subhead {
  flex: 0 1 460px;
  color: var(--rv-muted);
  font-size: var(--rv-fs-lede);
  line-height: 1.55;
  padding-top: 6px;
}
/* Stage centred in the remaining pinned space */
.rv-wb__stagewrap {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.rv-wb__stage {
  position: relative;
  width: min(86vw, 840px);
  aspect-ratio: 1568 / 984; /* matches board-after.webp */
}
/* No rounding / clipping / shadow — the device image is shown exactly as-is. */
.rv-wb__board {
  position: absolute;
  inset: 0;
}
/* `cover`, not `contain`: the two exports are 1568x1004 (before) and 1568x992
   (after). Under `contain` each was letterboxed to its own aspect, so the after
   frame rendered ~10px wider and the board edges visibly shifted during the
   crossfade. `cover` makes both fill the stage identically; the 12px height
   difference becomes a ~1% crop nobody can see. */
.rv-wb__board-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.rv-wb__board-img--after {
  opacity: 0; /* JS crossfades this in */
}
/* Floating feature panels — anchored at stage centre, positioned by JS */
.rv-wb__float {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 30%;
  z-index: 4;
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
  transform: translate(-50%, -50%);
}
.rv-wb__float img {
  width: 100%;
  height: auto;
  display: block;
  filter: drop-shadow(0 16px 30px rgba(15, 30, 60, 0.18));
}
/* All four panels render at the SAME scale: one factor (k = 0.30 css px per
   source px) applied to each asset's own canvas width, so a pixel of artwork is
   the same size in every panel. Widths are % of the 840px stage:
     pen 1119px -> 40.0%   lang 777px -> 27.7%
     curriculum 912px -> 32.6%   solver 1107px -> 39.5%
   Because these are percentages of the stage, the whole set scales together as
   the stage shrinks — no per-breakpoint overrides needed.
   If an asset is re-exported at a different canvas size, recompute its % as
   sourceWidth * 0.30 / 840. The resting scale (s1 in the JS CFG) is 0.84 for all
   four and multiplies these, so keep it uniform too. */
.rv-wb__float--pen {
  width: 40%;
}
.rv-wb__float--lang {
  width: 27.7%;
}
.rv-wb__float--curriculum {
  width: 32.6%;
}
.rv-wb__float--solver {
  width: 39.5%;
}

/* Responsive */
@media (max-width: 1024px) {
  .rv-wb {
    height: 260vh;
  }
  /* Centre heading + copy + board as one group so the leftover space in the
     pinned viewport is shared evenly instead of pooling around the board. */
  .rv-wb__pin {
    justify-content: center;
    gap: 28px;
    padding: 40px 24px 44px;
  }
  .rv-wb__stagewrap {
    flex: 0 0 auto;
  }
  /* Narrower stage + smaller panels so the floats can't clip off the edges */
  .rv-wb__stage {
    width: 78vw;
  }
  .rv-wb__float {
    width: 30%;
  }
}
/* Phones: the section DOES pin and scrub, same as desktop — the panels fly in
   from off-stage to their corners and the board colourises. The board itself is
   scaled down so the four panels have room around it instead of piling on top
   of it. (An earlier revision un-pinned here and parked the panels with CSS;
   that read as cluttered.) */
@media (max-width: 640px) {
  .rv-wb {
    height: 220vh; /* shorter travel than desktop's 300vh */
  }
  .rv-wb__pin {
    /* 77vh, not 100vh: the composition is ~394px tall and centring it in a full
       viewport left ~200px of band above and below. Shrinking the pin halves
       those bands (to ~100 / ~96) while padding-top still clears the fixed
       header (62px on phones — desktop uses 96px for the same reason).
       The dots sit on the section with attachment: fixed, so the track below the
       pin stays visually continuous. max() guards short phones, since the pin
       clips (overflow: hidden). */
    height: max(600px, 77vh);
    justify-content: center;
    gap: 16px;
    padding: 86px 16px 20px;
  }
  .rv-wb__intro {
    flex-direction: column;
    align-items: center;
    text-align: center; /* centre title + copy on mobile */
    gap: 10px;
  }
  .rv-wb__heading {
    /* 2 lines: "Designed for teachers." / "Perfected in classrooms." */
    max-width: 22ch;
  }
  .rv-wb__subhead {
    flex-basis: auto;
  }
  .rv-wb__stage {
    width: 100%;
  }
  /* Panel sizes only — positions come from the JS scrub, as on desktop. */
}

/* ==========================================================================
   Section 5 — Connected Classroom App (sticky intro + stacking card deck)
   ========================================================================== */
.rv-cc {
  --rv-navy: #14284d;
  --rv-muted: #4c6389;
  --cc-top: 88px; /* where the intro parks (clears the fixed header) */
  --cc-stick: 300px; /* where each card parks, below the intro */
  --cc-hold: 26vh; /* scroll distance a card holds before the next covers it */
  /* Trailing room so the LAST card can still reach its parked position:
     must exceed (viewport − card height − --cc-stick). */
  --cc-tail: 30vh;
  position: relative;
  background: #f4f8fd; /* shared section tint */
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.rv-cc__inner {
  /* 1248 = 1200 content + the 24px side padding below, so the content
     edge matches every other section. */
  max-width: 1248px;
  margin: 0 auto;
  padding: 0 24px 12vh;
}
/* --- Sticky intro --- */
/* Sticky intro — parks at the top and stays for the whole section.
   NB z-index sits BELOW the cards. Sticky release depends on element height,
   so the (much taller) cards un-stick well before the intro does; if the intro
   painted on top, its opaque band sliced through the card on the way out.
   Cards never rise above their own parked position, so there is no overlap
   until the section exits — at which point they correctly slide over it. */
.rv-cc__intro {
  position: sticky;
  top: var(--cc-top);
  z-index: 1;
  background: #f4f8fd; /* must match .rv-cc */
  width: 100%;
  padding: 26px 0 28px;
  display: flex;
  gap: 40px;
  justify-content: space-between;
  align-items: flex-start;
}
.rv-cc__heading {
  color: var(--rv-navy);
  font-weight: var(--rv-fw-h2);
  font-style: normal;
  font-size: var(--rv-fs-h2);
  line-height: 1.14;
  max-width: 16ch;
  text-wrap: balance;
}
.rv-cc__kicker {
  margin-top: 10px;
  color: var(--rv-navy);
  font-size: var(--rv-fs-lede);
  line-height: 1.5;
}
.rv-cc__copy {
  flex: 0 1 430px;
  color: var(--rv-muted);
  font-size: var(--rv-fs-lede);
  line-height: 1.55;
  padding-top: 6px;
}
/* --- Card deck --- */
.rv-cc__stack {
  position: relative;
  z-index: 2; /* above the intro — see the note on .rv-cc__intro */
  display: flex;
  flex-direction: column;
  gap: var(--cc-hold);
  /* The stack's own box overlaps the intro once scrolled; it is transparent,
     but without this it would swallow selection/clicks on the heading. */
  pointer-events: none;
}
.rv-cc__card {
  pointer-events: auto;
}
/* Trailing room so the LAST card can still reach its parked position and hold.
   This must be a real item, not padding: sticky is constrained by the parent's
   CONTENT box, which padding sits outside of. */
.rv-cc__stack::after {
  content: "";
  flex: 0 0 auto;
  height: var(--cc-tail);
}
/* Each card parks at the same offset, so the next one slides up and covers it
   completely — opaque, no fade (matches the reference). Later cards paint on
   top of earlier ones by DOM order. */
.rv-cc__card {
  position: sticky;
  top: var(--cc-stick);
}
.rv-cc__card-inner {
  display: flex;
  gap: 30px;
  align-items: stretch;
  padding: 26px;
  border-radius: var(--rv-radius-card);
  background: #ffffff;
  box-shadow: 0 18px 44px rgba(20, 40, 77, 0.1);
}
.rv-cc__card-text {
  flex: 1 1 44%;
  display: flex;
  flex-direction: column;
  padding: 6px 6px 14px 10px;
}
.rv-cc__card-icons {
  width: 190px;
  max-width: 60%;
  height: auto;
  display: block;
}
.rv-cc__card-copy {
  margin-top: auto; /* title + desc sit at the bottom, as in the design */
}
.rv-cc__card-title {
  color: var(--rv-navy);
  font-weight: 500;
  font-style: normal;
  font-size: clamp(1.4rem, 2.4vw, 2rem);
  line-height: 1.2;
}
.rv-cc__card-desc {
  margin-top: 12px;
  max-width: 42ch;
  color: var(--rv-muted);
  font-size: var(--rv-fs-body);
  line-height: 1.55;
}
.rv-cc__card-media {
  flex: 1 1 56%;
  border-radius: var(--rv-radius-media);
  overflow: hidden;
}
.rv-cc__card-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (max-width: 1024px) {
  .rv-cc {
    --cc-stick: 250px;
  }
  .rv-cc__card-inner {
    gap: 20px;
    padding: 20px;
  }
  .rv-cc__card-icons {
    width: 150px;
  }
}

/* Phones: cards still stack, but the intro scrolls away rather than staying
   pinned — a sticky intro plus a full-width card doesn't fit in a phone
   viewport, and the card would slide underneath it. */
@media (max-width: 640px) {
  .rv-cc {
    --cc-stick: 76px;
    --cc-hold: 16vh;
    /* Cards are short relative to a tall phone viewport, so the last one needs
       a lot more trailing room to reach its parked position. */
    --cc-tail: 58vh;
  }
  .rv-cc__inner {
    padding: 0 16px 48px;
  }
  .rv-cc__intro {
    position: static;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    padding: 40px 0 24px;
  }
  .rv-cc__heading {
    max-width: 100%; /* "Connected Classroom App" fits on one line at 24px */
  }
  .rv-cc__copy {
    flex-basis: auto;
    padding-top: 0;
  }
  /* cards keep position: sticky from the base rule so they stack */
  .rv-cc__card-inner {
    flex-direction: column;
    gap: 18px;
    padding: 16px;
  }
  .rv-cc__card-text {
    padding: 4px 4px 0;
  }
  .rv-cc__card-copy {
    margin-top: 16px;
  }
  .rv-cc__card-icons {
    width: 140px;
  }
  .rv-cc__card-media {
    order: -1; /* image above the copy on phones */
  }
}

/* ==========================================================================
   Section 6 — VisionX (auto-advancing feature list + crossfading screenshot)
   ========================================================================== */
.rv-vx {
  --rv-navy: #14284d;
  --rv-muted: #4c6389;
  --rv-idle: #9aa9c2;
  --rv-rule: #dbe3ef;
  background: #ffffff;
  padding: 64px 24px 72px;
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.rv-vx__inner {
  max-width: 1200px;
  margin: 0 auto;
}
/* --- Brand lockup --- */
/* Head: heading left, sub-copy right — the same flex row used by the agents,
   whiteboard, connected and hardware sections. */
.rv-vx__head {
  display: flex;
  gap: 40px;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 34px;
}
.rv-vx__subtitle {
  color: var(--rv-navy);
  font-weight: var(--rv-fw-h2);
  font-style: normal;
  font-size: var(--rv-fs-h2);
  line-height: 1.14;
  max-width: 22ch;
}
.rv-vx__lede {
  flex: 0 1 430px;
  color: var(--rv-muted);
  font-size: var(--rv-fs-lede);
  line-height: 1.55;
  padding-top: 6px;
}

/* --- Two-column body --- */

.rv-vx__body {
  display: flex;
  gap: 44px;
  align-items: center;
}
/* No panel styling here — each screenshot asset already ships with its own
   light rounded backdrop and padding, which is also why the column is capped:
   at full width that built-in padding left a lot of dead space beside the list. */
.rv-vx__media {
  flex: 0 1 52%;
  max-width: 540px;
  min-width: 0;
}
/* Screenshots stack and crossfade; the first one holds the height. */
.rv-vx__shots {
  position: relative;
}
.rv-vx__shot {
  width: 100%;
  height: auto;
  display: block;
  opacity: 0;
  transition: opacity 0.5s ease;
}
.rv-vx__shot:not(:first-child) {
  position: absolute;
  inset: 0;
  height: 100%;
  object-fit: contain;
}
.rv-vx__shot.is-active {
  opacity: 1;
}
/* --- Feature list --- */
.rv-vx__list {
  flex: 1 1 44%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.rv-vx__item {
  display: flex;
  gap: 18px;
  align-items: stretch;
  padding: 18px 0;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.rv-vx__item:focus-visible {
  outline: 2px solid #2f6bff;
  outline-offset: 4px;
  border-radius: 6px;
}
/* Left rule; the fill doubles as the auto-advance timer. */
.rv-vx__bar {
  position: relative;
  flex: 0 0 3px;
  border-radius: 3px;
  background: var(--rv-rule);
  overflow: hidden;
}
.rv-vx__bar-fill {
  position: absolute;
  inset: 0;
  background: var(--rv-navy);
  transform: scaleY(0);
  transform-origin: top center;
}
.rv-vx__item-title {
  display: block;
  color: var(--rv-idle);
  font-weight: var(--rv-fw-title);
  font-style: normal;
  font-size: var(--rv-fs-title);
  line-height: 1.3;
  transition: color 0.3s ease;
}
.rv-vx__item.is-active .rv-vx__item-title {
  color: var(--rv-navy);
}
/* Description only shows on the active item */
.rv-vx__item-desc {
  display: block;
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  color: var(--rv-muted);
  font-size: var(--rv-fs-body);
  line-height: 1.5;
  transition: max-height 0.4s ease, opacity 0.3s ease, margin-top 0.4s ease;
}
.rv-vx__item.is-active .rv-vx__item-desc {
  max-height: 8em;
  opacity: 1;
  margin-top: 8px;
}

@media (max-width: 1024px) {
  .rv-vx {
    padding: 48px 24px 56px;
  }
  .rv-vx__body {
    gap: 32px;
  }
  .rv-vx__head {
    margin-bottom: 28px;
  }
}
@media (max-width: 860px) {
  .rv-vx__body {
    flex-direction: column;
    align-items: center;
    gap: 24px;
  }
  .rv-vx__media {
    flex: 0 0 auto;
    width: 100%;
    max-width: 560px;
  }
  .rv-vx__list {
    width: 100%;
  }
}
@media (max-width: 640px) {
  .rv-vx {
    padding: 40px 16px 48px;
  }
  .rv-vx__head {
    flex-direction: column;
    align-items: center;
    text-align: center; /* matches the other stacked heads */
    gap: 12px;
    margin-bottom: 22px;
  }
  .rv-vx__subtitle {
    max-width: 22ch;
  }
  /* Column flex: basis applies to height, so 430px would force a 430px-tall
     paragraph. Same reset the agents/hardware ledes use. */
  .rv-vx__lede {
    flex-basis: auto;
    padding-top: 0;
  }
  .rv-vx__item {
    gap: 14px;
    padding: 10px 0;
  }
}
/* No auto-advance without motion: show every description so nothing is hidden
   behind a timer the user can't perceive. */
@media (prefers-reduced-motion: reduce) {
  .rv-vx__shot {
    transition: none;
  }
  .rv-vx__item-desc {
    max-height: 8em;
    opacity: 1;
    margin-top: 8px;
    transition: none;
  }
  .rv-vx__item-title {
    color: var(--rv-navy);
  }
}

/* ==========================================================================
   Section 7 — Powered by Next-Gen Hardware (bento grid)
   ========================================================================== */
.rv-hw {
  --rv-navy: #14284d;
  --rv-muted: #4c6389;
  background: #f4f8fd;
  padding: 64px 24px 72px;
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.rv-hw__inner {
  max-width: 1200px;
  margin: 0 auto;
}
.rv-hw__head {
  display: flex;
  gap: 40px;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 34px;
}
.rv-hw__heading {
  color: var(--rv-navy);
  font-weight: var(--rv-fw-h2);
  font-style: normal;
  font-size: var(--rv-fs-h2);
  line-height: 1.14;
  max-width: 16ch;
  text-wrap: balance;
}
.rv-hw__intro {
  flex: 0 1 380px;
  color: var(--rv-muted);
  font-size: var(--rv-fs-lede);
  line-height: 1.55;
  padding-top: 6px;
}

/* --- Bento grid: tall | stacked pair | tall --- */
.rv-hw__grid {
  display: grid;
  grid-template-columns: 1fr 1fr 0.94fr;
  grid-template-rows: repeat(2, minmax(0, 1fr));
  gap: 16px;
  height: clamp(520px, 46vw, 650px);
}
.rv-hw__card--octa {
  grid-column: 1;
  grid-row: 1 / span 2;
}
.rv-hw__card--camera {
  grid-column: 2;
  grid-row: 1;
}
.rv-hw__card--mic {
  grid-column: 2;
  grid-row: 2;
}
.rv-hw__card--npu {
  grid-column: 3;
  grid-row: 1 / span 2;
}

.rv-hw__card {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  position: relative;
  isolation: isolate; /* keep the gradient layer's z-index local to the card */
  border-radius: var(--rv-radius-card);
  /* Resting face. The product renders all carry an alpha channel, so they
     composite straight onto it. */
  background: #dedede;
  /* Same shadow as the Connected Classroom cards */
  box-shadow: 0 18px 44px rgba(20, 40, 77, 0.1);
  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 0.35s ease;
}
/* Hover face. A solid colour cannot be transitioned into a gradient, so the
   gradient lives on its own layer and its opacity is faded instead. Clipped to
   the card radius by the card's overflow: hidden. */
.rv-hw__card::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 0;
  background: linear-gradient(180deg, #3b75d7 0%, #009eff 100%);
  opacity: 0;
  transition: opacity 0.35s ease;
}
.rv-hw__media {
  position: relative;
  z-index: 1; /* above .rv-hw__card::before */
  flex: 1 1 auto;
  min-height: 0;
}
.rv-hw__media img {
  --img-scale: 1; /* per-card resting scale; hover multiplies it */
  display: block;
  width: 100%;
  height: 100%;
  transform: scale(var(--img-scale));
  transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Touch devices can't hover, so the same highlight follows the scroll instead:
   initHardwareSpotlight() marks whichever card is nearest the middle of the
   viewport as .is-active, and it hands off to the next card as you scroll.
   Gated to (hover: none) so it can never fight the real hover state above.
   Deliberately no lift or image scale here — the card is already moving with
   the scroll, and adding transform on top read as jitter. */
@media (hover: none) {
  .rv-hw__card.is-active {
    box-shadow: 0 28px 60px rgba(20, 40, 77, 0.17);
  }
  .rv-hw__card.is-active::before {
    opacity: 1;
  }
  .rv-hw__card.is-active .rv-hw__label {
    color: #ffffff;
  }
}

/* Hover: the card lifts and the render pushes in slightly (clipped by the
   card's overflow). Only where hover actually exists, so tapping on touch
   devices doesn't leave a card stuck in its hovered state. */
@media (hover: hover) {
  .rv-hw__card:hover {
    transform: translateY(-6px);
    box-shadow: 0 28px 60px rgba(20, 40, 77, 0.17);
  }
  .rv-hw__card:hover .rv-hw__media img {
    transform: scale(calc(var(--img-scale) * 1.05));
  }
  .rv-hw__card:hover::before {
    opacity: 1;
  }
  /* Navy on #dedede would not hold on the blue gradient */
  .rv-hw__card:hover .rv-hw__label {
    color: #ffffff;
  }
}
.rv-hw__label {
  position: relative;
  z-index: 1; /* above .rv-hw__card::before */
  flex: 0 0 auto;
  padding: 0 26px 26px;
  color: var(--rv-navy);
  transition: color 0.35s ease;
  font-weight: var(--rv-fw-title);
  font-style: normal;
  font-size: var(--rv-fs-title);
  line-height: 1.25;
}

/* The two tall cards bleed their render to the card edges; the two small ones
   keep the product centred with breathing room. */
.rv-hw__card--octa .rv-hw__media img {
  object-fit: cover;
}
/* The NPU render sits in a lot of empty canvas (231px of its 1254px height is
   above the chip), so at its natural size it floated with dead card above and
   below. It is scaled up to fill the card and scaled from the TOP edge, not the
   centre, so the growth goes downward: that keeps clear space under the label and
   lets the chip bleed off the bottom and sides, as in the design.
   Scale is tied to this asset's padding — re-measure if the render changes. */
.rv-hw__card--npu .rv-hw__media img {
  object-fit: cover;
  transform-origin: center top;
  --img-scale: 1.4;
}
.rv-hw__card--camera .rv-hw__media {
  padding: 26px 26px 8px;
}
.rv-hw__card--camera .rv-hw__media img {
  object-fit: contain;
}
/* The mic render carries a lot of empty canvas around a thin bar, so it is
   scaled up (and clipped by the card) to read at the size in the design. */
.rv-hw__card--mic .rv-hw__media {
  padding: 10px 0 0;
}
/* The mic render's transparent margins are asymmetric (74px left vs 56px right),
   so `contain` — which letterboxes the raster inside the card before scaling —
   left a visible gap on the left while the right edge sat flush. `cover` makes
   the raster width track the card width at every breakpoint, so the artwork
   bleeds past both edges regardless of the card's aspect ratio. */
.rv-hw__card--mic .rv-hw__media img {
  object-fit: cover;
  --img-scale: 1.32;
}
/* NPU reads top-down, so its label needs the padding above instead. */
.rv-hw__card--npu .rv-hw__label {
  padding: 26px 26px 0;
}

@media (max-width: 1024px) {
  .rv-hw {
    padding: 48px 24px 56px;
  }
  /* Explicit row heights: with auto rows the cards size to the images'
     intrinsic height and balloon. */
  .rv-hw__grid {
    gap: 14px;
    height: auto;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 280px 280px 320px;
  }
  .rv-hw__card--npu {
    grid-column: 1 / span 2;
    grid-row: 3;
  }
  .rv-hw__label {
    padding: 0 20px 20px;
  }
  .rv-hw__card--npu .rv-hw__label {
    padding: 20px 20px 0;
  }
}
@media (max-width: 640px) {
  .rv-hw {
    padding: 40px 16px 48px;
  }
  .rv-hw__head {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    margin-bottom: 24px;
  }
  .rv-hw__intro {
    flex-basis: auto;
    padding-top: 0;
  }
  /* One card per row, all the same height. Don't give a card an explicit
     height here — anything taller than the row overflows onto the next card. */
  .rv-hw__grid {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    grid-auto-rows: 300px;
  }
  .rv-hw__card--octa,
  .rv-hw__card--camera,
  .rv-hw__card--mic,
  .rv-hw__card--npu {
    grid-column: 1;
    grid-row: auto;
  }
}

/* Keep the shadow feedback, drop the movement */
@media (prefers-reduced-motion: reduce) {
  .rv-hw__card,
  .rv-hw__media img {
    transition: none;
  }
  .rv-hw__card:hover {
    transform: none;
  }
  .rv-hw__card:hover .rv-hw__media img {
    transform: scale(var(--img-scale));
  }
}

/* ==========================================================================
   Section 8 — Certified by Google (3-up trust badges)
   ========================================================================== */
.rv-gc {
  --rv-navy: #14284d;
  background: #ffffff;
  padding: 64px 24px 72px;
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.rv-gc__inner {
  max-width: 1200px;
  margin: 0 auto;
}
.rv-gc__heading {
  /* Capped so the longer copy breaks into two balanced lines instead of one
     very wide single line across the 1200px inner. */
  max-width: 24ch;
  margin: 0 auto 56px;
  text-align: center;
  color: var(--rv-navy);
  font-weight: var(--rv-fw-h2);
  font-style: normal;
  font-size: var(--rv-fs-h2);
  line-height: 1.2;
  text-wrap: balance;
}
.rv-gc__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.rv-gc__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
/* Fixed box so the three badges sit on a common baseline despite the
   renders having different amounts of built-in padding. */
.rv-gc__icon {
  height: 132px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 26px;
}
.rv-gc__icon img {
  max-height: 100%;
  width: auto;
  display: block;
  object-fit: contain;
}
/* Per-badge optical sizing — the Android head is short and wide, the shield
   tall and narrow, so equal box heights alone don't read as balanced. */
.rv-gc__icon--apps img {
  max-height: 96%;
}
.rv-gc__icon--protect img {
  max-height: 100%;
}
.rv-gc__icon--android img {
  max-height: 112%;
}
/* 19ch and NO balance: that reproduces the design's break points
   ("Built-In Enterprise / Security", "Future-Ready OS / Performance").
   Balancing re-optimises them into different, less natural splits. */
.rv-gc__title {
  max-width: 19ch;
  color: var(--rv-navy);
  font-weight: var(--rv-fw-title);
  font-style: normal;
  font-size: var(--rv-fs-title);
  line-height: 1.3;
}

@media (max-width: 1024px) {
  .rv-gc {
    padding: 48px 24px 56px;
  }
  .rv-gc__heading {
    margin-bottom: 40px;
  }
  .rv-gc__grid {
    gap: 28px;
  }
  .rv-gc__icon {
    height: 108px;
    margin-bottom: 20px;
  }
}
@media (max-width: 640px) {
  .rv-gc {
    padding: 40px 16px 48px;
  }
  .rv-gc__heading {
    margin-bottom: 32px;
  }
  .rv-gc__grid {
    grid-template-columns: 1fr;
    gap: 36px;
  }
  .rv-gc__icon {
    height: 96px;
    margin-bottom: 16px;
  }
}

/* ==========================================================================
   Section 9 — Customer Testimonials (click-to-play video carousel)
   ========================================================================== */
.rv-tm {
  --rv-navy: #14284d;
  --rv-muted: #4c6389;
  /* Matches the 1200px + 24px container the other sections use, so the first
     card's left edge lines up with every other section's content edge while
     the track itself still scrolls full-bleed. */
  --rv-gutter: max(24px, calc((100% - 1200px) / 2));
  background: #f4f8fd; /* same tint as the hardware section */
  padding: 64px 0 72px;
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
/* Same size/weight as the other section headings */
.rv-tm__heading {
  margin: 0 auto 36px;
  padding: 0 24px;
  text-align: center;
  color: var(--rv-navy);
  font-weight: var(--rv-fw-h2);
  font-style: normal;
  font-size: var(--rv-fs-h2);
  line-height: 1.14;
}
.rv-tm__viewport {
  position: relative;
}
/* Native horizontal scroller: snaps, works with touch/trackpad, arrows just
   nudge it by one card. */
.rv-tm__track {
  display: flex;
  gap: 24px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding: 6px var(--rv-gutter) 26px;
  /* Without this, snap-align:start snaps cards to the scrollport edge and the
     track silently scrolls past its own left padding, killing the alignment. */
  scroll-padding-inline: var(--rv-gutter);
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.rv-tm__track::-webkit-scrollbar {
  display: none;
}
.rv-tm__card {
  flex: 0 0 min(84vw, 540px);
  scroll-snap-align: start;
}
/* Gradient border, same treatment as the EduAI agent cards */
.rv-tm__card-inner {
  height: 100%;
  border: 2px solid transparent;
  border-radius: var(--rv-radius-card);
  padding: 14px;
  background:
    linear-gradient(#ffffff, #ffffff) padding-box,
    linear-gradient(155deg, #7b07ff 0%, #07acff 100%) border-box;
}

/* --- Video facade --- */
.rv-tm__video {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: var(--rv-radius-media);
  overflow: hidden;
  background: #0e1116;
}
.rv-tm__poster,
.rv-tm__video iframe,
.rv-tm__video video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  object-fit: cover;
}
/* Stand-in poster until real thumbnails land */
.rv-tm__poster--placeholder {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 10px;
  padding: clamp(18px, 3.4vw, 34px);
  background: linear-gradient(135deg, #1b2430 0%, #0e1116 55%, #131a24 100%);
}
.rv-tm__brand {
  position: absolute;
  top: clamp(14px, 2.4vw, 22px);
  right: clamp(14px, 2.4vw, 22px);
  color: #ffffff;
  font-size: clamp(0.8rem, 1.3vw, 1rem);
  font-weight: 500;
}
.rv-tm__poster-title {
  max-width: 14ch;
  color: #ffffff;
  font-weight: 700;
  font-size: clamp(1.1rem, 2.6vw, 1.85rem);
  line-height: 1.22;
}
.rv-tm__rule {
  width: clamp(70px, 12vw, 130px);
  height: 2px;
  background: rgba(255, 255, 255, 0.85);
}
.rv-tm__winning {
  color: #ffffff;
  font-size: clamp(0.75rem, 1.3vw, 1rem);
  font-weight: 600;
}
.rv-tm__winning b {
  color: #ffb020;
  font-weight: 700;
}
/* Play button */
.rv-tm__play {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 68px;
  height: 68px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.94);
  color: #14284d;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
  transition: transform 0.25s ease, background-color 0.25s ease;
}
.rv-tm__play svg {
  width: 30px;
  height: 30px;
  margin-left: 3px;
}
.rv-tm__play:hover {
  transform: translate(-50%, -50%) scale(1.07);
}
.rv-tm__play:focus-visible {
  outline: 3px solid #07acff;
  outline-offset: 3px;
}
.rv-tm__play[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
}
.rv-tm__play[disabled]:hover {
  transform: translate(-50%, -50%);
}
/* Once playing, the facade is replaced and the button is gone */
.rv-tm__video.is-playing .rv-tm__poster,
.rv-tm__video.is-playing .rv-tm__play {
  display: none;
}

/* --- Card footer --- */
.rv-tm__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 6px 6px;
}
.rv-tm__person {
  color: var(--rv-navy);
  font-size: clamp(0.95rem, 1.4vw, 1.25rem);
  line-height: 1.35;
}
/* Institution reads first, country beneath it as supporting detail. */
.rv-tm__institution,
.rv-tm__country {
  display: block;
}
.rv-tm__institution {
  font-weight: 600;
}
.rv-tm__country {
  margin-top: 2px;
  color: var(--rv-muted);
  font-size: 0.85em;
}
.rv-tm__logo {
  flex: 0 0 auto;
  width: 62px;
  height: 62px;
  border-radius: 50%;
  object-fit: contain;
}
.rv-tm__logo--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #eef2f8;
  border: 1px dashed #c3cede;
  color: #8798b3;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.06em;
}

/* --- Controls: below the track, right-aligned to the content edge --- */
.rv-tm__nav {
  display: flex;
  justify-content: flex-end;
  gap: 14px;
  padding: 4px var(--rv-gutter) 0;
}
.rv-tm__arrow {
  width: 52px;
  height: 52px;
  padding: 0;
  border: 1px solid #c9d4e6;
  border-radius: 50%;
  background: transparent;
  color: var(--rv-navy);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background-color 0.2s ease, border-color 0.2s ease,
    opacity 0.2s ease;
}
.rv-tm__arrow:hover {
  background: #f2f6ff;
  border-color: var(--rv-navy);
}
.rv-tm__arrow:focus-visible {
  outline: 2px solid #07acff;
  outline-offset: 3px;
}
.rv-tm__arrow svg {
  width: 22px;
  height: 22px;
}
/* Dim rather than remove, so the pair doesn't jump around */
.rv-tm__arrow[hidden] {
  display: flex;
  opacity: 0.35;
  pointer-events: none;
}

@media (max-width: 1024px) {
  .rv-tm {
    padding: 48px 0 56px;
  }
  .rv-tm__heading {
    margin-bottom: 30px;
  }
}
@media (max-width: 640px) {
  .rv-tm {
    --rv-gutter: 16px; /* other sections drop to 16px side padding here */
    padding: 40px 0 48px;
  }
  .rv-tm__track {
    gap: 16px;
  }
  .rv-tm__card {
    flex-basis: 86vw;
  }
  .rv-tm__card-inner {
    padding: 10px;
  }
  .rv-tm__play {
    width: 54px;
    height: 54px;
  }
  .rv-tm__play svg {
    width: 24px;
    height: 24px;
  }
  .rv-tm__logo {
    width: 50px;
    height: 50px;
  }
  /* Arrows are below the track now, so they can stay on mobile too */
  .rv-tm__arrow {
    width: 44px;
    height: 44px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .rv-tm__track {
    scroll-behavior: auto;
  }
  .rv-tm__play {
    transition: none;
  }
}

/* ----- Testimonial video lightbox -----------------------------------------
   Fixed overlay holding a 16:9 dialog. The player itself is injected by JS on
   open and torn out on close, so nothing third-party loads until a visitor
   asks for it and audio genuinely stops when the modal closes. */
.rv-tm__modal[hidden] {
  display: none;
}
.rv-tm__modal {
  position: fixed;
  z-index: 1000;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.rv-tm__modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(9, 18, 35, 0.82);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.rv-tm__modal-dialog {
  position: relative;
  width: min(100%, 1100px);
  aspect-ratio: 16 / 9;
  border-radius: var(--rv-radius-card);
  overflow: hidden;
  background: #000;
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);
}
.rv-tm__modal-frame,
.rv-tm__modal-frame iframe,
.rv-tm__modal-frame video {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}
.rv-tm__modal-close {
  position: absolute;
  z-index: 2;
  top: 10px;
  right: 10px;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  cursor: pointer;
  transition: background-color 0.2s ease;
}
.rv-tm__modal-close:hover {
  background: rgba(0, 0, 0, 0.8);
}
.rv-tm__modal-close:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}
.rv-tm__modal-close svg {
  width: 20px;
  height: 20px;
}
/* Set on <body> while the lightbox is open so the page behind cannot scroll */
body.rv-tm-modal-open {
  overflow: hidden;
}

@media (max-width: 640px) {
  .rv-tm__modal {
    padding: 12px;
  }
  .rv-tm__modal-close {
    top: 6px;
    right: 6px;
    width: 34px;
    height: 34px;
  }
}

/* ==========================================================================
   Section 10 — Support beyond software (3-up cards)
   ========================================================================== */
.rv-sp {
  --rv-navy: #14284d;
  --rv-muted: #4c6389;
  background: #ffffff;
  padding: 64px 24px 72px;
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.rv-sp__inner {
  max-width: 1200px;
  margin: 0 auto;
}
/* No balance, and 24ch (a `ch` is wider than an average lowercase glyph) to
   reproduce the design's break: "Support beyond software that / only we provide" */
.rv-sp__heading {
  max-width: 24ch;
  margin: 0 auto 44px;
  text-align: center;
  color: var(--rv-navy);
  font-weight: var(--rv-fw-h2);
  font-style: normal;
  font-size: var(--rv-fs-h2);
  line-height: 1.14;
}
.rv-sp__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.rv-sp__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 34px 30px 38px;
  border-radius: var(--rv-radius-card);
  background: #ffffff;
  /* White on white — the shadow is what defines the card */
  box-shadow: 0 10px 34px rgba(20, 40, 77, 0.09);
}
.rv-sp__icon {
  width: 90px;
  height: 90px;
  display: block;
  /* No grayscale filter. It was here because the previous three icons shipped
     in different hues (blue / purple / amber) and needed flattening to read as
     one set. The current set is already a single palette — navy #203A66 on a
     #ECF8FF circle — so desaturating would only grey out the intended design. */
}
.rv-sp__title {
  margin-top: 18px;
  max-width: 18ch;
  color: var(--rv-navy);
  font-weight: var(--rv-fw-title);
  font-style: normal;
  font-size: var(--rv-fs-title);
  line-height: 1.3;
}
.rv-sp__desc {
  margin-top: 12px;
  max-width: 26ch;
  color: var(--rv-muted);
  font-size: var(--rv-fs-body);
  line-height: 1.5;
}

@media (max-width: 1024px) {
  .rv-sp {
    padding: 48px 24px 56px;
  }
  .rv-sp__heading {
    margin-bottom: 32px;
  }
  .rv-sp__grid {
    gap: 18px;
  }
  .rv-sp__card {
    padding: 26px 18px 30px;
  }
  .rv-sp__icon {
    width: 76px;
    height: 76px;
  }
}
@media (max-width: 640px) {
  .rv-sp {
    padding: 40px 16px 48px;
  }
  .rv-sp__heading {
    max-width: 22ch;
    margin-bottom: 26px;
  }
  .rv-sp__grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  .rv-sp__card {
    padding: 28px 24px 30px;
  }
}

/* Reduced motion — no pin, coloured board shown, floats hidden */
@media (prefers-reduced-motion: reduce) {
  .rv-wb {
    height: auto;
  }
  .rv-wb__pin {
    position: relative;
    height: auto;
    padding: 72px 24px;
  }
  .rv-wb__board-img--after {
    opacity: 1;
  }
  .rv-wb__float {
    display: none;
  }
}
