/* ==========================================================================
   6Analytix brand system
   Ported from the client-approved Direction A homepage design.
   Tokens are emitted as CSS custom properties by functions.php
   (sixanalytix_output_css_vars) so this file and Elementor share one source.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Base
   -------------------------------------------------------------------------- */

body {
  font-family: var(--sa-font);
  background: var(--sa-bg-dark);
  color: var(--sa-text-on-dark);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--sa-font);
  margin: 0;
  text-wrap: balance;
  letter-spacing: -0.03em;
  line-height: 1.14;
}

p {
  margin: 0;
  text-wrap: pretty;
}

/* Headline weight is deliberately 500, not 700. The client specifically
   asked for a lighter hero weight after reviewing the first pass. */
h1 {
  font-weight: 500;
  letter-spacing: -0.038em;
  line-height: 1.05;
}

h2 { font-weight: 600; }
h3 { font-weight: 600; letter-spacing: -0.012em; line-height: 1.3; }

a {
  color: var(--sa-accent);
  text-decoration: none;
  transition: color 200ms cubic-bezier(0.4, 0, 0.2, 1);
}

a:hover { color: var(--sa-accent-dark); }

a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--sa-accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* --------------------------------------------------------------------------
   2. Typographic scale
   -------------------------------------------------------------------------- */

.sa-display   { font-size: clamp(38px, 4.4vw, 60px); font-weight: 500; letter-spacing: -0.038em; line-height: 1.05; }
.sa-h2        { font-size: clamp(28px, 2.6vw, 38px); font-weight: 600; }
.sa-h3        { font-size: clamp(18px, 1.4vw, 21px); font-weight: 600; }
.sa-lede      { font-size: 16.5px; line-height: 1.6; }
.sa-body      { font-size: 15px; line-height: 1.62; }
.sa-small     { font-size: 13.5px; line-height: 1.55; }

/* Section eyebrow. Used sparingly, not above every section. */
.sa-eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sa-accent);
}

/* --------------------------------------------------------------------------
   3. Theme surfaces
   -------------------------------------------------------------------------- */

.sa-section-dark  { background: var(--sa-bg-dark); color: var(--sa-text-on-dark); }
.sa-section-alt   { background: var(--sa-bg-dark-alt); color: var(--sa-text-on-dark); }
.sa-section-light { background: var(--sa-bg-light); color: var(--sa-text-on-light); }

.sa-section-light h1,
.sa-section-light h2,
.sa-section-light h3 { color: var(--sa-text-on-light); }

/* Dark sections need this stated explicitly. UiCore sets its own heading
   colour, which wins over inheriting from body and renders headings
   near-invisible on the navy sections. */
.sa-section-dark h1,
.sa-section-dark h2,
.sa-section-dark h3,
.sa-section-alt h1,
.sa-section-alt h2,
.sa-section-alt h3 { color: var(--sa-text-on-dark); }

.sa-section-light p  { color: var(--sa-text-muted-lt); }
.sa-section-dark p,
.sa-section-alt p    { color: var(--sa-text-muted-dk); }

.sa-section-light .sa-eyebrow { color: var(--sa-accent-dark); }

/* --------------------------------------------------------------------------
   4. Buttons
   Small, uppercase, letterspaced. Teal is reserved for the single primary
   action per view; the header CTA is cream so it does not compete.
   -------------------------------------------------------------------------- */

.sa-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: 40px;
  padding: 0 20px;
  border-radius: 100px;
  border: 1px solid transparent;
  font-family: var(--sa-font);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 200ms cubic-bezier(0.4, 0, 0.2, 1),
              color 200ms cubic-bezier(0.4, 0, 0.2, 1),
              transform 120ms ease;
}

.sa-btn:active { transform: translateY(1px); }

.sa-btn--teal        { background: var(--sa-accent); color: #06231a; }
.sa-btn--teal:hover  { background: #27c295; color: #06231a; }

.sa-btn--cream       { background: #f4f6f2; color: #09131f; }
.sa-btn--cream:hover { background: #ffffff; color: #09131f; }

.sa-btn--glass       { background: rgba(255,255,255,0.08); color: var(--sa-text-on-dark); border-color: rgba(255,255,255,0.22); }
.sa-btn--glass:hover { background: rgba(255,255,255,0.14); color: #ffffff; }

.sa-btn--outline       { background: transparent; color: var(--sa-text-on-light); border-color: rgba(14,30,51,0.22); }
.sa-btn--outline:hover { background: rgba(14,30,51,0.05); color: var(--sa-text-on-light); }

/* --------------------------------------------------------------------------
   5. Cards
   One radius system: 14px cards, 100px pills. Do not mix in others.
   -------------------------------------------------------------------------- */

.sa-card {
  background: var(--sa-bg-card-dark);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 14px;
  padding: 26px 24px;
}

.sa-card--light {
  background: #ffffff;
  border: 1px solid rgba(14,30,51,0.09);
  border-radius: 16px;
}

.sa-card--glass {
  background: rgba(14,30,54,0.62);
  border: 1px solid rgba(255,255,255,0.16);
  backdrop-filter: blur(18px) saturate(120%);
  -webkit-backdrop-filter: blur(18px) saturate(120%);
  border-radius: 16px;
}

/* Floating industry pills used over the hero portrait. Decorative only. */
.sa-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px 5px 6px;
  border-radius: 100px;
  background: rgba(255,255,255,0.10);
  border: 1px solid rgba(255,255,255,0.14);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  font-size: 10px;
  font-weight: 500;
  color: rgba(255,255,255,0.86);
  white-space: nowrap;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   6. Motion
   The brief called for real motion. Everything is transform/opacity only so
   it stays on the compositor, and everything collapses under reduced motion.
   -------------------------------------------------------------------------- */

/* Scoped to .sa-js (set inline in <head> before first paint). Without JS the
   content renders normally rather than being stranded at opacity 0. */
.sa-js .sa-reveal {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 700ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 700ms cubic-bezier(0.16, 1, 0.3, 1);
  will-change: transform, opacity;
}

.sa-js .sa-reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Staggered children, driven by an index custom property set in markup. */
.sa-js .sa-stagger > * {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 620ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 620ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 80ms);
}

.sa-js .sa-stagger.is-visible > * {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .sa-js .sa-reveal,
  .sa-js .sa-stagger > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .sa-btn { transition: none; }
}

/* --------------------------------------------------------------------------
   7. Elementor overrides
   UiCore and Elementor both ship opinionated defaults. Keep these narrow.
   -------------------------------------------------------------------------- */

.elementor-widget-heading .elementor-heading-title {
  font-family: var(--sa-font);
  letter-spacing: -0.03em;
}

.elementor-section.sa-section-light,
.elementor-section.sa-section-dark,
.elementor-section.sa-section-alt {
  background-attachment: scroll;
}

/* Container width matches the design comp. */
.sa-container {
  max-width: 1180px;
  margin-inline: auto;
  padding-inline: 40px;
}

@media (max-width: 640px) {
  .sa-container { padding-inline: 20px; }
}

/* --------------------------------------------------------------------------
   8. Header
   Transparent over the hero on the front page, solid once scrolled.
   -------------------------------------------------------------------------- */

.sa-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--sa-accent);
  color: #06231a;
  padding: 10px 18px;
  border-radius: 0 0 8px 0;
  font-weight: 700;
  font-size: 13px;
}
.sa-skip-link:focus { left: 0; }

.sa-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--sa-bg-dark);
  transition: background-color 300ms cubic-bezier(0.4, 0, 0.2, 1),
              backdrop-filter 300ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Front page only: sit over the hero image until the user scrolls. */
.sa-header--transparent {
  position: fixed;
  inset-inline: 0;
  background: transparent;
}

.sa-header--transparent.is-stuck {
  background: rgba(18,35,63, 0.86);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.sa-header__inner {
  max-width: 1180px;
  margin-inline: auto;
  padding: 22px 40px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.sa-logo {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--sa-text-on-dark);
  flex-shrink: 0;
}
.sa-logo:hover { color: var(--sa-text-on-dark); }

.sa-logo__mark {
  width: 31px;
  height: 31px;
  border-radius: 9px;
  background: var(--sa-accent);
  color: #06231a;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 16.5px;
  flex-shrink: 0;
}

.sa-logo__word {
  font-weight: 800;
  font-size: 16.5px;
  letter-spacing: 0.02em;
}

.sa-nav__list {
  display: flex;
  align-items: center;
  gap: 32px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.sa-nav__list a {
  color: rgba(255, 255, 255, 0.78);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.sa-nav__list a:hover { color: #ffffff; }

.sa-header__cta { height: 38px; font-size: 10.5px; }

/* Mobile menu */
.sa-burger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 0;
}
.sa-burger span {
  display: block;
  height: 2px;
  width: 22px;
  background: var(--sa-text-on-dark);
  margin-inline: auto;
  transition: transform 260ms cubic-bezier(0.4, 0, 0.2, 1), opacity 200ms;
}
.sa-burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.sa-burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.sa-burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.sa-mobile-nav {
  position: fixed;
  inset: 0;
  z-index: 99;
  background: var(--sa-bg-dark);
  padding: 110px 32px 40px;
  display: flex;
  flex-direction: column;
  gap: 26px;
  overflow-y: auto;
}
.sa-mobile-nav[hidden] { display: none; }

.sa-mobile-nav__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.sa-mobile-nav__list a {
  color: var(--sa-text-on-dark);
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

@media (max-width: 900px) {
  .sa-nav, .sa-header__cta { display: none; }
  .sa-burger { display: flex; }
  .sa-header__inner { padding: 16px 22px; }
}

/* --------------------------------------------------------------------------
   9. Footer
   -------------------------------------------------------------------------- */

.sa-footer {
  background: var(--sa-bg-dark-alt);
  border-top: 1px solid rgba(255, 255, 255, 0.10);
  color: var(--sa-text-on-dark);
}

.sa-footer__grid {
  padding-top: 64px;
  padding-bottom: 48px;
  display: grid;
  grid-template-columns: 1.5fr repeat(3, minmax(0, 1fr));
  gap: 36px;
}

.sa-footer__brand { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }

.sa-footer__blurb {
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--sa-text-muted-dk);
  max-width: 260px;
}

.sa-footer__address {
  font-style: normal;
  font-size: 13px;
  line-height: 1.65;
  color: var(--sa-text-muted-dk);
}

.sa-footer__col { display: flex; flex-direction: column; gap: 9px; }

.sa-footer__heading {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sa-text-on-dark);
  margin-bottom: 5px;
}
.sa-footer__heading--spaced { margin-top: 22px; }

.sa-footer__link {
  font-size: 13px;
  color: var(--sa-text-muted-dk);
  line-height: 1.45;
}
.sa-footer__link:hover { color: var(--sa-text-on-dark); }

.sa-footer__mail { font-size: 13px; color: var(--sa-accent); }

.sa-footer__platforms {
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--sa-text-muted-dk);
}

.sa-footer__bar { border-top: 1px solid rgba(255, 255, 255, 0.10); }

.sa-footer__bar-inner {
  padding-top: 20px;
  padding-bottom: 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
  font-size: 12.5px;
  color: var(--sa-text-muted-dk);
}

.sa-footer__legal { display: flex; gap: 22px; }
.sa-footer__legal a { color: var(--sa-text-muted-dk); }
.sa-footer__legal a:hover { color: var(--sa-text-on-dark); }

@media (max-width: 900px) {
  .sa-footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .sa-footer__grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   10. Layout utilities
   -------------------------------------------------------------------------- */

.sa-block { padding-block: 72px; }
.sa-mt-10 { margin-top: 10px; }
.sa-mt-14 { margin-top: 14px; }
.sa-mt-18 { margin-top: 18px; }
.sa-mt-32 { margin-top: 32px; }
.sa-mt-40 { margin-top: 40px; }
.sa-mt-46 { margin-top: 46px; }
.sa-mt-48 { margin-top: 48px; }
.sa-mt-60 { margin-top: 60px; }

.sa-split {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 64px;
  align-items: start;
}
.sa-split--end { align-items: flex-end; }

.sa-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 32px; }
.sa-grid-svc { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }

@media (max-width: 900px) {
  .sa-split { grid-template-columns: 1fr; gap: 28px; }
  .sa-grid-3, .sa-grid-svc { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sa-block { padding-block: 56px; }
}
@media (max-width: 640px) {
  /*
   * minmax(0, 1fr), never a bare 1fr. A bare 1fr floors the column at
   * min-content, and the platform marquee inside this grid is a ~3000px track,
   * which forced the single column to 3028px on a 390px screen and clipped
   * every card in the section. The 900px rule above was already safe.
   */
  .sa-grid-3, .sa-grid-svc { grid-template-columns: minmax(0, 1fr); }
}

/* Same guard on the items themselves, so no future wide child can do this. */
.sa-grid-3 > *, .sa-grid-svc > * { min-width: 0; }

/* --------------------------------------------------------------------------
   11. Hero
   -------------------------------------------------------------------------- */

.sa-hero {
  position: relative;
  min-height: 840px;
  display: flex;
  align-items: center;
  overflow: hidden;
}

.sa-hero__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 54% 20%;
  z-index: 0;
}

/* Edge and base scrim. Keeps the face readable, darkens the frame,
   and fades cleanly into the section below. */
.sa-hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(180deg, rgba(12,26,49,0.62) 0%, rgba(12,26,49,0.14) 30%, rgba(12,26,49,0.58) 62%, rgba(16,32,56,0.94) 84%, var(--sa-bg-dark) 100%),
    linear-gradient(90deg, rgba(11,23,42,0.55) 0%, rgba(11,23,42,0) 32%, rgba(11,23,42,0) 68%, rgba(11,23,42,0.45) 100%);
}

.sa-hero__pills { position: absolute; inset: 0; z-index: 3; pointer-events: none; }

.sa-pill__dot {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: rgba(47, 217, 170, 0.28);
  flex-shrink: 0;
}

.sa-hero__inner {
  position: relative;
  z-index: 5;
  max-width: 860px;
  margin-inline: auto;
  padding: 0 40px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.sa-hero__eyebrow {
  font-size: 11.5px;
  text-shadow: 0 1px 12px rgba(7,21,39, 0.65);
  margin-bottom: 15px;
}

.sa-hero__title {
  font-size: clamp(34px, 4.4vw, 60px);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.038em;
  color: #ffffff;
  max-width: 780px;
}

.sa-hero__lede {
  margin-top: 20px;
  font-size: 15.5px;
  line-height: 1.58;
  color: rgba(255, 255, 255, 0.74);
  max-width: 470px;
}

.sa-hero__actions { display: flex; gap: 10px; margin-top: 26px; flex-wrap: wrap; justify-content: center; }

@media (max-width: 900px) {
  .sa-hero { min-height: 640px; }
  .sa-hero__inner { padding-inline: 22px; }
}

/* --------------------------------------------------------------------------
   12. Client strip
   -------------------------------------------------------------------------- */

.sa-clients { padding: 8px 0 64px; }
.sa-clients__inner { display: flex; flex-direction: column; align-items: center; gap: 26px; }

.sa-clients__label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(159, 176, 196, 0.72);
}

.sa-clients__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 18px 46px;
}

.sa-clients__name {
  font-size: 15.5px;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: rgba(244, 247, 248, 0.66);
}

/* --------------------------------------------------------------------------
   13. Services
   -------------------------------------------------------------------------- */

.sa-card--link { display: block; color: inherit; transition: border-color 220ms, transform 220ms cubic-bezier(0.16,1,0.3,1); }
.sa-card--link:hover { border-color: rgba(47, 217, 170, 0.45); transform: translateY(-3px); color: inherit; }
.sa-card--link h3 { color: var(--sa-text-on-dark); }

.sa-card--platforms {
  grid-column: span 2;
  background: var(--sa-bg-dark-alt);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 14px;
  padding: 26px 24px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
}

.sa-eyebrow--muted { color: var(--sa-text-muted-dk); }

.sa-platform-row { display: flex; flex-wrap: wrap; gap: 8px; }

.sa-chip {
  padding: 7px 14px;
  border-radius: 100px;
  border: 1px solid rgba(255, 255, 255, 0.10);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--sa-text-on-dark);
}

@media (max-width: 640px) { .sa-card--platforms { grid-column: span 1; } }

/* --------------------------------------------------------------------------
   14. Approach
   -------------------------------------------------------------------------- */

/*
 * Columns were 0.85/1.15 for a cropped photo. The globe is a round object that
 * needs room, and the text column had grown half again as tall as it, so the
 * split is now even and the stage is allowed to fill it.
 */
.sa-approach { display: grid; grid-template-columns: 1fr 1fr; gap: 92px; align-items: center; }
.sa-approach__media { border-radius: 18px; overflow: hidden; }
.sa-approach__media img { display: block; width: 100%; height: 380px; object-fit: cover; }

/*
 * Numbered hairline list. The previous 2x2 grid gave four identically weighted
 * grey pairs with nothing to scan, so the column read flat. The numerals carry
 * the scale contrast and the accent, the rules carry the rhythm.
 */
/*
 * Two columns, so four entries occupy two rows rather than four. Stacked, this
 * list ran roughly half again as tall as the globe beside it and the section
 * lost its balance.
 */
.sa-values {
  list-style: none;
  margin: 0;
  padding: 0;
}

.sa-values--grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 34px;
}

.sa-values__row {
  position: relative;
  display: block;
  padding: 15px 12px 17px 0;
  border-top: 1px solid color-mix(in srgb, var(--sa-text-on-light) 13%, transparent);
  transition: padding-left 340ms cubic-bezier(.16,1,.3,1);
}

/*
 * The accent rule grows out of the hairline on hover. Cheaper visually than a
 * background tint, which fought the globe for attention.
 */
.sa-values__row::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  height: 1px;
  width: 0;
  background: var(--sa-accent);
  transition: width 420ms cubic-bezier(.16,1,.3,1);
}

.sa-values__row:hover::before { width: 100%; }
.sa-values__row:hover { padding-left: 10px; }

.sa-values__num {
  display: block;
  margin-bottom: 9px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  font-variant-numeric: tabular-nums;
  color: var(--sa-accent-dark);
}

.sa-values__text { display: flex; flex-direction: column; gap: 5px; }
.sa-values__title { font-weight: 600; font-size: 16.5px; color: var(--sa-text-on-light); }
.sa-values__desc  { font-size: 13.5px; line-height: 1.55; color: var(--sa-text-muted-lt); }

/* Matches the card spotlight language already used elsewhere on the page. */
.sa-accent-word { color: var(--sa-accent-dark); }

.sa-approach__note-lead {
  font-size: 19px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--sa-text-on-light);
  margin: 0 0 9px;
}

@media (max-width: 900px) {
  .sa-approach { grid-template-columns: 1fr; gap: 32px; }
  .sa-values--grid { grid-template-columns: 1fr; column-gap: 0; }
  .sa-approach__media img { height: 280px; }
}

/* --------------------------------------------------------------------------
   15. Testimonials
   -------------------------------------------------------------------------- */

.sa-testimonials { position: relative; padding-block: 104px; overflow: hidden; }

.sa-testimonials__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

/* Deliberately lighter than a flat wash so the skyline actually reads.
   The client flagged it as invisible when this sat at 0.9 opacity. */
.sa-testimonials__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg,
    rgba(11,25,46,0.86) 0%,
    rgba(11,25,46,0.58) 34%,
    rgba(11,25,46,0.50) 58%,
    rgba(11,25,46,0.78) 100%);
}

.sa-testimonials__head { position: relative; z-index: 2; max-width: 700px; }
.sa-testimonials__head h2 { color: #ffffff; }
.sa-testimonials .sa-grid-3 { position: relative; z-index: 2; gap: 20px; }

.sa-quote { padding: 30px 26px; display: flex; flex-direction: column; gap: 18px; margin: 0; }
.sa-quote blockquote { margin: 0; font-size: 15px; line-height: 1.62; color: rgba(255, 255, 255, 0.90); }
.sa-quote figcaption { margin-top: auto; display: flex; flex-direction: column; gap: 3px; }
.sa-quote__name { font-size: 14px; font-weight: 600; color: #ffffff; }
.sa-quote__role { font-size: 13px; color: rgba(255, 255, 255, 0.58); }

/* --------------------------------------------------------------------------
   16. Insights + CTA
   -------------------------------------------------------------------------- */

.sa-insight { overflow: hidden; display: flex; flex-direction: column; }
.sa-insight img { display: block; width: 100%; height: 172px; object-fit: cover; }
.sa-insight__body { padding: 24px 24px 26px; }
.sa-insight h3 a { color: var(--sa-text-on-light); }
.sa-insight h3 a:hover { color: var(--sa-accent-dark); }

.sa-cta { padding-block: 110px; text-align: center; }
.sa-cta__inner { display: flex; flex-direction: column; align-items: center; gap: 24px; }

.sa-cta__title {
  font-size: clamp(28px, 3vw, 40px);
  font-weight: 600;
  letter-spacing: -0.032em;
  line-height: 1.13;
  color: var(--sa-text-on-dark);
  max-width: 640px;
}

.sa-cta__btn { height: 46px; padding: 0 28px; font-size: 12px; }

.sa-cta__contact { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 20px; }
.sa-cta__contact a { font-size: 13.5px; color: var(--sa-text-muted-dk); }
.sa-cta__contact a:hover { color: var(--sa-text-on-dark); }

/* --------------------------------------------------------------------------
   17. Hero pill motion
   Each pill drifts on its own slow loop so the group never pulses in unison,
   plus a staggered entrance. Transform/opacity only.
   -------------------------------------------------------------------------- */

.sa-js .sa-pill {
  opacity: 0;
  transform: translateY(14px) scale(0.94);
  transition: opacity 760ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 760ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(560ms + var(--i, 0) * 130ms);
}

.sa-js .sa-hero.is-ready .sa-pill {
  opacity: 1;
  transform: none;
}

/* Idle drift. Starts only once the entrance has finished so the two
   transforms never fight. Durations are deliberately mismatched. */
.sa-js .sa-hero.is-ready .sa-pill {
  animation: sa-drift var(--dur, 9s) ease-in-out infinite;
  animation-delay: calc(1400ms + var(--i, 0) * 220ms);
}

@keyframes sa-drift {
  0%,  100% { translate: 0 0; }
  50%       { translate: 0 -9px; }
}

/* Soft halo that breathes behind each dot, so the pills read as "live"
   signals rather than static chips. */
.sa-pill__dot {
  position: relative;
  box-shadow: 0 0 0 0 rgba(47, 217, 170, 0.45);
}

.sa-js .sa-hero.is-ready .sa-pill__dot {
  animation: sa-ping 3.4s ease-out infinite;
  animation-delay: calc(1600ms + var(--i, 0) * 260ms);
}

@keyframes sa-ping {
  0%        { box-shadow: 0 0 0 0 rgba(47, 217, 170, 0.42); }
  70%, 100% { box-shadow: 0 0 0 9px rgba(47, 217, 170, 0); }
}

/* --------------------------------------------------------------------------
   18. Hero depth
   Slow parallax drift on the portrait, driven by a scroll-linked animation
   where supported. Falls back to static, never to jank.
   -------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .sa-hero__bg {
      animation: sa-parallax linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
    @keyframes sa-parallax {
      from { transform: scale(1.06) translateY(-1.5%); }
      to   { transform: scale(1.06) translateY(3.5%); }
    }
  }
}

/* Headline settles in word by word. */
.sa-js .sa-hero__title .sa-word {
  display: inline-block;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 760ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 760ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--w, 0) * 62ms);
}
.sa-js .sa-hero.is-ready .sa-hero__title .sa-word {
  opacity: 1;
  transform: none;
}

/* --------------------------------------------------------------------------
   19. Interaction polish
   -------------------------------------------------------------------------- */

/* Service cards lift and pick up a teal edge, with the accent bleeding in
   from the top rather than a flat colour swap. */
.sa-card--link {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.sa-card--link::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(160deg, rgba(47, 217, 170, 0.14), transparent 58%);
  opacity: 0;
  transition: opacity 320ms cubic-bezier(0.4, 0, 0.2, 1);
}

.sa-card--link:hover::before { opacity: 1; }

/* Platform chips brighten individually on hover. */
.sa-chip {
  transition: border-color 220ms, color 220ms, transform 220ms cubic-bezier(0.16,1,0.3,1);
}
.sa-chip:hover {
  border-color: rgba(47, 217, 170, 0.55);
  transform: translateY(-2px);
}

/* Client wordmarks come up from muted to full on hover, the way a real
   logo wall behaves. */
.sa-clients__name {
  transition: color 260ms cubic-bezier(0.4, 0, 0.2, 1), transform 260ms cubic-bezier(0.16,1,0.3,1);
}
.sa-clients__name:hover {
  color: var(--sa-text-on-dark);
  transform: translateY(-1px);
}

/* Buttons get a subtle sheen sweep on hover. */
.sa-btn--teal { position: relative; overflow: hidden; }
.sa-btn--teal::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 20%, rgba(255,255,255,0.42) 50%, transparent 80%);
  transform: translateX(-120%);
  transition: transform 620ms cubic-bezier(0.16, 1, 0.3, 1);
}
.sa-btn--teal:hover::after { transform: translateX(120%); }

@media (prefers-reduced-motion: reduce) {
  .sa-js .sa-pill,
  .sa-js .sa-hero__title .sa-word {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
  .sa-js .sa-hero.is-ready .sa-pill,
  .sa-js .sa-hero.is-ready .sa-pill__dot,
  .sa-hero__bg { animation: none !important; }
  .sa-btn--teal::after { display: none; }
}

/* --------------------------------------------------------------------------
   20. Restored iconography
   Positioning lives on .sa-pill-pos so the pill's own entrance and drift
   transforms never collide with the centring translate on the wide pill.
   -------------------------------------------------------------------------- */

.sa-pill-pos { position: absolute; }

.sa-pill__dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: rgba(47, 217, 170, 0.22);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.sa-pill__icon { width: 9px; height: 9px; color: var(--sa-accent); }

.sa-card__icon {
  width: 22px;
  height: 22px;
  color: var(--sa-accent);
  display: block;
  margin-bottom: 12px;
}

.sa-quote__mark { width: 26px; height: 20px; color: var(--sa-accent); display: block; }

/* --------------------------------------------------------------------------
   21. Responsive corrections
   -------------------------------------------------------------------------- */

@media (max-width: 1024px) {
  /* The floating industry pills are absolutely positioned around a centred
     headline. Below this width they collide with the copy, and they are
     decorative (aria-hidden) rather than load-bearing, so they come out.
     The full industry list still appears in the footer and on /industries/. */
  .sa-pill-pos { display: none; }
}

@media (max-width: 900px) {
  .sa-hero { min-height: 78svh; }
  .sa-hero__actions { width: 100%; }
  .sa-clients__row { gap: 14px 28px; }
  .sa-clients__name { font-size: 14px; }
}

@media (max-width: 640px) {
  .sa-hero { min-height: 74svh; padding-block: 96px 56px; }
  .sa-hero__title { font-size: clamp(30px, 8.4vw, 40px); }
  .sa-hero__lede { font-size: 15px; }
  .sa-hero__actions { flex-direction: column; align-items: stretch; }
  .sa-hero__actions .sa-btn { width: 100%; }

  .sa-clients { padding-bottom: 48px; }
  .sa-clients__row { flex-direction: column; gap: 12px; }

  .sa-block { padding-block: 48px; }
  .sa-grid-3 { gap: 26px; }
  .sa-values { grid-template-columns: 1fr; gap: 18px; }
  .sa-testimonials { padding-block: 72px; }
  .sa-cta { padding-block: 76px; }
  .sa-cta__contact { flex-direction: column; gap: 10px; }
  .sa-footer__bar-inner { flex-direction: column; align-items: flex-start; }
}

/* Never let a long capability name break the layout on narrow screens. */
.sa-card h3, .sa-clients__name, .sa-footer__link { overflow-wrap: anywhere; }

/* "What we solve" icons. Ink-coloured, matching the light section. */
.sa-solve__icon {
  width: 26px;
  height: 26px;
  color: var(--sa-text-on-light);
  display: block;
  margin-bottom: 12px;
}

/* --------------------------------------------------------------------------
   22. Client logo wall
   Six logos arriving in six different brand palettes and three file formats.
   Rendering them to a single tone is standard practice for a client wall and
   is the only way the row reads as one thing. Note this does deviate from most
   brand guidelines, which specify full-colour reproduction.
   -------------------------------------------------------------------------- */

.sa-clients__logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 38px;
  width: 132px;
}

.sa-clients__logo img {
  max-height: 100%;
  max-width: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  /* Force every mark to flat white, then hold it back so the row supports
     the headline rather than competing with it. */
  filter: brightness(0) invert(1);
  opacity: 0.62;
  transition: opacity 260ms cubic-bezier(0.4, 0, 0.2, 1),
              transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
}

.sa-clients__logo img:hover {
  opacity: 1;
  transform: translateY(-2px);
}

.sa-clients__row { gap: 20px 40px; }

@media (max-width: 640px) {
  .sa-clients__row { flex-direction: row; flex-wrap: wrap; gap: 18px 24px; }
  .sa-clients__logo { height: 30px; width: 104px; }
}

/* --------------------------------------------------------------------------
   23. Client wall: signal sweep
   A slow wave of brightness travels left to right across the logos, echoing
   the teal beam in the hero. For a data consultancy it reads as a signal
   passing through, rather than decoration for its own sake.

   The animation starts only once the row has revealed, so it cannot fight
   the entrance transition. Delay is derived from the same --i index the
   stagger already sets.
   -------------------------------------------------------------------------- */

.sa-js .sa-stagger.is-visible > .sa-clients__logo img {
  animation: sa-signal 7s cubic-bezier(0.4, 0, 0.2, 1) infinite;
  animation-delay: calc(900ms + var(--i, 0) * 420ms);
}

@keyframes sa-signal {
  0%, 62%, 100% {
    opacity: 0.5;
    transform: translateY(0);
    filter: brightness(0) invert(1) drop-shadow(0 0 0 rgba(47, 217, 170, 0));
  }
  16% {
    opacity: 1;
    transform: translateY(-3px);
    filter: brightness(0) invert(1) drop-shadow(0 0 10px rgba(47, 217, 170, 0.55));
  }
  34% {
    opacity: 0.72;
    transform: translateY(0);
    filter: brightness(0) invert(1) drop-shadow(0 0 0 rgba(47, 217, 170, 0));
  }
}

/* Hover always wins over the sweep, so the row stays interactive. */
.sa-clients__logo img:hover {
  animation-play-state: paused;
  opacity: 1;
  filter: brightness(0) invert(1) drop-shadow(0 0 12px rgba(47, 217, 170, 0.7));
}

/* A hairline that pulses across the row beneath the logos, reinforcing the
   left-to-right read without adding another moving object at logo scale. */
.sa-clients__inner { position: relative; }

.sa-clients__inner::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -22px;
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(47, 217, 170, 0) 15%,
    rgba(47, 217, 170, 0.55) 50%,
    rgba(47, 217, 170, 0) 85%,
    transparent 100%);
  opacity: 0;
  transform: scaleX(0.3);
  transform-origin: center;
}

.sa-js .sa-reveal.is-visible .sa-clients__inner::after,
.sa-js .sa-clients__inner::after {
  animation: sa-hairline 7s cubic-bezier(0.4, 0, 0.2, 1) infinite;
  animation-delay: 900ms;
}

@keyframes sa-hairline {
  0%, 100% { opacity: 0; transform: scaleX(0.3); }
  22%      { opacity: 0.9; transform: scaleX(1); }
  55%      { opacity: 0; transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
  .sa-js .sa-stagger.is-visible > .sa-clients__logo img,
  .sa-js .sa-clients__inner::after {
    animation: none !important;
  }
  .sa-clients__logo img { opacity: 0.62; }
  .sa-clients__inner::after { opacity: 0.35; transform: scaleX(1); }
}

/* --------------------------------------------------------------------------
   24. Insights: index, cards, article
   -------------------------------------------------------------------------- */

.sa-page-head { padding: 150px 0 72px; }
.sa-page-head__title {
  margin-top: 14px;
  font-size: clamp(30px, 3.4vw, 46px);
  font-weight: 600;
  letter-spacing: -0.032em;
  line-height: 1.12;
  color: var(--sa-text-on-dark);
  max-width: 760px;
}
.sa-page-head__lede {
  margin-top: 18px;
  font-size: 16px;
  line-height: 1.62;
  color: var(--sa-text-muted-dk);
  max-width: 560px;
}

/* Category filter */
.sa-filter { display: flex; flex-wrap: wrap; gap: 10px; }
.sa-filter__chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 16px;
  border-radius: 100px;
  border: 1px solid rgba(14,30,51,0.16);
  font-size: 13px;
  font-weight: 600;
  color: var(--sa-text-on-light);
  transition: border-color 200ms, background-color 200ms, color 200ms;
}
.sa-filter__chip:hover { border-color: var(--sa-accent-dark); color: var(--sa-accent-dark); }
.sa-filter__chip.is-active {
  background: var(--sa-text-on-light);
  border-color: var(--sa-text-on-light);
  color: var(--sa-bg-light);
}
.sa-filter__count { font-size: 11px; opacity: 0.55; font-weight: 500; }
.sa-filter__chip.is-active .sa-filter__count { opacity: 0.7; }

/* Cards */
.sa-insight__media { display: block; overflow: hidden; }
.sa-insight__media img {
  transition: transform 620ms cubic-bezier(0.16, 1, 0.3, 1);
}
.sa-insight:hover .sa-insight__media img { transform: scale(1.045); }
.sa-insight__placeholder {
  display: block;
  height: 172px;
  background: linear-gradient(135deg, var(--sa-bg-card-dark), var(--sa-bg-dark-alt));
}
.sa-insight__category {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sa-accent-dark);
}
.sa-insight__meta {
  margin-top: 14px;
  font-size: 12.5px;
  color: var(--sa-text-muted-lt);
}
.sa-insight__read {
  margin-top: auto;
  padding-top: 16px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sa-accent-dark);
}
.sa-insight__read svg { width: 11px; height: 11px; transition: transform 260ms cubic-bezier(0.16,1,0.3,1); }
.sa-insight:hover .sa-insight__read svg { transform: translateX(3px); }

/* Pagination */
.sa-pagination { display: flex; gap: 8px; justify-content: center; margin-top: 48px; flex-wrap: wrap; }
.sa-pagination .page-numbers {
  min-width: 40px;
  height: 40px;
  padding: 0 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 100px;
  border: 1px solid rgba(14,30,51,0.16);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--sa-text-on-light);
}
.sa-pagination .page-numbers.current {
  background: var(--sa-text-on-light);
  border-color: var(--sa-text-on-light);
  color: var(--sa-bg-light);
}

.sa-empty { padding: 40px 0 20px; }

/* Article */
.sa-article__cat { display: inline-block; }
.sa-article__meta {
  margin-top: 18px;
  display: flex;
  gap: 10px;
  font-size: 13.5px;
  color: var(--sa-text-muted-dk);
}
.sa-article__hero { background: var(--sa-bg-dark); padding-bottom: 0; }
.sa-article__hero img {
  display: block;
  width: 100%;
  height: clamp(240px, 34vw, 460px);
  object-fit: cover;
  border-radius: 18px;
  margin-bottom: -60px;
  position: relative;
  z-index: 2;
}

.sa-prose { max-width: 68ch; margin-inline: auto; }
.sa-prose > * + * { margin-top: 1.15em; }
.sa-prose p { font-size: 17px; line-height: 1.75; color: var(--sa-text-on-light); }
.sa-prose h2 { font-size: 27px; margin-top: 1.8em; color: var(--sa-text-on-light); }
.sa-prose h3 { font-size: 21px; margin-top: 1.6em; color: var(--sa-text-on-light); }
.sa-prose ul, .sa-prose ol { padding-left: 1.3em; }
.sa-prose li { font-size: 17px; line-height: 1.7; color: var(--sa-text-on-light); margin-top: 0.5em; }
.sa-prose a { color: var(--sa-accent-dark); text-decoration: underline; text-underline-offset: 3px; }
.sa-prose img { border-radius: 12px; }
.sa-prose blockquote {
  border-left: 3px solid var(--sa-accent);
  padding-left: 22px;
  font-size: 20px;
  line-height: 1.55;
  color: var(--sa-text-on-light);
}

.sa-article__tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 44px; justify-content: center; }
.sa-chip--light { border-color: rgba(14,30,51,0.16); color: var(--sa-text-on-light); }

.sa-related { border-top: 1px solid rgba(14,30,51,0.09); }

@media (max-width: 900px) {
  .sa-page-head { padding: 118px 0 52px; }
  .sa-article__hero img { margin-bottom: -34px; }
}

/* --------------------------------------------------------------------------
   25. Single-post typography reclaim
   UiCore ships `.single-post article h1/h2` rules (specificity 0-1-2) driven
   by its own blog typography variables. Those outrank plain utility classes,
   which flattened the article title and section headings. Re-assert ours at
   0-2-0 so the article matches the rest of the site.
   -------------------------------------------------------------------------- */

.sa-article .sa-page-head__title {
  font-family: var(--sa-font);
  font-size: clamp(30px, 3.4vw, 46px);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.032em;
  color: var(--sa-text-on-dark);
}

.sa-article .sa-h2 {
  font-family: var(--sa-font);
  font-size: clamp(28px, 2.6vw, 38px);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.03em;
  color: var(--sa-text-on-light);
}

.sa-article .sa-h3 {
  font-family: var(--sa-font);
  font-size: clamp(18px, 1.4vw, 21px);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.012em;
}

.sa-article .sa-prose h2 {
  font-family: var(--sa-font);
  font-size: 27px;
  font-weight: 600;
  letter-spacing: -0.028em;
  color: var(--sa-text-on-light);
}

.sa-article .sa-prose h3 {
  font-family: var(--sa-font);
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--sa-text-on-light);
}

.sa-article .sa-prose p,
.sa-article .sa-prose li {
  font-family: var(--sa-font);
  font-size: 17px;
  line-height: 1.75;
  color: var(--sa-text-on-light);
}

/* The related grid should hold three columns even when only two posts exist,
   otherwise the cards stretch to half-width and dwarf the index cards. */
.sa-related .sa-grid-3 { grid-auto-columns: minmax(0, 1fr); }
.sa-related .sa-grid-3 > * { max-width: 100%; }

/* --------------------------------------------------------------------------
   26. Insights toolbar: category filter + search
   The chips are the primary control and take the left. The search field sits
   opposite them as a quiet secondary control with no submit button, so the row
   reads as one toolbar rather than two competing widgets.
   -------------------------------------------------------------------------- */

.sa-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px 32px;
  padding-bottom: 24px;
  border-bottom: 1px solid color-mix(in srgb, var(--sa-text-on-light) 12%, transparent);
}

.sa-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
}

.sa-search {
  position: relative;
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  width: 260px;
  height: 40px;
  padding: 0 10px 0 36px;
  border: 1px solid transparent;
  border-radius: 100px;
  background: color-mix(in srgb, var(--sa-text-on-light) 6%, transparent);
  transition: background 200ms ease, border-color 200ms ease, width 260ms ease;
}

.sa-search:focus-within,
.sa-search.is-filled {
  width: 300px;
  background: #fff;
  border-color: color-mix(in srgb, var(--sa-text-on-light) 18%, transparent);
}

.sa-search__icon {
  position: absolute;
  left: 13px;
  width: 16px;
  height: 16px;
  color: var(--sa-text-muted-lt);
  pointer-events: none;
  transition: color 200ms ease;
}

.sa-search:focus-within .sa-search__icon { color: var(--sa-accent-dark); }

.sa-search__input {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  padding: 0;
  border: 0;
  outline: 0;
  background: transparent;
  font-family: var(--sa-font);
  font-size: 14px;
  color: var(--sa-text-on-light);
  -webkit-appearance: none;
  appearance: none;
}

.sa-search__input::-webkit-search-cancel-button,
.sa-search__input::-webkit-search-decoration { display: none; }
.sa-search__input::placeholder { color: var(--sa-text-muted-lt); }

.sa-search__clear {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  color: var(--sa-text-muted-lt);
  background: color-mix(in srgb, var(--sa-text-on-light) 9%, transparent);
  transition: color 160ms ease, background 160ms ease;
}
.sa-search__clear:hover {
  color: #fff;
  background: var(--sa-text-on-light);
}
.sa-search__clear svg { width: 12px; height: 12px; }

/* --------------------------------------------------------------------------
   27. Bylines
   Card byline is a single baseline row so every card in a grid row lines up.
   Article byline is the two-line version, where the author's title earns the
   extra line.
   -------------------------------------------------------------------------- */

.sa-byline {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.sa-avatar {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: 50%;
  background: linear-gradient(140deg, var(--sa-accent), var(--sa-accent-dark));
  color: var(--sa-text-ink);
  font-family: var(--sa-font);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1;
  user-select: none;
}

.sa-byline__name {
  font-family: var(--sa-font);
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
}

.sa-byline__role,
.sa-byline__meta,
.sa-byline__stats {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--sa-font);
  line-height: 1.2;
  white-space: nowrap;
}

/* Separator between date and reading time. */
.sa-dot {
  flex: 0 0 auto;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.5;
}

/* -- Card byline: one line ------------------------------------------------ */

.sa-byline--sm {
  gap: 9px;
  flex: 1 1 auto;
  overflow: hidden;
}

.sa-byline--sm .sa-avatar { width: 26px; height: 26px; font-size: 10px; }
.sa-byline--sm .sa-byline__name { font-size: 12.5px; color: var(--sa-text-on-light); }
.sa-byline--sm .sa-byline__meta { font-size: 12px; color: var(--sa-text-muted-lt); }

.sa-byline--sm .sa-byline__rule {
  flex: 0 0 auto;
  width: 1px;
  height: 12px;
  background: color-mix(in srgb, var(--sa-text-on-light) 22%, transparent);
}

/* -- Article byline: two lines, with the author's title -------------------- */

.sa-byline--lg { margin-top: 28px; gap: 14px; }
.sa-byline--lg .sa-avatar { width: 44px; height: 44px; font-size: 15px; }
.sa-byline--lg .sa-byline__name { font-size: 15px; color: var(--sa-text-on-dark); }
.sa-byline--lg .sa-byline__role { font-size: 13px; color: var(--sa-accent); font-weight: 500; letter-spacing: 0.015em; }
.sa-byline--lg .sa-byline__stats { font-size: 13.5px; color: var(--sa-text-muted-dk); }

.sa-byline--lg .sa-byline__text {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.sa-byline--lg .sa-byline__rule {
  flex: 0 0 auto;
  width: 1px;
  height: 32px;
  margin: 0 4px;
  background: color-mix(in srgb, var(--sa-text-on-dark) 20%, transparent);
}

.sa-insight__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-top: auto;
  padding-top: 18px;
  border-top: 1px solid color-mix(in srgb, var(--sa-text-on-light) 10%, transparent);
}

/* --------------------------------------------------------------------------
   28. Archive and search page heads
   One text column with a shared left edge and a real measure. The count sits
   under a hairline that spans that column, so it reads as metadata belonging
   to the block rather than a label floating on its own.
   -------------------------------------------------------------------------- */

.sa-page-head--archive .sa-page-head__title,
.sa-page-head--archive .sa-page-head__lede,
.sa-page-head--archive .sa-page-head__count {
  max-width: 34ch;
}

.sa-page-head--archive .sa-page-head__lede {
  max-width: 58ch;
  margin-top: 20px;
}

.sa-backlink {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 30px;
  font-family: var(--sa-font);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.045em;
  text-transform: uppercase;
  color: var(--sa-text-muted-dk);
  transition: color 200ms ease;
}
.sa-backlink:hover { color: var(--sa-accent); }
.sa-backlink svg {
  width: 15px;
  height: 15px;
  transition: transform 200ms ease;
}
.sa-backlink:hover svg { transform: translateX(-3px); }

.sa-page-head__count {
  display: block;
  width: fit-content;
  min-width: 200px;
  margin-top: 30px;
  padding-top: 16px;
  border-top: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 16%, transparent);
  font-family: var(--sa-font);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--sa-accent);
}

/* --------------------------------------------------------------------------
   29. Pagination
   -------------------------------------------------------------------------- */

.sa-pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 56px;
}

.sa-pagination .page-numbers {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-width: 42px;
  height: 42px;
  padding: 0 14px;
  border: 1px solid color-mix(in srgb, var(--sa-text-on-light) 15%, transparent);
  border-radius: 100px;
  font-family: var(--sa-font);
  font-size: 14px;
  font-weight: 600;
  color: var(--sa-text-on-light);
  background: #fff;
  transition: border-color 200ms ease, background 200ms ease, color 200ms ease;
}

.sa-pagination .page-numbers svg { width: 15px; height: 15px; }
.sa-pagination .page-numbers:hover { border-color: var(--sa-accent); color: var(--sa-accent-dark); }

.sa-pagination .page-numbers.current {
  background: var(--sa-bg-dark);
  border-color: var(--sa-bg-dark);
  color: var(--sa-text-on-dark);
}

.sa-pagination .page-numbers.dots {
  border-color: transparent;
  background: transparent;
  min-width: 24px;
  padding: 0;
}

.sa-empty { text-align: center; padding: 60px 0 20px; }

@media (max-width: 640px) {
  .sa-toolbar { gap: 16px; }
  .sa-search { flex: 1 1 100%; }
  .sa-byline--lg { flex-wrap: wrap; row-gap: 12px; }
  .sa-byline__rule { display: none; }
  .sa-byline--lg .sa-byline__stats { flex-basis: 100%; }
  .sa-pagination .page-numbers span { display: none; }
  .sa-pagination .page-numbers { padding: 0 12px; }
}

/* --------------------------------------------------------------------------
   30. Insight card alignment
   Cards in a row must share a baseline for the byline and the Read affordance.
   Without an explicit flex column on both the card and its body, `margin-top:
   auto` on the footer has no free space to consume, so the footer sits
   directly under the excerpt and rides up or down with the excerpt length.
   -------------------------------------------------------------------------- */

.sa-grid-3 > .sa-insight { height: 100%; }

.sa-insight {
  display: flex;
  flex-direction: column;
}

.sa-insight__media { flex: 0 0 auto; }

.sa-insight__body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

/* The excerpt takes the slack so the footer lands on a common baseline. */
.sa-insight__body > .sa-small { flex: 1 1 auto; }

.sa-insight__foot {
  align-items: center;
  flex: 0 0 auto;
}

.sa-insight__read {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  line-height: 1;
}

/* --------------------------------------------------------------------------
   31. Toolbar interaction states
   Focus rings and press feedback. The search sits on a light section, so the
   ring has to read against #eef1f2 rather than white.
   -------------------------------------------------------------------------- */

.sa-search:focus-within {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sa-accent) 30%, transparent);
}

.sa-filter__chip:focus-visible,
.sa-backlink:focus-visible,
.sa-pagination .page-numbers:focus-visible,
.sa-insight__body h3 a:focus-visible {
  outline: 2px solid var(--sa-accent-dark);
  outline-offset: 3px;
  border-radius: 6px;
}

.sa-filter__chip { transition: transform 120ms ease, border-color 200ms ease, background 200ms ease, color 200ms ease; }
.sa-filter__chip:active { transform: scale(0.97); }
.sa-pagination .page-numbers:active { transform: scale(0.97); }

@media (prefers-reduced-motion: reduce) {
  .sa-search,
  .sa-filter__chip,
  .sa-backlink svg,
  .sa-pagination .page-numbers { transition: none; }
  .sa-filter__chip:active,
  .sa-pagination .page-numbers:active { transform: none; }
}

@media (max-width: 900px) {
  .sa-toolbar { gap: 16px; }
  .sa-search,
  .sa-search:focus-within,
  .sa-search.is-filled { width: 100%; }
  .sa-page-head--archive .sa-page-head__title { max-width: none; }
}

/* --------------------------------------------------------------------------
   32. Toolbar and archive-head corrections
   -------------------------------------------------------------------------- */

/*
 * `flex: 1 1 auto` let the chip row consume the whole toolbar width, which
 * pushed the search field onto its own line even when there was room for
 * both. Chips size to content; space-between then seats the search on the
 * right of the same row.
 */
.sa-toolbar .sa-filter { flex: 0 1 auto; }

/*
 * UiCore colours paragraphs inside its dark sections, and that rule outranks
 * a single utility class, so the article count rendered muted grey instead of
 * the brand accent.
 */
.sa-page-head--archive .sa-page-head__count,
.sa-section-dark .sa-page-head__count {
  color: var(--sa-accent);
}

/* The active chip is a filled navy pill, so its count needs to lift off it. */
.sa-filter__chip.is-active .sa-filter__count {
  color: var(--sa-accent);
  opacity: 1;
}

/* --------------------------------------------------------------------------
   33. Chip strip: scroll instead of wrap
   Six categories plus the search field overflow one row by a few pixels, and
   the chips wrapped, dropping the search onto its own line looking orphaned.
   Wrapping also degrades as categories are added. A single-line strip that
   scrolls horizontally holds the toolbar to one row at any category count.
   -------------------------------------------------------------------------- */

.sa-toolbar .sa-filter {
  flex: 1 1 0;
  min-width: 0;
  flex-wrap: nowrap;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* Vertical room so focus rings and the active pill are not clipped. */
  padding: 4px 2px;
  margin: -4px -2px;
}

.sa-toolbar .sa-filter::-webkit-scrollbar { display: none; }

.sa-toolbar .sa-filter__chip {
  flex: 0 0 auto;
  scroll-snap-align: start;
  white-space: nowrap;
}

.sa-toolbar .sa-search {
  flex: 0 0 auto;
  margin-left: 4px;
}

@media (max-width: 900px) {
  .sa-toolbar { flex-wrap: wrap; }
  .sa-toolbar .sa-search,
  .sa-toolbar .sa-search:focus-within,
  .sa-toolbar .sa-search.is-filled {
    flex: 1 1 100%;
    width: 100%;
    margin-left: 0;
    order: -1;
  }
  .sa-toolbar .sa-filter { flex: 1 1 100%; }
}

/* --------------------------------------------------------------------------
   34. Card meta line
   Replaces the card byline. Date and reading time on the left, Read on the
   right, both on one baseline.
   -------------------------------------------------------------------------- */

.sa-insight__meta {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--sa-font);
  font-size: 12.5px;
  line-height: 1;
  letter-spacing: 0.005em;
  color: var(--sa-text-muted-lt);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   35. Chip strip edge fades and search sizing
   -------------------------------------------------------------------------- */

/*
 * The fade only applies once JS has confirmed the strip actually overflows,
 * so a short category list never gets a phantom fade on its last chip.
 */
.sa-toolbar .sa-filter.has-overflow {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
}

.sa-toolbar .sa-filter.has-overflow.at-start {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent 100%);
          mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent 100%);
}

.sa-toolbar .sa-filter.has-overflow.at-end {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 26px);
          mask-image: linear-gradient(to right, transparent 0, #000 26px);
}

.sa-toolbar .sa-filter.has-overflow.at-start.at-end {
  -webkit-mask-image: none;
          mask-image: none;
}

/*
 * A fixed width in every state. Growing the field on focus shifted the whole
 * toolbar sideways the moment the user clicked into it.
 */
.sa-toolbar .sa-search,
.sa-toolbar .sa-search:focus-within,
.sa-toolbar .sa-search.is-filled {
  width: 250px;
  margin-left: 20px;
}

/* Keep the focus ring inside the container instead of over the last chip. */
.sa-toolbar .sa-search:focus-within {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sa-accent) 26%, transparent);
}

@media (max-width: 900px) {
  .sa-toolbar .sa-search,
  .sa-toolbar .sa-search:focus-within,
  .sa-toolbar .sa-search.is-filled {
    width: 100%;
    margin-left: 0;
  }
}

/* --------------------------------------------------------------------------
   36. Search focus: one indicator, not two
   The theme applies an outline via a `:focus-visible` rule, which outranks the
   plain `.sa-search__input { outline: 0 }` (0-2-0 beats 0-1-0). The result was
   a hard rectangle drawn around the input, inside the rounded pill, alongside
   our own halo. Suppress the input's outline and let the pill carry a single
   clear focus indicator.
   -------------------------------------------------------------------------- */

.sa-search .sa-search__input:focus,
.sa-search .sa-search__input:focus-visible,
.sa-search .sa-search__input:-webkit-any-link:focus {
  outline: none;
  box-shadow: none;
  border: 0;
}

/*
 * The pill is now the only focus affordance, so it has to be unmistakable:
 * a solid accent border plus the halo, both against the light section.
 */
.sa-toolbar .sa-search:focus-within {
  background: #fff;
  border-color: var(--sa-accent-dark);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sa-accent) 30%, transparent);
}

.sa-toolbar .sa-search:focus-within .sa-search__icon { color: var(--sa-accent-dark); }

/* --------------------------------------------------------------------------
   37. Footer social links
   -------------------------------------------------------------------------- */

.sa-social {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
}

.sa-social__link {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 16%, transparent);
  color: var(--sa-text-muted-dk);
  background: transparent;
  transition: color 200ms ease, border-color 200ms ease, background 200ms ease, transform 160ms ease;
}

.sa-social__link svg { width: 16px; height: 16px; }

.sa-social__link:hover {
  color: var(--sa-text-ink);
  background: var(--sa-accent);
  border-color: var(--sa-accent);
  transform: translateY(-2px);
}

.sa-social__link:focus-visible {
  outline: 2px solid var(--sa-accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .sa-social__link { transition: color 200ms ease, background 200ms ease; }
  .sa-social__link:hover { transform: none; }
}

/* ==========================================================================
   38. THE PIPELINE SPINE
   Signature device for composed pages. A line in the left gutter that fills
   with scroll progress, carries packets flowing downward, and lights a node
   as each section arrives.

   Motivation, since motion needs one: the product being sold is a data
   pipeline, so the page is built as one. Progress, flow and sequence are the
   three things it communicates, and each has its own layer.

   Purely decorative. Removed below 1080px and collapsed under reduced motion.
   ========================================================================== */

.sa-spined { position: relative; }

/* Rail sits inside the container gutter: container is 1180 wide with 40px
   padding, so 18px in from its edge lands between edge and content. */
.sa-spined {
  --sa-rail-x: max(18px, calc(50% - 590px + 18px));
}

.sa-spine {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--sa-rail-x);
  width: 9px;
  pointer-events: none;
  z-index: 2;
}

.sa-spine__track,
.sa-spine__fill {
  position: absolute;
  left: 4px;
  width: 1px;
  top: 0;
  bottom: 0;
}

.sa-spine__track { background: color-mix(in srgb, var(--sa-accent) 20%, transparent); }

.sa-spine__fill {
  background: linear-gradient(180deg, var(--sa-accent), var(--sa-accent-dark));
  transform-origin: top;
  transform: scaleY(0);
}

/*
 * Scroll-linked fill with no scroll listener. Where the browser supports
 * scroll-driven animation this is driven entirely off the compositor; where it
 * does not, JS sets --sa-spine-p instead (see motion.js).
 */
@supports (animation-timeline: scroll()) {
  .sa-spine__fill {
    animation: sa-spine-grow linear both;
    animation-timeline: scroll(root block);
  }
}

@keyframes sa-spine-grow {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

/* JS fallback path. */
.sa-spine__fill { transform: scaleY(var(--sa-spine-p, 0)); }
@supports (animation-timeline: scroll()) {
  .sa-spine__fill { transform: scaleY(0); }
}

/* Packets: continuous downward flow, showing the pipe is live. */
.sa-spine__packet {
  position: absolute;
  left: 2.5px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--sa-accent);
  box-shadow: 0 0 10px 2px color-mix(in srgb, var(--sa-accent) 55%, transparent);
  opacity: 0;
  animation: sa-spine-flow 5.5s linear infinite;
}

.sa-spine__packet--2 { animation-delay: 1.9s; }
.sa-spine__packet--3 { animation-delay: 3.7s; }

@keyframes sa-spine-flow {
  0%        { top: 0;    opacity: 0; }
  8%        {            opacity: 1; }
  92%       {            opacity: 1; }
  100%      { top: 100%; opacity: 0; }
}

/* Node markers. Each spined section carries one, aligned to the rail. */
.sa-spined.has-spine > section { position: relative; }

.sa-spined.has-spine > section::before {
  content: "";
  position: absolute;
  top: 96px;
  left: var(--sa-rail-x);
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--sa-bg-dark);
  border: 1px solid color-mix(in srgb, var(--sa-accent) 45%, transparent);
  z-index: 3;
  transition: background 420ms ease, box-shadow 420ms ease, transform 420ms ease;
}

.sa-spined.has-spine > section.sa-section-light::before { background: var(--sa-bg-light); }

.sa-spined.has-spine > section.is-onscreen::before {
  background: var(--sa-accent);
  border-color: var(--sa-accent);
  transform: scale(1.35);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--sa-accent) 16%, transparent);
}

/* The hero opens the pipe, the CTA closes it. Neither needs a node. */
.sa-spined.has-spine > .sa-b-hero::before,
.sa-spined.has-spine > .sa-b-cta::before { display: none; }

@media (max-width: 1080px) {
  .sa-spine { display: none; }
  .sa-spined.has-spine > section::before { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .sa-spine__packet { display: none; }
  .sa-spine__fill { transform: scaleY(1); animation: none; }
  .sa-spined.has-spine > section::before { transition: none; }
}

/* ==========================================================================
   39. BLOCK: hero
   ========================================================================== */

.sa-b-hero__icon {
  display: inline-grid;
  place-items: center;
  width: 54px;
  height: 54px;
  margin-bottom: 26px;
  border-radius: 15px;
  color: var(--sa-accent);
  background: color-mix(in srgb, var(--sa-accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--sa-accent) 26%, transparent);
}

.sa-b-hero__icon svg { width: 26px; height: 26px; }
.sa-b-hero__cta { margin-top: 34px; }
.sa-b-hero .sa-page-head__lede { max-width: 62ch; }

/* ==========================================================================
   40. BLOCK: statement
   ========================================================================== */

.sa-b-statement__text {
  max-width: 24ch;
  font-family: var(--sa-font);
  font-size: clamp(28px, 3.4vw, 46px);
  font-weight: 600;
  line-height: 1.16;
  letter-spacing: -0.032em;
}

.sa-section-light .sa-b-statement__text { color: var(--sa-text-on-light); }
.sa-section-dark  .sa-b-statement__text { color: var(--sa-text-on-dark); }

.sa-b-statement__attr {
  margin-top: 24px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--sa-accent);
}

/* ==========================================================================
   41. BLOCK: split
   The image bleeds past the container edge rather than sitting in a neat
   column, which is what stops this reading as a stock two-column row.
   ========================================================================== */

.sa-b-split__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  align-items: center;
  gap: 72px;
}

.sa-b-split.is-flipped .sa-b-split__copy  { order: 2; }
.sa-b-split.is-flipped .sa-b-split__media { order: 1; }

.sa-b-split__body { margin-top: 22px; }
.sa-b-split__body p { font-size: 16.5px; line-height: 1.72; }

.sa-b-split__points {
  margin: 30px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 15px;
}

.sa-b-split__points li {
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: 13px;
  align-items: start;
  font-size: 15.5px;
  line-height: 1.6;
}

.sa-section-light .sa-b-split__points li { color: var(--sa-text-on-light); }
.sa-section-dark  .sa-b-split__points li { color: var(--sa-text-on-dark); }

.sa-b-split__tick { width: 20px; height: 20px; margin-top: 1px; color: var(--sa-accent-dark); }
.sa-section-dark .sa-b-split__tick { color: var(--sa-accent); }

.sa-b-split__media {
  margin: 0;
  border-radius: 20px;
  overflow: hidden;
  /* Bleed toward the viewport edge. */
  margin-right: calc((100vw - min(100vw, 1180px)) / -2 - 0px);
  max-width: none;
}

.sa-b-split.is-flipped .sa-b-split__media {
  margin-right: 0;
  margin-left: calc((100vw - min(100vw, 1180px)) / -2);
}

.sa-b-split__media img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

@media (max-width: 900px) {
  .sa-b-split__grid { grid-template-columns: 1fr; gap: 40px; }
  .sa-b-split.is-flipped .sa-b-split__copy,
  .sa-b-split.is-flipped .sa-b-split__media { order: initial; }
  .sa-b-split__media,
  .sa-b-split.is-flipped .sa-b-split__media { margin-inline: 0; }
}

/* ==========================================================================
   42. BLOCK: icon grid
   ========================================================================== */

.sa-b-icons__head { max-width: 62ch; }
.sa-b-icons__lede { margin-top: 18px; font-size: 16.5px; line-height: 1.7; }

.sa-b-icons__grid {
  display: grid;
  gap: 22px;
  margin-top: 48px;
}

.sa-b-icons__grid[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.sa-b-icons__grid[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sa-b-icons__grid[data-cols="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.sa-b-icons__card {
  position: relative;
  padding: 32px 28px 34px;
  border-radius: 18px;
  overflow: hidden;
  transition: transform 320ms cubic-bezier(.16,1,.3,1), border-color 320ms ease;
}

.sa-section-light .sa-b-icons__card {
  background: #fff;
  border: 1px solid color-mix(in srgb, var(--sa-text-on-light) 9%, transparent);
}

.sa-section-dark .sa-b-icons__card {
  background: var(--sa-bg-card-dark);
  border: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 9%, transparent);
}

/* A teal edge-light sweeps in from the top on hover, rather than the card
   simply lifting. Reads as the pipe energising that capability. */
.sa-b-icons__card::after {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--sa-accent), transparent);
  transform: scaleX(0);
  transition: transform 460ms cubic-bezier(.16,1,.3,1);
}

.sa-b-icons__card:hover { transform: translateY(-4px); }
.sa-b-icons__card:hover::after { transform: scaleX(1); }
.sa-b-icons__card:hover { border-color: color-mix(in srgb, var(--sa-accent) 40%, transparent); }

.sa-b-icons__mark {
  display: inline-grid;
  place-items: center;
  width: 46px;
  height: 46px;
  margin-bottom: 22px;
  border-radius: 13px;
  color: var(--sa-accent-dark);
  background: color-mix(in srgb, var(--sa-accent) 13%, transparent);
}

.sa-section-dark .sa-b-icons__mark { color: var(--sa-accent); }
.sa-b-icons__mark svg { width: 23px; height: 23px; }

@media (max-width: 1000px) {
  .sa-b-icons__grid[data-cols="4"],
  .sa-b-icons__grid[data-cols="3"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .sa-b-icons__grid,
  .sa-b-icons__grid[data-cols="2"],
  .sa-b-icons__grid[data-cols="3"],
  .sa-b-icons__grid[data-cols="4"] { grid-template-columns: 1fr; }
}

/* ==========================================================================
   43. BLOCK: steps
   ========================================================================== */

.sa-b-steps__head { max-width: 62ch; }
.sa-b-steps__lede { margin-top: 18px; font-size: 16.5px; line-height: 1.7; }

.sa-b-steps__list {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 34px;
  margin: 52px 0 0;
  padding: 0;
  list-style: none;
  counter-reset: sa-step;
}

.sa-b-steps__item { counter-increment: sa-step; }

.sa-b-steps__rule {
  display: block;
  height: 2px;
  margin-bottom: 22px;
  background: color-mix(in srgb, var(--sa-accent) 28%, transparent);
  position: relative;
}

.sa-b-steps__rule::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 34px;
  background: var(--sa-accent);
}

@media (max-width: 1000px) { .sa-b-steps__list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .sa-b-steps__list { grid-template-columns: 1fr; gap: 28px; } }

/* ==========================================================================
   44. BLOCK: stats
   ========================================================================== */

.sa-b-stats { padding-block: 74px; }

.sa-b-stats__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 34px;
}

.sa-b-stats__item {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-left: 22px;
  border-left: 2px solid color-mix(in srgb, var(--sa-accent) 40%, transparent);
}

.sa-b-stats__figure {
  font-family: var(--sa-font);
  font-size: clamp(30px, 3vw, 42px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--sa-accent);
}

.sa-b-stats__label {
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--sa-text-muted-dk);
}

.sa-section-light .sa-b-stats__label { color: var(--sa-text-muted-lt); }
.sa-section-light .sa-b-stats__figure { color: var(--sa-accent-dark); }

@media (max-width: 900px) { .sa-b-stats__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px; } }
@media (max-width: 520px) { .sa-b-stats__grid { grid-template-columns: 1fr; } }

/* ==========================================================================
   45. BLOCK: capabilities
   Hairline rows in two columns. Not a bulleted list, which stops being
   readable past about five items.
   ========================================================================== */

.sa-b-caps__head { max-width: 62ch; }
.sa-b-caps__lede { margin-top: 18px; font-size: 16.5px; line-height: 1.7; }

.sa-b-caps__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 64px;
  margin-top: 46px;
}

.sa-b-caps__row {
  padding: 26px 0;
  border-top: 1px solid color-mix(in srgb, var(--sa-text-on-light) 12%, transparent);
  transition: padding-left 340ms cubic-bezier(.16,1,.3,1);
}

.sa-section-dark .sa-b-caps__row { border-top-color: color-mix(in srgb, var(--sa-text-on-dark) 14%, transparent); }

.sa-b-caps__row:hover { padding-left: 12px; }

.sa-b-caps__title {
  font-family: var(--sa-font);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.35;
}

.sa-b-caps__body { margin-top: 9px; font-size: 15px; line-height: 1.65; }

@media (max-width: 820px) { .sa-b-caps__grid { grid-template-columns: 1fr; column-gap: 0; } }

/* ==========================================================================
   46. BLOCK: quote
   ========================================================================== */

.sa-b-quote figure { margin: 0; max-width: 30ch; }

.sa-b-quote__mark {
  display: block;
  font-family: var(--sa-font);
  font-size: 78px;
  font-weight: 700;
  line-height: 0.7;
  color: var(--sa-accent);
}

.sa-b-quote__text {
  margin: 22px 0 0;
  padding: 0;
  border: 0;
  font-family: var(--sa-font);
  font-size: clamp(21px, 2.2vw, 29px);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.022em;
}

.sa-section-dark .sa-b-quote__text { color: var(--sa-text-on-dark); }
.sa-section-light .sa-b-quote__text { color: var(--sa-text-on-light); }

.sa-b-quote__attr {
  margin-top: 26px;
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--sa-accent);
}

/* ==========================================================================
   47. BLOCK: logo strip
   ========================================================================== */

.sa-b-logos__title {
  font-family: var(--sa-font);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sa-text-muted-lt);
}

.sa-section-dark .sa-b-logos__title { color: var(--sa-text-muted-dk); }

.sa-b-logos__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 46px;
  margin: 34px 0 0;
  padding: 0;
  list-style: none;
}

.sa-b-logos__item img {
  display: block;
  height: calc(30px * var(--sa-logo-scale, 1));
  width: auto;
  opacity: 0.72;
  filter: grayscale(1);
  transition: opacity 300ms ease, filter 300ms ease;
}

.sa-b-logos__item img:hover { opacity: 1; filter: grayscale(0); }

/* ==========================================================================
   48. BLOCK: FAQ
   ========================================================================== */

.sa-b-faq__list { max-width: 860px; }

.sa-b-faq__item {
  border-top: 1px solid color-mix(in srgb, var(--sa-text-on-light) 12%, transparent);
}
.sa-b-faq__item:last-child {
  border-bottom: 1px solid color-mix(in srgb, var(--sa-text-on-light) 12%, transparent);
}

.sa-section-dark .sa-b-faq__item,
.sa-section-dark .sa-b-faq__item:last-child { border-color: color-mix(in srgb, var(--sa-text-on-dark) 14%, transparent); }

.sa-b-faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 26px 0;
  cursor: pointer;
  list-style: none;
  font-family: var(--sa-font);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.4;
}

.sa-b-faq__q::-webkit-details-marker { display: none; }
.sa-b-faq__q:focus-visible { outline: 2px solid var(--sa-accent-dark); outline-offset: 3px; }

.sa-b-faq__chev {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  color: var(--sa-accent-dark);
  background: color-mix(in srgb, var(--sa-accent) 13%, transparent);
  transition: transform 340ms cubic-bezier(.16,1,.3,1), background 300ms ease;
}

.sa-section-dark .sa-b-faq__chev { color: var(--sa-accent); }
.sa-b-faq__chev svg { width: 15px; height: 15px; }
.sa-b-faq__item[open] .sa-b-faq__chev { transform: rotate(135deg); }

.sa-b-faq__a { padding: 0 60px 28px 0; }
.sa-b-faq__a p { font-size: 16px; line-height: 1.72; margin: 0 0 14px; }
.sa-b-faq__a p:last-child { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) {
  .sa-b-faq__chev, .sa-b-caps__row, .sa-b-icons__card, .sa-b-icons__card::after { transition: none; }
}

/* ==========================================================================
   49. BLOCK: related
   ========================================================================== */

.sa-b-related__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}

.sa-b-related__card {
  position: relative;
  display: block;
  padding: 30px 28px 62px;
  border-radius: 18px;
  background: #fff;
  border: 1px solid color-mix(in srgb, var(--sa-text-on-light) 9%, transparent);
  transition: transform 320ms cubic-bezier(.16,1,.3,1), border-color 300ms ease;
}

.sa-section-dark .sa-b-related__card {
  background: var(--sa-bg-card-dark);
  border-color: color-mix(in srgb, var(--sa-text-on-dark) 9%, transparent);
}

.sa-b-related__card:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--sa-accent) 40%, transparent);
}

.sa-b-related__mark {
  display: inline-grid;
  place-items: center;
  width: 42px;
  height: 42px;
  margin-bottom: 20px;
  border-radius: 12px;
  color: var(--sa-accent-dark);
  background: color-mix(in srgb, var(--sa-accent) 13%, transparent);
}
.sa-section-dark .sa-b-related__mark { color: var(--sa-accent); }
.sa-b-related__mark svg { width: 21px; height: 21px; }

.sa-b-related__go {
  position: absolute;
  left: 28px;
  bottom: 26px;
  color: var(--sa-accent-dark);
  transition: transform 300ms cubic-bezier(.16,1,.3,1);
}
.sa-section-dark .sa-b-related__go { color: var(--sa-accent); }
.sa-b-related__go svg { width: 18px; height: 18px; }
.sa-b-related__card:hover .sa-b-related__go { transform: translateX(6px); }

@media (max-width: 940px) { .sa-b-related__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) { .sa-b-related__grid { grid-template-columns: 1fr; } }

/* ==========================================================================
   50. BLOCK: CTA
   ========================================================================== */

.sa-b-cta__body {
  max-width: 52ch;
  margin: 18px auto 0;
  font-size: 16.5px;
  line-height: 1.7;
}

@media (prefers-reduced-motion: reduce) {
  .sa-b-related__card,
  .sa-b-related__go,
  .sa-b-logos__item img { transition: none; }
  .sa-b-related__card:hover { transform: none; }
  .sa-b-icons__card:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   51. Composed-page corrections
   -------------------------------------------------------------------------- */

/* The icon was inline-grid, so the eyebrow sat beside it on the same line
   instead of the icon owning its own row above the eyebrow. */
.sa-b-hero__icon { display: grid; width: 54px; }

/* A split with no image collapses to a single readable measure rather than
   leaving an empty second column. */
.sa-b-split.is-solo .sa-b-split__grid { grid-template-columns: minmax(0, 74ch); }

/* Give the FAQ block room to breathe under its heading. */
.sa-b-faq__list { margin-top: 34px; }

/* --------------------------------------------------------------------------
   52. DESIGNED DATA PANELS
   The visual on a service page is a diagram of the thing being sold, drawn in
   brand colours, rather than a photograph of an office. Every stroke means
   something: sources, the tested hub, the trunk, the output, and packets
   moving through. That is why the motion is here and nowhere decorative.
   -------------------------------------------------------------------------- */

.sa-panel {
  position: relative;
  border-radius: 20px;
  overflow: hidden;
  background:
    radial-gradient(120% 100% at 12% 0%, color-mix(in srgb, var(--sa-accent) 11%, transparent), transparent 58%),
    linear-gradient(150deg, var(--sa-bg-dark-alt), var(--sa-bg-dark));
  border: 1px solid color-mix(in srgb, var(--sa-accent) 20%, transparent);
  box-shadow: 0 30px 70px -40px rgba(0, 0, 0, 0.85);
}

.sa-panel svg {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
}

/* Background measure. Deliberately faint: it reads as a coordinate space,
   not as decoration. */
.sa-panel__grid path {
  stroke: color-mix(in srgb, var(--sa-text-on-dark) 8%, transparent);
  stroke-width: 1;
}

/* Inactive routes: options not taken, sources not yet joined. */
.sa-panel__wire path,
.sa-panel__wire rect {
  stroke: color-mix(in srgb, var(--sa-text-on-dark) 26%, transparent);
  stroke-width: 1.6;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The committed path. */
.sa-panel__trunk path {
  stroke: var(--sa-accent);
  stroke-width: 2.4;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 0 8px color-mix(in srgb, var(--sa-accent) 45%, transparent));
}

.sa-panel__nodes circle {
  fill: var(--sa-bg-dark);
  stroke: color-mix(in srgb, var(--sa-text-on-dark) 40%, transparent);
  stroke-width: 1.6;
}

/* The governed point everything resolves through. */
.sa-panel__hub circle,
.sa-panel__hub rect {
  fill: none;
  stroke: var(--sa-accent);
  stroke-width: 1.8;
}

.sa-panel__hub circle:last-child {
  stroke: color-mix(in srgb, var(--sa-accent) 34%, transparent);
  stroke-dasharray: 3 6;
  transform-origin: center;
  animation: sa-panel-spin 22s linear infinite;
}

@keyframes sa-panel-spin { to { transform: rotate(360deg); } }

.sa-panel__out rect {
  fill: color-mix(in srgb, var(--sa-accent) 14%, transparent);
  stroke: var(--sa-accent);
  stroke-width: 1.6;
}

.sa-panel__out path {
  stroke: var(--sa-accent);
  stroke-width: 1.8;
  stroke-linecap: round;
}

/* Packets in transit. */
.sa-panel__flow circle {
  fill: var(--sa-accent);
  filter: drop-shadow(0 0 6px var(--sa-accent));
}

/* Reference lines: a threshold, or perfect calibration. */
.sa-panel__threshold path {
  stroke: color-mix(in srgb, var(--sa-text-on-dark) 22%, transparent);
  stroke-width: 1.2;
  stroke-dasharray: 5 6;
}

.sa-panel__ideal path {
  stroke: color-mix(in srgb, var(--sa-text-on-dark) 20%, transparent);
  stroke-width: 1.2;
  stroke-dasharray: 4 7;
}

/* Uncertainty, stated rather than hidden. */
.sa-panel__band path {
  fill: color-mix(in srgb, var(--sa-accent) 13%, transparent);
  stroke: none;
}

/* The legacy stack fades, the new one is lit. */
.sa-panel__legacy path {
  stroke: color-mix(in srgb, var(--sa-text-on-dark) 18%, transparent);
  stroke-width: 3;
  stroke-linecap: round;
}

.sa-panel__new path {
  stroke: color-mix(in srgb, var(--sa-accent) 62%, transparent);
  stroke-width: 3;
  stroke-linecap: round;
}

/* Panels sit in the split block's media column and keep the same bleed. */
.sa-b-split__media--panel { border-radius: 20px; overflow: visible; }

/* A diagram is not a photograph. The band crops to 3/2 with object-fit: cover,
   which sheared the left edge off the AWS data mesh diagram and took the first
   letter of its title with it. Diagrams sit contained on a dark ground, and
   keep out of the viewport bleed so nothing runs off the edge. */
.sa-b-split__media--diagram {
  margin-right: 0;
  margin-left: 0;
  background: #0b0b12;
}
.sa-b-split.is-flipped .sa-b-split__media--diagram { margin-left: 0; margin-right: 0; }
.sa-b-split__media--diagram img {
  aspect-ratio: 16 / 10;
  object-fit: contain;
}

@media (prefers-reduced-motion: reduce) {
  .sa-panel__hub circle:last-child { animation: none; }
  .sa-panel__flow { display: none; }
}

/* --------------------------------------------------------------------------
   53. BLOCK: contact form
   Labels above inputs, never placeholder-as-label. Focus, error and success
   states all carry sufficient contrast against the light section.
   -------------------------------------------------------------------------- */

.sa-b-form__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
  gap: 72px;
  align-items: start;
}

.sa-b-form__lede { margin-top: 20px; font-size: 16.5px; line-height: 1.72; }

.sa-b-form__aside { margin: 40px 0 0; }

.sa-b-form__aside dt {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--sa-accent-dark);
}

.sa-b-form__aside dd {
  margin: 7px 0 24px;
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--sa-text-on-light);
}

.sa-b-form__aside dd a { color: var(--sa-text-on-light); border-bottom: 1px solid color-mix(in srgb, var(--sa-accent) 55%, transparent); }
.sa-b-form__aside dd a:hover { color: var(--sa-accent-dark); }

.sa-b-form__panel {
  padding: 40px;
  border-radius: 22px;
  background: #fff;
  border: 1px solid color-mix(in srgb, var(--sa-text-on-light) 10%, transparent);
  box-shadow: 0 30px 70px -50px rgba(14, 30, 51, 0.5);
}

.sa-b-form__note {
  margin: 0 0 26px;
  padding: 15px 18px;
  border-radius: 12px;
  font-size: 15px;
  line-height: 1.55;
}

.sa-b-form__note--ok {
  color: #0d5c46;
  background: color-mix(in srgb, var(--sa-accent) 18%, #fff);
  border: 1px solid color-mix(in srgb, var(--sa-accent) 45%, transparent);
}

.sa-b-form__note--err {
  color: #8a2020;
  background: #fdf1f1;
  border: 1px solid #f0c8c8;
}

.sa-form__trap {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.sa-form__row { margin-bottom: 22px; }

.sa-form__label {
  display: block;
  margin-bottom: 9px;
  font-family: var(--sa-font);
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--sa-text-on-light);
}

.sa-form__optional {
  margin-left: 7px;
  font-weight: 500;
  font-size: 12px;
  color: var(--sa-text-muted-lt);
}

.sa-form__input {
  width: 100%;
  padding: 13px 16px;
  border: 1px solid color-mix(in srgb, var(--sa-text-on-light) 20%, transparent);
  border-radius: 11px;
  background: #fff;
  font-family: var(--sa-font);
  font-size: 15.5px;
  line-height: 1.5;
  color: var(--sa-text-on-light);
  transition: border-color 200ms ease, box-shadow 200ms ease;
  -webkit-appearance: none;
  appearance: none;
}

.sa-form__input::placeholder { color: var(--sa-text-muted-lt); }

.sa-form__input:focus,
.sa-form__input:focus-visible {
  outline: none;
  border-color: var(--sa-accent-dark);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sa-accent) 26%, transparent);
}

.sa-form__textarea { resize: vertical; min-height: 128px; }

.sa-form__help {
  margin: 9px 0 0;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--sa-text-muted-lt);
}

.sa-form__submit { margin-top: 10px; width: auto; border: 0; cursor: pointer; }
.sa-form__submit:active { transform: scale(0.985); }

.sa-form__trust {
  margin: 18px 0 0;
  font-size: 13px;
  color: var(--sa-text-muted-lt);
}

@media (max-width: 900px) {
  .sa-b-form__grid { grid-template-columns: 1fr; gap: 40px; }
  .sa-b-form__panel { padding: 28px 22px; }
}

@media (prefers-reduced-motion: reduce) {
  .sa-form__input { transition: none; }
  .sa-form__submit:active { transform: none; }
}

/* --------------------------------------------------------------------------
   54. Linked icon cards
   An icon card with a destination becomes the link itself, so the whole card
   is one target and one tab stop.
   -------------------------------------------------------------------------- */

.sa-b-icons__card.is-linked {
  display: block;
  padding-bottom: 64px;
  color: inherit;
  text-decoration: none;
}

.sa-b-icons__card.is-linked .sa-h3 { color: inherit; }

.sa-b-icons__go {
  position: absolute;
  left: 28px;
  bottom: 26px;
  color: var(--sa-accent-dark);
  transition: transform 300ms cubic-bezier(.16,1,.3,1);
}

.sa-section-dark .sa-b-icons__go { color: var(--sa-accent); }
.sa-b-icons__go svg { width: 18px; height: 18px; }
.sa-b-icons__card.is-linked:hover .sa-b-icons__go { transform: translateX(6px); }

.sa-b-icons__card.is-linked:focus-visible {
  outline: 2px solid var(--sa-accent-dark);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .sa-b-icons__go { transition: none; }
  .sa-b-icons__card.is-linked:hover .sa-b-icons__go { transform: none; }
}

/* --------------------------------------------------------------------------
   55. Linked platform chips and footer platform links
   The platform names on the homepage and in the footer named twelve pages
   that had no way of being reached from either place.
   -------------------------------------------------------------------------- */

.sa-chip--link {
  text-decoration: none;
  transition: border-color 220ms ease, color 220ms ease, background 220ms ease;
}

.sa-chip--link:hover {
  border-color: color-mix(in srgb, var(--sa-accent) 55%, transparent);
  color: var(--sa-accent);
}

.sa-chip--link:focus-visible {
  outline: 2px solid var(--sa-accent);
  outline-offset: 3px;
}

.sa-footer__platforms a {
  color: inherit;
  text-decoration: none;
  transition: color 200ms ease;
}

.sa-footer__platforms a:hover { color: var(--sa-accent); }
.sa-footer__sep { margin: 0 5px; opacity: 0.45; }

@media (prefers-reduced-motion: reduce) {
  .sa-chip--link, .sa-footer__platforms a { transition: none; }
}

/* --------------------------------------------------------------------------
   56. Icons in capabilities and steps, and the hero backdrop
   The pages were carrying icons only in the hero and the card grid, which left
   the two densest sections as walls of text and made the whole page read flat.
   -------------------------------------------------------------------------- */

.sa-b-caps__row {
  display: grid;
  grid-template-columns: 38px 1fr;
  column-gap: 18px;
  align-items: start;
}

.sa-b-caps__mark {
  grid-row: 1 / span 2;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  margin-top: 2px;
  border-radius: 11px;
  color: var(--sa-accent-dark);
  background: color-mix(in srgb, var(--sa-accent) 13%, transparent);
  transition: background 300ms ease, color 300ms ease, transform 300ms cubic-bezier(.16,1,.3,1);
}

.sa-section-dark .sa-b-caps__mark { color: var(--sa-accent); }
.sa-b-caps__mark svg { width: 19px; height: 19px; }

.sa-b-caps__row:hover .sa-b-caps__mark {
  background: var(--sa-accent);
  color: var(--sa-text-ink);
  transform: translateY(-2px);
}

/* A row with no icon keeps the same left edge as one that has an icon. */
.sa-b-caps__row:not(:has(.sa-b-caps__mark)) { grid-template-columns: 1fr; }

.sa-b-steps__mark {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  margin-bottom: 16px;
  border-radius: 12px;
  color: var(--sa-accent-dark);
  background: color-mix(in srgb, var(--sa-accent) 13%, transparent);
}

.sa-section-dark .sa-b-steps__mark { color: var(--sa-accent); }
.sa-b-steps__mark svg { width: 20px; height: 20px; }

/* -- Hero backdrop -------------------------------------------------------- */

.sa-b-hero.has-backdrop { position: relative; overflow: hidden; }
.sa-b-hero.has-backdrop .sa-container { position: relative; z-index: 2; }

.sa-b-hero__backdrop {
  position: absolute;
  top: 50%;
  right: -6%;
  width: min(60%, 780px);
  transform: translateY(-50%);
  opacity: 0.5;
  pointer-events: none;
  z-index: 1;
  /* Fade toward the copy so nothing competes with the headline. */
  -webkit-mask-image: linear-gradient(100deg, transparent 0%, #000 42%, #000 82%, transparent 100%);
          mask-image: linear-gradient(100deg, transparent 0%, #000 42%, #000 82%, transparent 100%);
}

.sa-b-hero__backdrop .sa-panel {
  background: none;
  border: 0;
  box-shadow: none;
}

.sa-b-hero__backdrop .sa-panel__grid { display: none; }

@media (max-width: 1100px) {
  .sa-b-hero__backdrop { opacity: 0.24; width: 78%; right: -18%; }
}

@media (max-width: 700px) {
  .sa-b-hero__backdrop { display: none; }
  .sa-b-caps__row { grid-template-columns: 34px 1fr; column-gap: 14px; }
}

@media (prefers-reduced-motion: reduce) {
  .sa-b-caps__mark { transition: none; }
  .sa-b-caps__row:hover .sa-b-caps__mark { transform: none; }
}

/* --------------------------------------------------------------------------
   57. BLOCK: full-bleed media band
   Photography of actual people, breaking up pages that would otherwise be
   diagrams and text all the way down. The scrim is graded rather than flat so
   the image still reads as a photograph behind the words.
   -------------------------------------------------------------------------- */

.sa-b-band {
  position: relative;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: var(--sa-bg-dark);
  isolation: isolate;
}

.sa-b-band--tall  { min-height: clamp(420px, 58vh, 620px); }
.sa-b-band--short { min-height: clamp(280px, 36vh, 400px); }

.sa-b-band__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
}

.sa-b-band__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--sa-bg-dark) 74%, transparent) 0%,
                           color-mix(in srgb, var(--sa-bg-dark) 38%, transparent) 44%,
                           transparent 76%),
    linear-gradient(0deg,  color-mix(in srgb, var(--sa-bg-dark) 82%, transparent) 0%,
                           transparent 46%);
}

.sa-b-band--right { justify-content: flex-end; }

.sa-b-band--right .sa-b-band__scrim {
  background:
    linear-gradient(270deg, color-mix(in srgb, var(--sa-bg-dark) 74%, transparent) 0%,
                            color-mix(in srgb, var(--sa-bg-dark) 38%, transparent) 44%,
                            transparent 76%),
    linear-gradient(0deg,   color-mix(in srgb, var(--sa-bg-dark) 82%, transparent) 0%,
                            transparent 46%);
}

.sa-b-band__inner {
  position: relative;
  padding-block: 54px;
  max-width: 1180px;
}

.sa-b-band--right .sa-b-band__inner { text-align: right; }

.sa-b-band__title {
  max-width: 22ch;
  font-family: var(--sa-font);
  font-size: clamp(26px, 3vw, 40px);
  font-weight: 600;
  line-height: 1.16;
  letter-spacing: -0.03em;
  color: var(--sa-text-on-dark);
}

.sa-b-band--right .sa-b-band__title { margin-left: auto; }

.sa-b-band__body {
  max-width: 52ch;
  margin-top: 16px;
  font-size: 16.5px;
  line-height: 1.7;
  color: var(--sa-text-muted-dk);
}

.sa-b-band--right .sa-b-band__body { margin-left: auto; }

/* The band is edge to edge, so it never carries a spine node. */
.sa-spined.has-spine > .sa-b-band::before { display: none; }

@media (max-width: 700px) {
  .sa-b-band--tall { min-height: 380px; }
  .sa-b-band__scrim,
  .sa-b-band--right .sa-b-band__scrim {
    background: linear-gradient(0deg,
      color-mix(in srgb, var(--sa-bg-dark) 92%, transparent) 0%,
      color-mix(in srgb, var(--sa-bg-dark) 55%, transparent) 70%,
      color-mix(in srgb, var(--sa-bg-dark) 35%, transparent) 100%);
  }
  .sa-b-band--right .sa-b-band__inner { text-align: left; }
  .sa-b-band--right .sa-b-band__title,
  .sa-b-band--right .sa-b-band__body { margin-left: 0; }
}

/* --------------------------------------------------------------------------
   58. Technology hero: the slab
   Services get the pipeline spine and hairline capability rows. Technology
   pages get this instead: the platform name set large against its diagram,
   then card grids. The two templates should not read as one template.
   -------------------------------------------------------------------------- */

.sa-b-hero--slab { padding-block: clamp(90px, 12vw, 150px); }

.sa-b-hero--slab .sa-page-head__title {
  font-size: clamp(46px, 7vw, 96px);
  font-weight: 700;
  letter-spacing: -0.045em;
  line-height: 0.98;
}

.sa-b-hero--slab .sa-eyebrow { color: var(--sa-accent); }

.sa-b-hero--slab .sa-page-head__lede {
  max-width: 46ch;
  margin-top: 26px;
  font-size: 17.5px;
}

/* The diagram sits higher and stronger here than it does on a service hero. */
.sa-b-hero--slab .sa-b-hero__backdrop {
  opacity: 0.62;
  right: -4%;
  width: min(54%, 700px);
}

/* A hairline under the platform name, echoing the spine without repeating it. */
.sa-b-hero--slab .sa-container::after {
  content: "";
  display: block;
  width: 88px;
  height: 3px;
  margin-top: 34px;
  border-radius: 2px;
  background: var(--sa-accent);
}

@media (max-width: 700px) {
  .sa-b-hero--slab .sa-page-head__title { font-size: clamp(38px, 11vw, 54px); }
}

/* --------------------------------------------------------------------------
   59. Primary nav dropdown
   Services opens onto the seven service pages. Hover opens it for pointer
   users; focus-within opens it for keyboard users, so it is reachable by Tab
   without any JavaScript.
   -------------------------------------------------------------------------- */

.sa-nav__list > li.menu-item-has-children { position: relative; }

.sa-nav__list > li.menu-item-has-children > a::after {
  content: "";
  display: inline-block;
  width: 5px;
  height: 5px;
  margin-left: 8px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 240ms ease;
}

.sa-nav__list > li.menu-item-has-children:hover > a::after,
.sa-nav__list > li.menu-item-has-children:focus-within > a::after {
  transform: translateY(0) rotate(225deg);
}

.sa-nav__list .sub-menu {
  position: absolute;
  top: calc(100% + 14px);
  left: 50%;
  transform: translateX(-50%) translateY(-6px);
  min-width: 288px;
  margin: 0;
  padding: 10px;
  list-style: none;
  border-radius: 16px;
  background: var(--sa-bg-card-dark);
  border: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 12%, transparent);
  box-shadow: 0 30px 60px -30px rgba(0, 0, 0, 0.8);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 220ms ease, transform 220ms cubic-bezier(.16,1,.3,1), visibility 220ms;
  z-index: 60;
}

/* A bridge across the gap, so the menu does not close while the pointer
   travels from the parent item down to the panel. */
.sa-nav__list .sub-menu::before {
  content: "";
  position: absolute;
  inset: -16px 0 100% 0;
}

.sa-nav__list > li.menu-item-has-children:hover > .sub-menu,
.sa-nav__list > li.menu-item-has-children:focus-within > .sub-menu {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}

.sa-nav__list .sub-menu li { margin: 0; }

.sa-nav__list .sub-menu a {
  display: block;
  padding: 11px 14px;
  border-radius: 10px;
  font-family: var(--sa-font);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.35;
  color: var(--sa-text-muted-dk);
  transition: background 200ms ease, color 200ms ease;
}

.sa-nav__list .sub-menu a:hover,
.sa-nav__list .sub-menu a:focus-visible {
  background: color-mix(in srgb, var(--sa-accent) 14%, transparent);
  color: var(--sa-text-on-dark);
}

/* Mobile: the submenu is a plain indented list, always visible. */
.sa-mobile-nav__list .sub-menu {
  list-style: none;
  margin: 6px 0 14px;
  padding-left: 16px;
  border-left: 1px solid color-mix(in srgb, var(--sa-accent) 30%, transparent);
}

.sa-mobile-nav__list .sub-menu a {
  font-size: 15px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  opacity: 0.82;
}

@media (prefers-reduced-motion: reduce) {
  .sa-nav__list .sub-menu,
  .sa-nav__list > li.menu-item-has-children > a::after { transition: none; }
}

/* --------------------------------------------------------------------------
   60. Vendor logos
   Shown on a white tile rather than knocked out to white, because the brand
   colours are how these marks are recognised.
   -------------------------------------------------------------------------- */

.sa-b-hero__logo {
  display: inline-flex;
  align-items: center;
  height: 74px;
  padding: 0 26px;
  margin-bottom: 30px;
  border-radius: 16px;
  background: #fff;
  box-shadow: 0 20px 44px -26px rgba(0, 0, 0, 0.8);
}

.sa-b-hero__logo img { height: 34px; width: auto; display: block; }

.sa-b-related__logo {
  display: inline-flex;
  align-items: center;
  height: 52px;
  padding: 0 16px;
  margin-bottom: 20px;
  border-radius: 12px;
  background: #fff;
  border: 1px solid color-mix(in srgb, var(--sa-text-on-light) 8%, transparent);
}

.sa-b-related__logo img { height: 24px; width: auto; display: block; }

/* On dark related grids the tile still needs to read as a tile. */
.sa-section-dark .sa-b-related__logo { border-color: transparent; }

@media (max-width: 700px) {
  .sa-b-hero__logo { height: 62px; padding: 0 20px; }
  .sa-b-hero__logo img { height: 28px; }
}

/* The logo tile is inline-flex for its own sizing, so it needs to be forced
   onto its own row or the eyebrow sits alongside it. Same fix as the icon. */
.sa-b-hero__logo { display: flex; width: fit-content; }

/* --------------------------------------------------------------------------
   61. Long-form legal and editorial pages
   Pages with no authored blocks fall through to the editor content. Give that
   a readable measure and the same prose rhythm as an Insights article.
   -------------------------------------------------------------------------- */

.sa-prose { max-width: 72ch; }

.sa-prose h2 {
  margin-top: 46px;
  font-family: var(--sa-font);
  font-size: 26px;
  font-weight: 600;
  letter-spacing: -0.026em;
  color: var(--sa-text-on-light);
}

.sa-prose h3 {
  margin-top: 32px;
  font-family: var(--sa-font);
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.018em;
  color: var(--sa-text-on-light);
}

.sa-prose p,
.sa-prose li {
  font-size: 16.5px;
  line-height: 1.75;
  color: var(--sa-text-muted-lt);
}

.sa-prose p { margin: 14px 0 0; }
.sa-prose ul { margin: 18px 0 0; padding-left: 22px; }
.sa-prose li { margin-bottom: 10px; }

.sa-prose a {
  color: var(--sa-text-on-light);
  border-bottom: 1px solid color-mix(in srgb, var(--sa-accent) 60%, transparent);
}
.sa-prose a:hover { color: var(--sa-accent-dark); }
.sa-prose strong { color: var(--sa-text-on-light); font-weight: 600; }

/* --------------------------------------------------------------------------
   62. Vendor logo strip
   Full-colour on a light band. These are third-party marks, so they are shown
   as their owners intend rather than knocked out to a single colour.
   -------------------------------------------------------------------------- */

.sa-b-logos--tech .sa-b-logos__row { gap: 34px 48px; }

.sa-b-logos__item img {
  height: 26px;
  opacity: 0.55;
  filter: grayscale(1);
  transition: opacity 300ms ease, filter 300ms ease, transform 300ms ease;
}

.sa-b-logos__item img:hover {
  opacity: 1;
  filter: grayscale(0);
  transform: translateY(-2px);
}

@media (prefers-reduced-motion: reduce) {
  .sa-b-logos__item img { transition: opacity 300ms ease, filter 300ms ease; }
  .sa-b-logos__item img:hover { transform: none; }
}

/* ==========================================================================
   63. FOOTER, REBUILT
   Structure borrowed from Maze: pill-chip column labels, a card on the right,
   an accent follow bar, and an oversized wordmark anchoring the base. Executed
   in the 6Analytix navy and teal rather than Maze's cream and lime.
   Overrides the earlier footer rules further up this file.
   ========================================================================== */

.sa-footer {
  background: var(--sa-bg-dark);
  color: var(--sa-text-muted-dk);
  padding-top: 96px;
  overflow: hidden;
}

.sa-footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 372px;
  gap: 72px;
  align-items: start;
}

.sa-footer__cols {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 40px 28px;
}

.sa-footer__col { display: flex; flex-direction: column; align-items: flex-start; }

/* Pill-chip column label. */
.sa-footer__chip {
  display: inline-block;
  margin-bottom: 20px;
  padding: 6px 13px;
  border-radius: 100px;
  background: color-mix(in srgb, var(--sa-text-on-dark) 10%, transparent);
  font-family: var(--sa-font);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--sa-text-on-dark);
}

.sa-footer__chip--light {
  background: color-mix(in srgb, var(--sa-accent) 18%, transparent);
  color: var(--sa-accent);
}

.sa-footer__link {
  display: block;
  margin-bottom: 12px;
  font-family: var(--sa-font);
  font-size: 14.5px;
  line-height: 1.45;
  color: var(--sa-text-muted-dk);
  transition: color 200ms ease, transform 200ms ease;
}

.sa-footer__link:hover { color: var(--sa-text-on-dark); transform: translateX(3px); }
.sa-footer__link:focus-visible { outline: 2px solid var(--sa-accent); outline-offset: 3px; border-radius: 4px; }
.sa-footer__link--more { color: var(--sa-accent); font-weight: 600; }

/* -- The card ------------------------------------------------------------- */

.sa-footer__aside { display: flex; flex-direction: column; gap: 14px; }

.sa-footer__card {
  padding: 30px;
  border-radius: 22px;
  background: var(--sa-bg-card-dark);
  border: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 10%, transparent);
}

.sa-footer__card-title {
  margin: 4px 0 0;
  font-family: var(--sa-font);
  font-size: 21px;
  font-weight: 600;
  line-height: 1.28;
  letter-spacing: -0.022em;
  color: var(--sa-text-on-dark);
}

.sa-footer__card-body { margin: 12px 0 0; font-size: 14.5px; line-height: 1.62; }
.sa-footer__card-btn { margin-top: 22px; }

.sa-footer__meta {
  margin: 28px 0 0;
  padding-top: 24px;
  border-top: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 10%, transparent);
}

.sa-footer__meta dt {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--sa-accent);
}

.sa-footer__meta dd { margin: 6px 0 16px; font-size: 14px; line-height: 1.55; color: var(--sa-text-muted-dk); }
.sa-footer__meta dd:last-child { margin-bottom: 0; }
.sa-footer__meta a { color: var(--sa-text-muted-dk); transition: color 200ms ease; }
.sa-footer__meta a:hover { color: var(--sa-accent); }

/* -- Follow bar ----------------------------------------------------------- */

.sa-footer__follow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 16px 24px;
  border-radius: 18px;
  background: var(--sa-accent);
}

.sa-footer__follow-label {
  font-family: var(--sa-font);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--sa-text-ink);
}

.sa-footer__follow .sa-social { margin: 0; gap: 8px; }

.sa-footer__follow .sa-social__link {
  width: 34px;
  height: 34px;
  border-color: color-mix(in srgb, var(--sa-text-ink) 26%, transparent);
  color: var(--sa-text-ink);
}

.sa-footer__follow .sa-social__link:hover {
  background: var(--sa-bg-dark);
  border-color: var(--sa-bg-dark);
  color: var(--sa-accent);
}

.sa-footer__follow .sa-social__link svg { width: 15px; height: 15px; }

/* -- Oversized wordmark --------------------------------------------------- */

.sa-footer__wordmark {
  display: flex;
  align-items: center;
  gap: 0.06em;
  margin: 82px 0 0;
  font-family: var(--sa-font);
  font-size: clamp(64px, 15.5vw, 232px);
  font-weight: 800;
  line-height: 0.82;
  letter-spacing: -0.055em;
  color: color-mix(in srgb, var(--sa-text-on-dark) 8%, transparent);
  user-select: none;
  white-space: nowrap;
}

/*
 * The glyph must sit inside the box the way it does in the header logo, where
 * the box is 31px and the digit 16.5px: the glyph is 53% of the box.
 *
 * The trap here is that `em` in width and height resolves against this
 * element's OWN font-size, not the parent's. Declaring `width: 0.82em` next to
 * `font-size: 0.86em` therefore produced a box smaller than the glyph, so the
 * 6 burst out of it. Every length below is expressed in the element's own em
 * and derived from the header's proven ratios:
 *
 *   font-size    0.46em of the wordmark
 *   box          1.87em of its own font-size  ->  0.86em of the wordmark
 *   radius       0.29 of the box, matching the header's 9/31
 */
.sa-footer__wordmark-mark {
  display: inline-grid;
  place-items: center;
  font-size: 0.46em;
  width: 1.87em;
  height: 1.87em;
  margin-right: 0.24em;
  border-radius: 0.54em;
  background: color-mix(in srgb, var(--sa-accent) 16%, transparent);
  color: color-mix(in srgb, var(--sa-accent) 62%, transparent);
  font-weight: 800;
  line-height: 1;
  text-align: center;
  /*
   * The wordmark is a nowrap flex row that deliberately overflows on narrow
   * viewports. Without this the mark is the flex item that gives way, so the
   * box squashed to 76px wide against a 192px tall glyph. The header logo mark
   * carries the same guard for the same reason.
   */
  flex: 0 0 auto;
}

/* -- Legal bar ------------------------------------------------------------ */

.sa-footer__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 28px;
  margin-top: 40px;
  padding: 26px 0 34px;
  border-top: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 10%, transparent);
  font-size: 12.5px;
  letter-spacing: 0.03em;
}

.sa-footer__legal { display: flex; flex-wrap: wrap; gap: 24px; }

.sa-footer__legal a {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--sa-text-muted-dk);
  transition: color 200ms ease;
}

.sa-footer__legal a:hover { color: var(--sa-accent); }

@media (max-width: 1100px) {
  .sa-footer__top { grid-template-columns: 1fr; gap: 52px; }
  .sa-footer__aside { max-width: 460px; }
}

@media (max-width: 780px) {
  .sa-footer { padding-top: 64px; }
  .sa-footer__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 34px 20px; }
  .sa-footer__wordmark { margin-top: 54px; }
  .sa-footer__bar { flex-direction: column; align-items: flex-start; }
}

@media (max-width: 460px) {
  .sa-footer__cols { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  .sa-footer__link, .sa-footer__meta a, .sa-footer__legal a { transition: color 200ms ease; }
  .sa-footer__link:hover { transform: none; }
}

/* ==========================================================================
   64. DENSITY PASS
   The statement, quote and CTA blocks were each running 400 to 600px tall for
   under 200 characters. That reads as unfinished rather than as confidence.
   Tightened, and given an accent element so the space is doing something.
   ========================================================================== */

.sa-b-statement { padding-block: 74px; }

.sa-b-statement__rule {
  display: block;
  width: 68px;
  height: 3px;
  margin-bottom: 30px;
  border-radius: 2px;
  background: var(--sa-accent);
  transform-origin: left;
  transform: scaleX(0);
  transition: transform 900ms cubic-bezier(.16,1,.3,1) 120ms;
}

.sa-js .sa-reveal.is-visible .sa-b-statement__rule,
.sa-b-statement .sa-container:not(.sa-reveal) .sa-b-statement__rule { transform: scaleX(1); }

.sa-b-statement__text { max-width: 26ch; }

.sa-b-quote { padding-block: 76px; }
.sa-b-quote figure { max-width: 34ch; }
.sa-b-quote__mark { font-size: 60px; margin-bottom: -6px; }
.sa-b-quote__text { margin-top: 14px; }
.sa-b-quote__attr { margin-top: 20px; }

.sa-cta.sa-b-cta { padding-block: 78px; }

/* Related cards were rendering with an empty body, leaving a tall card with a
   title floating in it. */
.sa-b-related__card { padding: 26px 26px 58px; }

@media (prefers-reduced-motion: reduce) {
  .sa-b-statement__rule { transition: none; transform: scaleX(1); }
}

/* ==========================================================================
   65. MOTION PASS
   Every rule here is gated on prefers-reduced-motion and each has a reason:
   headline arrival, drawing the eye to a figure, depth on a photograph, and
   acknowledging a click. Nothing loops for the sake of it.
   ========================================================================== */

/* -- Hero headline word reveal -------------------------------------------- */

.sa-word {
  display: inline-block;
  overflow: hidden;
  /* Room for descenders, which overflow:hidden would otherwise clip. */
  padding-bottom: 0.12em;
  margin-bottom: -0.12em;
  vertical-align: bottom;
}

.sa-word__in {
  display: inline-block;
  transform: translateY(110%);
  transition: transform 820ms cubic-bezier(.16, 1, .3, 1);
}

.sa-page-head__title.is-revealed .sa-word__in { transform: translateY(0); }

/* -- Media band parallax --------------------------------------------------
   Scroll-driven where the browser supports it, so it runs on the compositor
   with no scroll listener at all. Browsers without support get a still image,
   which is a perfectly good outcome.
   ------------------------------------------------------------------------ */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .sa-b-band__img {
      height: 122%;
      top: -11%;
      animation: sa-band-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
  }
}

@keyframes sa-band-drift {
  from { transform: translateY(-5%); }
  to   { transform: translateY(5%); }
}

/* -- Buttons acknowledge the press ---------------------------------------- */

.sa-btn {
  transition: transform 160ms cubic-bezier(.16,1,.3,1), background 220ms ease, box-shadow 220ms ease;
}

.sa-btn--teal:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px -12px color-mix(in srgb, var(--sa-accent) 70%, transparent);
}

.sa-btn:active { transform: translateY(0) scale(0.985); }

/* -- FAQ opens rather than jumps ------------------------------------------ */

.sa-b-faq__item[open] .sa-b-faq__a {
  animation: sa-faq-open 420ms cubic-bezier(.16,1,.3,1);
}

@keyframes sa-faq-open {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* -- Nav links get an underline that draws in ----------------------------- */

.sa-nav__list > li > a {
  position: relative;
}

.sa-nav__list > li > a::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -6px;
  height: 1.5px;
  background: var(--sa-accent);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 320ms cubic-bezier(.16,1,.3,1);
}

.sa-nav__list > li > a:hover::before,
.sa-nav__list > li.current-menu-item > a::before {
  transform: scaleX(1);
  transform-origin: left;
}

/* The parent of an open dropdown should not also underline. */
.sa-nav__list > li.menu-item-has-children > a::before { display: none; }

@media (prefers-reduced-motion: reduce) {
  .sa-word__in { transform: none; transition: none; }
  .sa-b-band__img { animation: none; height: 100%; top: 0; }
  .sa-btn, .sa-nav__list > li > a::before { transition: none; }
  .sa-btn--teal:hover, .sa-btn:active { transform: none; }
  .sa-b-faq__item[open] .sa-b-faq__a { animation: none; }
}

/* ==========================================================================
   66. Orphaned final card
   Seven items in a three-column grid leaves the seventh alone on its own row,
   which reads as a mistake. Where that happens the last card spans the full
   width and lays out horizontally, so it reads as a deliberate closing item
   rather than a leftover.
   ========================================================================== */

.sa-b-icons__grid[data-cols="3"] > :nth-child(7):last-child,
.sa-b-icons__grid[data-cols="4"] > :nth-child(7):last-child {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  align-items: start;
  column-gap: 22px;
  padding-bottom: 32px;
}

.sa-b-icons__grid > :nth-child(7):last-child .sa-b-icons__mark {
  grid-row: 1 / span 2;
  margin-bottom: 0;
}

.sa-b-icons__grid > :nth-child(7):last-child .sa-h3 { align-self: center; }

.sa-b-icons__grid > :nth-child(7):last-child .sa-small {
  max-width: 78ch;
  margin-top: 8px;
}

/* A linked orphan keeps its arrow bottom-left, so the affordance is unchanged. */
.sa-b-icons__grid > :nth-child(7):last-child.is-linked { padding-bottom: 58px; }

@media (max-width: 1000px) {
  .sa-b-icons__grid[data-cols="3"] > :nth-child(7):last-child,
  .sa-b-icons__grid[data-cols="4"] > :nth-child(7):last-child { grid-column: 1 / -1; }
}

@media (max-width: 640px) {
  /* [data-cols] is load-bearing: without it this selector is less specific
     than the desktop rule above, which then won at every width and crushed
     the description into the 46px icon column, one word per line
     (responsive audit, 19 Sept). */
  .sa-b-icons__grid[data-cols] > :nth-child(7):last-child {
    grid-template-columns: 1fr;
    row-gap: 0;
  }
  .sa-b-icons__grid[data-cols] > :nth-child(7):last-child .sa-b-icons__mark {
    grid-row: auto;
    margin-bottom: 22px;
  }
}

/* ==========================================================================
   67. MOBILE NAV, REBUILT
   Full-screen navy panel: logo and close at the top, large bold rows split by
   hairlines, submenus that expand in place, and a pinned outlined CTA.
   Expanding in place is what removes the need for any "Back" control.
   ========================================================================== */

.sa-mobile-nav {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  background: var(--sa-bg-dark);
  overscroll-behavior: contain;
  /*
   * The original mobile panel further up this file sets 110px top padding,
   * 40px bottom and a 26px gap. Those were sized for the old centred list and
   * they were quietly eating 202px of an 844px viewport here, which is why the
   * menu scrolled and sat low. The rebuilt panel manages its own spacing per
   * section, so all three are cleared.
   */
  padding: 0;
  gap: 0;
  overflow: hidden;
}

.sa-mobile-nav[hidden] { display: none; }

/* -- Top bar -------------------------------------------------------------- */

.sa-mobile-nav__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: 0 0 auto;
  padding: 20px 20px 26px;
}

.sa-mobile-nav__close {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--sa-text-on-dark);
  cursor: pointer;
  transition: background 200ms ease;
}

.sa-mobile-nav__close:hover { background: color-mix(in srgb, var(--sa-text-on-dark) 10%, transparent); }
.sa-mobile-nav__close:focus-visible { outline: 2px solid var(--sa-accent); outline-offset: 2px; }
.sa-mobile-nav__close svg { width: 24px; height: 24px; }

/* -- Scrollable list ------------------------------------------------------ */

.sa-mobile-nav__scroll {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.sa-mobile-nav__list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 14%, transparent);
}

.sa-mobile-nav__list > li {
  border-bottom: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 14%, transparent);
}

/* The row holds the link and the chevron side by side, so tapping the label
   still navigates while the chevron only expands. */
.sa-mobile-nav__row {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
}

.sa-mobile-nav__list > li > a,
.sa-mobile-nav__row > a {
  flex: 1 1 auto;
  display: block;
  padding: 21px 20px;
  font-family: var(--sa-font);
  font-size: 25px;
  font-weight: 700;
  letter-spacing: -0.028em;
  line-height: 1.2;
  color: var(--sa-text-on-dark);
  text-transform: none;
  text-decoration: none;
}

.sa-mobile-nav__list > li > a:active,
.sa-mobile-nav__row > a:active { color: var(--sa-accent); }

.sa-mobile-nav__toggle {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 66px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--sa-text-on-dark);
  cursor: pointer;
}

.sa-mobile-nav__toggle svg {
  width: 24px;
  height: 24px;
  transition: transform 320ms cubic-bezier(.16,1,.3,1);
}

.sa-mobile-nav__list > li.is-open .sa-mobile-nav__toggle svg { transform: rotate(180deg); }
.sa-mobile-nav__list > li.is-open .sa-mobile-nav__toggle { color: var(--sa-accent); }
.sa-mobile-nav__toggle:focus-visible { outline: 2px solid var(--sa-accent); outline-offset: -4px; }

/* -- Submenu, expanding in place ------------------------------------------ */

.sa-mobile-nav__list .sub-menu {
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: 0;
  overflow: hidden;
  border-left: 0;
  transition: max-height 380ms cubic-bezier(.16,1,.3,1);
}

.sa-mobile-nav__list .sub-menu li:last-child a { padding-bottom: 20px; }

.sa-mobile-nav__list .sub-menu a {
  display: block;
  padding: 12px 20px 12px 34px;
  font-family: var(--sa-font);
  font-size: 15.5px;
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1.35;
  text-transform: none;
  color: var(--sa-text-muted-dk);
  opacity: 1;
}

.sa-mobile-nav__list .sub-menu a:active { color: var(--sa-accent); }

/* A teal rule marks the expanded group without needing a second screen. */
.sa-mobile-nav__list > li.is-open .sub-menu {
  box-shadow: inset 3px 0 0 color-mix(in srgb, var(--sa-accent) 55%, transparent);
}

/* -- Pinned CTA ----------------------------------------------------------- */

.sa-mobile-nav__foot {
  flex: 0 0 auto;
  padding: 20px 20px calc(24px + env(safe-area-inset-bottom, 0px));
}

.sa-mobile-nav__cta {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 56px;
  border-radius: 100px;
  font-size: 14px;
}

.sa-btn--ghost {
  background: transparent;
  border: 1.5px solid color-mix(in srgb, var(--sa-text-on-dark) 45%, transparent);
  color: var(--sa-text-on-dark);
}

.sa-btn--ghost:hover,
.sa-btn--ghost:active {
  background: var(--sa-accent);
  border-color: var(--sa-accent);
  color: var(--sa-text-ink);
}

@media (prefers-reduced-motion: reduce) {
  .sa-mobile-nav__toggle svg,
  .sa-mobile-nav__list .sub-menu { transition: none; }
}

/* UiCore injects a "Back" row into each submenu for its own slide-to-second-
   screen pattern. Ours expands in place, so a Back row means nothing. JS
   removes them; this is the backstop if the injection lands late. */
.sa-mobile-nav li.back,
.sa-mobile-nav li.menu-item.back { display: none !important; }

/* ==========================================================================
   68. Insight cards: one click target, and more life
   The "Read" affordance looked clickable and was not, because only the title
   was a link. The title anchor now stretches over the whole card.
   ========================================================================== */

.sa-insight { position: relative; }

.sa-insight__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}

/* The category chip stays above the stretched link so it remains its own
   target where it is a link, and text stays selectable. */
.sa-insight__category,
.sa-insight__foot { position: relative; z-index: 2; }
.sa-insight__foot { pointer-events: none; }

/* Whole-card hover, driven from the card rather than the link. */
.sa-insight {
  transition: transform 340ms cubic-bezier(.16,1,.3,1),
              box-shadow 340ms ease,
              border-color 300ms ease;
}

.sa-insight:hover {
  transform: translateY(-6px);
  box-shadow: 0 26px 50px -30px rgba(14, 30, 51, 0.45);
  border-color: color-mix(in srgb, var(--sa-accent) 42%, transparent);
}

.sa-insight:focus-within {
  border-color: color-mix(in srgb, var(--sa-accent) 55%, transparent);
}

.sa-insight__link:focus-visible::after {
  outline: 2px solid var(--sa-accent-dark);
  outline-offset: 3px;
}

/* The image lifts and warms slightly, so the card feels like one object. */
.sa-insight__media {
  overflow: hidden;
  display: block;
}

.sa-insight__media img {
  display: block;
  width: 100%;
  height: auto;
  transition: transform 620ms cubic-bezier(.16,1,.3,1), filter 420ms ease;
  filter: saturate(0.92);
}

.sa-insight:hover .sa-insight__media img {
  transform: scale(1.055);
  filter: saturate(1.06);
}

/* The Read affordance moves, which is the cue that the card is a target. */
.sa-insight__read {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--sa-accent-dark);
  transition: gap 300ms cubic-bezier(.16,1,.3,1), color 260ms ease;
}

.sa-insight:hover .sa-insight__read { gap: 12px; color: var(--sa-accent-dark); }
.sa-insight__read svg { width: 15px; height: 15px; }

/* A teal edge draws across the top on hover, matching the icon cards. */
.sa-insight::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  z-index: 3;
  background: linear-gradient(90deg, transparent, var(--sa-accent), transparent);
  transform: scaleX(0);
  transition: transform 520ms cubic-bezier(.16,1,.3,1);
}

.sa-insight:hover::before { transform: scaleX(1); }

@media (prefers-reduced-motion: reduce) {
  .sa-insight,
  .sa-insight__media img,
  .sa-insight__read,
  .sa-insight::before { transition: none; }
  .sa-insight:hover { transform: none; }
  .sa-insight:hover .sa-insight__media img { transform: none; }
}

/* ==========================================================================
   69. "Visit Insights" button
   It sat in a grid row and inherited `stretch`, so a three-word button ran
   the full width of its column. Buttons size to their label.
   ========================================================================== */

.sa-split .sa-btn,
.sa-split--end .sa-btn {
  justify-self: end;
  align-self: center;
  width: auto;
  flex: 0 0 auto;
}

@media (max-width: 760px) {
  .sa-split .sa-btn,
  .sa-split--end .sa-btn { justify-self: start; }
}

/* ==========================================================================
   70. Mobile nav: everything in one view
   The panel is full height but the list area was not taking the space, so the
   menu scrolled by a couple of pixels for no reason. A flex child will not
   shrink below its content without `min-height: 0`, which is what was missing.
   Rows are also tightened so all five items, an expanded submenu and the CTA
   all fit on a phone without scrolling.
   ========================================================================== */

.sa-mobile-nav__scroll {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.sa-mobile-nav__list { flex: 0 0 auto; }

.sa-mobile-nav__list > li > a,
.sa-mobile-nav__row > a {
  padding: 17px 20px;
  font-size: 23px;
}

.sa-mobile-nav__toggle { width: 60px; }

.sa-mobile-nav__list .sub-menu a {
  padding: 10px 20px 10px 34px;
  font-size: 14.5px;
}

.sa-mobile-nav__list .sub-menu li:last-child a { padding-bottom: 16px; }

.sa-mobile-nav__bar { padding: 16px 20px 20px; }
.sa-mobile-nav__foot { padding: 16px 20px calc(20px + env(safe-area-inset-bottom, 0px)); }
.sa-mobile-nav__cta { height: 52px; }

/*
 * Short phones: an expanded submenu can still exceed the viewport, so the list
 * area scrolls rather than the CTA being pushed off screen. The CTA stays
 * pinned because it lives outside the scrolling area.
 */
@media (max-height: 700px) {
  .sa-mobile-nav__list > li > a,
  .sa-mobile-nav__row > a { padding: 13px 20px; font-size: 20px; }
  .sa-mobile-nav__list .sub-menu a { padding: 8px 20px 8px 34px; font-size: 14px; }
}

/* The largest submenu is seven items. Trimming the sub-item rhythm by a few
   pixels means even the widest group fits on a standard phone without the list
   scrolling at all. */
.sa-mobile-nav__list .sub-menu a { padding: 8px 20px 8px 34px; }
.sa-mobile-nav__list .sub-menu li:first-child a { padding-top: 4px; }
.sa-mobile-nav__list .sub-menu li:last-child a { padding-bottom: 14px; }

/* ==========================================================================
   71. Header CTA hover
   The cream button only shifted #f4f6f2 to #ffffff on hover, which is close to
   invisible, so the primary action felt dead. It now takes the brand accent
   with navy text, lifts, and a highlight sweeps across it. Navy on teal is
   high contrast, so the label stays readable throughout.
   ========================================================================== */

.sa-header__cta {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  transition: background 260ms ease,
              color 260ms ease,
              transform 220ms cubic-bezier(.16,1,.3,1),
              box-shadow 260ms ease;
}

.sa-header__cta:hover,
.sa-header__cta:focus-visible {
  background: var(--sa-accent);
  color: var(--sa-text-ink);
  transform: translateY(-2px);
  box-shadow: 0 10px 26px -10px color-mix(in srgb, var(--sa-accent) 80%, transparent);
}

.sa-header__cta:active { transform: translateY(0) scale(0.98); }

/* The sweep. A soft highlight crossing left to right, once per hover. */
.sa-header__cta::after {
  content: "";
  position: absolute;
  top: 0;
  left: -70%;
  width: 55%;
  height: 100%;
  z-index: -1;
  background: linear-gradient(
    100deg,
    transparent,
    color-mix(in srgb, #ffffff 62%, transparent),
    transparent
  );
  transform: skewX(-18deg);
  opacity: 0;
  pointer-events: none;
}

.sa-header__cta:hover::after,
.sa-header__cta:focus-visible::after {
  animation: sa-cta-sweep 720ms cubic-bezier(.22,.9,.3,1) forwards;
}

@keyframes sa-cta-sweep {
  0%   { left: -70%; opacity: 0; }
  18%  { opacity: 1; }
  100% { left: 125%; opacity: 0; }
}

.sa-header__cta:focus-visible {
  outline: 2px solid var(--sa-accent);
  outline-offset: 3px;
}

/* On the front page the header sits over the hero, where the cream button is
   the only light element. Keep the same behaviour there. */
.sa-header.is-transparent .sa-header__cta:hover {
  background: var(--sa-accent);
  color: var(--sa-text-ink);
}

@media (prefers-reduced-motion: reduce) {
  .sa-header__cta { transition: background 260ms ease, color 260ms ease; }
  .sa-header__cta:hover,
  .sa-header__cta:focus-visible { transform: none; }
  .sa-header__cta:active { transform: none; }
  .sa-header__cta::after,
  .sa-header__cta:hover::after,
  .sa-header__cta:focus-visible::after { animation: none; opacity: 0; }
}

/* ==========================================================================
   72. RESPONSIVE REVIEW FIXES
   From a measured audit across 13 widths and 7 pages, reviewed by Codex.
   Four evidenced defects, fixed in the order of their user impact.
   ========================================================================== */

/* -- 1. Touch targets ------------------------------------------------------
   Footer links measured 21px tall, the logo 31px, desktop nav anchors 13px.
   Scoped to coarse pointers so desktop hit areas keep their tight rhythm.
   -------------------------------------------------------------------------- */

@media (hover: none) and (pointer: coarse) {
  .sa-footer__link,
  .sa-footer__meta a,
  .sa-footer__legal a,
  .sa-footer__platforms a,
  .sa-insight__link,
  .sa-b-faq__q {
    display: flex;
    align-items: center;
    min-height: 44px;
  }

  .sa-footer__link { margin-bottom: 2px; }

  .sa-logo,
  .sa-mobile-nav__close,
  .sa-burger {
    min-height: 44px;
    min-width: 44px;
  }

  .sa-search { height: 46px; }
  .sa-filter__chip { min-height: 40px; }

  /* A stretched card link is already the whole card, so it needs no minimum. */
  .sa-insight__link { display: inline; min-height: 0; }
}

/* -- 2. Line length --------------------------------------------------------
   Measured up to 109ch on /contact/ at 834px and 100ch on service pages from
   1024px up. Anything past roughly 75ch is tiring to read, so the paragraphs
   themselves are capped rather than only their containers.
   -------------------------------------------------------------------------- */

.sa-b-split__body p,
.sa-b-caps__body,
.sa-b-icons__lede,
.sa-b-steps__lede,
.sa-b-caps__lede,
.sa-b-form__lede,
.sa-b-band__body,
.sa-b-cta__body,
.sa-page-head__lede,
.sa-prose p,
.sa-prose li {
  max-inline-size: 68ch;
}

.sa-form__trust,
.sa-form__help { max-inline-size: 60ch; }

.sa-b-split.is-solo .sa-b-split__grid { grid-template-columns: minmax(0, 68ch); }
.sa-b-icons__grid > :nth-child(7):last-child .sa-small { max-inline-size: 68ch; }

/* -- 3. Structural label sizes --------------------------------------------
   10px was the smallest rendered text sitewide. These labels carry structure,
   not decoration, so they move up to 12px. The floating hero pill stays small
   because it genuinely is decorative.
   -------------------------------------------------------------------------- */

.sa-insight__category { font-size: 12px; letter-spacing: 0.1em; }
.sa-footer__chip      { font-size: 11.5px; }
.sa-nav__list a       { font-size: 11.5px; }
.sa-pill              { font-size: 11px; }

/* -- 4. Container gutter ---------------------------------------------------
   Padding stepped from 20px to 40px at exactly 641px, so the content area
   shrank by 39px as the viewport grew by 1px. One fluid gutter instead.
   -------------------------------------------------------------------------- */

.sa-container { padding-inline: clamp(20px, 4vw, 40px); }

/* -- 5. Breakpoint consolidation ------------------------------------------
   Card families were switching at 820, 900, 940 and 1000px while the header
   stayed in burger mode until 1024. Pages could therefore reach three-column
   density while still presenting a mobile header. All card grids now share the
   same two thresholds, and they size intrinsically so a 481 to 639px viewport
   is no longer stuck with one very wide card.
   -------------------------------------------------------------------------- */

@media (min-width: 560px) and (max-width: 1023px) {
  .sa-b-icons__grid[data-cols="2"],
  .sa-b-icons__grid[data-cols="3"],
  .sa-b-icons__grid[data-cols="4"],
  .sa-b-related__grid,
  .sa-grid-3 {
    grid-template-columns: repeat(auto-fit, minmax(248px, 1fr));
  }
}

@media (min-width: 820px) and (max-width: 1023px) {
  .sa-b-caps__grid { grid-template-columns: 1fr; column-gap: 0; }
}

/* ==========================================================================
   73. Footer aside on tablet
   Once the footer stacks below 1100px the contact card was still capped at
   460px, so it sat in the left half of a full-width footer with an empty
   column beside it. It now spans the width, and the contact details lay out
   in columns so the card does not become a tall narrow strip.
   ========================================================================== */

@media (max-width: 1100px) {
  .sa-footer__aside { max-width: none; }

  .sa-footer__card {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: 44px;
    align-items: start;
    padding: 34px;
  }

  /* Left column: the pitch and the button. */
  .sa-footer__chip--light,
  .sa-footer__card-title,
  .sa-footer__card-body,
  .sa-footer__card-btn { grid-column: 1; }

  .sa-footer__card-title { max-width: 22ch; }

  /* Right column: the contact details, aligned to the top of the card. */
  .sa-footer__meta {
    grid-column: 2;
    grid-row: 1 / span 4;
    margin: 0;
    padding-top: 0;
    border-top: 0;
    border-left: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 10%, transparent);
    padding-left: 34px;
  }
}

@media (max-width: 700px) {
  /* Back to a single column before the two get too narrow to read. */
  .sa-footer__card {
    grid-template-columns: 1fr;
    padding: 28px 22px;
  }

  .sa-footer__chip--light,
  .sa-footer__card-title,
  .sa-footer__card-body,
  .sa-footer__card-btn,
  .sa-footer__meta { grid-column: 1; }

  .sa-footer__meta {
    grid-row: auto;
    margin-top: 28px;
    padding-top: 24px;
    padding-left: 0;
    border-left: 0;
    border-top: 1px solid color-mix(in srgb, var(--sa-text-on-dark) 10%, transparent);
  }
}

/* Grid items stretch by default, so the chip and the button were running the
   full width of their column. Both size to their content. */
@media (max-width: 1100px) {
  .sa-footer__card > .sa-footer__chip--light,
  .sa-footer__card-btn { justify-self: start; }
}

/* ==========================================================================
   74. THE ROTATING GLOBE
   Collins liked a rotating earth on Slalom's site and wanted it connected to
   sustainable data storage. This is our own build, not theirs: a wireframe
   earth whose meridians narrow and widen exactly as lines of longitude do when
   a sphere turns, which gives real rotation without a 3D transform or a
   library. Nodes sit on the surface and a satellite tracks the outer orbit.
   ========================================================================== */

.sa-globe__sphere { stroke: none; }

.sa-globe__rim {
  fill: none;
  stroke: color-mix(in srgb, var(--sa-accent) 55%, transparent);
  stroke-width: 1.6;
}

.sa-globe__lat ellipse,
.sa-globe__lon ellipse {
  fill: none;
  stroke: color-mix(in srgb, var(--sa-accent) 30%, transparent);
  stroke-width: 1.1;
}

/*
 * Each meridian animates its horizontal radius on its own phase. Together they
 * read as one sphere turning. Pure attribute animation on the compositor.
 */
.sa-globe__m1 { animation: sa-mer-a 14s linear infinite; }
.sa-globe__m2 { animation: sa-mer-b 14s linear infinite; }
.sa-globe__m3 { animation: sa-mer-c 14s linear infinite; }
.sa-globe__m4 { animation: sa-mer-d 14s linear infinite; }

@keyframes sa-mer-a {
  0%,100% { rx: 150px; opacity: .95; }
  25%     { rx: 60px;  opacity: .45; }
  50%     { rx: 4px;   opacity: .2; }
  75%     { rx: 60px;  opacity: .45; }
}
@keyframes sa-mer-b {
  0%,100% { rx: 112px; opacity: .8; }
  25%     { rx: 150px; opacity: .95; }
  50%     { rx: 112px; opacity: .8; }
  75%     { rx: 20px;  opacity: .3; }
}
@keyframes sa-mer-c {
  0%,100% { rx: 58px;  opacity: .55; }
  25%     { rx: 120px; opacity: .8; }
  50%     { rx: 150px; opacity: .95; }
  75%     { rx: 120px; opacity: .8; }
}
@keyframes sa-mer-d {
  0%,100% { rx: 6px;   opacity: .25; }
  25%     { rx: 66px;  opacity: .5; }
  50%     { rx: 128px; opacity: .85; }
  75%     { rx: 150px; opacity: .95; }
}

.sa-globe__nodes circle {
  fill: var(--sa-accent);
  filter: drop-shadow(0 0 7px color-mix(in srgb, var(--sa-accent) 75%, transparent));
}

.sa-globe__arc path {
  fill: none;
  stroke: var(--sa-accent);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-dasharray: 4 7;
  opacity: 0.75;
  animation: sa-globe-arc 3.4s linear infinite;
}

@keyframes sa-globe-arc { to { stroke-dashoffset: -110; } }

.sa-globe__orbit ellipse {
  fill: none;
  stroke: color-mix(in srgb, var(--sa-text-on-dark) 16%, transparent);
  stroke-width: 1;
  stroke-dasharray: 2 8;
}

.sa-globe__sat circle {
  fill: var(--sa-accent);
  filter: drop-shadow(0 0 9px var(--sa-accent));
}

@media (prefers-reduced-motion: reduce) {
  .sa-globe__m1, .sa-globe__m2, .sa-globe__m3, .sa-globe__m4,
  .sa-globe__arc path { animation: none; }
  .sa-globe__sat { display: none; }
}

/* ==========================================================================
   75. Responsive fixes, second pass
   Three things the first pass missed, each found by re-measuring rather than
   assuming the fix had worked.
   ========================================================================== */

/* FAQ answers were the remaining 100ch paragraphs. They were not in the first
   cap list because they are generated by wpautop, not authored markup. */
.sa-b-faq__a p,
.sa-b-faq__a li { max-inline-size: 68ch; }

/* The header CTA label was the last 10.5px text on the page. */
.sa-header__cta { font-size: 11.5px; }

/*
 * Touch targets, by viewport rather than by pointer type.
 *
 * The first pass scoped these to `(pointer: coarse)`, which is correct in
 * principle but cannot be verified in a desktop browser and silently does
 * nothing on hybrid devices that report a fine pointer. Below 900px the layout
 * is the mobile one regardless of input device, so the minimum applies there
 * unconditionally. The coarse-pointer block above still covers touch on larger
 * screens.
 */
@media (max-width: 900px) {
  .sa-footer__link,
  .sa-footer__meta a,
  .sa-footer__legal a {
    display: flex;
    align-items: center;
    min-height: 44px;
    margin-bottom: 0;
  }

  .sa-footer__platforms a {
    display: inline-block;
    padding-block: 10px;
  }

  .sa-logo { min-height: 44px; }
  .sa-search { height: 46px; }
  .sa-filter__chip { min-height: 40px; }
  .sa-b-faq__q { min-height: 56px; }
}

/* ==========================================================================
   76. Line length, expressed correctly
   The earlier caps used `ch`, which is the advance width of the "0" glyph. In
   Plus Jakarta Sans that is about 0.63em, while an average lowercase character
   is nearer 0.5em. So `68ch` was actually letting through roughly 86
   characters, which is why the measure still read long after the first fix.
   Expressed in `em` instead: 34em is about 68 real characters.
   ========================================================================== */

.sa-b-split__body p,
.sa-b-caps__body,
.sa-b-icons__lede,
.sa-b-steps__lede,
.sa-b-caps__lede,
.sa-b-form__lede,
.sa-b-band__body,
.sa-b-cta__body,
.sa-page-head__lede,
.sa-prose p,
.sa-prose li,
.sa-b-faq__a p,
.sa-b-faq__a li {
  max-inline-size: 34em;
}

.sa-form__trust,
.sa-form__help { max-inline-size: 30em; }

.sa-b-split.is-solo .sa-b-split__grid { grid-template-columns: minmax(0, 36em); }
.sa-b-icons__grid > :nth-child(7):last-child .sa-small { max-inline-size: 34em; }

/* ==========================================================================
   77. PLATFORM LOGO MARQUEE
   Collins asked for this by name: the real vendor logos sliding sideways, the
   way Slalom presents its technology partners, instead of the static text
   chips that were here.

   The twelve logos are rendered twice inside one track and the track moves by
   exactly -50%. When the animation loops, the second copy is sitting precisely
   where the first started, so there is no seam and no jump. One transform, so
   it runs on the compositor rather than on layout.

   Each mark sits on a white tile. Several of these logos are dark (dbt,
   ThoughtSpot, Microsoft's wordmark) and would disappear against the navy
   card, and knocking multi-colour marks to white would lose the brand
   recognition that makes a partner strip worth showing at all.
   ========================================================================== */

.sa-marquee {
  position: relative;
  margin-top: 22px;
  overflow: hidden;
  /* Fade both ends so logos enter and leave rather than being cut off. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}

.sa-marquee__track {
  display: flex;
  width: max-content;
  animation: sa-marquee-slide 42s linear infinite;
}

/* Hovering lets someone read a logo or aim for a link. */
.sa-marquee:hover .sa-marquee__track,
.sa-marquee:focus-within .sa-marquee__track {
  animation-play-state: paused;
}

@keyframes sa-marquee-slide {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

.sa-marquee__set {
  display: flex;
  align-items: center;
  gap: 26px;
  margin: 0;
  padding: 0 13px;
  list-style: none;
}

.sa-marquee__item { flex: 0 0 auto; }

/*
 * No tile. The logos are the reversed set, so they sit directly on the navy
 * exactly as Slalom's partner strip does. Resting opacity is slightly held
 * back so the strip reads as a quiet band rather than twelve competing marks,
 * and hover brings the one you are pointing at to full strength.
 */
.sa-marquee__link {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 54px;
  padding: 0 26px;
  opacity: 0.72;
  transition: opacity 300ms ease, transform 300ms cubic-bezier(.16,1,.3,1);
}

.sa-marquee__link img {
  height: 26px;
  width: auto;
  display: block;
}

.sa-marquee__link:hover {
  opacity: 1;
  transform: translateY(-2px);
}

.sa-marquee__link:focus-visible {
  outline: 2px solid var(--sa-accent);
  outline-offset: 3px;
}

@media (max-width: 640px) {
  .sa-marquee__link { height: 46px; padding: 0 18px; }
  .sa-marquee__link img { height: 21px; }
  .sa-marquee__set { gap: 18px; }
}

/*
 * Reduced motion: the strip stops and wraps instead, so every logo is still
 * reachable without anything moving.
 */
@media (prefers-reduced-motion: reduce) {
  .sa-marquee { -webkit-mask-image: none; mask-image: none; }
  .sa-marquee__track { animation: none; width: 100%; flex-wrap: wrap; }
  .sa-marquee__set:last-child { display: none; }
  .sa-marquee__set { flex-wrap: wrap; gap: 10px; }
  .sa-marquee__link { transition: none; }
  .sa-marquee__link:hover { transform: none; }
}



/* ==========================================================================
   78. Technology hero logo, no tile
   Kept deliberately, not by accident. Collins raised how logos read against
   the background, so the platform mark in a technology hero uses the reversed
   set and sits directly on the navy, the same treatment as the marquee on the
   home page. Without this rule the earlier white-tile declaration further up
   this file wins again on the next deploy.
   ========================================================================== */

.sa-b-hero__logo {
  display: flex;
  width: fit-content;
  height: auto;
  padding: 0;
  margin-bottom: 28px;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

.sa-b-hero__logo img { height: 44px; width: auto; display: block; }

@media (max-width: 700px) {
  .sa-b-hero__logo img { height: 34px; }
}

/* ==========================================================================
   79. CARDS THAT COME FORWARD
   Collins liked the existing pop on the service cards and asked for them to
   "come forward" more, plus more interaction generally. So: a larger lift, a
   real shadow so the card reads as lifted rather than just moved, a lit edge,
   and a light that follows the pointer across the face.
   ========================================================================== */

.sa-card--link,
.sa-b-icons__card,
.sa-b-related__card {
  --sa-mx: 50%;
  --sa-my: 50%;
  position: relative;
  isolation: isolate;
  transition: transform 380ms cubic-bezier(.16, 1, .3, 1),
              box-shadow 380ms ease,
              border-color 300ms ease;
}

/* The travelling light. Sits under the content, above the card background. */
.sa-card--link::after,
.sa-b-icons__card::after,
.sa-b-related__card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  opacity: 0;
  transition: opacity 380ms ease;
  pointer-events: none;
  background: radial-gradient(
    260px circle at var(--sa-mx) var(--sa-my),
    color-mix(in srgb, var(--sa-accent) 16%, transparent),
    transparent 70%
  );
}

.sa-card--link:hover::after,
.sa-b-icons__card:hover::after,
.sa-b-related__card:hover::after { opacity: 1; }

/* Dark cards lift into shadow. */
.sa-card--link:hover {
  transform: translateY(-8px) scale(1.012);
  border-color: color-mix(in srgb, var(--sa-accent) 60%, transparent);
  box-shadow: 0 26px 52px -24px rgba(0, 0, 0, 0.85),
              0 0 0 1px color-mix(in srgb, var(--sa-accent) 22%, transparent);
}

/* Light cards need a tinted shadow, never a black one on off-white. */
.sa-b-icons__card:hover,
.sa-b-related__card:hover {
  transform: translateY(-8px) scale(1.012);
  box-shadow: 0 26px 50px -26px rgba(14, 30, 51, 0.42);
}

/* The icon comes with it, which is what sells the card as one object. */
.sa-card--link .sa-card__icon,
.sa-b-icons__mark,
.sa-b-related__mark {
  transition: transform 380ms cubic-bezier(.16, 1, .3, 1), color 300ms ease;
}

.sa-card--link:hover .sa-card__icon { transform: translateY(-3px) scale(1.08); }

.sa-b-icons__card:hover .sa-b-icons__mark,
.sa-b-related__card:hover .sa-b-related__mark {
  transform: translateY(-3px) scale(1.06);
}

.sa-card--link:active,
.sa-b-icons__card:active,
.sa-b-related__card:active { transform: translateY(-3px) scale(1.002); }

@media (prefers-reduced-motion: reduce) {
  .sa-card--link,
  .sa-b-icons__card,
  .sa-b-related__card,
  .sa-card--link .sa-card__icon,
  .sa-b-icons__mark,
  .sa-b-related__mark { transition: border-color 300ms ease; }

  .sa-card--link:hover,
  .sa-b-icons__card:hover,
  .sa-b-related__card:hover,
  .sa-card--link:active { transform: none; }

  .sa-card--link::after,
  .sa-b-icons__card::after,
  .sa-b-related__card::after { display: none; }
}

/* ==========================================================================
   80. SECTION BACKGROUND IMAGERY
   Collins wanted something behind the Services section rather than flat navy,
   and imagery on the plain white area above it.
   ========================================================================== */

/* -- Image behind a dark section ------------------------------------------ */

.sa-section-dark.has-bg { position: relative; isolation: isolate; overflow: hidden; }

.sa-section-bg {
  position: absolute;
  inset: 0;
  z-index: -2;
  pointer-events: none;
}

.sa-section-bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center right;
  opacity: 0.72;
}

/*
 * A scrim over the image, not an opacity drop on the section. Graded so the
 * left, where the heading sits, stays close to solid navy while the right,
 * behind the cards, keeps more of the texture.
 */
.sa-section-dark.has-bg::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(90deg,
      var(--sa-bg-dark) 0%,
      color-mix(in srgb, var(--sa-bg-dark) 82%, transparent) 30%,
      color-mix(in srgb, var(--sa-bg-dark) 42%, transparent) 72%,
      color-mix(in srgb, var(--sa-bg-dark) 24%, transparent) 100%),
    linear-gradient(180deg,
      var(--sa-bg-dark) 0%,
      transparent 18%,
      transparent 82%,
      var(--sa-bg-dark) 100%);
}

@media (max-width: 900px) {
  /* Narrow columns need the text ground to be quieter still. */
  .sa-section-bg img { opacity: 0.4; }
}

/*
 * The cards sit on top of the image, so they need their own ground rather than
 * relying on the section colour. Slight translucency lets the texture read
 * through the gaps without ever reaching the text.
 */
.sa-section-dark.has-bg .sa-card,
.sa-section-dark.has-bg .sa-card--platforms {
  background: color-mix(in srgb, var(--sa-bg-card-dark) 88%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

/* -- Supporting figure on a light section --------------------------------- */

.sa-figure {
  margin: 0;
  border-radius: 22px;
  overflow: hidden;
  background: var(--sa-bg-dark);
}

.sa-figure img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 21 / 8;
  object-fit: cover;
}


@media (max-width: 640px) {
  .sa-figure img { aspect-ratio: 3 / 2; }
}

/* --------------------------------------------------------------------------
   81. The living globe (Approach)
   
   Collins' AKQA reference: a foliage sphere with a line of type orbiting it.
   Two rules carry the look. The object never sits in a frame, so the media
   column loses the card treatment the boardroom photo had. And the clip's flat
   ground is dissolved with a radial mask rather than colour-matched, so it
   composites cleanly whatever the section is doing behind it.
   -------------------------------------------------------------------------- */

.sa-globe { margin: 0; display: flex; align-items: center; justify-content: center; }

/* Overrides the shared media rule above: no radius, no crop, no clipping. */
.sa-globe.sa-approach__media { border-radius: 0; overflow: visible; }

.sa-globe__stage {
  position: relative;
  width: 100%;
  max-width: 540px;
  margin-inline: auto;
  aspect-ratio: 1 / 1;
}

.sa-globe__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;

  /*
   * Keeps the sphere and its contact shadow, fades the flat studio ground to
   * nothing. Centred slightly high because the shadow sits below the sphere.
   */
  -webkit-mask-image: radial-gradient(circle at 50% 49%, #000 66%, rgba(0,0,0,0.5) 77%, transparent 87%);
          mask-image: radial-gradient(circle at 50% 49%, #000 66%, rgba(0,0,0,0.5) 77%, transparent 87%);
}

/* The orbiting line. Sits outside the sphere, turning against it. */
.sa-globe__ring {
  position: absolute;
  inset: -7%;
  width: 114%;
  height: 114%;
  pointer-events: none;
  overflow: visible;
}

/*
 * Rotate the text inside the fixed SVG box, never the box itself. Spinning the
 * element swings its square corners outside the circle, which swelled its
 * layout box, pushed the caption around and overflowed the page on mobile.
 */
.sa-globe__ring text {
  transform-box: view-box;
  transform-origin: 150px 150px;
  animation: sa-globe-ring 24s linear infinite;
}

.sa-globe__ring text {
  font-family: var(--sa-font);
  font-size: 11.6px;
  font-weight: 700;
  letter-spacing: 0.05em;
  fill: var(--sa-text-muted-lt);
  text-transform: uppercase;
}

@keyframes sa-globe-ring { to { transform: rotate(360deg); } }

/*
 * Pointer response. Collins specifically liked that the reference reacts, so
 * hovering the stage speeds the ring up and lifts the sphere a little. The
 * --sa-globe-tilt values are written by motion.js from pointer position.
 */
.sa-globe__stage { --sa-globe-tx: 0deg; --sa-globe-ty: 0deg; }

.sa-globe__video {
  transform: perspective(900px) rotateX(var(--sa-globe-ty)) rotateY(var(--sa-globe-tx));
  transition: transform 420ms cubic-bezier(.16,1,.3,1);
}

.sa-globe__stage:hover .sa-globe__ring text { animation-duration: 10s; }

.sa-approach__note {
  margin-top: 34px;
  padding-top: 26px;
  border-top: 1px solid color-mix(in srgb, var(--sa-text-on-light) 12%, transparent);
  max-width: 62ch;
}

.sa-approach__note p {
  font-size: 14.5px;
  line-height: 1.66;
  color: var(--sa-text-muted-lt);
  margin: 0;
}

/* Button rather than an inline link: this is a real asset, not a footnote. */
.sa-approach__doc { margin-top: 20px; }

/*
 * The ring is drawn 114% of the stage, so it sits outside the sphere and
 * therefore outside the stage box. On narrow screens that overflowed the
 * container on the left and collided with the caption below, so the stage is
 * capped against the viewport and the gap opened up to clear the ring.
 */
@media (max-width: 900px) {
  .sa-globe__stage { max-width: min(380px, 74vw); }
}

@media (max-width: 640px) {
  .sa-globe__stage { max-width: 70vw; }
  .sa-globe__ring text { font-size: 10.4px; letter-spacing: 0.03em; }
}

@media (prefers-reduced-motion: reduce) {
  .sa-globe__ring text { animation: none; }
  .sa-globe__stage:hover .sa-globe__ring text { animation: none; }
  .sa-globe__video { transition: none; transform: none; }
}

/* --------------------------------------------------------------------------
   82. Hero slideshow

   Five slides crossfading in place. The media and the copy fade on separate
   timings, copy trailing the image slightly, so the change reads as one move
   rather than two things swapping at once.
   -------------------------------------------------------------------------- */

.sa-hero--deck { --sa-hero-dur: 5000ms; }   /* must match DURATION in motion.js */

.sa-hero__media { position: absolute; inset: 0; z-index: 0; }

/*
 * Extra darkening behind the copy. The original scrim was tuned for one
 * portrait; across five frames, some of them bright and detailed, the centre
 * needs its own pool of shade or the headline sits on top of monitors.
 */
.sa-hero--deck .sa-hero__scrim::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(62% 52% at 50% 46%, rgba(11,23,42, 0.62) 0%, rgba(11,23,42, 0.34) 55%, rgba(11,23,42, 0) 100%);
}

.sa-hero__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 480ms cubic-bezier(.37, 0, .27, 1);
  will-change: opacity;
}

.sa-hero__slide.is-active { opacity: 1; }

/* Held at the end scale while idle so an incoming slide never snaps back. */
.sa-hero__slide .sa-hero__bg { transform: scale(1.09); }

.sa-hero__slide.is-active .sa-hero__bg {
  animation: sa-hero-kb 5800ms linear forwards;
}

@keyframes sa-hero-kb {
  from { transform: scale(1.015); }
  to   { transform: scale(1.09); }
}

/* Copy slides stack in a single grid cell so they cross over in place. */
.sa-hero__copydeck { display: grid; }
.sa-hero__copydeck > * { grid-area: 1 / 1; }

.sa-hero__copy {
  opacity: 0;
  pointer-events: none;
  transition: opacity 380ms ease;
}

.sa-hero__copy.is-active {
  opacity: 1;
  pointer-events: auto;
  transition-delay: 110ms;
}

/*
 * Slides 2 to 5 carry an h2 rather than an h1, so only the theme's default h2
 * margin needs clearing. The .sa-hero__title class already carries the type.
 */
.sa-hero__copy h2.sa-hero__title { margin: 0; }

/* The floating industry pills belong to the opening frame only. */
.sa-hero--deck .sa-hero__pills { transition: opacity 700ms ease; }
.sa-hero--deck.is-past-first .sa-hero__pills { opacity: 0; }

/*
 * Parked on the bottom edge of the hero rather than under the buttons, and
 * held at low opacity until the pointer is in the section. It is an
 * orientation cue; it should not compete with the headline.
 */
.sa-hero__nav {
  position: absolute;
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%);
  z-index: 5;
  display: flex;
  gap: 0;
  opacity: 0.4;
  transition: opacity 420ms ease;
}

.sa-hero:hover .sa-hero__nav,
.sa-hero__nav:focus-within { opacity: 1; }

/*
 * Deliberately quiet. These are an orientation cue, not a control anyone came
 * for, so they read as a hairline until the pointer is near them.
 */
.sa-hero__dot {
  position: relative;
  width: 30px;
  height: 24px;          /* WCAG 2.2 minimum target; the line stays a hairline */
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

/*
 * The visible hairline. This used to be the button's own 1px box, with a
 * bigger hit area from ::after. But the button also had overflow: hidden,
 * which clipped that ::after straight back to the 1px line, so on a phone the
 * dots could barely be tapped (responsive audit, 19 Sept). The button is now
 * the full 24px target and the line is drawn inside it.
 */
.sa-hero__dot::before {
  content: "";
  position: absolute;
  left: 4px;
  right: 4px;
  top: 50%;
  height: 1px;
  border-radius: 1px;
  background: rgba(255, 255, 255, 0.16);
  transition: background-color 260ms ease;
}

.sa-hero__dot:hover::before { background: rgba(255, 255, 255, 0.34); }
.sa-hero__dot:focus-visible { outline: 2px solid var(--sa-accent); outline-offset: 2px; border-radius: 2px; }

.sa-hero__dot-fill {
  position: absolute;
  left: 4px;
  top: 50%;
  height: 1px;
  width: 0;
  background: rgba(255, 255, 255, 0.4);
}

.sa-hero__dot.is-active .sa-hero__dot-fill {
  animation: sa-hero-dot var(--sa-hero-dur) linear forwards;
}

.sa-hero--deck.is-paused .sa-hero__dot-fill { animation-play-state: paused; }

@keyframes sa-hero-dot { from { width: 0; } to { width: calc(100% - 8px); } }

@media (max-width: 640px) {
  .sa-hero__dot { width: 26px; }
  .sa-hero__nav { bottom: 22px; opacity: 0.55; }
}

@media (prefers-reduced-motion: reduce) {
  .sa-hero__slide { transition: none; }
  .sa-hero__slide.is-active .sa-hero__bg { animation: none; }
  .sa-hero__copy { transition: none; transform: none; }
  .sa-hero__dot.is-active .sa-hero__dot-fill { animation: none; width: 100%; }
}

/* --------------------------------------------------------------------------
   83. Hero transition, and hero copy alignment

   The copy deck stacks its slides in one grid cell. That cell stretches, so a
   max-width paragraph inside it hugged the left edge and sat 155px off centre
   from the headline. Centring is restored on the copy block itself rather than
   by touching the shared lede rule, which other sections rely on.
   -------------------------------------------------------------------------- */

.sa-hero__copy {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Headline rises out of a mask rather than simply fading up. */



/* --------------------------------------------------------------------------
   84. Hero, motion-forward

   The incoming frame wipes across on a diagonal, over the outgoing one rather
   than over an empty stage, with a teal edge riding the boundary and the
   headline landing word by word behind it.
   -------------------------------------------------------------------------- */

.sa-hero--deck .sa-hero__slide {
  opacity: 0;
  z-index: 0;
  transition: none;
}

/* Outgoing frame, held one layer down for the length of the wipe. */
.sa-hero--deck .sa-hero__slide.is-prev {
  opacity: 1;
  z-index: 1;
  animation: sa-hero-recede 820ms cubic-bezier(.3, 0, .2, 1) forwards;
}

.sa-hero--deck .sa-hero__slide.is-active {
  opacity: 1;
  z-index: 2;
  animation: sa-hero-wipe 740ms cubic-bezier(.76, 0, .24, 1) forwards;
}

/* Leading edge is a diagonal, so the frame arrives on a slant. */
@keyframes sa-hero-wipe {
  from { clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); }
  to   { clip-path: polygon(0 0, 132% 0, 104% 100%, 0 100%); }
}

/* The frame being replaced drifts and dims, so the change has depth. */
@keyframes sa-hero-recede {
  from { transform: scale(1) translateX(0);        filter: brightness(1); }
  to   { transform: scale(1.05) translateX(-2.5%); filter: brightness(0.72); }
}

/* Teal edge travelling with the wipe boundary. */
.sa-hero--deck .sa-hero__media::after {
  content: "";
  position: absolute;
  top: -12%;
  left: 0;
  width: 22vw;
  height: 124%;
  z-index: 3;
  opacity: 0;
  pointer-events: none;
  transform: translateX(-30vw) skewX(-13deg);
  background: linear-gradient(
    90deg,
    rgba(47, 217, 170, 0) 0%,
    rgba(47, 217, 170, 0.10) 62%,
    rgba(120, 255, 220, 0.55) 92%,
    rgba(255, 255, 255, 0.85) 100%);
}

.sa-hero--deck.is-changing .sa-hero__media::after {
  animation: sa-hero-beam 740ms cubic-bezier(.76, 0, .24, 1) forwards;
}

@keyframes sa-hero-beam {
  0%   { opacity: 0;    transform: translateX(-30vw) skewX(-13deg); }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { opacity: 0;    transform: translateX(122vw) skewX(-13deg); }
}

/*
 * Headline words. Overrides the load-time is-ready gate above, which only ever
 * knew about a single static headline.
 */
.sa-js .sa-hero--deck .sa-hero__copy .sa-hero__title .sa-word {
  opacity: 0;
  transform: translateY(26px);
  filter: blur(6px);
  transition: opacity 440ms cubic-bezier(.16, 1, .3, 1),
              transform 440ms cubic-bezier(.16, 1, .3, 1),
              filter 340ms ease;
  transition-delay: calc(90ms + var(--w, 0) * 26ms);
}

.sa-js .sa-hero--deck .sa-hero__copy.is-active .sa-hero__title .sa-word {
  opacity: 1;
  transform: none;
  filter: blur(0);
}

@media (prefers-reduced-motion: reduce) {
  .sa-hero--deck .sa-hero__slide.is-active,
  .sa-hero--deck .sa-hero__slide.is-prev { animation: none; }
  .sa-hero--deck .sa-hero__slide.is-prev { opacity: 0; }
  .sa-hero--deck.is-changing .sa-hero__media::after { animation: none; opacity: 0; }
  .sa-js .sa-hero--deck .sa-hero__copy .sa-hero__title .sa-word {
    opacity: 1; transform: none; filter: none; transition: none;
  }
}

/* File-type hint on the strategy link, so nobody is surprised by a download. */
.sa-approach__doc-meta {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 2px 6px;
  border-radius: 3px;
  color: var(--sa-accent-dark);
  background: color-mix(in srgb, var(--sa-accent) 14%, transparent);
}

/* --------------------------------------------------------------------------
   85. Section texture on light sections

   Collins, 28 Aug: the sides of the inner pages read "spaced out, like empty".
   The container is 1180px, so a 1920px screen leaves ~370px of dead margin
   each side. These line-art SVGs live in that margin and nowhere else: the
   scrim below is solid across the content column and only turns transparent
   once past the gutter, so the texture can never sit behind body copy.

   Drawn rather than photographed, so the whole set costs less than one JPEG
   and stays sharp at any width.
   -------------------------------------------------------------------------- */

.sa-section-light.has-bg {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

.sa-section-light.has-bg .sa-section-bg img {
  opacity: 0.5;
  object-position: center;

  /* Feathers the top and bottom so sections do not butt against each other. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 84%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 84%, transparent 100%);
}

.sa-section-bg { overflow: hidden; }
.sa-section-bg img { position: absolute; inset: 0; }

.sa-section-light .sa-section-bg__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;

  /*
   * Tracks the real gutter rather than a guessed percentage, so the reveal is
   * correct at every viewport instead of only at the one it was tuned on.
   */
  --sa-edge: max(0px, (100vw - 1240px) / 2);

  background: linear-gradient(
    90deg,
    transparent 0,
    var(--sa-bg-light) var(--sa-edge),
    var(--sa-bg-light) calc(100% - var(--sa-edge)),
    transparent 100%);
}

.sa-section-dark .sa-section-bg__scrim { display: none; }

/*
 * Below the container width there is no gutter left to fill, so the texture
 * would be sitting behind the copy. Drop it entirely.
 */
@media (max-width: 1280px) {
  .sa-section-light.has-bg .sa-section-bg { display: none; }
}

/* The same textures read well reversed out of the dark sections. */
.sa-section-dark.has-bg .sa-section-bg img.sa-texture,
.sa-section-alt.has-bg .sa-section-bg img.sa-texture {
  filter: invert(1);
  opacity: 0.32;
}

/* --------------------------------------------------------------------------
   86. Statement block, filled out

   The measure is capped at 24ch, which is right for reading but left the
   right-hand half of the section empty. Rather than stretching the line and
   spoiling it, the texture is anchored to that empty half and allowed to bleed
   off the right edge, so the void becomes the point instead of a gap.
   -------------------------------------------------------------------------- */

.sa-b-statement__text { max-width: 26ch; }

/* Closes the dead band where this block's padding met the next block's. */
.sa-b-statement { padding-block: 64px 44px; }

.sa-b-statement.has-bg:not(:has(.has-panel)) .sa-section-bg {
  left: 46%;
  right: -8%;
}

.sa-b-statement.has-bg:not(:has(.has-panel)) .sa-section-bg img {
  opacity: 0.6;
  object-fit: contain;
  object-position: right center;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 26%, #000 100%);
          mask-image: linear-gradient(90deg, transparent 0%, #000 26%, #000 100%);
}

/*
 * Only the copy needs protecting here, so the scrim covers the left column and
 * stops. The full-width version would have hidden the whole thing.
 */
.sa-b-statement.has-bg:not(:has(.has-panel)) .sa-section-bg__scrim {
  background: linear-gradient(
    90deg,
    var(--sa-bg-light) 0%,
    var(--sa-bg-light) 40%,
    transparent 62%);
}

/* Below the two-column threshold the text reflows wide, so the art must go. */
@media (max-width: 1080px) {
  .sa-b-statement.has-bg:not(:has(.has-panel)) .sa-section-bg { display: none; }
  .sa-b-statement__text { max-width: 22ch; }
}

/* --------------------------------------------------------------------------
   87. Statement with a panel

   Measuring the block showed 494px of empty width beside a 160px-tall
   headline. A hairline texture was never going to hold that; a designed panel
   does, and reuses artwork the split block already ships.
   -------------------------------------------------------------------------- */

.sa-b-statement .sa-container.has-panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.86fr);
  gap: 72px;
  align-items: center;
}

.sa-b-statement .sa-container.has-panel .sa-b-statement__text {
  max-width: 18ch;   /* narrower still, now that it shares the row */
}

.sa-b-statement__panel .sa-panel {
  aspect-ratio: 3 / 2;
  width: 100%;
}

/* The panel needs more room to breathe than the bare headline did. */
.sa-b-statement:has(.has-panel) { padding-block: 72px 60px; }

@media (max-width: 1000px) {
  .sa-b-statement .sa-container.has-panel { grid-template-columns: 1fr; gap: 40px; }
  .sa-b-statement .sa-container.has-panel .sa-b-statement__text { max-width: 22ch; }
  .sa-b-statement__panel { max-width: 460px; }
}

/* --------------------------------------------------------------------------
   88. Inner-page motion

   Collins scored the site at "about 40% dynamic". The homepage carries most of
   the motion; inner pages had almost none. Everything here is either
   scroll-driven or pointer-driven, so nothing loops for its own sake.
   -------------------------------------------------------------------------- */

/* Textures drift against the scroll, which gives the gutters depth. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .sa-section-bg {
      animation: sa-bg-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }

    @keyframes sa-bg-drift {
      from { transform: translate3d(0, -3.5%, 0) scale(1.06); }
      to   { transform: translate3d(0, 3.5%, 0) scale(1.06); }
    }
  }
}

/*
 * Capability rows had no hover state at all. The accent rule grows from the
 * hairline, matching the numbered list on the homepage Approach section rather
 * than inventing a third interaction language.
 */
.sa-js .sa-b-caps__grid > * { position: relative; }
.sa-js .sa-b-caps__grid.is-visible > * {
  transition: opacity 620ms cubic-bezier(.16, 1, .3, 1),
              transform 620ms cubic-bezier(.16, 1, .3, 1),
              padding-left 340ms cubic-bezier(.16, 1, .3, 1);
}

.sa-b-caps__grid > *::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  height: 1px;
  width: 0;
  background: var(--sa-accent);
  transition: width 420ms cubic-bezier(.16, 1, .3, 1);
}

.sa-b-caps__grid > *:hover::after { width: 100%; }
.sa-b-caps__grid > *:hover { padding-left: 10px; }

/* Icon cards get the same cursor-tracked lift the homepage service cards have. */
.sa-b-icons__card {
  --sa-mx: 50%;
  --sa-my: 50%;
  position: relative;
  isolation: isolate;
  transition: transform 380ms cubic-bezier(.16, 1, .3, 1),
              box-shadow 380ms ease,
              border-color 300ms ease;
}

.sa-section-light .sa-b-icons__card:hover {
  transform: translateY(-6px);
  border-color: color-mix(in srgb, var(--sa-accent) 42%, transparent);
  box-shadow: 0 24px 46px -28px rgba(14, 30, 51, 0.42);
}

/* Steps already stagger via the shared .sa-stagger system at the top of this
   file. Nothing to add here. */

@media (prefers-reduced-motion: reduce) {
  .sa-b-caps__grid > *,
  .sa-b-icons__card { transition: none; }
  .sa-b-caps__grid > *::after { display: none; }
}

/* --------------------------------------------------------------------------
   89. Dashboard panels

   Stand-ins until real screenshots exist. Every label is an abstract bar, so
   nothing here can be read as a client's data. Charts draw themselves in when
   the panel enters the viewport, which is where most of the "more dynamism"
   Collins asked for actually lands: the page moves because the work moves.
   -------------------------------------------------------------------------- */

.sa-panel[class*="sa-panel--dash"] svg { display: block; }

.sa-dash__chrome rect { fill: none; stroke: rgba(255,255,255,0.16); stroke-width: 1.2; }
.sa-dash__chrome path { stroke: rgba(255,255,255,0.14); stroke-width: 1.2; }
.sa-dash__chrome circle { fill: rgba(255,255,255,0.22); }

.sa-dash__panel rect { fill: rgba(255,255,255,0.035); stroke: rgba(255,255,255,0.10); stroke-width: 1; }
.sa-dash__label   { fill: rgba(255,255,255,0.24); }
.sa-dash__big rect{ fill: rgba(255,255,255,0.62); }
.sa-dash__axis, .sa-dash__rule { stroke: rgba(255,255,255,0.12); stroke-width: 1; }
.sa-dash__ok      { fill: var(--sa-accent); }
.sa-dash__dots circle { fill: var(--sa-accent); }

.sa-dash__tile rect  { fill: rgba(255,255,255,0.05); stroke: rgba(255,255,255,0.11); stroke-width: 1; }
.sa-dash__meter      { fill: color-mix(in srgb, var(--sa-accent) 55%, transparent); }
.sa-dash__track      { fill: rgba(255,255,255,0.09); }
.sa-dash__fill       { fill: color-mix(in srgb, var(--sa-accent) 60%, transparent); }
.sa-dash__bar        { fill: var(--sa-accent); }
.sa-dash__bar--was   { fill: rgba(255,255,255,0.20); }
.sa-dash__hbar       { fill: color-mix(in srgb, var(--sa-accent) 52%, transparent); }
.sa-dash__legend rect{ fill: rgba(255,255,255,0.20); }
.sa-dash__legend-b   { fill: var(--sa-accent); }

.sa-dash__line  { stroke: var(--sa-accent); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.sa-dash__line--pred { stroke-dasharray: 6 6; opacity: 0.75; }
.sa-dash__spark { stroke: color-mix(in srgb, var(--sa-accent) 70%, transparent); stroke-width: 1.8; stroke-linecap: round; }
.sa-dash__area  { fill: color-mix(in srgb, var(--sa-accent) 12%, transparent); }
.sa-dash__band  { fill: color-mix(in srgb, var(--sa-accent) 9%, transparent); }

.sa-dash__cell rect   { fill: rgba(255,255,255,0.04); stroke: rgba(255,255,255,0.09); stroke-width: 1; }
.sa-dash__cell circle { fill: var(--sa-accent); }
.sa-dash__cell--flag circle { fill: #f0a33c; }
.sa-dash__cell--flag rect   { stroke: rgba(240,163,60,0.45); }

.sa-dash__donut-bg { fill: none; stroke: rgba(255,255,255,0.12); stroke-width: 9; }
.sa-dash__donut-fg {
  fill: none; stroke: var(--sa-accent); stroke-width: 9; stroke-linecap: round;
  transform: rotate(-90deg); transform-origin: 500px 126px;
}

/* --- draw-in, once the panel is on screen ------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__line,
  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__spark {
    stroke-dasharray: 1000; stroke-dashoffset: 1000;
  }
  .sa-js .sa-b-statement__panel.is-visible .sa-dash__line,
  .sa-js .sa-b-statement__panel.is-visible .sa-dash__spark {
    stroke-dasharray: 1000; stroke-dashoffset: 0;
    transition: stroke-dashoffset 1400ms cubic-bezier(.22, 1, .36, 1) 260ms;
  }

  /* Bars, meters and tracks grow from their own edge. */
  .sa-js .sa-b-statement__panel .sa-dash__bar,
  .sa-js .sa-b-statement__panel .sa-dash__meter,
  .sa-js .sa-b-statement__panel .sa-dash__fill,
  .sa-js .sa-b-statement__panel .sa-dash__hbar {
    transform-box: fill-box;
    transition: transform 820ms cubic-bezier(.22, 1, .36, 1);
    transition-delay: calc(200ms + var(--n, 0) * 90ms);
  }
  .sa-js .sa-b-statement__panel .sa-dash__bar { transform-origin: bottom; }
  .sa-js .sa-b-statement__panel .sa-dash__meter,
  .sa-js .sa-b-statement__panel .sa-dash__fill,
  .sa-js .sa-b-statement__panel .sa-dash__hbar { transform-origin: left; }

  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__bar { transform: scaleY(0); }
  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__meter,
  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__fill,
  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__hbar { transform: scaleX(0); }

  /* Tiles, rows and cells arrive in sequence. */
  .sa-js .sa-b-statement__panel .sa-dash__tile,
  .sa-js .sa-b-statement__panel .sa-dash__row,
  .sa-js .sa-b-statement__panel .sa-dash__cell,
  .sa-js .sa-b-statement__panel .sa-dash__pair {
    transition: opacity 620ms ease, transform 620ms cubic-bezier(.22, 1, .36, 1);
    transition-delay: calc(160ms + var(--n, 0) * 70ms);
  }
  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__tile,
  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__row,
  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__cell,
  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__pair { opacity: 0; }

  /* The donut sweeps round to its value. */
  .sa-js .sa-b-statement__panel .sa-dash__donut-fg {
    stroke-dasharray: 214; transition: stroke-dashoffset 1200ms cubic-bezier(.22,1,.36,1) 300ms;
  }
  .sa-js .sa-b-statement__panel:not(.is-visible) .sa-dash__donut-fg { stroke-dashoffset: 214; }
  .sa-js .sa-b-statement__panel.is-visible .sa-dash__donut-fg { stroke-dashoffset: 30; }

  /* The live point on the AI forecast keeps breathing. */
  .sa-panel--dash-ai .sa-dash__dots circle {
    animation: sa-dash-pulse 2.4s ease-in-out infinite;
  }
  @keyframes sa-dash-pulse {
    0%, 100% { opacity: 1;   r: 4.5; }
    50%      { opacity: .55; r: 7;   }
  }
}

/* --------------------------------------------------------------------------
   90. Photographic backdrops on dark sections

   The light sections carry drawn textures in their gutters. The dark ones were
   still flat navy, which is most of what read as bland running down an inner
   page. These sit behind the copy at low opacity, weighted right so the
   headline side stays clean, and are layered under the same scrim element the
   textures use rather than a ::before, which several blocks already own.
   -------------------------------------------------------------------------- */

.sa-section-dark.has-bg,
.sa-cta.has-bg {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

/*
 * These backdrops are near-black with teal highlights (mean luminance under 14
 * of 255), so at plain 40% they dimmed the navy rather than adding to it:
 * measured 27.7 against 33 for bare navy.
 *
 * mix-blend-mode: screen was the obvious fix and does not work here. The
 * wrapper carries a transform for the parallax, which creates a stacking
 * context, so the image blends against transparency inside that context
 * instead of the navy behind it. It measured 16.2, worse again.
 *
 * Lifting the image itself is what actually works, and needs no assumption
 * about what it is sitting on.
 */
.sa-section-bg--photo img {
  opacity: 1;
  filter: brightness(4.4) saturate(1.25) contrast(1.08);
  object-fit: cover;
  object-position: center right;
}

.sa-section-dark .sa-section-bg--photo .sa-section-bg__scrim,
.sa-cta .sa-section-bg--photo .sa-section-bg__scrim {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;

  /* Solid where the copy sits, opening up toward the outer edge. */
  background:
    linear-gradient(90deg,
      var(--sa-bg-dark) 0%,
      color-mix(in srgb, var(--sa-bg-dark) 72%, transparent) 34%,
      color-mix(in srgb, var(--sa-bg-dark) 12%, transparent) 66%,
      transparent 100%),
    linear-gradient(180deg,
      var(--sa-bg-dark) 0%,
      transparent 22%,
      transparent 78%,
      var(--sa-bg-dark) 100%);
}

/* Cards sit on top of a photograph now, so they need their own ground back. */
.sa-section-dark.has-bg .sa-b-icons__card,
.sa-section-dark.has-bg .sa-b-caps__grid > * {
  backdrop-filter: blur(2px);
}

/* Narrow screens have no spare width; keep the photograph well back. */
@media (max-width: 900px) {
  .sa-section-bg--photo img { opacity: 0.6; }
}

/*
 * Centred blocks need a centred scrim.
 *
 * The rule above protects the left edge, which is right for left-aligned
 * sections but leaves centred copy sitting on the brightest part of the
 * backdrop. The CTA is centred, so it gets a vignette that protects the middle
 * and opens at both edges instead.
 */
.sa-cta .sa-section-bg--photo .sa-section-bg__scrim {
  background:
    radial-gradient(68% 78% at 50% 50%,
      var(--sa-bg-dark) 0%,
      color-mix(in srgb, var(--sa-bg-dark) 88%, transparent) 46%,
      color-mix(in srgb, var(--sa-bg-dark) 40%, transparent) 78%,
      transparent 100%),
    linear-gradient(180deg,
      var(--sa-bg-dark) 0%,
      transparent 26%,
      transparent 74%,
      var(--sa-bg-dark) 100%);
}

/* --------------------------------------------------------------------------
   91. Section rhythm

   Collins, 3 Sept: "maybe we can shrink some of the spaces on the website ...
   it's quite like the spaces between this section and the next", naming the
   gap above "What we build" and around "Built to be handed over" on the Data
   Engineering page.

   Measured, those joins were 172px and 192px. The cause is that every section
   pays its own padding at both ends, so two sections that share a background
   put 96 + 96 between them with nothing to look at. Where the background
   changes, the edge itself does the separating and the full pad is warranted;
   where it does not, it is dead space.
   -------------------------------------------------------------------------- */

.sa-section-light + .sa-section-light,
.sa-section-dark  + .sa-section-dark,
.sa-section-alt   + .sa-section-alt {
  padding-top: 44px;
}

/* A band is a full-bleed image, so it needs no help separating from anything. */
.sa-b-band + .sa-section-light,
.sa-b-band + .sa-section-dark { padding-top: 64px; }

@media (max-width: 900px) {
  .sa-section-light + .sa-section-light,
  .sa-section-dark  + .sa-section-dark,
  .sa-section-alt   + .sa-section-alt { padding-top: 34px; }
}

/* Real dashboard screenshots in the statement panel. Framed rather than
   bled, because a dashboard has edges and should look like a screen. */
.sa-b-statement__panel--shot {
  margin: 0;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--sa-text-on-light) 14%, transparent);
  box-shadow: 0 30px 60px -34px rgba(14, 30, 51, 0.42);
  background: #fff;
}

.sa-b-statement__panel--shot img {
  display: block;
  width: 100%;
  height: auto;
}

/* --------------------------------------------------------------------------
   92. Dashboard screenshots as a screen

   A bare screenshot on a page reads as a picture of a file. Framing it with a
   window bar and depth makes it read as software, which is what it is. The
   frame is drawn rather than an image, so it costs nothing and stays sharp.
   -------------------------------------------------------------------------- */

.sa-shot {
  --sa-shot-tilt: 7deg;
  margin: 0;
  position: relative;
  border-radius: 14px;
  overflow: hidden;
  background: linear-gradient(180deg, #f7f9fa 0%, #eceff1 100%);
  border: 1px solid color-mix(in srgb, var(--sa-text-on-light) 16%, transparent);
  box-shadow:
    0 2px 4px -2px rgba(14, 30, 51, 0.20),
    0 26px 50px -28px rgba(14, 30, 51, 0.45),
    0 60px 90px -60px rgba(14, 30, 51, 0.55);

  transform: perspective(1400px) rotateY(var(--sa-shot-tilt)) rotateX(1.4deg) scale(0.985);
  transform-origin: left center;
  transition: transform 760ms cubic-bezier(.16, 1, .3, 1),
              box-shadow 760ms ease;
}

/* Straightens as it settles into view, and again, further, on hover. */
.sa-shot.is-visible { --sa-shot-tilt: 2.4deg; }

.sa-shot:hover {
  --sa-shot-tilt: 0deg;
  transform: perspective(1400px) rotateY(0deg) rotateX(0deg) scale(1);
  box-shadow:
    0 2px 4px -2px rgba(14, 30, 51, 0.22),
    0 34px 60px -30px rgba(14, 30, 51, 0.5),
    0 80px 110px -70px rgba(14, 30, 51, 0.6);
}

.sa-shot__bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 9px 13px;
  background: color-mix(in srgb, var(--sa-text-on-light) 6%, #fff);
  border-bottom: 1px solid color-mix(in srgb, var(--sa-text-on-light) 11%, transparent);
}

.sa-shot__bar i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--sa-text-on-light) 22%, transparent);
}

.sa-shot__screen { position: relative; display: block; }
.sa-shot__screen img { display: block; width: 100%; height: auto; }

/* A slow diagonal sheen, so the panel reads as glass rather than paper. */
.sa-shot__sheen {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(104deg,
    transparent 30%,
    rgba(255, 255, 255, 0.42) 46%,
    transparent 62%);
  transform: translateX(-120%);
  transition: transform 1200ms cubic-bezier(.22, 1, .36, 1);
}

.sa-shot:hover .sa-shot__sheen { transform: translateX(120%); }

@media (max-width: 1000px) {
  .sa-shot { transform: none; }
  .sa-shot.is-visible { --sa-shot-tilt: 0deg; }
}

@media (prefers-reduced-motion: reduce) {
  .sa-shot,
  .sa-shot:hover { transform: none; transition: none; }
  .sa-shot__sheen { display: none; }
}

/* --------------------------------------------------------------------------
   93. Section heading paired with an image

   The heading measure is capped at 62ch so it stays readable, which left the
   right of the row empty on every inner page. A paired photograph fills it
   without widening the text, which is how Slalom handles the same problem.
   -------------------------------------------------------------------------- */

.sa-b-caps__head.has-media {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.82fr);
  gap: 56px;
  align-items: center;
  max-width: none;
}

.sa-b-caps__head.has-media .sa-b-caps__head-copy { max-width: 52ch; }

.sa-b-caps__head-media {
  margin: 0;
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 26px 50px -34px rgba(14, 30, 51, 0.45);
}

.sa-b-caps__head-media img {
  display: block;
  width: 100%;
  height: 260px;
  object-fit: cover;
  transform: scale(1.02);
  transition: transform 900ms cubic-bezier(.16, 1, .3, 1);
}

.sa-b-caps__head.has-media:hover .sa-b-caps__head-media img { transform: scale(1.07); }

@media (max-width: 1000px) {
  .sa-b-caps__head.has-media { grid-template-columns: 1fr; gap: 30px; }
  .sa-b-caps__head-media img { height: 200px; }
}

@media (prefers-reduced-motion: reduce) {
  .sa-b-caps__head-media img { transition: none; transform: none; }
}

/* --------------------------------------------------------------------------
   94. Why 6Analytix

   Sandra, 5 Sept: a short section showing what makes 6Analytix different.
   Reuses the numbered hairline list from the Approach section rather than
   introducing a sixth way of presenting a list of points.
   -------------------------------------------------------------------------- */

.sa-why__head { max-width: 60ch; }

/* Five items, so a two-column grid leaves an orphan. Three columns on wide
   screens gives 3 + 2, which reads deliberately rather than short. */
.sa-why__list.sa-values--grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: 40px;
}

@media (max-width: 1000px) {
  .sa-why__list.sa-values--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 700px) {
  .sa-why__list.sa-values--grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   95. The chrome hand: text over it, and movement

   Collins, 5 Sept: "if there was a way that hand, that image of the robotic
   arm, was even moving." It is a static JPEG, so the movement is a slow drift
   plus a reflection travelling across the metal. Cheaper than a video and it
   does not undo the mobile weight work from 1 September.
   -------------------------------------------------------------------------- */

.sa-figure--live {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

/* The drift was standing in for movement the image did not have. It has real
   movement now, so the only job left is to fill the frame. */
.sa-figure--live .sa-figure__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

}

/* Reflection crossing the chrome. Long gap so it reads as occasional. */
.sa-figure__sheen {
  position: absolute;
  inset: -20% -40%;
  pointer-events: none;
  background: linear-gradient(102deg,
    transparent 42%,
    rgba(255, 255, 255, 0.10) 48%,
    rgba(190, 240, 255, 0.20) 51%,
    rgba(255, 255, 255, 0.10) 54%,
    transparent 60%);
  animation: sa-figure-sheen 11s cubic-bezier(.6, 0, .35, 1) infinite;
}

@keyframes sa-figure-sheen {
  0%, 62%  { transform: translateX(-70%); }
  92%, 100%{ transform: translateX(70%); }
}

/* Copy sits on the image, bottom left, with its own scrim so it stays legible
   wherever the drift happens to put the bright streaks. */
.sa-figure__over {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 92px 40px 34px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: linear-gradient(0deg,
    rgba(7, 15, 28, 0.86) 0%,
    rgba(7, 15, 28, 0.45) 55%,
    transparent 100%);
}

.sa-figure__kicker {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sa-accent);
}

.sa-figure__line {
  font-size: clamp(22px, 2.6vw, 34px);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: #fff;
}

@media (max-width: 640px) {
  .sa-figure__over { padding: 64px 22px 24px; }
}

@media (prefers-reduced-motion: reduce) {
  .sa-figure--live .sa-figure__video { animation: none; }
  .sa-figure__sheen { display: none; }
}

/* --------------------------------------------------------------------------
   96. Per-page theming, hung off the slug class on page.php

   Collins wants the Power BI page in its own brand colours, and each service
   page leaning toward its own service. Scoped to the page, never the tokens.
   -------------------------------------------------------------------------- */

.sa-page--power-bi {
  --sa-accent: #f2c811;        /* Power BI yellow */
  --sa-accent-dark: #8a6d00;
  --sa-bg-dark: #1c1c1c;       /* near-black, per Collins */
  --sa-bg-dark-alt: #262626;
  --sa-bg-card-dark: #2e2e2e;
}

/* Yellow on near-black is bright; the teal-on-navy weights do not carry over. */
.sa-page--power-bi .sa-btn--teal { color: #1c1c1c; }
.sa-page--power-bi .sa-eyebrow { letter-spacing: 0.16em; }

/* --------------------------------------------------------------------------
   97. Rotating band, and the hover Collins asked for

   Two things from the 5 Sept call:
   - the dashboards should be "larger and go like a slideshow"
   - the definition rows should "be blue coloured, and when you hover around it
     it pops up further even more"
   -------------------------------------------------------------------------- */

.sa-b-band__set { position: absolute; inset: 0; display: block; }

.sa-b-band__img--set {
  opacity: 0;
  animation: sa-band-cycle 20s linear infinite;   /* 4 frames x 5s */
  animation-delay: calc(var(--k, 0) * 5s);
}

/*
 * Keyframe offsets must be literal percentages - calc() and var() are not
 * allowed in a selector position, and an invalid one makes the browser drop
 * the whole rule, which would leave every frame at opacity 0 and the band
 * blank. So this is written for FOUR frames: each holds a quarter of the
 * cycle. Changing the number of images means changing these percentages.
 */
@keyframes sa-band-cycle {
  0%   { opacity: 0; }
  2%   { opacity: 1; }
  25%  { opacity: 1; }
  27%  { opacity: 0; }
  100% { opacity: 0; }
}

/* Definition rows: tint toward the brand blue and lift further on hover. */
.sa-js .sa-b-caps__grid.is-visible > *:hover {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--sa-bg-dark) 6%, transparent) 0%,
    transparent 78%);
  transform: translateY(-3px);
}

.sa-section-dark .sa-b-caps__grid.is-visible > *:hover {
  background: linear-gradient(90deg,
    rgba(255, 255, 255, 0.07) 0%,
    transparent 78%);
}

.sa-b-caps__grid > *:hover .sa-b-caps__title { color: var(--sa-accent-dark); }
.sa-section-dark .sa-b-caps__grid > *:hover .sa-b-caps__title { color: var(--sa-accent); }

@media (prefers-reduced-motion: reduce) {
  .sa-b-band__img--set { animation: none; opacity: 1; }
  .sa-b-band__img--set:not(:first-child) { opacity: 0; }
  .sa-js .sa-b-caps__grid.is-visible > *:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   98. Dashboard panel as a slideshow, and more life on service pages

   Collins, 5 Sept: the dashboard "in that box" should be blurred, larger and
   run as a slideshow; and the service pages should do more as the cursor moves.
   -------------------------------------------------------------------------- */

.sa-shot--set .sa-shot__screen { position: relative; display: block; }

.sa-shot__frame {
  display: block;
  width: 100%;
  height: auto;
}

/* Frames 2-4 stack on top of the first, which keeps the box its natural height. */
.sa-shot__frame:not(:first-of-type) {
  position: absolute;
  inset: 0;
  height: 100%;
  object-fit: cover;
}

@media (prefers-reduced-motion: no-preference) {
  .sa-shot__frame {
    opacity: 0;
    animation: sa-shot-cycle 16s linear infinite;   /* 4 frames x 4s */
    animation-delay: calc(var(--k, 0) * 4s);
  }

  /* Literal percentages only: calc() in a keyframe selector invalidates the
     whole rule and every frame would sit at zero. Overlaps by 2% so there is
     never a blank moment between frames. */
  @keyframes sa-shot-cycle {
    0%   { opacity: 0; }
    2%   { opacity: 1; }
    25%  { opacity: 1; }
    27%  { opacity: 0; }
    100% { opacity: 0; }
  }
}

/* Reduced motion, or no animation support: show the first frame only. */
@media (prefers-reduced-motion: reduce) {
  .sa-shot__frame:not(:first-of-type) { opacity: 0; }
}

/* Bigger, per "it'd be nice if it's larger".

   Must stay inside a min-width query. Unscoped, this sits after the
   max-width:1000px rule that collapses the row to one column, so it won a
   width it was never meant to touch: at 390px the statement was still a
   two-column grid, giving the headline a 143px measure (two words a line) and
   squeezing the dashboard into 167px. Desktop only. */
@media (min-width: 1001px) {
  .sa-b-statement .sa-container.has-panel {
    grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  }
}

/* --- more life as the cursor moves -------------------------------------- */

/* Icon cards get the cursor-tracked glow the homepage service cards have. */
.sa-b-icons__card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0;
  border-radius: inherit;
  background: radial-gradient(240px circle at var(--sa-mx, 50%) var(--sa-my, 50%),
              color-mix(in srgb, var(--sa-accent) 22%, transparent), transparent 70%);
  transition: opacity 320ms ease;
}

.sa-b-icons__card:hover::after { opacity: 1; }

/* Pop: lift further, brighten the mark, push the arrow. */
.sa-b-icons__card:hover {
  transform: translateY(-9px) scale(1.014);
}

.sa-b-icons__card:hover .sa-b-icons__mark {
  background: color-mix(in srgb, var(--sa-accent) 26%, transparent);
  transform: scale(1.08) rotate(-3deg);
}

.sa-b-icons__mark {
  transition: background-color 300ms ease, transform 420ms cubic-bezier(.16, 1, .3, 1);
}

.sa-b-icons__card:hover .sa-b-icons__go { transform: translateX(6px); }
.sa-b-icons__go { transition: transform 340ms cubic-bezier(.16, 1, .3, 1); }

/* Steps: the numeral leads the row on hover. */
.sa-b-steps__list > li { transition: transform 420ms cubic-bezier(.16, 1, .3, 1); }
.sa-b-steps__list > li:hover { transform: translateX(6px); }

@media (prefers-reduced-motion: reduce) {
  .sa-b-icons__card:hover,
  .sa-b-steps__list > li:hover { transform: none; }
  .sa-b-icons__card::after { display: none; }
}

/* --------------------------------------------------------------------------
   99. The statement line reacts

   Collins wanted the service pages to do something as the cursor moves. The
   statement is the largest thing on the page and had no state at all, so
   hovering it did nothing. The rule under it draws across and the panel beside
   it straightens, which ties the two halves of the row together.
   -------------------------------------------------------------------------- */

.sa-b-statement__copy { position: relative; }

.sa-b-statement__text {
  transition: color 160ms ease, transform 280ms cubic-bezier(.16, 1, .3, 1);
}

.sa-b-statement__rule {
  transition: width 280ms cubic-bezier(.16, 1, .3, 1),
              background-color 160ms ease;
}

.sa-b-statement .sa-container:hover .sa-b-statement__rule {
  width: 96px;
  background: var(--sa-accent);
}

.sa-b-statement .sa-container:hover .sa-b-statement__text {
  transform: translateX(5px);
  color: var(--sa-accent-dark);
}

/* On the dark tone the dark accent would disappear, so use the bright one. */
.sa-b-statement.sa-section-dark .sa-container:hover .sa-b-statement__text {
  color: var(--sa-accent);
}

/* Hovering the copy also settles the dashboard, so the row responds as one. */
.sa-b-statement .sa-container:hover .sa-shot {
  --sa-shot-tilt: 0deg;
  transform: perspective(1400px) rotateY(0deg) rotateX(0deg) scale(1);
  box-shadow:
    0 2px 4px -2px rgba(14, 30, 51, 0.22),
    0 34px 60px -30px rgba(14, 30, 51, 0.5),
    0 80px 110px -70px rgba(14, 30, 51, 0.6);
}

@media (prefers-reduced-motion: reduce) {
  .sa-b-statement .sa-container:hover .sa-b-statement__text { transform: none; }
  .sa-b-statement .sa-container:hover .sa-shot { transform: none; }
}

/* --------------------------------------------------------------------------
   100. Text that responds

   Collins, on the service pages: "the more you move your cursor around, it's
   doing stuff". Headings, questions and list items all had static text. Each
   now acknowledges the cursor, using the same accent-rule language already
   established on the Approach list rather than a new idiom per element.
   -------------------------------------------------------------------------- */

/* Section headings grow an accent rule under the first line. */
.sa-b-caps__head h2,
.sa-b-icons__head h2,
.sa-b-steps__head h2,
.sa-b-faq__head h2,
.sa-b-related h2 {
  position: relative;
  display: inline-block;
  padding-bottom: 8px;
  cursor: default;
}

.sa-b-caps__head h2::after,
.sa-b-icons__head h2::after,
.sa-b-steps__head h2::after,
.sa-b-faq__head h2::after,
.sa-b-related h2::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 0;
  border-radius: 2px;
  background: var(--sa-accent);
  transition: width 520ms cubic-bezier(.16, 1, .3, 1);
}

.sa-b-caps__head h2:hover::after,
.sa-b-icons__head h2:hover::after,
.sa-b-steps__head h2:hover::after,
.sa-b-faq__head h2:hover::after,
.sa-b-related h2:hover::after { width: 100%; }

/* FAQ questions: the whole row leans in, not just the marker. */
.sa-b-faq__q {
  transition: color 280ms ease, padding-left 380ms cubic-bezier(.16, 1, .3, 1);
}

.sa-b-faq__q:hover {
  padding-left: 8px;
  color: var(--sa-accent-dark);
}

.sa-section-dark .sa-b-faq__q:hover { color: var(--sa-accent); }

/* Capability and step titles pick up the accent as the row is hovered. */
.sa-b-caps__row:hover .sa-b-caps__title,
.sa-b-steps__list > li:hover .sa-b-steps__title {
  color: var(--sa-accent-dark);
}

.sa-section-dark .sa-b-caps__row:hover .sa-b-caps__title,
.sa-section-dark .sa-b-steps__list > li:hover .sa-b-steps__title {
  color: var(--sa-accent);
}

.sa-b-caps__title,
.sa-b-steps__title { transition: color 280ms ease; }

/* The lede lifts very slightly with its heading, so the block moves as one. */
.sa-b-caps__head:hover .sa-b-caps__lede,
.sa-b-icons__head:hover .sa-b-icons__lede {
  transform: translateX(3px);
}

.sa-b-caps__lede,
.sa-b-icons__lede {
  transition: transform 480ms cubic-bezier(.16, 1, .3, 1);
}

@media (prefers-reduced-motion: reduce) {
  .sa-b-caps__head h2::after,
  .sa-b-icons__head h2::after,
  .sa-b-steps__head h2::after,
  .sa-b-faq__head h2::after,
  .sa-b-related h2::after { display: none; }

  .sa-b-faq__q:hover { padding-left: 0; }
  .sa-b-caps__head:hover .sa-b-caps__lede,
  .sa-b-icons__head:hover .sa-b-icons__lede { transform: none; }
}

/* --------------------------------------------------------------------------
   101. Statement line

   A cursor-tracked glow lived here, built on background-clip: text. It caused
   three separate rendering failures on live pages - text clipped at the box
   edge, text rendering as nothing where the gradient did not reach, and finally
   lines overlapping each other. The technique needs the fill to be transparent,
   so every one of those failures makes the headline unreadable rather than
   merely ugly. Not worth it for a decorative highlight.

   What is left is the plain, robust version: the rule under it draws out and
   the line shifts slightly, which is legible in every browser and cannot fail
   to a blank headline.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   102. Photo heroes

   Collins, via Sandra, 19 Sept: the inner pages open on a plain navy block and
   are "not captivating". Every hero now opens on a photograph, attached by
   slug in sixanalytix_hero_photo(). The copy keeps its position; a scrim that
   is solid behind the words and falls away to the right carries the contrast,
   so the photograph shows without the headline ever sitting on busy detail.
   -------------------------------------------------------------------------- */

.sa-b-hero.has-photo {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  min-height: clamp(540px, 78vh, 760px);
  display: flex;
  align-items: center;
}
.sa-b-hero.has-photo > .sa-container { position: relative; z-index: 2; width: 100%; }

.sa-b-hero__photo { position: absolute; inset: 0; z-index: 0; }
.sa-b-hero__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 70% 50%;
  display: block;
}

.sa-b-hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(90deg,
      rgba(18, 36, 61, 0.96) 0%,
      rgba(18, 36, 61, 0.88) 32%,
      rgba(18, 36, 61, 0.45) 62%,
      rgba(18, 36, 61, 0.12) 100%),
    linear-gradient(0deg, rgba(18, 36, 61, 0.55) 0%, rgba(18, 36, 61, 0) 35%),
    linear-gradient(180deg, rgba(18, 36, 61, 0.7) 0%, rgba(18, 36, 61, 0) 22%);
}

/* A slow push-in, so the opening frame is alive without anything looping. */
@media (prefers-reduced-motion: no-preference) {
  .sa-b-hero__photo img { animation: sa-hero-push 14s cubic-bezier(.2, .6, .3, 1) both; }
  @keyframes sa-hero-push {
    from { transform: scale(1.1); }
    to   { transform: scale(1.0); }
  }
}

.sa-b-hero.has-photo .sa-page-head__title { text-shadow: 0 2px 24px rgba(10, 20, 36, 0.35); }
.sa-b-hero.has-photo .sa-page-head__lede  { color: rgba(236, 242, 248, 0.86); }

/* Phones: the copy covers most of the frame, so the scrim goes vertical and
   heavier, and the photo keeps its subject in the top third. */
@media (max-width: 760px) {
  .sa-b-hero.has-photo { min-height: 0; align-items: flex-end; }
  .sa-b-hero__photo img { object-position: 60% 20%; }
  .sa-b-hero__scrim {
    background:
      linear-gradient(0deg,
        rgba(18, 36, 61, 0.97) 0%,
        rgba(18, 36, 61, 0.9) 48%,
        rgba(18, 36, 61, 0.35) 78%,
        rgba(18, 36, 61, 0.55) 100%);
  }
  .sa-b-hero.has-photo .sa-page-head { padding-top: 220px; }
  .sa-b-hero.has-photo { padding-top: 240px; }
}

/* --------------------------------------------------------------------------
   103. Photo cards (Slalom pattern)

   Photograph above, copy below, the whole card one target. On hover or focus
   the photo pushes in, an accent rule draws across the top edge, the card
   lifts and the title takes the accent. Built for the Who We Are page, where
   Collins wanted Slalom's "Explore our stories and insights" behaviour.
   -------------------------------------------------------------------------- */

.sa-b-cards__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
  margin-bottom: 44px;
}
.sa-b-cards__head .sa-h2 { margin-top: 10px; }
.sa-b-cards__lede { margin-top: 16px; max-width: 60ch; font-size: 16.5px; line-height: 1.7; opacity: 0.82; }

.sa-b-cards__more {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
  font-weight: 600;
  color: var(--sa-accent-dark);
  text-decoration: none;
}
.sa-section-dark .sa-b-cards__more { color: var(--sa-accent); }
.sa-b-cards__more svg { width: 18px; height: 18px; transition: transform 220ms ease; }
.sa-b-cards__more:hover svg { transform: translateX(4px); }

.sa-b-cards__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}
.sa-b-cards__grid[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.sa-b-cards__grid[data-cols="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.sa-b-cards__card {
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: 16px;
  overflow: hidden;
  background: #ffffff;
  color: inherit;
  text-decoration: none;
  box-shadow: 0 1px 2px rgba(14, 30, 51, 0.06), 0 10px 30px -18px rgba(14, 30, 51, 0.35);
  transition: transform 320ms cubic-bezier(.16, 1, .3, 1), box-shadow 320ms ease;
}
.sa-section-dark .sa-b-cards__card { background: var(--sa-bg-card-dark, #1e3757); }

.sa-b-cards__edge {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  height: 4px;
  width: 100%;
  background: var(--sa-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 420ms cubic-bezier(.16, 1, .3, 1);
}

.sa-b-cards__media { display: block; aspect-ratio: 4 / 3; overflow: hidden; background: #0f1f35; }
.sa-b-cards__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 700ms cubic-bezier(.16, 1, .3, 1), filter 400ms ease;
  filter: saturate(0.92);
}

.sa-b-cards__body { display: flex; flex-direction: column; gap: 10px; padding: 24px 24px 26px; flex: 1; }
.sa-b-cards__kicker {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sa-accent-dark);
}
.sa-section-dark .sa-b-cards__kicker { color: var(--sa-accent); }
.sa-b-cards__title {
  font-size: 20px;
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: -0.015em;
  transition: color 200ms ease;
}
.sa-b-cards__text { font-size: 15px; line-height: 1.65; opacity: 0.78; }
.sa-b-cards__go {
  margin-top: auto;
  padding-top: 8px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  font-size: 14px;
  color: var(--sa-accent-dark);
}
.sa-section-dark .sa-b-cards__go { color: var(--sa-accent); }
.sa-b-cards__go svg { width: 16px; height: 16px; transition: transform 220ms ease; }

.sa-b-cards__card:hover,
.sa-b-cards__card:focus-within,
.sa-b-cards__card:focus-visible {
  transform: translateY(-6px);
  box-shadow: 0 2px 4px rgba(14, 30, 51, 0.08), 0 28px 50px -26px rgba(14, 30, 51, 0.55);
}
.sa-b-cards__card:hover .sa-b-cards__edge,
.sa-b-cards__card:focus-within .sa-b-cards__edge,
.sa-b-cards__card:focus-visible .sa-b-cards__edge { transform: scaleX(1); }
.sa-b-cards__card:hover .sa-b-cards__media img,
.sa-b-cards__card:focus-visible .sa-b-cards__media img { transform: scale(1.06); filter: saturate(1.05); }
.sa-b-cards__card:hover .sa-b-cards__title { color: var(--sa-accent-dark); }
.sa-section-dark .sa-b-cards__card:hover .sa-b-cards__title { color: var(--sa-accent); }
.sa-b-cards__card:hover .sa-b-cards__go svg { transform: translateX(4px); }
.sa-b-cards__card:focus-visible { outline: 3px solid var(--sa-accent); outline-offset: 3px; }

@media (max-width: 1080px) {
  .sa-b-cards__grid[data-cols="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 860px) {
  .sa-b-cards__grid,
  .sa-b-cards__grid[data-cols="3"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sa-b-cards__head { flex-direction: column; align-items: flex-start; }
}
@media (max-width: 560px) {
  .sa-b-cards__grid,
  .sa-b-cards__grid[data-cols] { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  .sa-b-cards__card, .sa-b-cards__media img, .sa-b-cards__edge { transition: none; }
  .sa-b-cards__card:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   104. Stat band with a photograph behind it

   The figures already count up (motion.js). What the band lacked was anything
   to look at: four numbers on flat navy. An optional photograph now sits
   behind them under a heavy scrim, with an optional heading line above.
   -------------------------------------------------------------------------- */

.sa-b-stats.has-photo { position: relative; overflow: hidden; isolation: isolate; }
.sa-b-stats.has-photo > .sa-container { position: relative; z-index: 2; }
.sa-b-stats__photo { position: absolute; inset: 0; z-index: 0; }
.sa-b-stats__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sa-b-stats__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(18, 36, 61, 0.84) 0%, rgba(18, 36, 61, 0.72) 100%);
}
.sa-b-stats__heading {
  position: relative;
  z-index: 2;
  max-width: 22ch;
  margin-bottom: 48px;
  font-size: clamp(28px, 3.2vw, 44px);
  line-height: 1.12;
  letter-spacing: -0.03em;
  font-weight: 500;
  color: var(--sa-text-on-dark);
}
.sa-b-stats__heading em { font-style: normal; color: var(--sa-accent); }

/* --------------------------------------------------------------------------
   105. Flip cards

   Photograph and title on the front, the explanation on the back. Flips on
   hover where there is a real pointer, on keyboard focus, and on tap for
   touch (motion.js adds .is-flipped). Under reduced motion the back crossfades
   in rather than rotating.
   -------------------------------------------------------------------------- */

.sa-b-cards__card.is-flip {
  height: 440px;
  background: transparent;
  box-shadow: none;
  overflow: visible;
  perspective: 1600px;
  cursor: pointer;
  outline: none;
}
.sa-b-cards__grid[data-cols="3"] .sa-b-cards__card.is-flip { height: 460px; }

.sa-b-cards__flip {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 720ms cubic-bezier(.2, .75, .2, 1);
}

.sa-b-cards__face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  border-radius: 16px;
  overflow: hidden;
  -webkit-backface-visibility: hidden;
          backface-visibility: hidden;
  box-shadow: 0 1px 2px rgba(14, 30, 51, 0.08), 0 16px 36px -22px rgba(14, 30, 51, 0.55);
}

/* Front: the photograph, full bleed, title set over its lower edge. */
.sa-b-cards__front { justify-content: flex-end; background: #0f1f35; color: #fff; }
.sa-b-cards__front img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 900ms cubic-bezier(.16, 1, .3, 1);
}
.sa-b-cards__front::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(12, 24, 42, 0.95) 0%, rgba(12, 24, 42, 0.55) 42%, rgba(12, 24, 42, 0) 72%);
}
.sa-b-cards__front-copy { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 8px; padding: 24px; }
.sa-b-cards__front .sa-b-cards__kicker { color: var(--sa-accent); }
.sa-b-cards__front .sa-b-cards__title { color: #fff; font-size: 21px; }
.sa-b-cards__hint {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 6px;
  font-size: 12.5px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.72);
}
.sa-b-cards__hint svg { width: 14px; height: 14px; }
.sa-b-cards__hint-tap { display: none; }
@media (hover: none) {
  .sa-b-cards__hint-hover { display: none; }
  .sa-b-cards__hint-tap { display: inline; }
}

/* Back: the copy, on the page's own ground so it reads as the same card. */
.sa-b-cards__back {
  transform: rotateY(180deg);
  justify-content: flex-start;
  gap: 12px;
  padding: 28px 26px;
  background: #ffffff;
  color: var(--sa-text-on-light, #0e1e33);
  border-top: 4px solid var(--sa-accent);
}
.sa-section-dark .sa-b-cards__back { background: var(--sa-bg-card-dark, #1e3757); color: var(--sa-text-on-dark, #eef3f8); }
.sa-b-cards__back .sa-b-cards__title { font-size: 20px; }
.sa-b-cards__back .sa-b-cards__text { font-size: 15px; line-height: 1.68; opacity: 0.86; }
.sa-b-cards__back .sa-b-cards__go { margin-top: auto; text-decoration: none; }

/* The flip itself. Hover only where hover is real, so a phone does not get
   stuck half-way on a sticky :hover after a tap. */
@media (hover: hover) {
  .sa-b-cards__card.is-flip:hover .sa-b-cards__flip { transform: rotateY(180deg); }
  .sa-b-cards__card.is-flip:hover .sa-b-cards__front img { transform: scale(1.06); }
}
.sa-b-cards__card.is-flip:focus-within .sa-b-cards__flip,
.sa-b-cards__card.is-flip.is-flipped .sa-b-cards__flip { transform: rotateY(180deg); }
.sa-b-cards__card.is-flip:focus-visible .sa-b-cards__back { box-shadow: 0 0 0 3px var(--sa-accent); }

/* The generic card lift must not fight the flip. */
.sa-b-cards__card.is-flip:hover,
.sa-b-cards__card.is-flip:focus-within { transform: none; box-shadow: none; }

@media (max-width: 560px) {
  .sa-b-cards__card.is-flip,
  .sa-b-cards__grid[data-cols="3"] .sa-b-cards__card.is-flip { height: 400px; }
}

@media (prefers-reduced-motion: reduce) {
  .sa-b-cards__flip { transition: none; transform: none !important; }
  .sa-b-cards__back { transform: none; opacity: 0; transition: opacity 200ms linear; }
  .sa-b-cards__card.is-flip:hover .sa-b-cards__back,
  .sa-b-cards__card.is-flip:focus-within .sa-b-cards__back,
  .sa-b-cards__card.is-flip.is-flipped .sa-b-cards__back { opacity: 1; }
}

/* Power BI is themed black and yellow (section 96), so its hero scrim is
   black too. The navy one tinted the gold photograph blue. */
.sa-page--power-bi .sa-b-hero__scrim {
  background:
    linear-gradient(90deg, rgba(20, 20, 20, 0.95) 0%, rgba(20, 20, 20, 0.84) 32%, rgba(20, 20, 20, 0.35) 64%, rgba(20, 20, 20, 0.05) 100%),
    linear-gradient(0deg, rgba(20, 20, 20, 0.55) 0%, rgba(20, 20, 20, 0) 35%);
}
@media (max-width: 760px) {
  .sa-page--power-bi .sa-b-hero__scrim {
    background: linear-gradient(0deg, rgba(20, 20, 20, 0.97) 0%, rgba(20, 20, 20, 0.88) 48%, rgba(20, 20, 20, 0.3) 78%, rgba(20, 20, 20, 0.5) 100%);
  }
}

/* --------------------------------------------------------------------------
   106. Hero video

   Sits over the hero photograph and fades in only once it is playing, so
   there is never a black frame. Desktop only (see motion.js); phones keep the
   photograph.
   -------------------------------------------------------------------------- */

.sa-b-hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 70% 50%;
  opacity: 0;
  transition: opacity 900ms ease;
}
.sa-b-hero__video.is-playing { opacity: 1; }
/* The photo's push-in would fight the footage's own motion. */
.sa-b-hero__photo:has(.sa-b-hero__video.is-playing) img { animation: none; }

/* --------------------------------------------------------------------------
   107. Heroes run up behind the menu

   Inner pages with a photo hero now use the front page's floating header
   (transparent, frosted navy once scrolled). The header leaves the flow, so
   the hero starts at the very top of the window and its copy is pushed down
   by the header's height to stay clear of the navigation. The scrim's top
   band (section 102) keeps the menu legible over bright footage.
   -------------------------------------------------------------------------- */

.sa-has-transparent-header .sa-b-hero.has-photo {
  --sa-header-h: 84px;
  padding-top: calc(var(--sa-header-h) + 96px);
  min-height: clamp(620px, 86vh, 860px);
}
.sa-has-transparent-header .sa-b-hero.has-photo .sa-b-hero__scrim {
  background:
    linear-gradient(180deg, rgba(18, 36, 61, 0.78) 0%, rgba(18, 36, 61, 0.35) 12%, rgba(18, 36, 61, 0) 24%),
    linear-gradient(90deg,
      rgba(18, 36, 61, 0.96) 0%,
      rgba(18, 36, 61, 0.88) 32%,
      rgba(18, 36, 61, 0.45) 62%,
      rgba(18, 36, 61, 0.12) 100%),
    linear-gradient(0deg, rgba(18, 36, 61, 0.55) 0%, rgba(18, 36, 61, 0) 35%);
}
.sa-page--power-bi.sa-page--power-bi .sa-b-hero__scrim,
.sa-has-transparent-header .sa-page--power-bi .sa-b-hero.has-photo .sa-b-hero__scrim {
  background:
    linear-gradient(180deg, rgba(20, 20, 20, 0.8) 0%, rgba(20, 20, 20, 0.35) 12%, rgba(20, 20, 20, 0) 24%),
    linear-gradient(90deg, rgba(20, 20, 20, 0.95) 0%, rgba(20, 20, 20, 0.84) 32%, rgba(20, 20, 20, 0.35) 64%, rgba(20, 20, 20, 0.05) 100%),
    linear-gradient(0deg, rgba(20, 20, 20, 0.55) 0%, rgba(20, 20, 20, 0) 35%);
}

@media (max-width: 760px) {
  .sa-has-transparent-header .sa-b-hero.has-photo {
    --sa-header-h: 70px;
    min-height: 0;
    padding-top: calc(var(--sa-header-h) + 190px);
  }
  .sa-has-transparent-header .sa-b-hero.has-photo .sa-b-hero__scrim {
    background:
      linear-gradient(180deg, rgba(18, 36, 61, 0.8) 0%, rgba(18, 36, 61, 0) 18%),
      linear-gradient(0deg, rgba(18, 36, 61, 0.97) 0%, rgba(18, 36, 61, 0.9) 48%, rgba(18, 36, 61, 0.3) 80%, rgba(18, 36, 61, 0) 100%);
  }
}

/* --------------------------------------------------------------------------
   108. Responsive audit fixes (19 Sept)

   34 pages x 6 widths (320, 390, 768, 1024, 1440, 1920). No sideways scroll,
   no squeezed grids, no broken images, no menu overflow anywhere. What it did
   find is fixed here and in sections 66 (orphan card) and the hero dots.
   -------------------------------------------------------------------------- */

/* Tap targets under 24px (WCAG 2.2 AA). Padding goes on, margin comes off by
   the same amount, so nothing moves visually. */
.sa-footer__link { padding-block: 2px; margin-bottom: 8px; }

.sa-nav__list > li > a { display: inline-block; padding-block: 6px; }
.sa-nav__list > li > a::before { bottom: 0; }

.sa-b-form__aside dd a,
.sa-footer a[href^="mailto:"],
.sa-footer a[href^="tel:"] { display: inline-block; padding-block: 3px; }

/* Uppercase labels and button text sat at 11px, which is small on a phone
   or tablet. 12px on touch-sized screens; wide desktop is unchanged. */
@media (max-width: 1024px) {
  .sa-btn,
  .sa-eyebrow,
  .sa-footer__chip,
  .sa-clients__label,
  .sa-filter__count,
  .sa-article__cat,
  .sa-figure__kicker,
  .sa-values__num,
  .sa-approach__doc-meta,
  .sa-insight__read { font-size: 12px; }
  .sa-footer__legal a,
  .sa-footer__meta a { font-size: 12.5px; }
}

/* "Read the sustainability strategy" is the longest button label on the site.
   Buttons are a fixed 40px tall, so when this one wrapped on a phone the
   second line pressed into the border. Tighter tracking keeps it on one line
   at most phone widths; where it still has to wrap, the button grows. */
@media (max-width: 480px) {
  .sa-approach__doc {
    height: auto;
    min-height: 40px;
    padding-block: 9px;
    line-height: 1.35;
    letter-spacing: 0.07em;
    white-space: normal;
    text-align: left;
  }
}

/* Tablet orphans. Where a three-up grid drops to two columns, the odd item
   out sat alone at half width against empty space (7th service, 3rd
   testimonial, 3rd insight, 5th "Why 6Analytix" point). Centred at card
   width instead of stretched, so images keep their proportions and the row
   reads as deliberate. Same thinking as section 66 on desktop. */
@media (min-width: 641px) and (max-width: 900px) {
  .sa-grid-3 > :last-child:nth-child(odd):not(:first-child),
  .sa-grid-svc > :nth-child(7):not(.sa-card--platforms) {
    grid-column: 1 / -1;
    justify-self: center;
    width: calc(50% - 16px);
  }
}
@media (min-width: 701px) and (max-width: 1000px) {
  .sa-why__list.sa-values--grid > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
    justify-self: center;
    width: calc(50% - 20px);
  }
}

/* The full menu needs about 1100px. Between 901 and 1099 (an iPad in
   landscape is 1024) it wrapped: dropdown chevrons fell below their labels,
   "Who We Are" broke over two lines and "Start a conversation" spilled out of
   its pill. Below 1100 the burger and full-screen panel take over instead;
   above it, labels are held on one line. (Responsive audit, 19 Sept.) */
@media (max-width: 1099px) {
  .sa-nav, .sa-header__cta { display: none; }
  .sa-burger { display: flex; }
}
.sa-nav__list > li > a,
.sa-header__cta { white-space: nowrap; }

/* "Telecommunications" is wider than a flip-card title at four across around
   1100px. Titles scale with the viewport in four-up grids, and any word that
   still does not fit hyphenates rather than spilling out of the card. */
.sa-b-cards__title { overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto; }
.sa-b-cards__grid[data-cols="4"] .sa-b-cards__front .sa-b-cards__title,
.sa-b-cards__grid[data-cols="4"] .sa-b-cards__back .sa-b-cards__title { font-size: clamp(17px, 1.55vw, 21px); }
