/* ── Motion: "set in place" ────────────────────────────────────────────────
   The hero composes itself once on load, line by line, the way type is set:
   each line rises into its slot from behind a hard edge rather than fading in.
   On scroll, sections settle quietly, and only content that genuinely is a
   list (the stat row, the expertise columns, the client rows, the badges)
   staggers, with the total delay capped.

   Safety contract: every element is visible by default. The `.rv-on` class is
   added to <html> in <head> before first paint, and only when the visitor has
   not asked for reduced motion. No script, or reduced motion, means nothing is
   ever hidden.
   ───────────────────────────────────────────────────────────────────────── */

:root {
  --rv-ease: cubic-bezier(.16, 1, .3, 1);
}

/* Settle: the quiet default. Opacity plus a short rise. */
.rv-on [data-rv="up"] {
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition:
    opacity 620ms var(--rv-ease),
    transform 620ms var(--rv-ease);
  transition-delay: var(--rv-delay, 0ms);
}
.rv-on [data-rv="up"].rv-in {
  opacity: 1;
  transform: none;
}

/* Line-set: a headline line is revealed upward from behind a hard edge.
   clip-path does the occluding, so the type is uncovered rather than faded,
   which is what makes it read as setting rather than appearing. The extra
   .09em of padding is clip headroom for ascenders at line-height 1.02; the
   matching negative margin keeps the layout byte-identical. */
.rv-line {
  display: block;
  padding-block: .09em;
  margin-block: -.09em;
}
.rv-on [data-rv="line"] {
  clip-path: inset(100% 0 0 0);
  transform: translate3d(0, .14em, 0);
  transition:
    clip-path 620ms var(--rv-ease),
    transform 620ms var(--rv-ease);
  transition-delay: var(--rv-delay, 0ms);
}
.rv-on [data-rv="line"].rv-in {
  clip-path: inset(0 0 0 0);
  transform: none;
}

/* Frame: the portrait uncrops upward and settles out of a slight overscale.
   One gesture, two properties, 1.2s. This is the slowest thing on the site
   and the only place that budget is spent. */
.rv-on [data-rv="frame"] {
  clip-path: inset(100% 0 0 0);
  transition: clip-path 800ms var(--rv-ease);
  transition-delay: var(--rv-delay, 0ms);
}
.rv-on [data-rv="frame"].rv-in {
  clip-path: inset(0 0 0 0);
}
.rv-on [data-rv="frame"] > img {
  transform: scale(1.06);
  transition: transform 1200ms var(--rv-ease);
  transition-delay: var(--rv-delay, 0ms);
}
.rv-on [data-rv="frame"].rv-in > img {
  transform: none;
}

/* ── Feedback ──────────────────────────────────────────────────────────────
   The certification badges became links in this pass. A hover lift is how a
   visitor learns which ones are clickable, since the four unlinked badges
   look identical at rest. Instant colour swaps on the article cards get a
   duration so they read as responsive rather than as a repaint. */

a.gh-badge {
  transition:
    transform 150ms var(--rv-ease),
    opacity 150ms ease;
}
a.gh-badge:hover {
  transform: translateY(-3px);
}
a.gh-badge:focus-visible {
  transform: translateY(-3px);
}

.tl-art h3,
.tl-art .read {
  transition: color 150ms ease;
}

@media (prefers-reduced-motion: reduce) {
  a.gh-badge,
  a.gh-badge:hover,
  a.gh-badge:focus-visible {
    transform: none;
    transition: none;
  }
  .tl-art h3,
  .tl-art .read {
    transition: none;
  }
}
