/* ─── Cloudnaut design tokens ─────────────────────────────────
   Ground lifted off pure black to a deep AWS navy: the old #070b16
   left body copy at ~4.3:1 against a live particle field. Text now
   sits on opaque surfaces; the 3D scene shows through in deliberate
   full-bleed windows instead of behind every paragraph.
   ──────────────────────────────────────────────────────────── */
:root {
  --ink: #071033;           /* page ground — deep navy, not black */
  --ink-2: #0d1a44;         /* raised surfaces */
  --ink-3: #12235a;         /* highest surfaces / hover */
  --void: #040a20;          /* canvas windows — darkest, particles live here */
  --paper: #ffffff;         /* primary text */
  --muted: #c3d0ef;         /* secondary text — 9.8:1 on --ink */
  --muted-2: #8fa3ce;       /* tertiary / captions — 5.1:1, never body copy */
  --cyan: #5ccef5;
  --blue: #4d7cff;
  --violet: #9b6bff;
  --line: rgba(195, 208, 239, 0.16);
  --grad: linear-gradient(100deg, var(--cyan), var(--blue) 45%, var(--violet));
  --font-display: "Space Grotesk", system-ui, sans-serif;
  --font-body: "Inter", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;
  --rail: 3px;

  /* Layout: the page now uses its width. Old measure was 1180px with
     five sections capped at 760px — the "Word document" effect. */
  --shell: 1500px;
  --shell-wide: 1840px;
  --shell-text: 1360px;
  --gutter: clamp(1.5rem, 5vw, 5rem);
  --section-y: clamp(5.5rem, 11vw, 10rem);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* No CSS smooth-scroll — it stacks with Lenis and makes wheel input sluggish.
   Lenis handles smoothing; anchor clicks are routed through it in main.js. */

body {
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.65;
  /* `clip`, not `hidden`: overflow-x:hidden makes body a scroll container,
     which silently disables position:sticky in every descendant (the article
     rail in blog.css). `clip` prevents the same horizontal scroll without
     creating that container. */
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--blue); color: #fff; }

img { max-width: 100%; }

/* ─── 3D layer ────────────────────────────────────────────────
   Fixed behind everything. Only .band--canvas sections are
   transparent, so the scene reads as depth rather than noise. */
#scene {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: block;
}

#static-fallback {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(60vw 60vh at 72% 18%, rgba(77, 124, 255, 0.22), transparent 70%),
    radial-gradient(50vw 50vh at 18% 82%, rgba(155, 107, 255, 0.16), transparent 70%),
    var(--void);
}

body.no-webgl #scene { display: none; }
body.no-webgl #static-fallback { display: block; }

@media (prefers-reduced-motion: reduce) {
  #scene { display: none; }
  #static-fallback { display: block; }
  html { scroll-behavior: auto; }
  .reveal, .reveal-img { opacity: 1 !important; transform: none !important; clip-path: none !important; }
}

/* ─── Progress rail ───────────────────────────────────────── */
.progress-rail {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: var(--rail);
  z-index: 60;
  background: rgba(195, 208, 239, 0.12);
}
.progress-fill {
  height: 100%;
  width: 0%;
  background: var(--grad);
}

/* ─── Nav ─────────────────────────────────────────────────── */
.nav {
  position: fixed;
  top: var(--rail); left: 0; right: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  padding: 1rem var(--gutter);
  transition: background 0.4s ease, box-shadow 0.4s ease, padding 0.4s ease;
}
/* Transparent over the hero, solid once you're reading — set by main.js */
.nav::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(7, 16, 51, 0.92);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
  opacity: 0;
  transition: opacity 0.4s ease;
}
.nav.is-stuck::before { opacity: 1; }
.nav.is-stuck { padding-block: 0.7rem; }
.nav > * { position: relative; }

.nav-logo {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  text-decoration: none;
}
.nav-logo img { display: block; height: 31px; width: auto; }
/* Wordmark. The gap between "cloudnaut" and "TECHNOLOGIES" was leading, not
   margin: the 20px text sat in a 33px line box because `line-height: 1.65`
   inherits from body, and the block `em` added its own on top. Both are pinned
   here, so the space between the two lines is only what `em`'s margin sets. */
.nav-word {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.42rem;
  line-height: 1.02;
  letter-spacing: -0.022em;
  color: var(--paper);
}
.nav-word em {
  display: block;
  font-family: var(--font-mono);
  font-style: normal;
  font-weight: 400;
  font-size: 0.6rem;
  line-height: 1;
  /* Tracked out so the lockup's second line optically matches the width of
     "cloudnaut" above it. `text-indent` cancels the trailing letter-space that
     letter-spacing adds after the final S, which otherwise shifts it left. */
  letter-spacing: 0.475em;
  text-indent: 0.475em;
  text-transform: uppercase;
  margin-top: 0.28rem;
  color: var(--cyan);
}

/* Eight links now that Data & AI/ML is in. `white-space: nowrap` is the
   load-bearing bit: without it "Amazon Connect" and "Data & AI/ML" break
   across two lines between ~1180 and 1400px and the bar grows to two rows.
   The gap tightens with viewport instead, and the whole cluster is allowed
   to shrink before the CTA does. */
.nav-links {
  display: flex;
  /* Lower bound dropped 0.85 -> 0.55rem. Careers replaced Approach in the bar,
     and at 1101px (just above the burger breakpoint) the old minimum left the
     last link overlapping the CTA — by 28px on careers.html, whose CTA reads
     "See open roles" rather than "Talk to us". Links stay on one row while
     overflowing *into* the button, so a row-count check does not catch it:
     measure the gap between the last link and .nav-cta instead. */
  gap: clamp(0.55rem, 1.1vw, 1.9rem);
  min-width: 0;
}
.nav-links a {
  position: relative;
  white-space: nowrap;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.92rem;
  font-weight: 500;
  padding-block: 0.3rem;
  transition: color 0.25s;
}
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--grad);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}
.nav-links a:hover, .nav-links a:focus-visible { color: var(--paper); }
.nav-links a:hover::after, .nav-links a:focus-visible::after,
.nav-links a.is-active::after { transform: scaleX(1); }
.nav-links a.is-active { color: var(--paper); }

.nav-cta {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--paper);
  text-decoration: none;
  white-space: nowrap;   /* "Talk to us" was breaking into two lines at 1280px */
  flex: none;
  padding: 0.6rem 1.3rem;
  border: 1px solid rgba(77, 124, 255, 0.6);
  border-radius: 999px;
  transition: background 0.25s, border-color 0.25s, transform 0.25s;
}
.nav-cta:hover, .nav-cta:focus-visible {
  background: rgba(77, 124, 255, 0.2);
  border-color: var(--blue);
  transform: translateY(-1px);
}

/* 1100–1400px is the tight band: 8 links + wordmark + CTA against a --gutter
   that is still clamping up on vw. Reclaim the padding and the flex gap here
   rather than shrinking the links, which are already nowrap at their minimum.
   Verified clear (>=14px between last link and CTA) at 1101/1180/1280/1366 on
   every page, including careers.html whose CTA is the longest. */
@media (min-width: 1101px) and (max-width: 1400px) {
  .nav { gap: 1rem; padding-inline: 1.5rem; }
  .nav-word { font-size: 1.3rem; }
}

/* ─── Mobile menu ─────────────────────────────────────────────
   .nav-links is hidden below 1100px (the horizontal bar cannot hold 8 links
   at that width — see README). Without a replacement, nine sections were
   reachable only by scrolling: the burger + overlay below is that replacement.
   1100px also catches iPad portrait, so this is not a phone-only path.

   The overlay carries a 9th link (#trust) that the desktop bar has no room
   for. Vertical list, no nowrap constraint, so the limit doesn't apply. */
.nav-burger {
  display: none;   /* shown at <=1100px */
  position: relative;
  z-index: 2;
  width: 44px; height: 44px;   /* 44px: minimum comfortable tap target */
  flex: none;
  padding: 0;
  background: none;
  border: 1px solid rgba(195, 208, 239, 0.34);
  border-radius: 999px;
  cursor: pointer;
}
.nav-burger span {
  position: absolute;
  left: 50%; top: 50%;
  width: 17px; height: 1.5px;
  margin-left: -8.5px;
  background: var(--paper);
  border-radius: 2px;
  transition: transform 0.34s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.2s ease;
}
.nav-burger span:first-child { transform: translateY(-4px); }
.nav-burger span:last-child  { transform: translateY(3px); }
/* Burger morphs into an X while the menu is open */
.nav-burger[aria-expanded="true"] span:first-child { transform: rotate(45deg); }
.nav-burger[aria-expanded="true"] span:last-child  { transform: rotate(-45deg); }
.nav-burger:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 45;   /* under .nav (50) so the header and burger stay clickable */
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.35rem;
  padding: 6.5rem var(--gutter) 2.5rem;
  overflow-y: auto;
  background:
    radial-gradient(80vw 60vh at 85% 0%, rgba(77, 124, 255, 0.16), transparent 65%),
    rgba(4, 10, 32, 0.98);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  opacity: 0;
  transition: opacity 0.32s ease;
}
/* [hidden] is toggled by JS a frame apart from .is-open, so the fade can run.
   Needs !important: [hidden] loses to display:flex on specificity alone. */
.mobile-menu[hidden] { display: none !important; }
.mobile-menu.is-open { opacity: 1; }

.mobile-links { display: flex; flex-direction: column; }
.mobile-links a {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 6vw, 2.1rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--paper);
  text-decoration: none;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--line);
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.4s ease, transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
              color 0.25s ease;
}
.mobile-menu.is-open .mobile-links a { opacity: 1; transform: none; }
/* Stagger down the list — 9 links, 40ms apart */
.mobile-menu.is-open .mobile-links a:nth-child(1) { transition-delay: 0.04s; }
.mobile-menu.is-open .mobile-links a:nth-child(2) { transition-delay: 0.08s; }
.mobile-menu.is-open .mobile-links a:nth-child(3) { transition-delay: 0.12s; }
.mobile-menu.is-open .mobile-links a:nth-child(4) { transition-delay: 0.16s; }
.mobile-menu.is-open .mobile-links a:nth-child(5) { transition-delay: 0.20s; }
.mobile-menu.is-open .mobile-links a:nth-child(6) { transition-delay: 0.24s; }
.mobile-menu.is-open .mobile-links a:nth-child(7) { transition-delay: 0.28s; }
.mobile-menu.is-open .mobile-links a:nth-child(8) { transition-delay: 0.32s; }
.mobile-menu.is-open .mobile-links a:nth-child(9) { transition-delay: 0.36s; }
.mobile-links a:active { color: var(--cyan); }

.mobile-cta {
  align-self: flex-start;
  margin-top: 2rem;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.4s ease 0.42s, transform 0.5s cubic-bezier(0.22, 1, 0.36, 1) 0.42s;
}
.mobile-menu.is-open .mobile-cta { opacity: 1; transform: none; }

/* Locks the page behind the overlay. Lenis is stopped in JS as well —
   overflow:hidden alone doesn't stop a Lenis-driven scroll. */
body.menu-open { overflow: hidden; }

@media (max-width: 1100px) {
  .nav-links { display: none; }
  .nav-burger { display: block; }
  /* The header CTA duplicates the one inside the overlay, and at narrow
     widths the logo + CTA + burger crowd each other. */
  .nav-cta { display: none; }
}
@media (min-width: 1101px) {
  /* Desktop never shows the overlay, even if a resize leaves it open */
  .mobile-menu { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  .mobile-menu { transition: none; }
  .mobile-links a, .mobile-cta {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .nav-burger span { transition: none; }
}

/* ─── Layout primitives ───────────────────────────────────── */
main { position: relative; z-index: 10; }

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* Card/grid bands run wider than prose bands: at 2000px a 1500px shell left
   the cards looking inset next to the edge-to-edge split bands. */
.band--wide > .shell { max-width: var(--shell-wide); }
/* Prose-width only where a single column of running text needs a measure —
   never used to shrink a whole section any more. */
.measure { max-width: 62ch; }
.measure-wide { max-width: 78ch; }

/* Bands replace the old uniform full-height .chapter.
   --solid: opaque ground, text-safe.  --canvas: particles show through. */
.band {
  position: relative;
  padding-block: var(--section-y);
  /* Clear the fixed nav when jumped to via an anchor, so headlines never
     land underneath it. */
  scroll-margin-top: 5.5rem;
}
.band--solid { background: var(--ink); }
.band--alt {
  background:
    radial-gradient(80vw 60vh at 85% 0%, rgba(77, 124, 255, 0.1), transparent 65%),
    var(--ink-2);
}
.band--canvas { background: transparent; }
/* Canvas bands get a vertical scrim so headlines stay legible over particles */
.band--canvas::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(4, 10, 32, 0.94) 0%,
    rgba(4, 10, 32, 0.55) 30%,
    rgba(4, 10, 32, 0.55) 70%,
    rgba(4, 10, 32, 0.94) 100%);
  pointer-events: none;
}
.band--canvas > * { position: relative; }
.band--flush { padding-block: 0; }

/* Signature: a hairline gradient seam between bands. Echoes the brand
   gradient without painting an opaque shape over the particle windows — the
   previous filled-SVG "sweep" read as black blobs on transparent bands.
   Uses ::after because .band--canvas::before is the scrim. */
.band + .band::after {
  content: "";
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: min(var(--shell), calc(100% - var(--gutter) * 2));
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(77, 124, 255, 0.55) 25%,
              rgba(155, 107, 255, 0.55) 75%, transparent);
  pointer-events: none;
  z-index: 3;
}

/* ─── Type ────────────────────────────────────────────────── */
.eyebrow {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--cyan);
  margin-bottom: 1.3rem;
  padding-bottom: 0.55rem;
  background-image: var(--grad);
  background-repeat: no-repeat;
  background-position: left bottom;
  background-size: 52px 2px; /* GSAP animates from 0% on scroll-in */
}

h1 {
  font-family: var(--font-display);
  font-weight: 700;
  /* Sized to fit "possibilities." on one line in the hero's left column —
     the previous 6.4rem cap forced a three-line wrap. */
  font-size: clamp(2.6rem, 4.6vw, 4.6rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
  min-height: 2.1em; /* reserve space through the intro so CTAs don't jump */
}
h1 .line { display: block; }

h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 4.2vw, 3.6rem);
  line-height: 1.08;
  letter-spacing: -0.025em;
  margin-bottom: 1.3rem;
}

h3 { font-family: var(--font-display); }

.grad {
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.lede {
  margin-top: 1.6rem;
  max-width: 46ch;
  font-size: clamp(1.1rem, 1.5vw, 1.32rem);
  line-height: 1.6;
  color: var(--muted);
}

.body-lg {
  font-size: clamp(1.04rem, 1.25vw, 1.18rem);
  color: var(--muted);
  max-width: 68ch;
}

/* Section headers that span the full shell, with the intro text beside them
   instead of stacked in a narrow column — this is what reclaims the dead
   left/right space on wide screens. */
.band-head {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(1.5rem, 5vw, 5rem);
  align-items: end;
  margin-bottom: clamp(2.5rem, 5vw, 4.5rem);
}
.band-head > :last-child { padding-bottom: 0.4rem; }
/* Top-align when the headline runs to 3 lines: bottom-alignment pushed short
   intros down and left ~174px of empty column above them. */
@media (min-width: 901px) {
  .band-head { align-items: start; }
  .band-head > :last-child { padding-top: 0.5rem; }
}
.band-head p.body-lg { max-width: 52ch; }
.band-head h2 { margin-bottom: 0; }
@media (max-width: 900px) {
  .band-head { grid-template-columns: 1fr; align-items: start; gap: 1.4rem; }
}

/* ─── Hero ────────────────────────────────────────────────── */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-block: clamp(7rem, 15vh, 11rem) clamp(5rem, 10vh, 7rem);
  background: transparent;
}
/* Directional scrim: dark where the type sits, clear where the particle
   infinity resolves on the right. Fixes the hero contrast without hiding the scene. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(100deg, rgba(4, 10, 32, 0.95) 0%, rgba(4, 10, 32, 0.82) 38%,
                    rgba(4, 10, 32, 0.3) 62%, rgba(4, 10, 32, 0.12) 100%),
    linear-gradient(180deg, rgba(4, 10, 32, 0.8) 0%, transparent 22%);
  pointer-events: none;
}
.hero > * { position: relative; }

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
  width: 100%;
}
/* Right column is intentionally empty on desktop: it is the stage for the
   particle mark, which would otherwise be covered by text. */
.hero-stage { min-height: 340px; }
@media (max-width: 1000px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero-stage { display: none; }
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 2.4rem;
}

.btn {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.98rem;
  text-decoration: none;
  padding: 0.9rem 1.9rem;
  border-radius: 999px;
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.28s, background 0.25s;
}
.btn-primary {
  color: #fff;
  background: var(--grad);
  box-shadow: 0 10px 34px rgba(77, 124, 255, 0.4);
}
.btn-primary:hover, .btn-primary:focus-visible {
  transform: translateY(-3px);
  box-shadow: 0 16px 46px rgba(77, 124, 255, 0.55);
}
.btn-ghost {
  color: var(--paper);
  border: 1px solid rgba(195, 208, 239, 0.4);
  backdrop-filter: blur(4px);
}
.btn-ghost:hover, .btn-ghost:focus-visible {
  background: rgba(195, 208, 239, 0.14);
  transform: translateY(-3px);
}

/* Hero intro sequence: type → C/N word-play → tagline */
.type-line { display: block; }
.caret {
  display: inline-block;
  width: 0.055em;
  height: 0.92em;
  margin-left: 0.08em;
  background: var(--cyan);
  vertical-align: -0.06em;
  animation: blink 0.85s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

.pair-line {
  display: none;
  gap: 0.35em;
  align-items: baseline;
}
.pair-line.on { display: flex; flex-wrap: wrap; }
.pair-half b { font-weight: 700; }
.pair-x {
  font-size: 0.5em;
  color: var(--muted-2);
  transform: translateY(-0.25em);
}
#c-rest, #n-rest { color: var(--paper); opacity: 0.94; }

.final-lines { display: none; }
.final-lines.on { display: block; }

@media (prefers-reduced-motion: reduce) {
  .type-line, .pair-line { display: none !important; }
  .final-lines { display: block !important; }
  .caret { animation: none; }
}

.scroll-hint {
  position: absolute;
  bottom: 1.8rem;
  left: var(--gutter);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--muted-2);
  display: flex;
  align-items: center;
  gap: 0.8rem;
}
.scroll-hint span {
  width: 54px; height: 1px;
  background: linear-gradient(90deg, var(--blue), transparent);
  animation: drip 2.4s ease-in-out infinite;
}
@keyframes drip {
  0% { transform: scaleX(0); transform-origin: left; }
  55% { transform: scaleX(1); transform-origin: left; }
  56% { transform-origin: right; }
  100% { transform: scaleX(0); transform-origin: right; }
}

/* ─── Hero badge strip ────────────────────────────────────── */
.badge-strip {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.8rem;
  margin-top: 2.8rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}
.badge-strip li {
  font-family: var(--font-mono);
  font-size: 0.71rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.badge-strip li::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--grad);
}

/* ─── Metric row (problem band) ───────────────────────────── */
/* Canvas bands reserve an empty column so the particle field has somewhere to
   live that isn't underneath a paragraph. */
.canvas-split {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
.canvas-window { min-height: 300px; }
@media (max-width: 1100px) {
  .canvas-split { grid-template-columns: 1fr; }
  .canvas-window { display: none; }
}

.metric-row {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: clamp(1.5rem, 3vw, 3rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-top: 2.4rem;
  border-top: 1px solid var(--line);
}
.metric-row dt, .metric-row strong {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.4vw, 2.1rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 0.45rem;
}
.metric-row dd, .metric-row span {
  color: var(--muted);
  font-size: 0.96rem;
  margin: 0;
}

/* ─── Split band: full-bleed image beside text ─────────────
   The photo runs to the viewport edge so it occupies half the screen instead
   of sitting in a 300px card.

   Column split is derived from the shell, not a flat 50/50: the media edge
   lands on the shell's own gridline, so the copy beside it starts on exactly
   the same rail as every non-split section. A flat 1fr 1fr put the text at
   1088px while the rest of the page sat at 280px — that mismatched left edge
   is what read as patchwork. */
.split {
  /* Distance from the viewport edge to the shell's text rail — the same
     280px (at 2000px wide) that .shell produces. */
  --rail: max(var(--gutter), (100vw - var(--shell)) / 2 + var(--gutter));
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: center;
  gap: 0;
  /* No min-height: it forced the band taller than its content, leaving ~96px
     of empty column under the last step. The media min-height carries it. */
}
.split-media {
  position: relative;
  align-self: stretch;
  min-height: 60vh;
  overflow: hidden;
}
.split-media img {
  position: absolute;
  /* Pan headroom comes from extra height, NOT a leftover scale() transform:
     a persistent scale resamples the bitmap and read as blur. Now the image
     renders 1:1 and the parallax just slides it. */
  top: -6%; left: 0;
  width: 100%;
  height: 112%;
  object-fit: cover;
}
.split-media figcaption {
  position: absolute;
  z-index: 2;
  bottom: 0; left: 0; right: 0;
  padding: 4.5rem 2rem 1.5rem;
  font-size: 0.86rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.86);
  background: linear-gradient(180deg, transparent, rgba(4, 10, 32, 0.92));
}
/* Photo on the left → copy sits in the right half, indented by the shell
   gutter so it reads as a continuation of the grid rather than a new one. */
.split-copy {
  padding-block: clamp(3rem, 7vw, 6rem);
  padding-inline: var(--gutter) var(--rail);
}
/* Reverse: photo right, copy left — copy now starts on the outer shell rail,
   the same left edge as every .shell section above and below it. */
.split--reverse .split-media { order: 2; }
.split--reverse .split-copy { padding-inline: var(--rail) var(--gutter); }

@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; min-height: 0; }
  .split-media { min-height: 58vh; order: 1 !important; }
  .split-copy, .split--reverse .split-copy { padding-inline: var(--gutter); }
}

/* ─── Service cards ───────────────────────────────────────── */
/* Six cards: 3×2 on wide screens. auto-fit gave 4+2 at ~2000px, leaving two
   empty cells on the second row. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.5rem;
}
@media (max-width: 1150px) { .card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px)  { .card-grid { grid-template-columns: 1fr; } }
/* ─── Insight stage ──────────────────────────────────────────
   The double-flip was structural: 17 boxes each owning a hover state means
   leaving one and entering another runs two opposing animations at once. No
   easing fixes that. So there is exactly ONE reveal per section now.

   Hovering any card opens a shared stage over the whole grid: the cards
   recede and dim, and the stage draws a live architecture diagram for the
   hovered capability with data pulsing along the connections. Moving between
   cards re-renders INSIDE an already-open stage — no close, no re-open, no
   thrash. The stage only animates when you enter or leave the grid entirely.

   It deliberately looks nothing like the card: different ground, an
   engineering diagram instead of prose, and numbers the card can't carry. */

.stage-host { position: relative; }

.card-grid, .connect-grid, .agentic-plays {
  transition: opacity 0.5s ease, transform 0.7s cubic-bezier(0.22, 1, 0.36, 1),
              filter 0.5s ease;
}
/* Cards recede so the stage owns the space */
.stage-host.is-open .card-grid,
.stage-host.is-open .connect-grid,
.stage-host.is-open .agentic-plays {
  opacity: 0.1;
  transform: scale(0.98);
  filter: blur(4px);
  pointer-events: auto;   /* still the hover target */
}

.stage {
  position: absolute;
  /* inset alone sizes the stage to its host — do NOT also set an explicit
     height in JS, the two compounded into ~28px of dead space below the grid. */
  inset: -1.2rem -1.2rem;
  z-index: 4;
  border-radius: 24px;
  overflow: hidden;
  background:
    radial-gradient(90% 120% at 88% -10%, rgba(77, 124, 255, 0.24), transparent 60%),
    linear-gradient(150deg, #0b1740 0%, #071033 55%, #050d28 100%);
  border: 1px solid rgba(124, 160, 255, 0.34);
  box-shadow: 0 40px 110px rgba(3, 8, 26, 0.72);
  opacity: 0;
  visibility: hidden;
  transform: perspective(1600px) rotateX(7deg) translateY(26px) scale(0.972);
  transform-origin: 50% 0;
  transition:
    opacity 0.5s ease,
    transform 0.72s cubic-bezier(0.2, 1, 0.3, 1),
    visibility 0.5s;
  pointer-events: none;
}
.stage-host.is-open .stage {
  opacity: 1;
  visibility: visible;
  transform: perspective(1600px) rotateX(0) translateY(0) scale(1);
  pointer-events: auto;   /* the rail and close button need to be clickable */
}
/* ...but the panel background must not steal hover from the cards beneath */
.stage-host.is-open .stage-inner { pointer-events: none; }
.stage-host.is-open .stage-rail,
.stage-host.is-open .stage-close { pointer-events: auto; }

/* The Three.js layer: a flow field that follows the diagram */
.stage-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 1;
  /* Fade the mark out behind the copy column so text always wins */
  -webkit-mask: linear-gradient(90deg, transparent 4%, rgba(0,0,0,0.35) 26%, #000 55%);
  mask: linear-gradient(90deg, transparent 4%, rgba(0,0,0,0.35) 26%, #000 55%);
}

.stage-inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  gap: clamp(1.6rem, 3.5vw, 3.4rem);
  align-items: center;
  height: 100%;
  padding: clamp(1.8rem, 3vw, 2.8rem) clamp(1.8rem, 3.4vw, 3rem);
}
@media (max-width: 900px) {
  .stage-inner { grid-template-columns: 1fr; align-content: start; gap: 1.2rem; }
  .stage-viz { display: none; }
}

.stage-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--cyan);
  margin-bottom: 0.7rem;
}
.stage-title {
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 2.2vw, 2rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.14;
  color: #fff;
  margin-bottom: 1.15rem;
}
.stage-facts { display: grid; gap: 0.9rem; }
.stage-facts dt {
  font-family: var(--font-mono);
  font-size: 0.61rem;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: rgba(160, 190, 255, 0.9);
  margin-bottom: 0.24rem;
}
.stage-facts dd {
  margin: 0;
  color: #dee8ff;
  font-size: 0.93rem;
  line-height: 1.6;
  max-width: 46ch;
}
/* Facts stagger in once the stage is open */
.stage-facts > div, .stage-title, .stage-eyebrow {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.45s ease, transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.stage-host.is-open .stage-eyebrow { opacity: 1; transform: none; transition-delay: 0.1s; }
.stage-host.is-open .stage-title   { opacity: 1; transform: none; transition-delay: 0.16s; }
.stage-host.is-open .stage-facts > div { opacity: 1; transform: none; }
.stage-host.is-open .stage-facts > div:nth-child(1) { transition-delay: 0.24s; }
.stage-host.is-open .stage-facts > div:nth-child(2) { transition-delay: 0.3s; }
.stage-host.is-open .stage-facts > div:nth-child(3) { transition-delay: 0.36s; }
/* Re-render inside an open stage: quick cross-fade, no delays */
.stage-host.is-swapping .stage-copy, .stage-host.is-swapping .stage-viz {
  opacity: 0;
  transition: opacity 0.16s ease;
}
.stage-copy, .stage-viz { transition: opacity 0.3s ease; }

/* ── Getting out, and moving on ──
   Two escapes, because hovering out was the only one and that meant
   travelling to the edge of a full-width panel:
     1. a close button (top-right)
     2. a rail of the other capabilities inside the stage, so you can move
        to the next without leaving at all
   Escape also closes it. */
.stage-close {
  position: absolute;
  top: 1rem; right: 1rem;
  z-index: 3;
  width: 34px; height: 34px;
  display: grid;
  place-items: center;
  font-size: 0.85rem;
  color: var(--muted);
  background: rgba(8, 18, 48, 0.7);
  border: 1px solid rgba(124, 160, 255, 0.3);
  border-radius: 50%;
  cursor: pointer;
  transition: color 0.25s, border-color 0.25s, background 0.25s, transform 0.25s;
}
.stage-close:hover, .stage-close:focus-visible {
  color: #fff;
  border-color: var(--cyan);
  background: rgba(20, 44, 96, 0.9);
  transform: rotate(90deg);
}

.stage-rail {
  position: absolute;
  left: clamp(1.8rem, 3.4vw, 3rem);
  right: clamp(1.8rem, 3.4vw, 3rem);
  bottom: 1.05rem;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding-top: 0.9rem;
  border-top: 1px solid rgba(124, 160, 255, 0.16);
}
.stage-rail button {
  font-family: var(--font-mono);
  font-size: 0.63rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted-2);
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 0.32rem 0.8rem;
  cursor: pointer;
  transition: color 0.25s, background 0.25s, border-color 0.25s;
}
.stage-rail button:hover, .stage-rail button:focus-visible {
  color: #fff;
  background: rgba(77, 124, 255, 0.18);
  border-color: rgba(124, 160, 255, 0.45);
}
.stage-rail button[aria-current="true"] {
  color: #fff;
  background: rgba(77, 124, 255, 0.26);
  border-color: var(--cyan);
}
/* Keep the copy and diagram clear of the rail */
.stage-inner { padding-bottom: clamp(3.6rem, 5vw, 4.4rem); }
@media (max-width: 900px) {
  .stage-rail { position: static; margin-top: 1rem; left: auto; right: auto; }
  .stage-inner { padding-bottom: clamp(1.8rem, 3vw, 2.8rem); }
}

/* ── The architecture diagram ── */
.stage-viz { position: relative; }

/* Narrow-column stages (Amazon Connect, Data & AI/ML, Agentic AI) sit in one
   half of a two-column section, so the diagram was being asked to fit roughly
   a quarter of the viewport and came out unreadably small — fine in the
   full-width Services stage, too small here. Give the viz the larger share of
   the panel, tighten the gap and side padding, and let it use more height. */
.split-copy .stage-host .stage-inner,
.agentic-grid .stage-host .stage-inner {
  grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr);
  gap: clamp(1rem, 1.6vw, 1.6rem);
  padding-inline: clamp(1.2rem, 1.8vw, 1.7rem);
}
/* Narrower copy column, so drop the display size a step — at the full-width
   scale "Legacy contact-centre migration" broke onto four lines. */
.split-copy .stage-host .stage-title,
.agentic-grid .stage-host .stage-title {
  font-size: clamp(1.25rem, 1.6vw, 1.65rem);
}
.split-copy .stage-host .stage-svg,
.agentic-grid .stage-host .stage-svg {
  max-height: 72vh;
}
/* The node labels are set in SVG user units, so they scale with the viewBox.
   Nudging them up keeps them legible once the diagram is bigger but the
   column is still narrow. */
.split-copy .stage-host .stage-svg .node text,
.agentic-grid .stage-host .stage-svg .node text { font-size: 9.4px; }
.split-copy .stage-host .stage-svg .node .svc,
.agentic-grid .stage-host .stage-svg .node .svc { font-size: 6.9px; }
/* max-height was 46vh, which clipped the growth won by fitting the viewBox
   to the node bounds. The diagram is the right-hand half of a panel that is
   itself vh-bounded, so it can afford more. */
.stage-svg {
  width: 100%;
  height: auto;
  max-height: 62vh;
  overflow: visible;
}
.stage-svg .node rect {
  fill: rgba(12, 26, 66, 0.86);
  stroke: rgba(124, 160, 255, 0.5);
  stroke-width: 1;
  rx: 6;
}
.stage-svg .node text {
  font-family: var(--font-body);
  font-size: 8.5px;
  font-weight: 500;
  fill: #eaf0ff;
}
.stage-svg .node .svc {
  font-family: var(--font-mono);
  font-size: 6.4px;
  fill: rgba(150, 180, 245, 0.95);
  letter-spacing: 0.06em;
}
.stage-svg .node--accent rect { stroke: var(--cyan); fill: rgba(20, 44, 96, 0.9); }
.stage-svg .link {
  fill: none;
  stroke: rgba(124, 160, 255, 0.42);
  stroke-width: 1.1;
  stroke-dasharray: var(--len) var(--len);
  stroke-dashoffset: var(--len);
}
.stage-host.is-open .stage-svg .link {
  animation: draw-link 0.85s cubic-bezier(0.3, 0.9, 0.3, 1) forwards;
}
@keyframes draw-link { to { stroke-dashoffset: 0; } }
/* Data pulsing along each connection */
.stage-svg .pulse { fill: var(--cyan); r: 1.9; opacity: 0; }
.stage-host.is-open .stage-svg .pulse { animation: pulse-run 2.1s linear infinite; }
@keyframes pulse-run {
  0%   { opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { opacity: 0; }
}
.stage-svg .node { opacity: 0; animation: none; }
.stage-host.is-open .stage-svg .node {
  animation: node-in 0.5s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
@keyframes node-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

.stage-caption {
  margin-top: 0.7rem;
  font-family: var(--font-mono);
  font-size: 0.63rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(150, 180, 245, 0.8);
  text-align: center;
}

/* ── The cards themselves ── */
.card-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.5rem; }
@media (max-width: 1150px) { .card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px)  { .card-grid { grid-template-columns: 1fr; } }

.pcard {
  position: relative;
  display: flex;
  flex-direction: column;
  /* Restores the card height the flip version reserved. Without it the grid
     collapsed to its content and the section's own padding read as a void. */
  min-height: 300px;
  padding: 1.9rem 1.8rem 1.6rem;
  border: 1px solid var(--line);
  border-radius: 20px;
  background: linear-gradient(160deg, var(--ink-3), var(--ink-2));
  cursor: default;
  transition: border-color 0.35s, transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
/* The hovered card stays lit and forward while its siblings recede */
.stage-host.is-open .pcard { opacity: 0.9; }
.pcard.is-active {
  border-color: var(--cyan);
  transform: translateY(-4px);
}
.pcard h3 { font-family: var(--font-display); font-size: 1.16rem; font-weight: 600; line-height: 1.3; margin-bottom: 0.6rem; }
.pcard-lede { color: var(--muted); font-size: 0.94rem; margin-bottom: 1rem; }
.pcard strong {
  display: block;
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 600;
  margin-bottom: 0.3rem;
}
.pcard > span:not(.pcard-cue):not(.card-tag) { color: var(--muted); font-size: 0.88rem; }
.pcard--sm {
  min-height: 150px;
  padding: 1.4rem 1.3rem;
  border-left: 3px solid var(--blue);
  border-radius: 12px;
}
.card-tag {
  font-family: var(--font-mono);
  font-size: 0.65rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cyan);
  margin-top: auto;
}
/* Small infinity cue, bottom-right.
   Box matches the mark's real 1024:577 aspect (1.774) so `contain` fills it
   edge to edge; the old 26×13 box was cut for a wider stand-in mark and
   letterboxed the traced ribbon. */
.pcard-cue {
  position: absolute;
  right: 1.5rem; bottom: 1.3rem;
  width: 26px; height: 15px;
  background: var(--grad);
  -webkit-mask: url("../assets/logo-mark.svg") center / contain no-repeat;
  mask: url("../assets/logo-mark.svg") center / contain no-repeat;
  opacity: 0.4;
  transition: opacity 0.35s ease;
  pointer-events: none;
}
.pcard--sm .pcard-cue { right: 1.05rem; bottom: 1rem; width: 21px; height: 12px; }
.pcard.is-active .pcard-cue { opacity: 1; }

/* Touch and reduced motion: no stage, cards just sit there readable */
@media (hover: none) {
  .stage { display: none; }
  .stage-host.is-open .card-grid,
  .stage-host.is-open .connect-grid,
  .stage-host.is-open .agentic-plays {
    opacity: 1; transform: none; filter: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .stage { transition: opacity 0.25s ease, visibility 0.25s; transform: none; }
  .stage-host.is-open .stage { transform: none; }
  .stage-host.is-open .stage-svg .link { animation: none; stroke-dashoffset: 0; }
  .stage-host.is-open .stage-svg .pulse { animation: none; opacity: 0.85; }
  .stage-host.is-open .stage-svg .node { animation: none; opacity: 1; }
  .card-grid, .connect-grid, .agentic-plays { transition: opacity 0.25s ease; }
  .stage-host.is-open .card-grid,
  .stage-host.is-open .connect-grid,
  .stage-host.is-open .agentic-plays { transform: none; filter: none; }
}

.cap-list { list-style: none; margin-bottom: 1.3rem; }
.cap-list li {
  position: relative;
  padding-left: 1.2rem;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.75;
}
.cap-list li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.72em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--blue);
}

/* ─── Method: a real sequence, so it keeps its numbering ──── */
/* Navigate -> Build -> Transform is a real sequence, so it stacks: three
   side-by-side columns read as simultaneous AND squeezed the copy to ~24ch,
   which looked like newspaper filler. One column, proper measure. */
.method {
  list-style: none;
  counter-reset: step;
  display: grid;
  gap: clamp(1.5rem, 2.4vw, 2.2rem);
}
.method p { max-width: 58ch; }
.method li {
  counter-increment: step;
  padding-top: 2.6rem;
  border-top: 1px solid var(--line);
  position: relative;
}
.method li::before {
  content: counter(step, decimal-leading-zero);
  position: absolute;
  top: 1rem; left: 0;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  color: var(--cyan);
}
/* The rule above each step fills as it enters — the sequence draws itself */
.method li::after {
  content: "";
  position: absolute;
  top: -1px; left: 0;
  width: 0;
  height: 1px;
  background: var(--grad);
}
.method-step {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.3rem, 1.9vw, 1.7rem);
  letter-spacing: -0.015em;
  display: block;
  margin-bottom: 0.5rem;
}
.method p { color: var(--muted); font-size: 0.98rem; }

/* ─── Process grid ────────────────────────────────────────── */
.sub-head {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.2rem;
  margin-bottom: 1.4rem;
  color: var(--paper);
}
/* Both agentic columns carry a label of identical box height, so the first
   panel in each starts on the same line. Fixed height + fixed margin rather
   than min-height: the labels wrap differently, which was the residual 30px. */
.agentic-grid > div > .sub-head {
  display: flex;
  align-items: flex-end;
  height: 1.9rem;
  margin: 0 0 1.2rem;
  white-space: nowrap;
}
.process-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 0.9rem;
}
.process-grid li {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 1rem 1.2rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(13, 26, 68, 0.6);
  font-size: 0.95rem;
  transition: border-color 0.3s, background 0.3s;
}
.process-grid li:hover {
  border-color: rgba(77, 124, 255, 0.45);
  background: var(--ink-3);
}
.process-grid li > span {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--cyan);
  flex-shrink: 0;
  width: 1.7rem; height: 1.7rem;
  display: grid;
  place-items: center;
  border: 1px solid rgba(92, 206, 245, 0.42);
  border-radius: 50%;
}

/* ─── Amazon Connect capability grid ──────────────────────── */
.connect-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
  gap: 1.2rem;
}
.connect-item {
  display: flex;
  flex-direction: column;
  padding: 1.4rem 1.3rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--blue);
  border-radius: 12px;
  background: rgba(13, 26, 68, 0.72);
  transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1), background 0.3s;
}
.connect-item strong {
  display: block;
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 600;
  margin-bottom: 0.3rem;
}
.connect-item span { color: var(--muted); font-size: 0.9rem; }

/* ─── Agentic AI: stack diagram ───────────────────────────── */
.stack-diagram {
  display: grid;
  gap: 0.75rem;
  perspective: 1100px;
}
.stack-layer {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: linear-gradient(160deg, rgba(18, 35, 90, 0.86), rgba(13, 26, 68, 0.86));
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: 1.2rem 1.5rem;
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.35s, background 0.35s;
  will-change: transform;
}
.stack-layer[data-layer="1"] { border-left: 3px solid var(--cyan); }
.stack-layer[data-layer="2"] { border-left: 3px solid var(--blue); }
.stack-layer[data-layer="3"] { border-left: 3px solid #7a5cf8; }
.stack-layer[data-layer="4"] { border-left: 3px solid var(--violet); }
.stack-layer:hover {
  transform: translateX(12px) rotateY(-2deg);
  border-color: rgba(77, 124, 255, 0.55);
  background: linear-gradient(160deg, rgba(28, 48, 110, 0.92), rgba(18, 35, 90, 0.92));
}
.stack-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.8rem;
  margin-bottom: 0.35rem;
}
.stack-num {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--cyan);
  border: 1px solid rgba(92, 206, 245, 0.42);
  border-radius: 6px;
  padding: 0.12rem 0.45rem;
}
.stack-head strong { font-family: var(--font-display); font-size: 1.1rem; font-weight: 600; }
.stack-aws {
  font-family: var(--font-mono);
  font-size: 0.67rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted-2);
  margin-left: auto;
}
.stack-layer p { color: var(--muted); font-size: 0.92rem; margin: 0; }
@media (max-width: 640px) {
  .stack-aws { margin-left: 0; width: 100%; }
}


.agentic-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}
@media (max-width: 1000px) { .agentic-grid { grid-template-columns: 1fr; } }

.agentic-plays {
  display: grid;
  gap: 1.1rem;
  margin-top: 0;   /* the .sub-head above it already supplies the offset */
}
.play {
  border: 1px solid var(--line);
  border-radius: 16px;
  background: rgba(13, 26, 68, 0.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: 1.5rem 1.5rem 1.3rem;
  transition: border-color 0.3s, transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.play h3 { font-size: 1.12rem; font-weight: 600; margin-bottom: 0.5rem; }
.play p { color: var(--muted); font-size: 0.94rem; margin-bottom: 1rem; }

/* ─── Differentiators ─────────────────────────────────────── */
.diff-list {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: clamp(1.4rem, 2.5vw, 2.4rem);
}
.diff-list li {
  color: var(--muted);
  font-size: 1rem;
  padding-left: 1.6rem;
  position: relative;
}
.diff-list li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.55em;
  width: 8px; height: 8px;
  background: var(--grad);
  border-radius: 2px;
  transform: rotate(45deg);
}
.diff-list strong { color: var(--paper); font-weight: 600; }

/* ─── Insights ────────────────────────────────────────────── */
/* Five articles: the newest runs wide as the lead story, the other four sit
   in a 2×2 beside it. Fills the row instead of leaving a hole at the end. */
.insight-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.6rem;
}
.insight-card:first-child { grid-column: span 2; grid-row: span 2; }
.insight-card:first-child figure { aspect-ratio: 16 / 9; }
.insight-card:first-child h3 { font-size: clamp(1.3rem, 1.9vw, 1.75rem); }
@media (max-width: 1200px) {
  .insight-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .insight-card:first-child { grid-column: span 2; grid-row: span 1; }
}
@media (max-width: 720px) {
  .insight-grid { grid-template-columns: 1fr; }
  .insight-card:first-child { grid-column: span 1; }
}
.insight-card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  border: 1px solid var(--line);
  border-radius: 18px;
  overflow: hidden;
  background: var(--ink-2);
  transition: border-color 0.35s, box-shadow 0.35s;
}
/* No translateY here either — these cards already zoom their photo, and the
   lift caused the same edge flicker as the mosaic: card moves out from under
   the cursor, hover drops, card returns, hover re-fires. */
.insight-card:hover, .insight-card:focus-visible {
  border-color: rgba(77, 124, 255, 0.5);
  box-shadow: 0 22px 50px rgba(4, 10, 32, 0.6);
}
.insight-card figure {
  position: relative;
  aspect-ratio: 3 / 2;
  overflow: hidden;
}
.insight-card figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}
.insight-card:hover figure img, .insight-card:focus-visible figure img {
  transform: scale(1.05);
}
.insight-card figure .story-chip {
  position: absolute;
  top: 0.9rem; left: 0.9rem;
  margin-bottom: 0;
}
.insight-body {
  padding: 1.3rem 1.5rem 1.5rem;
  display: flex;
  flex-direction: column;
  flex: 1;
}
.insight-card time {
  font-family: var(--font-mono);
  font-size: 0.69rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cyan);
}
.insight-card h3 {
  font-size: 1.12rem;
  font-weight: 600;
  color: var(--paper);
  margin: 0.6rem 0 0;
  line-height: 1.32;
}
.insight-card p { color: var(--muted); font-size: 0.92rem; }

.insight-more {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  margin-top: 0;
  transition: grid-template-rows 0.45s ease, opacity 0.4s ease, margin-top 0.45s ease;
}
.insight-more > * { overflow: hidden; }
.insight-card:hover .insight-more, .insight-card:focus-visible .insight-more {
  grid-template-rows: 1fr;
  opacity: 1;
  margin-top: 0.75rem;
}
@media (hover: none) {
  .insight-more { grid-template-rows: 1fr; opacity: 1; margin-top: 0.75rem; }
}

.inline-link { color: var(--cyan); }
.inline-link:hover { color: var(--paper); }

/* ─── How we work — editorial mosaic, now at real scale ────
   Rows were a fixed 300px, which is what made the people photos read as
   thumbnails. Now viewport-relative and asymmetric. */
.story-mosaic {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: clamp(260px, 30vw, 420px);
  gap: 1.4rem;
}
.story-card {
  position: relative;
  display: block;
  border-radius: 20px;
  overflow: hidden;
  border: 1px solid var(--line);
  text-decoration: none;
  isolation: isolate;
}
.story-tall { grid-row: span 2; }
.story-wide { grid-column: span 2; }
.story-card img {
  position: absolute;
  /* Height headroom for the parallax pan instead of a scale() transform,
     which resampled the bitmap and looked soft. */
  top: -6%; left: 0;
  width: 100%;
  height: 112%;
  object-fit: cover;
  filter: brightness(0.94);
  /* `scale` is its own property here, not part of a transform shorthand:
     GSAP animates this element's yPercent for the scroll pan and would
     overwrite a combined transform. Hover zoom is set further down. */
  scale: 1;
  transition: filter 0.6s ease, scale 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}
.story-card:hover img, .story-card:focus-visible img { filter: brightness(1); }
.story-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(4, 10, 32, 0.1) 25%, rgba(4, 10, 32, 0.9) 100%);
  transition: background 0.45s ease;
}
.story-card:hover::after {
  background: linear-gradient(180deg, rgba(4, 10, 32, 0.25) 10%, rgba(4, 10, 32, 0.96) 100%);
}
.story-meta {
  position: absolute;
  z-index: 1;
  left: 1.4rem; right: 1.4rem; bottom: 1.3rem;
}
.story-chip {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.63rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--paper);
  padding: 0.33rem 0.78rem;
  border: 1px solid rgba(255, 255, 255, 0.48);
  border-radius: 999px;
  background: rgba(4, 10, 32, 0.6);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  margin-bottom: 0.7rem;
}
.story-meta h3 {
  font-size: clamp(1.15rem, 1.5vw, 1.4rem);
  font-weight: 600;
  line-height: 1.28;
  color: var(--paper);
  margin-bottom: 0.4rem;
}
.story-kicker { font-size: 0.84rem; color: rgba(255, 255, 255, 0.8); }

.story-more {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows 0.45s ease, opacity 0.4s ease, margin-top 0.45s ease;
  margin-top: 0;
}
.story-more > * { overflow: hidden; }
.story-more p { font-size: 0.88rem; line-height: 1.55; color: rgba(255, 255, 255, 0.88); }
.story-card:hover .story-more, .story-card:focus-visible .story-more {
  grid-template-rows: 1fr;
  opacity: 1;
  margin-top: 0.65rem;
}
.story-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0.8rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.83rem;
  color: #fff;
  background: var(--grad);
  padding: 0.52rem 1.15rem;
  border-radius: 999px;
}
.story-btn span { transition: transform 0.25s; }
.story-card:hover .story-btn span, .insight-card:hover .story-btn span { transform: translateX(4px); }

@media (hover: none) {
  .story-more { grid-template-rows: 1fr; opacity: 1; margin-top: 0.65rem; }
}
@media (max-width: 900px) {
  .story-mosaic { grid-template-columns: 1fr; grid-auto-rows: auto; }
  .story-card { min-height: 380px; }
  .story-tall, .story-wide { grid-row: span 1; grid-column: span 1; }
}

/* ─── Trust ───────────────────────────────────────────────── */
.trust-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 1.4rem;
}
.trust-item {
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 1.6rem 1.4rem;
  background: rgba(13, 26, 68, 0.7);
  transition: border-color 0.3s, transform 0.3s;
}
.trust-item strong {
  display: block;
  font-family: var(--font-display);
  font-size: 1.3rem;
  margin-bottom: 0.25rem;
}
.trust-item span { color: var(--muted); font-size: 0.9rem; }

/* ─── Contact ─────────────────────────────────────────────── */
.contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}
@media (max-width: 900px) { .contact-grid { grid-template-columns: 1fr; } }
.contact address {
  font-style: normal;
  font-size: 0.94rem;
  line-height: 1.75;
  color: var(--muted);
}
.contact-card {
  border: 1px solid var(--line);
  border-radius: 20px;
  background: linear-gradient(160deg, rgba(77, 124, 255, 0.14), rgba(155, 107, 255, 0.1));
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  padding: clamp(1.8rem, 3vw, 2.6rem);
}
.contact-card dl { display: grid; gap: 1.2rem; margin-top: 1.4rem; }
.contact-card dt {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cyan);
  margin-bottom: 0.2rem;
}
.contact-card dd { margin: 0; }
.contact-card a { color: var(--paper); text-decoration: none; border-bottom: 1px solid var(--line); }
.contact-card a:hover { border-color: var(--cyan); }

/* ─── Footer ──────────────────────────────────────────────── */
.footer {
  position: relative;
  z-index: 10;
  background: var(--void);
  border-top: 1px solid var(--line);
  padding: clamp(2.5rem, 5vw, 4rem) 0 2rem;
}
.footer-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 1.4rem;
  color: var(--muted);
  font-size: 0.88rem;
}
.footer-tag {
  font-family: var(--font-mono);
  letter-spacing: 0.14em;
  font-size: 0.78rem;
  text-transform: uppercase;
  color: var(--muted-2);
}
.footer a { color: var(--muted); }
.footer a:hover { color: var(--paper); }

/* ─── Motion primitives ──────────────────────────────────── */
.reveal { opacity: 0; transform: translateY(30px); }
/* Images wipe in from their own edge rather than fading — reads as intent */
.reveal-img { clip-path: inset(0 0 100% 0); }

:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }

/* Small-panel grids: the shared stage handles the reveal now, so these are
   plain grids with a hover cue and no per-tile animation. */
.connect-grid, .agentic-plays { align-items: stretch; }

/* Card hovers. Photo cards zoom their image; only .trust-item (no image,
   and it sits in a roomy grid) still lifts. */
/* Mosaic cards zoom the photo instead of lifting the card.
   translateY(-5px) caused two problems:
     1. Overlap — the card grew past its grid cell over its neighbours.
     2. Flicker — at the bottom edge the card moved out from under the
        cursor, hover ended, it dropped back, hover re-fired. A feedback
        loop that read as constant shaking.
   Scaling the image inside a card that already has overflow:hidden keeps
   every card exactly in its own cell, so neither can happen. The card box
   itself never transforms. */
.story-card { transition: border-color 0.4s, box-shadow 0.4s ease; }
.story-card:hover, .story-card:focus-visible {
  border-color: rgba(124, 160, 255, 0.45);
  box-shadow: 0 18px 44px rgba(3, 8, 26, 0.5);
}
.story-card:hover img, .story-card:focus-visible img { scale: 1.06; }
.trust-item:hover { transform: translateY(-4px); border-color: rgba(124, 160, 255, 0.45); }

@media (prefers-reduced-motion: reduce) {
  .trust-item:hover { transform: none; }
  /* Photo cards zoom their image rather than transforming the card box, so
     the zoom has to be reset on the image itself. */
  .story-card:hover img, .story-card:focus-visible img { scale: 1; }
  .insight-card:hover figure img, .insight-card:focus-visible figure img { transform: none; }
}
