:root {
  --bg: #09090b;
  --bg-alt: #0d0d10;
  --bg-nav: rgba(9, 9, 11, 0.72);
  --surface: #111113;
  --surface-2: #18181b;
  --border: rgba(255, 255, 255, 0.06);
  --border-strong: rgba(255, 255, 255, 0.12);
  --rule: rgba(255, 255, 255, 0.08);
  /* Standard foreground: the slightly off-white grey from the case studies
     page, promoted sitewide as THE text/header token (client feedback,
     Jul 10 — swap it here and everything follows). case-studies.css aliases
     --cs-fg / --cs-fg-2 onto these. */
  --fg: #fafafa;
  --fg-2: #ebebeb;
  --text: var(--fg);
  --text-muted: #a1a1aa;
  --text-dim: #71717a;
  /* Section titles use this in place of --text. Gradient/shimmer spans
     inside (.shimmer-text) keep their own gradient because they paint via
     background-clip:text + color:transparent, which isn't overridden by
     this fill. The old #A6A6A6 grey read as "very dark gray headers" and was
     rejected by the client (Jul 10); the hero keeps its two-tone design
     through --title-ghost. */
  --title-fill: var(--fg);
  /* Client call, Jul 20: every title is off-white, never pure white and
     never grey, home and platform heroes included. The ghost/subtitle
     tokens stay as knobs but now resolve to the standard off-white. */
  --title-ghost: var(--fg);
  --subtitle-fill: var(--fg);
  --accent-from: #fa6c00;
  --accent-to: #eb5173;
  --accent: #fa6c00;
  --accent-gradient: linear-gradient(90deg, var(--accent-from) 0%, var(--accent-to) 100%);
  /* Standard card surface (client feedback, Jul 10: one card style
     everywhere). Same recipe as the case-studies cards; case-studies.css
     aliases its --cs-card-* tokens onto these. */
  --card-grad: linear-gradient(180deg, #17171b 0%, #101013 100%);
  --card-line: rgba(255, 255, 255, 0.07);
  --card-line-hover: rgba(255, 255, 255, 0.16);
  --card-r: 20px;
  --card-edge: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  --card-lift: 0 16px 40px rgba(0, 0, 0, 0.45), 0 0 28px rgba(250, 108, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.07);
  --font-sans: "Inter", system-ui, -apple-system, Segoe UI, Helvetica, Arial, sans-serif;
  --font-display: "Inter", system-ui, -apple-system, Segoe UI, Helvetica, Arial, sans-serif;
  /* Safe zone (client feedback, Jul 10: sections stretched edge to edge and
     broke on ultrawide — "you guys will know it" for the exact number).
     1440px matches the approved case-studies Figma wrap; heroes, marquees
     and full-bleed banners keep their own wider rules on purpose. */
  --max-w: min(1440px, 90%);
  --max-w-tight: 1280px;
  --pad-x: 10px;
  /* Header included in the safe zone: on ultrawide the nav content stops
     stretching to the screen edges (the max() arm takes over past ~1600px). */
  --nav-pad-x: max(clamp(24px, 5vw, 88px), calc((100vw - 1440px) / 2));
  --pad-section: clamp(56px, 9vw, 140px);
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
}
*, *::before, *::after {
  box-sizing: border-box;
}
html, body {
  margin: 0;
  padding: 0;
}
html {
  scroll-behavior: smooth;
}
body {
  -webkit-font-smoothing: antialiased;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-feature-settings: "ss01", "ss02", "cv11";
  font-size: 15px;
  line-height: 1.55;
  overflow-x: clip;
  text-rendering: optimizeLegibility;
}
img, svg, video {
  display: block;
  max-width: 100%;
}
a {
  color: inherit;
  text-decoration: none;
}
button {
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  font: inherit;
  padding: 0;
}
::selection {
  background: var(--accent-from);
  color: #09090b;
}
/* Paper grain — one shared SVG noise layer, applied as a subtle overlay. */
body::before {
  background-image: url("data:image/svg+xml;utf8,<svg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.9 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 200px 200px;
  content: "";
  inset: 0;
  mix-blend-mode: overlay;
  opacity: 0.035;
  pointer-events: none;
  position: fixed;
  z-index: 1000;
}
.shimmer-text, .results__title-line--gradient, .services__title-line--accent, .meet-play__title-word--accent {
  -webkit-background-clip: text;
  animation: none !important;
  background: linear-gradient(90deg, var(--accent-from) 0%, var(--accent-to) 100%);
  background-clip: text;
  background-size: 100% 100% !important;
  color: transparent;
  display: inline-block;
  margin: -0.35em -0.15em -0.4em;
  padding: 0.35em 0.15em 0.4em;
}
.platform-performance__title-line--accent {
  -webkit-background-clip: text;
  animation: none !important;
  background: linear-gradient(90deg, var(--accent-from) 0%, var(--accent-to) 100%);
  background-clip: text;
  background-size: 100% 100% !important;
  color: transparent;
  display: inline-block;
  margin: -0.1em;
  /* 0.1em, was 0.2em — ported 2026-08-24 from the colleague's Aug 3 staging
     hot-fixes (platform hero pass, done on the server during the summer
     handover; git had drifted behind). */
  padding: 0.1em;
}
/* Same treatment for non-gradient display text so g/p/y descenders don't
   get visually clipped by the next line's content at line-height 1. */
.hero__title, .meet-play__title, .results__title, .services__title, .cta-banner__title {
  padding-bottom: 0.12em;
}
.orangespan {
  color: var(--accent-from);
}
/* ---------- Buttons ---------- */
.btn {
  align-items: center;
  border-radius: var(--radius-sm);
  display: inline-flex;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  gap: 10px;
  height: 40px;
  justify-content: center;
  letter-spacing: 0.01em;
  line-height: 1;
  padding: 0 20px;
  position: relative;
  transition: transform 160ms var(--ease), filter 160ms var(--ease), background 160ms var(--ease), color 160ms var(--ease);
  white-space: nowrap;
}
.btn:active {
  transform: translateY(1px);
}
.btn--primary {
  background: var(--accent-gradient);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.08) inset, 0 8px 24px -12px rgba(250, 108, 0, 0.45);
  color: #fff;
}
.btn--primary:hover {
  filter: brightness(1.08) saturate(1.05);
  transform: translateY(-1px);
}
.btn--ghost {
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-family: var(--font-sans);
  font-weight: 500;
  letter-spacing: 0.02em;
}
.btn--ghost:hover {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.22);
}
/* Editorial "link", underline that tracks on hover. align-self lets it sit
   left-aligned inside flex/grid parents (e.g. .results__copy); harmless
   elsewhere since the property is ignored on non flex/grid children. */
.link-arrow {
  align-items: center;
  align-self: flex-start;
  border-bottom: 1px solid var(--accent-from);
  color: var(--text);
  display: inline-flex;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  gap: 8px;
  letter-spacing: 0.02em;
  padding-bottom: 2px;
  transition: color 160ms var(--ease), border-color 160ms var(--ease), gap 160ms var(--ease);
}
.link-arrow:hover {
  color: var(--accent-from);
  gap: 12px;
}
.link-arrow span[aria-hidden] {
  font-weight: 400;
}
/* ---------- Nav ---------- */
.nav {
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  align-items: center;
  backdrop-filter: saturate(140%) blur(14px);
  background: var(--bg-nav);
  border-bottom: 1px solid var(--rule);
  display: flex;
  /* Breathing room between the logo and the links on narrow desktop/tablet
     (they touched at 768px — responsive audit, Jul 17). */
  gap: clamp(16px, 2.5vw, 40px);
  height: 72px;
  justify-content: space-between;
  padding-inline: var(--nav-pad-x);
  position: sticky;
  top: 0;
  z-index: 50;
}
.nav__logo {
  align-items: center;
  display: inline-flex;
  height: 42px;
}
.nav__logo img {
  height: 21px;
  width: auto;
}
.nav__right {
  align-items: center;
  display: flex;
  gap: 14px;
}
.nav__links {
  align-items: center;
  display: flex;
  gap: 36px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav__link {
  color: #fff;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 400;
  line-height: 1;
  text-decoration: none;
  transition: color 160ms var(--ease);
}
.nav__link:hover {
  color: var(--accent-from);
}
.nav__divider {
  background: #262626;
  display: inline-block;
  height: 24px;
  width: 1px;
}
/* ---------- Resources dropdown ---------- */
.nav__drop {
  position: relative;
}
.nav__drop-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.nav__drop-caret {
  transition: transform 180ms var(--ease);
}
.nav__drop:hover .nav__drop-caret,
.nav__drop:focus-within .nav__drop-caret {
  transform: rotate(180deg);
}
/* The panel: invisible padding bridges the hover gap under the link. */
.nav__drop-panel {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-58%);
  padding-top: 26px;
  opacity: 0;
  pointer-events: none;
  translate: 0 8px;
  transition: opacity 180ms var(--ease), translate 180ms var(--ease);
  z-index: 60;
}
.nav__drop:hover .nav__drop-panel,
.nav__drop:focus-within .nav__drop-panel {
  opacity: 1;
  pointer-events: auto;
  translate: 0 0;
}
.nav__drop-card {
  position: relative;
  display: flex;
  width: 620px;
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  background: rgba(13, 13, 16, 0.88);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  backdrop-filter: blur(20px) saturate(140%);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55), 0 0 40px rgba(250, 108, 0, 0.05);
  overflow: hidden;
}
/* Gradient hairline along the card top + ambient glow in the far corner */
.nav__drop-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1.5px;
  background: var(--accent-gradient, linear-gradient(90deg, #fa6c00, #eb5173));
  opacity: 0.85;
}
.nav__drop-card::after {
  content: "";
  position: absolute;
  top: -60px;
  right: -40px;
  width: 220px;
  height: 160px;
  background: radial-gradient(closest-side, rgba(250, 108, 0, 0.14), transparent 70%);
  pointer-events: none;
}
.nav__drop-col {
  padding: 26px 28px 24px;
}
.nav__drop-col--main {
  flex: 1 1 58%;
  background: rgba(255, 255, 255, 0.025);
  border-right: 1px solid rgba(255, 255, 255, 0.07);
}
.nav__drop-col--tools {
  flex: 1 1 42%;
}
/* Items cascade in as the panel opens */
.nav__drop-item,
.nav__drop-tool,
.nav__drop-row,
.nav__drop-eyebrow {
  opacity: 0;
  translate: 0 7px;
  transition: opacity 220ms var(--ease), translate 220ms var(--ease),
    color 160ms var(--ease);
}
.nav__drop:hover .nav__drop-item,
.nav__drop:focus-within .nav__drop-item,
.nav__drop:hover .nav__drop-tool,
.nav__drop:focus-within .nav__drop-tool,
.nav__drop:hover .nav__drop-row,
.nav__drop:focus-within .nav__drop-row,
.nav__drop:hover .nav__drop-eyebrow,
.nav__drop:focus-within .nav__drop-eyebrow {
  opacity: 1;
  translate: 0 0;
}
.nav__drop:hover .nav__drop-col > :nth-child(2),
.nav__drop:focus-within .nav__drop-col > :nth-child(2) {
  transition-delay: 40ms;
}
.nav__drop:hover .nav__drop-col > :nth-child(3),
.nav__drop:focus-within .nav__drop-col > :nth-child(3) {
  transition-delay: 80ms;
}
.nav__drop:hover .nav__drop-col > :nth-child(4),
.nav__drop:focus-within .nav__drop-col > :nth-child(4) {
  transition-delay: 120ms;
}
.nav__drop:hover .nav__drop-col > :nth-child(5),
.nav__drop:focus-within .nav__drop-col > :nth-child(5) {
  transition-delay: 160ms;
}
.nav__drop-eyebrow {
  margin: 0 0 18px;
  font-size: 13px;
  font-weight: 500;
  color: #9e9ea3;
}
.nav__drop-icon {
  flex: none;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  background: #17171b;
  color: #c9c9ce;
  transition: color 160ms var(--ease), border-color 160ms var(--ease),
    background 160ms var(--ease), box-shadow 160ms var(--ease);
}
.nav__drop-item {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  margin: 0 -12px;
  padding: 9px 12px;
  border-radius: 11px;
  text-decoration: none;
  transition: background 160ms var(--ease);
}
.nav__drop-item:hover {
  background: rgba(255, 255, 255, 0.045);
}
.nav__drop-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.nav__drop-title {
  font-size: 15px;
  font-weight: 600;
  color: #fff;
  transition: color 160ms var(--ease);
}
.nav__drop-desc {
  font-size: 12.5px;
  line-height: 1.45;
  color: #86868c;
}
.nav__drop-rule {
  display: block;
  height: 1px;
  margin: 14px 0;
  background: rgba(255, 255, 255, 0.09);
}
.nav__drop-row {
  display: flex;
  gap: 26px;
}
.nav__drop-mini,
.nav__drop-tool {
  display: flex;
  align-items: center;
  gap: 11px;
  font-size: 14px;
  font-weight: 500;
  color: #ececef;
  text-decoration: none;
  transition: color 160ms var(--ease);
}
.nav__drop-tool {
  padding: 7px 0;
}
.nav__drop-tool:hover {
  translate: 3px 0;
  transition-delay: 0ms;
}
.nav__drop-item:hover .nav__drop-title,
.nav__drop-mini:hover,
.nav__drop-tool:hover {
  color: var(--accent-from);
}
/* Hovered icon chip fills with the brand gradient */
.nav__drop-item:hover .nav__drop-icon,
.nav__drop-mini:hover .nav__drop-icon,
.nav__drop-tool:hover .nav__drop-icon {
  color: #fff;
  border-color: transparent;
  background: var(--accent-gradient, linear-gradient(135deg, #fa6c00, #eb5173));
  box-shadow: 0 4px 16px rgba(250, 108, 0, 0.35);
}
/* Sign-in pill is the .btn .btn--ghost shape with a tighter height/gap and
   a coloured "+" suffix in the trailing span. */
.nav__signin {
  gap: 6px;
  height: 33px;
  letter-spacing: 0.01em;
  padding: 0 17px;
}
.nav .btn--primary {
  font-size: 13px;
  height: 33px;
  padding: 0 20px;
}
.nav__menu {
  font-size: 13px;
  font-weight: 500;
  gap: 6px;
  height: 40px;
  letter-spacing: 0.08em;
  padding: 0 16px;
  text-transform: uppercase;
}
/* Desktop: hide the mobile MENU+ button. */
@media (min-width: 721px) {
  .nav__menu {
    display: none;
  }
}
/* Mobile: hide inline links, divider, sign-in. MENU+ takes their place. */
@media (max-width: 720px) {
  .nav__links, .nav__divider, .nav__signin {
    display: none;
  }
}
/* ---------- Eyebrows ---------- */
.eyebrow {
  color: var(--text-dim);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.22em;
  margin: 0;
  text-transform: uppercase;
}
.eyebrow--accent {
  color: var(--accent-from);
}
.eyebrow::before {
  background: currentColor;
  content: "";
  display: inline-block;
  height: 1px;
  margin-bottom: 2px;
  margin-right: 12px;
  opacity: 0.6;
  vertical-align: middle;
  width: 22px;
}
/* ========================================================= */
/*                    HERO — editorial spread                 */
/* ========================================================= */
.hero {
  isolation: isolate;
  overflow: visible;
  padding: clamp(28px, 4vw, 56px) 0 0;
  position: relative;
}
.hero::before {
  /* directional scrim replacing aurora — kept inside the section now that
     overflow is visible, so the blur doesn't bleed into the next section. */
  content: "";
  position: absolute;
  inset: 0 0 0 40%;
  background: radial-gradient(60% 60% at 80% 10%, rgba(250, 108, 0, 0.18) 0%, rgba(250, 108, 0, 0) 60%),
    radial-gradient(60% 60% at 90% 90%, rgba(235, 81, 115, 0.14) 0%, rgba(235, 81, 115, 0) 60%);
  filter: blur(40px);
  z-index: -1;
  pointer-events: none;
}
.hero::after {
  /* subtle vignette */
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 80% at 50% 0%, transparent 40%, rgba(0, 0, 0, 0.35) 100%);
  pointer-events: none;
  z-index: -1;
}
.hero__inner {
  position: relative;
  width: 95%;
  max-width: 1920px;
  margin: 0 auto;
  padding-inline: var(--pad-x);
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-template-rows: auto auto auto;
  column-gap: clamp(16px, 2vw, 32px);
  row-gap: 0;
  /* Fill the visible viewport below the sticky nav and the section's top
     padding. The stats + logo strip live below the fold as a sibling. */
  min-height: calc(100svh - 72px - clamp(28px, 4vw, 56px));
  align-content: center;
}
.hero__inner>.eyebrow {
  align-self: start;
  grid-column: 1 / 7;
  grid-row: 1;
  margin-bottom: 12px;
}
/* The logo wordmark is demoted — design system stays but visual weight shifts. */
.hero__title {
  font-family: var(--font-display);
  font-weight: 900;
  grid-column: 1 / 7;
  grid-row: 2;
  letter-spacing: -0.045em;
  line-height: 0.92;
  margin: 6px 0 clamp(4px, 0.6vw, 10px);
  padding-top: 0.04em;
  text-wrap: balance;
}
.hero__title-line {
  display: block;
  letter-spacing: -0.051em;
  line-height: 0.92;
}
.hero__title-line--ghost {
  color: var(--title-ghost);
  font-size: clamp(44px, 5vw, 72px);
}
.hero__title-line--gradient {
  -webkit-background-clip: text;
  background: linear-gradient(90deg, #fa6c00 0%, #eb5173 51.4%, #8912a0 100%);
  background-clip: text;
  color: transparent;
  display: inline-block;
  /* The written doc says +25% but the video review (04:11) only asks for
     "maybe a little bit". 7.3vw is the exact fit limit of the text column
     while it scales; past 1440px the column keeps growing while the old
     105px cap froze the word, so raising the cap alone gives the bump on
     large screens with zero overlap risk. The full +25% would need the hero
     recomposition (new chatbot/editor images from the client). */
  font-size: clamp(64px, 7.3vw, 118px);
  margin: -0.08em -0.04em -0.18em 0;
  padding: 0.08em 0.04em 0.18em 0;
}
.hero__lede {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: clamp(16px, 1.25vw, 19px);
  grid-column: 1 / 7;
  grid-row: 3;
  line-height: 1.55;
  margin: 0 0 clamp(16px, 2vw, 24px);
  max-width: 48ch;
}
/* Wraps the demo-panel + prompt as a single, attached unit on the right
   side of the hero. The panel sits on top, the prompt becomes its input
   bar — they share borders so they read as one tool. */
.hero__panel {
  grid-column: 7 / 13;
  grid-row: 1 / -1;
  align-self: center;
  justify-self: stretch;
  position: relative;
  width: 95%;
  /* max-width: 720px; */
  margin-left: auto;
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-lg);
  /* Same 3D + glare trick as the phone cards down-page. JS drives the
     CSS variables on pointermove. */
  transform: perspective(1100px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  transform-style: preserve-3d;
  transition: transform 220ms var(--ease);
  will-change: transform;
}
.hero__panel::before {
  background: radial-gradient(60% 60% at var(--mx, 50%) var(--my, 50%),
      rgba(255, 255, 255, 0.18),
      rgba(255, 255, 255, 0) 60%);
  border-radius: inherit;
  content: "";
  inset: 0;
  mix-blend-mode: overlay;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  transform: translateZ(8px);
  transition: opacity 280ms var(--ease);
  z-index: 5;
}
.hero__panel[data-tilted="true"]::before {
  opacity: 1;
}
.hero__panel-img {
  border-radius: inherit;
  box-shadow: 0 40px 80px -28px rgba(0, 0, 0, 0.6);
  display: block;
  height: auto;
  transform: translateZ(6px);
  width: 100%;
}
/* Two-layer hero art (client, Aug 25: the AI panel becomes its own "real"
   asset instead of being baked into one composite). The stack reserves the
   retired composite's exact box (1542x1071) and carries .hero__panel-img so
   the cables JS keeps anchoring to the same full-composition rect; the
   percentages below are the composite's measured geometry (editor 1440px
   anchored top-right, panel 537px at x3/y110). */
.hero__panel-img--stack {
  aspect-ratio: 1542 / 1071;
  box-shadow: none;
  position: relative;
}
.hero__panel-back {
  /* The retired composite had its rounding baked in; the flat re-export
     ships square (client, Aug 27) — inherit the stack's radius-lg. */
  border-radius: inherit;
  box-shadow: 0 40px 80px -28px rgba(0, 0, 0, 0.6);
  display: block;
  height: auto;
  position: absolute;
  right: 0;
  top: 0;
  width: 93.385%; /* 1440/1542 */
}
.hero__panel-front {
  display: block;
  /* drop-shadow, not box-shadow: the PNG has rounded corners and its glow
     rim — a box shadow would draw a hard rectangle around them. */
  filter: drop-shadow(0 24px 48px rgba(0, 0, 0, 0.55));
  height: auto;
  left: 0.19%; /* 3/1542 */
  position: absolute;
  top: 10.27%; /* 110/1071 */
  width: 34.82%; /* 537/1542 */
}
/* ============================================================
   Hero cables — node-editor wires from "Build with AI." → demo-panel
   ============================================================ */
.hero-cables {
  height: 100%;
  inset: 0;
  overflow: visible;
  pointer-events: none;
  position: absolute;
  width: 100%;
  z-index: -3;
}
.hero-cables__path {
  fill: none;
  stroke: url(#hero-cable-grad);
  stroke-width: 2;
  stroke-linecap: round;
  filter: url(#hero-cable-glow);
  opacity: 0;
  /* Hold invisible until 1500ms — covers the hero entrance animation
     (title 140ms+900ms, panel 360ms+900ms = ~1260ms) so cables only
     appear once their endpoints have settled at their final positions. */
  animation: hero-cable-fade 700ms var(--ease, cubic-bezier(0.2, 0.8, 0.2, 1)) 1500ms forwards;
}
.hero-cables__path--bot {
  animation-delay: 1620ms;
}
@keyframes hero-cable-fade {
  to {
    opacity: 0.9;
  }
}
.hero-cables__node {
  animation: hero-cable-node-pop 380ms var(--ease, cubic-bezier(0.2, 0.8, 0.2, 1)) 1800ms forwards;
  fill: #0e0e11;
  opacity: 0;
  stroke: #fa6c00;
  stroke-width: 2;
  transform-box: fill-box;
  transform-origin: center;
}
.hero-cables__node--dst {
  animation-delay: 1900ms;
  stroke: #eb5173;
}
@keyframes hero-cable-node-pop {
  from {
    opacity: 0;
    transform: scale(0.4);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero-cables__path, .hero-cables__node {
    animation: none;
    opacity: 1;
    transform: none;
  }
  .hero-cables__path {
    opacity: 0.9;
  }
}
/* Hide cables on stacked layouts — title sits above panel, the spatial
   metaphor breaks down. */
@media (max-width: 960px) {
  .hero-cables {
    display: none;
  }
}
/* ============================================================
   Services cables — same node-editor language as the hero, this
   time wiring the section title to the showreel video. JS draws
   the bezier paths from element rects; opacity is set on each
   frame by the balance-driver IIFE so the cables fade in lockstep
   with the reel's --reel-show dissolve.
   ============================================================ */
.services-cables {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* Below the reel (z:3) and above the balance lines (z:1). */
  z-index: 2;
  overflow: visible;
  /* Opacity set by JS each frame; resting value used until first paint. */
  opacity: 0.9;
}
.services-cables__path {
  fill: none;
  filter: url(#services-cable-glow);
  stroke: url(#services-cable-grad);
  stroke-linecap: round;
  stroke-width: 2;
}
.services-cables__node {
  fill: #0e0e11;
  stroke: #eb5173;
  stroke-width: 2;
}
@media (max-width: 960px) {
  .services-cables {
    display: none;
  }
}
/* Phone card screens: real ad creative (image or playable iframe) fills the
   frame; the gradient CTA pill below is a placeholder shown only when no
   media is injected. */
.phone-card__media {
  border-radius: 18px;
  display: block;
  height: 100%;
  inset: 0;
  object-fit: cover;
  position: absolute;
  width: 100%;
  z-index: 1;
}
iframe.phone-card__media {
  background: #000;
  border: 0;
  pointer-events: none;
}
.phone-card.is-focused iframe.phone-card__media {
  pointer-events: auto;
}
.phone-card__screen:has(.phone-card__media) .phone-card__cta {
  display: none;
}
/* Loader overlay shown over the phone screen until the iframe fires `load`.
   Spinner is a conic-gradient ring (orange → pink → purple) with a radial
   mask to carve the donut and a rotation animation. Pure CSS, no SVG. */
.phone-card__loader {
  background: #1c1c20;
  border-radius: 18px;
  display: grid;
  inset: 0;
  place-items: center;
  pointer-events: none;
  position: absolute;
  transition: opacity 280ms var(--ease);
  z-index: 2;
}
.phone-card__loader[data-state="done"] {
  opacity: 0;
}
.phone-card__spinner {
  -webkit-mask: radial-gradient(circle, transparent 56%, #000 59%);
  animation: phone-card-spin 1s linear infinite;
  aspect-ratio: 1 / 1;
  background: conic-gradient(
    from 0deg,
    rgb(250, 108, 0) 0%,
    rgb(235, 81, 115) 36%,
    rgb(137, 18, 160) 72%,
    transparent 74%,
    transparent 100%
  );
  border-radius: 50%;
  display: block;
  mask: radial-gradient(circle, transparent 56%, #000 59%);
  width: 38%;
}
@keyframes phone-card-spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .phone-card__spinner {
    animation: none;
  }
}
/* Stats + logo marquee — sits below the fold as a continuation of the
   hero. Runs edge-to-edge: the stats and logos band must hit the
   viewport sides with no horizontal inset, matching the maquette. */
.hero__meta {
  display: flex;
  flex-direction: column;
  gap: clamp(28px, 4vw, 48px);
  /* Top padding halved (was 40-80px): with the marquee's own padding the gap
     above the carousel measured ~2x the one below it (client feedback,
     Jul 10). */
  padding-block: clamp(24px, 3vw, 40px) clamp(16px, 2vw, 28px);
  padding-inline: 0;
  width: 100%;
}
.hero__stats {
  align-content: space-around;
  border-top: 1px solid var(--rule);
  color: var(--text-dim);
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  font-family: var(--font-sans);
  font-size: 1.1rem;
  font-variant-numeric: tabular-nums;
  gap: clamp(20px, 3vw, 48px);
  justify-content: space-evenly;
  letter-spacing: 0.1em;
  list-style: none;
  margin: 0;
  padding: 2.5rem 0 1rem;
  text-transform: uppercase;
}
.hero__stats span {
  display: block;
  text-align: center;
}
.hero__stats span b {
  color: var(--accent-from);
  font-size: 2rem;
  font-weight: 600;
}
/* Page-load choreography */
@media (prefers-reduced-motion: no-preference) {
  .hero__inner>* {
    animation: hero-in 900ms var(--ease-out-quart) forwards;
    opacity: 0;
    transform: translateY(24px);
  }
  .hero__inner>.eyebrow {
    animation-delay: 0ms;
  }
  .hero__title {
    animation-delay: 140ms;
  }
  .hero__lede {
    animation-delay: 280ms;
  }
  .hero__panel {
    animation-delay: 360ms;
    margin: 1rem -1.5rem;
    max-width: 100%;
  }
  .hero__meta {
    animation-delay: 520ms;
  }
  @keyframes hero-in {
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }
}
@media (max-width: 960px) {
  .hero__inner {
    align-content: start;
    grid-template-columns: 1fr;
  }
  .hero__inner>.eyebrow, .hero__title, .hero__lede, .hero__panel, .hero__meta {
    grid-column: 1 / -1;
    grid-row: auto;
  }
  .hero__title {
    margin-top: 20px;
  }
  .hero__panel {
    animation-delay: 360ms;
    margin: 1rem -1.5rem;
    max-width: 100%;
  }
  .hero__meta {
    margin-top: 12px;
  }
}
/* ---------- Our Partners section wrapper ---------- */
.partners {
  border-top: 1px solid var(--rule);
  padding-block: 0 clamp(40px, 4vw, 64px);
  position: relative;
}
/* ---------- Brand marquee (two rows, opposite directions) ---------- */
.brand-marquee {
  display: flex;
  flex-direction: column;
  gap: clamp(8px, 1.4vw, 24px);
  /* Tightened (was 1.5rem + 20-40px): the gap above the carousel read twice
     as tall as the one below it (client feedback, Jul 10 — measured 96px
     above vs ~48px below at 1600w). */
  margin-top: 0;
  padding-block: clamp(8px, 1vw, 16px) 0;
  position: relative;
  width: 100%;
}
.brand-marquee__caption {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: clamp(15px, 1.32vw, 19px);
  line-height: 1.55;
  margin: 0 0 clamp(12px, 1.5vw, 20px);
  text-align: center;
}
/* "Trusted By" heading ABOVE the caption (copy finale Jul 30, order flipped
   + spacing tightened per Charles). Optional: only when the field is filled. */
.brand-marquee__title {
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(28px, 3.2vw, 44px);
  font-weight: 900;
  letter-spacing: -0.01em;
  line-height: 1.05;
  margin: 0 0 clamp(6px, 0.8vw, 10px);
  text-align: center;
}
.brand-marquee__viewport {
  /* Client feedback (Jul 10): no edge-to-edge marquee — capped to the safe
     zone with a harder fade toward the edges. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 18%, #000 82%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 18%, #000 82%, transparent);
  margin-inline: auto;
  max-width: var(--max-w-tight, 1280px);
  overflow: hidden;
  position: relative;
  width: 100%;
}
.brand-marquee__track {
  display: flex;
  width: max-content;
  will-change: transform;
}
/* Duration rides a var set per band from the logo count (see
   cmp_marquee_duration) so the apparent px/s speed never changes when
   logos are added — 60s stays the fallback for legacy includes. */
.brand-marquee__track--ltr {
  animation: brand-marquee-ltr var(--marquee-dur, 60s) linear infinite;
}
.brand-marquee__track--rtl {
  animation: brand-marquee-rtl var(--marquee-dur, 60s) linear infinite;
}
.brand-marquee__row {
  align-items: center;
  display: flex;
  gap: clamp(48px, 6vw, 84px);
  height: clamp(72px, 9vw, 128px);
  list-style: none;
  margin: 0;
  padding: 0;
  padding-inline: clamp(24px, 3vw, 42px);
}
.brand-marquee__row img {
  display: block;
  max-height: clamp(34px, 3.6vw, 54px);
  object-fit: contain;
  opacity: 0.78;
  transition: opacity 200ms var(--ease);
  width: auto;
}
.brand-marquee__row img:hover {
  opacity: 1;
}
.brand-marquee__sr {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  list-style: none;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}
@keyframes brand-marquee-ltr {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}
@keyframes brand-marquee-rtl {
  from {
    transform: translateX(-50%);
  }
  to {
    transform: translateX(0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .brand-marquee__track--ltr, .brand-marquee__track--rtl {
    animation: none;
    transform: translateX(-25%);
  }
}
.demo-panel {
  aspect-ratio: 16 / 11;
  grid-column: 7 / 13;
  overflow: hidden;
  position: relative;
  width: 100%;
}
.demo-panel::before {
  background: repeating-linear-gradient(0deg, transparent 0, transparent 40px, rgba(255, 255, 255, 0.02) 40px, rgba(255, 255, 255, 0.02) 41px),
    repeating-linear-gradient(90deg, transparent 0, transparent 40px, rgba(255, 255, 255, 0.02) 40px, rgba(255, 255, 255, 0.02) 41px);
  content: "";
  inset: 0;
  pointer-events: none;
  position: absolute;
}
.demo-panel__bar {
  align-items: center;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  display: flex;
  gap: 6px;
  height: 36px;
  padding-inline: 14px;
  position: relative;
  z-index: 1;
}
.demo-panel__dot {
  background: rgba(255, 255, 255, 0.14);
  border-radius: 50%;
  height: 7px;
  width: 7px;
}
@media (max-width: 960px) {
  .demo-panel {
    aspect-ratio: 4/3;
    grid-column: 1 / -1;
  }
}
/* ========================================================= */
/*   GALLERY V2 — sticky landing + showcase wall rises on scroll */
/* ========================================================= */
.gallery-v2 {
  position: relative;
  /* Tall enough that the pinned wall stays visible across a long scroll
     range; phones rise in the first portion, then drift via parallax. */
  height: 340vh;
  background: var(--bg);
  border-top: 1px solid var(--rule);
}
.gallery-v2__sticky {
  align-items: center;
  display: flex;
  height: 100vh;
  justify-content: center;
  overflow: hidden;
  position: sticky;
  top: 0;
}
/* Black fade at the bottom — softens the last row of phones into the BG. */
.gallery-v2__sticky::after {
  background: linear-gradient(to bottom, rgba(9, 9, 11, 0) 0%, var(--bg) 75%);
  content: "";
  height: 28vh;
  inset: auto 0 0 0;
  pointer-events: none;
  position: absolute;
  z-index: 3;
}
/* Soft warm glow centred behind everything — keeps the lighting from the
   previous gallery scrim. */
.gallery-v2__glow {
  background: radial-gradient(50% 45% at 50% 55%, rgba(250, 108, 0, 0.22) 0%, rgba(250, 108, 0, 0) 60%),
    radial-gradient(40% 35% at 65% 45%, rgba(235, 81, 115, 0.16) 0%, rgba(235, 81, 115, 0) 65%);
  filter: blur(40px);
  inset: -10% -10%;
  pointer-events: none;
  position: absolute;
  z-index: 0;
}
.gallery-v2__header {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  padding-inline: var(--pad-x);
  text-align: center;
  transform: translate3d(0, -50%, 0);
  pointer-events: none;
  /* Sits behind the phones so they rise in front of it. */
  z-index: 1;
  /* As the phones finish rising the title gets blurred + faded so it
     stops competing with the wall. JS writes --phones-progress (0..1). */
  filter: blur(calc(var(--phones-progress, 0) * 14px));
  opacity: calc(1 - var(--phones-progress, 0) * 0.78);
  transition: opacity 240ms var(--ease), filter 240ms var(--ease);
  will-change: filter, opacity;
}
.gallery-v2__title {
  color: var(--text);
  font-family: var(--font-display);
  font-size: 84px;
  font-weight: 900;
  letter-spacing: 0em;
  line-height: 1;
  margin: 0 0 14px;
}
.gallery-v2__lede {
  color: var(--text-muted);
  font-size: 16px;
  margin: 0;
}
.gallery-v2__stage {
  position: absolute;
  inset: 0;
  /* Ultrawide: the wall stays around 1080p-wide, centred (client feedback,
     Jul 10), instead of stretching edge to edge. */
  margin-inline: auto;
  max-width: 1160px;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(2, 1fr);
  /* Column-major flow so adjacent cards form vertical columns; even columns
     get pushed down via --col-offset for the décalé pattern. */
  grid-auto-flow: column;
  gap: 12px;
  align-items: center;
  justify-items: center;
  /* The 1fr tracks can't shrink below the fixed card width (auto minimum),
     so past ~1494px wide the six tracks overflow the 1160px stage — and a
     grid overflows on the inline-END side only, shoving the wall right
     (client, Aug 24: "off centered at certain screen sizes"). Unsafe
     centering splits that overflow evenly on both sides, so the wall stays
     centred at every width. The 720px carousel mode below restores
     flex-start: centring a scroll container would clip the list's start. */
  justify-content: center;
  /* Vertical padding stops the top row of phones being clipped by the
     viewport edge once the décalé offset and natural card height combine. */
  padding: clamp(48px, 7vh, 90px) 12px;
  /* Above the title so phones occlude it as they rise into view. */
  z-index: 2;
}
.gallery-v2__stage .phone-card {
  width: clamp(140px, 12vw, 220px);
  /* Cap the height to roughly the available row height so on shorter
     viewports the cards still fit without overflowing the sticky stage. */
  max-height: 44vh;
  height: auto;
  aspect-ratio: 9 / 18;
  /* Compose: scroll-driven rise + column décalé + per-column parallax drift,
     then tilt rotation. perspective is inside the transform list so rotateX/Y
     after it gain the 3D depth. Tilt vars (--tilt-x/--tilt-y) are written by
     the tilt JS; --rise/--parallax are written by the rise driver. */
  transform: translate3d(0, calc(var(--rise, 110vh) + var(--col-offset, 0px) + var(--parallax, 0px)), 0) perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  transition: transform 120ms linear, opacity 320ms var(--ease), filter 320ms var(--ease);
  will-change: transform;
}
/* Play cursor on the playable tiles only (reversal Jul 28 of the Jul 10
   section-wide cursor). data-playable is stamped by playables.js, so decor
   cards keep the arrow. Hotspot 21 34 = centre of the play disc. */
.gallery-v2__stage .phone-card[data-playable="true"] {
  cursor: url("assets/click-to-play.svg") 21 34, pointer;
}
/* Décalé: every even column (cards 3–4, 7–8, 11–12) shifted down. */
.gallery-v2__stage .phone-card:nth-child(n
  /**/
  +3):nth-child(-n
  /**/
  +4), .gallery-v2__stage .phone-card:nth-child(n
  /**/
  +7):nth-child(-n
  /**/
  +8), .gallery-v2__stage .phone-card:nth-child(n
  /**/
  +11):nth-child(-n
  /**/
  +12) {
  --col-offset: 80px;
}
/* Dim scrim that fades in when a phone is focused — pulls everything back
   so the active playable reads as the only thing on the stage. The scrim
   lives INSIDE the stage so it shares the stage's stacking context. That
   way the focused phone (z-index 10) can sit above the scrim (z-index 1)
   while non-focused siblings (z-index auto) sit below it. */
.gallery-v2__stage::before {
  background: rgba(9, 9, 11, 0.62);
  content: "";
  inset: 0;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  transition: opacity 360ms var(--ease);
  z-index: 1;
}
.gallery-v2__stage[data-focused]::before {
  opacity: 1;
}
/* When a phone is focused, lift the stage above .gallery-v2__sticky::after
   (the bottom-fade gradient at z-index 3) so it stops dimming the centred
   card. Children stack inside the stage as before — focused phone z-index
   1000 stays above the scrim z-index 1. */
.gallery-v2__stage[data-focused] {
  z-index: 5;
}
.gallery-v2__stage[data-focused] .phone-card:not(.is-focused) {
  filter: brightness(0.18) saturate(0.5);
  pointer-events: none;
}
.gallery-v2__stage .phone-card.is-focused {
  /* "normal" is not a valid cursor value — the play cursor never went away
     on the opened card. A plain arrow while the playable is running. */
  cursor: default !important;
  transform: translate3d(0, calc(var(--rise, 0px) + var(--col-offset, 0px) + var(--parallax, 0px)), 0) translate(var(--focus-tx, 0px), var(--focus-ty, 0px)) scale(var(--focus-scale, 1.5));
  z-index: 10;
}
.is-focused {
  opacity: 1 !important;
  transition: opacity 320ms var(--ease) !important;
  z-index: 1000 !important;
}
.gallery-v2__stage .phone-card.is-focused, .gallery-v2__stage .phone-card.is-releasing {
  transition: transform 480ms cubic-bezier(0.22, 0.61, 0.36, 1),
    opacity 320ms var(--ease),
    filter 320ms var(--ease);
}
/* Close-animation layering (client bug report, Jul 10, seen on Uber Eats):
   data-focused leaves the stage the moment the card is released, so both the
   stage (z-index 5) and the card (z-index 10/1000) dropped back to auto and
   the exit animation played BEHIND the other tiles and the bottom fade.
   Keep both lifted for the 480ms release travel. */
.gallery-v2__stage:has(.phone-card.is-releasing) {
  z-index: 5;
}
.gallery-v2__stage .phone-card.is-releasing {
  z-index: 1000;
}
@media (max-width: 900px) {
  .gallery-v2 {
    height: 180vh;
  }
  .gallery-v2__title {
    font-size: 48px;
  }
  .gallery-v2__stage {
    gap: 8px;
    grid-template-columns: repeat(6, 1fr);
    grid-template-rows: repeat(2, 1fr);
    padding: 8px;
  }
  .gallery-v2__stage .phone-card {
    width: clamp(80px, 16vw, 140px);
  }
  .gallery-v2__stage .phone-card:nth-child(n
    /**/
    +3):nth-child(-n
    /**/
    +4), .gallery-v2__stage .phone-card:nth-child(n
    /**/
    +7):nth-child(-n
    /**/
    +8), .gallery-v2__stage .phone-card:nth-child(n
    /**/
    +11):nth-child(-n
    /**/
    +12) {
    --col-offset: 60px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .gallery-v2__stage .phone-card {
    transform: translate3d(0, var(--col-offset, 0px), 0);
  }
}
/* Scroll-position indicator under the mobile carousel — hidden on desktop
   where the gallery is a pinned wall instead of a swipe list. */
.gallery-v2__scrollbar {
  display: none;
}
/* Under tablet: switch the rising wall to a single-card horizontal scroll-snap
   carousel. Native swipe drives navigation; the gradient scrollbar below
   shows the user's position in the list. */
@media (max-width: 720px) {
  .gallery-v2 {
    border-top: 1px solid var(--rule);
    height: auto;
    min-height: 0;
  }
  .gallery-v2__sticky {
    align-items: stretch;
    flex-direction: column;
    gap: 20px;
    height: auto;
    justify-content: flex-start;
    overflow: visible;
    padding-block: 32px 48px;
    position: relative;
    top: auto;
  }
  .gallery-v2__sticky::after {
    display: none;
  }
  .gallery-v2__glow {
    inset: 0;
    pointer-events: none;
    position: absolute;
  }
  .gallery-v2__header {
    filter: none;
    inset: auto;
    left: 0;
    opacity: 1;
    padding-inline: var(--pad-x);
    position: relative;
    right: 0;
    text-align: center;
    top: auto;
    transform: none;
    z-index: 2;
  }
  .gallery-v2__title {
    font-size: clamp(32px, 9vw, 44px);
  }
  .gallery-v2__stage {
    -webkit-overflow-scrolling: touch;
    display: flex;
    flex-direction: row;
    gap: 0;
    grid-auto-flow: row;
    grid-template-columns: none;
    grid-template-rows: none;
    height: auto;
    inset: auto;
    /* Native horizontal swipe drives the carousel. Cards underneath are
       inert (pointer-events:none) so iframe content can't capture the
       gesture — the touch falls through to the stage, which scrolls. */
    /* Not center (set on the desktop grid above): on a scroller it would
       make the list's start unreachable. The padding-inline trick below
       does the centring here. */
    justify-content: flex-start;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    padding-block: 12px 16px;
    padding-inline: calc(50% - min(72vw, 280px) / 2);
    position: relative;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    touch-action: pan-x;
    width: 100%;
    z-index: 2;
  }
  .gallery-v2__stage::-webkit-scrollbar {
    display: none;
  }
  .gallery-v2__stage .phone-card, .gallery-v2__stage .phone-card:nth-child(n
    /**/
    +3):nth-child(-n
    /**/
    +4), .gallery-v2__stage .phone-card:nth-child(n
    /**/
    +7):nth-child(-n
    /**/
    +8), .gallery-v2__stage .phone-card:nth-child(n
    /**/
    +11):nth-child(-n
    /**/
    +12) {
    --col-offset: 0;
    aspect-ratio: 9 / 16;
    flex: 0 0 min(72vw, 280px);
    height: auto;
    max-height: none;
    scroll-snap-align: center;
    scroll-snap-stop: always;
    transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
    transition: transform 200ms var(--ease);
    width: min(72vw, 280px);
  }
  /* Focus mode (tap-to-zoom) is disabled on mobile — the carousel already
     shows one card at full readable size and the JS click handler bails out
     on this breakpoint. Clear the dim scrim so visited carts feel normal. */
  .gallery-v2__stage::before {
    display: none;
  }
  /* Drop the second sentence of the lede on small screens — keeps the
     intro to one tidy line above the carousel. */
  .gallery-v2__lede-extra {
    display: none;
  }
  /* Cards stay visible but inert — taps fall through to the stage so the
     iframe content (links, playables) can't capture the swipe gesture, but
     native scroll on the stage still drives the carousel. */
  .gallery-v2__stage .phone-card {
    cursor: default;
    pointer-events: none;
  }
  /* Trim the wall to a half-dozen examples on mobile: lighter network
     load, less scrolling distance, plays nicer with the snap carousel. */
  .gallery-v2__stage .phone-card:nth-child(n+7) {
    display: none;
  }
  /* The scrollbar is the lone interactive surface. Tap or drag it to scrub
     the carousel; the thumb mirrors the stage's scrollLeft. */
  .gallery-v2__scrollbar {
    background: linear-gradient(to right, var(--accent-from), var(--accent-to));
    border-radius: 999px;
    cursor: pointer;
    display: block;
    height: 6px;
    margin: 20px auto 8px;
    position: relative;
    touch-action: none;
    width: min(72vw, 280px);
  }
  /* Invisible padding zone — gives the thin bar a finger-friendly hit area
     without bulking up its visual weight. */
  .gallery-v2__scrollbar::before {
    content: '';
    inset: -22px -12px;
    position: absolute;
  }
  .gallery-v2__scrollbar-thumb {
    background: #ffffff;
    border-radius: 999px;
    cursor: grab;
    height: 20px;
    left: 0;
    pointer-events: none;
    position: absolute;
    top: 50%;
    transform: translate(0, -50%);
    width: 20px;
  }
  .gallery-v2__scrollbar[data-dragging="1"] {
    cursor: grabbing;
  }
}
/* ---------- Phone card ---------- */
.phone-card {
  flex: none;
  position: relative;
  width: 220px;
  height: 440px;
  background: #1c1c20;
  /* Thinner bezel (client, Aug 28: the phone frame overlaid the creatives
     and cut logos off) — border 1.5px -> 1px, padding 9.5px -> 4px. */
  border: 1px solid #37373c;
  border-radius: 26px;
  padding: 4px;
  box-sizing: border-box;
  /* 3D tilt on hover/move (JS drives --tilt-x / --tilt-y / --mx / --my) */
  transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  transform-style: preserve-3d;
  transition: border-color 200ms var(--ease);
  will-change: transform;
}
.phone-card:hover {
  border-color: rgba(250, 108, 0, 0.3);
}
/* The screen is pushed forward so the bezel reads as depth on tilt. */
.phone-card__screen {
  transform: translateZ(6px);
}
/* Glass glare — radial highlight that tracks the cursor. */
.phone-card::before {
  background: radial-gradient(60% 60% at var(--mx, 50%) var(--my, 50%),
      rgba(255, 255, 255, 0.18),
      rgba(255, 255, 255, 0) 60%);
  border-radius: inherit;
  content: "";
  inset: 0;
  mix-blend-mode: overlay;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  transform: translateZ(8px);
  transition: opacity 280ms var(--ease);
  z-index: 5;
}
.phone-card[data-tilted="true"]::before {
  opacity: 1;
}
.phone-card__screen {
  background: #090909;
  /* Concentric with the 26px card radius minus the 4px bezel. */
  border-radius: 22px;
  height: 100%;
  overflow: hidden;
  position: relative;
  width: 100%;
}
/* Static first frame of the playable, shown the instant the wall renders.
   The runtime iframe (.phone-card__media, injected by JS AFTER this img in
   DOM order) paints on top of it once loaded, so the poster covers the boot
   time — and the whole card when the mobile iframe cap skips it. */
.phone-card__poster {
  height: 100%;
  inset: 0;
  object-fit: cover;
  position: absolute;
  width: 100%;
}
/* The synthetic dynamic-island notch is gone (client, Aug 28: it overlaid
   the creatives and cut logos off) — the span is no longer rendered. */
.phone-card__cta {
  background: var(--accent-gradient);
  border-radius: 22px;
  bottom: 18px;
  height: 22px;
  left: 50%;
  position: absolute;
  transform: translateX(-50%);
  width: 99px;
  z-index: 2;
}
/* The fake CTA pill dressed the EMPTY phone shells of the original design.
   Over the real playable screenshots (which carry their own buttons) it
   reads as a stray orange block, so it only survives on poster-less cards. */
.phone-card__screen:has(.phone-card__poster) .phone-card__cta {
  display: none;
}
/* ========================================================= */
/*   UNMATCHED CREATIVE INTELLIGENCE                          */
/*   Bento grid: two columns, asymmetric heights.             */
/* ========================================================= */
.creative-intel {
  border-top: 1px solid var(--rule);
  overflow: hidden;
  padding-block: var(--pad-section);
  position: relative;
}
.creative-intel__header {
  margin: 0 auto clamp(48px, 6vw, 88px);
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
  position: relative;
  z-index: 1;
}
.creative-intel__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 5.4vw, 80px);
  letter-spacing: 0em;
  line-height: 1.1;
  margin: 0 0 22px;
  padding-top: 0.04em;
  text-wrap: balance;
}
.creative-intel__title-word {
  display: inline;
}
.creative-intel__title-word--ghost {
  color: var(--title-fill);
  font-weight: 900;
}
.creative-intel__title-word--gradient {
  -webkit-background-clip: text;
  background: linear-gradient(90deg, #fa6c00 0%, #eb5173 50.5%, #94199a 100%);
  background-clip: text;
  color: transparent;
  font-weight: 900;
}
.creative-intel__lede {
  color: var(--text-muted);
  font-size: 16px;
  line-height: 1.6;
  margin: 0;
  max-width: 25rem;
}
.creative-intel__bento {
  display: grid;
  gap: clamp(16px, 1.6vw, 24px);
  grid-template-columns: 1fr 1fr;
  margin: 0 auto;
  max-width: var(--max-w);
  position: relative;
  width: calc(100% - calc(var(--pad-x) * 2));
  z-index: 1;
}
.creative-intel__col {
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 1.6vw, 24px);
  height: 100%;
  min-width: 0;
}
.creative-intel__card {
  border: 1px solid var(--border-strong);
  border-radius: 18px;
  display: flex;
  flex-direction: column;
  isolation: isolate;
  overflow: hidden;
  padding: clamp(24px, 2.4vw, 40px);
  position: relative;
  transition: border-color 240ms var(--ease), transform 240ms var(--ease);
}
.creative-intel__card-image {
  height: auto;
  margin: 0 auto;
  margin-top: 1rem;
  object-fit: fill;
  transform: perspective(1100px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  transform-style: preserve-3d;
  transition: transform 200ms var(--ease);
  width: 100%;
  will-change: transform;
}
.creative-intel__card:hover {
  border-color: rgba(255, 255, 255, 0.18);
}
.creative-intel__card--builder {
  min-height: clamp(340px, 30vw, 480px);
}
.creative-intel__card--strategy {
  min-height: clamp(210px, 18vw, 260px);
}
.creative-intel__card--missing {
  flex: 1 1 auto;
  min-height: clamp(210px, 18vw, 260px);
}
.creative-intel__card--analytics {
  flex: 1 1 auto;
  min-height: clamp(290px, 22vw, 340px);
}
.creative-intel__card-head {
  position: relative;
  z-index: 2;
}
.creative-intel__card-title {
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(22px, 1.9vw, 30px);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
  margin: 0 0 12px;
}
.creative-intel__card-title--soft {
  color: rgba(255, 255, 255, 0.78);
}
.creative-intel__card-text {
  color: var(--text-muted);
  font-size: 13px;
  line-height: 1.6;
  margin: 0;
  max-width: 42ch;
}
.creative-intel__card-media {
  height: 60%;
  inset: auto 0 0 0;
  pointer-events: none;
  position: absolute;
  z-index: 1;
}
.creative-intel__reel {
  border: 1px solid var(--border-strong);
  border-radius: 18px;
  grid-column: 1 / -1;
  isolation: isolate;
  overflow: hidden;
  transition: border-color 240ms var(--ease);
}
.creative-intel__reel:hover {
  border-color: rgba(255, 255, 255, 0.18);
}
.creative-intel__reel video {
  display: block;
  height: auto;
  width: 100%;
}
@media (max-width: 720px) {
  .creative-intel__bento {
    grid-template-columns: 1fr;
  }
  /* Single column (client, Aug 28): dissolve the two desktop columns so the
     cards interleave image / text / image / text, and drop the reserved
     bento heights — the two text-only cards were carrying a slab of empty
     bottom padding. */
  .creative-intel__col {
    display: contents;
  }
  .creative-intel__card--builder, .creative-intel__card--missing, .creative-intel__card--strategy, .creative-intel__card--analytics {
    min-height: 0;
  }
  .creative-intel__card--builder {
    order: 1;
  }
  .creative-intel__card--missing {
    order: 2;
  }
  .creative-intel__card--analytics {
    order: 3;
  }
  .creative-intel__card--strategy {
    order: 4;
  }
  .creative-intel__reel {
    order: 5;
  }
}
/* ========================================================= */
/*   RIVE WAY INFOGRAPHIC — Rive artboard fitted to the       */
/*   section's content box, scales with --max-w on resize.    */
/* ========================================================= */
.rive-way {
  background: var(--bg);
  border-top: 1px solid var(--rule);
  padding-block: var(--pad-section);
  position: relative;
}
.rive-way__inner {
  margin: 0 auto;
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
}
.rive-way__head {
  margin: 0 0 clamp(36px, 4vw, 56px);
  text-align: left;
}
.rive-way__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 5.4vw, 80px);
  letter-spacing: 0em;
  line-height: 1.02;
  margin: 0;
  padding-top: 0.04em;
  text-wrap: balance;
}
.rive-way__title-word {
  color: var(--title-fill);
  display: block;
  font-family: var(--font-display);
  font-weight: 900;
  letter-spacing: 0em;
}
.rive-way__title-word--accent {
  -webkit-background-clip: text;
  background: var(--accent-gradient);
  background-clip: text;
  color: transparent;
  font-weight: 900;
  width: fit-content;
}
/* Stage owns the aspect ratio AND the cap. The canvas inside fills the
   stage 100%/100% so nothing fights for layout. Cap at --max-w-tight so
   wide viewports stop pushing the artboard past a comfortable size. */
.rive-way__stage {
  aspect-ratio: 975 / 923;
  margin: 0 auto;
  max-width: var(--max-w-tight);
  width: 100%;
}
/* Generic fill rule for any Rive canvas wired through the bootstrap. The
   parent (a `.*__stage` aspect-ratio wrapper) drives the size; the canvas
   stretches to fill it. Covers the original canvas AND the one rive.js
   recreates after auto-detecting the state machine, since both carry
   `data-rive-src`. */
canvas[data-rive-src] {
  display: block;
  height: 100%;
  width: 100%;
}
/* ========================================================= */
/*   PLATFORM BUILT-FOR  four-card row.                       */
/*   Each card frames a problem the visitor recognises, with  */
/*   an orange icon, a two-line heading, supporting copy and  */
/*   a ghost CTA. A thin accent gradient bar at the bottom    */
/*   of each card peeks out to the right as a visual signature. */
/* ========================================================= */
.built-for {
  background: var(--bg);
  border-top: 1px solid var(--rule);
  padding-block: var(--pad-section);
  position: relative;
}
.built-for__inner {
  margin: 0 auto;
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
}
.built-for__grid {
  display: grid;
  gap: clamp(16px, 1.6vw, 28px);
  grid-template-columns: repeat(4, minmax(0, 1fr));
  list-style: none;
  margin: 0;
  margin-top: clamp(36px, 4vw, 56px);
  padding: 0;
}
/* Section subtitle: paragraph under the headline, not a second headline
   (client feedback, Jul 10). */
.built-for__sub {
  color: var(--text-muted);
  font-size: clamp(16px, 1.25vw, 19px);
  line-height: 1.55;
  margin: 14px 0 0;
  max-width: 60ch;
}
.built-for__card {
  /* Standard card surface (client feedback: one card style everywhere). */
  background: var(--card-grad);
  border: 1px solid var(--card-line);
  border-radius: var(--card-r);
  box-shadow: var(--card-edge);
  display: flex;
  flex-direction: column;
  gap: clamp(14px, 1.4vw, 24px);
  isolation: isolate;
  padding: clamp(20px, 1.8vw, 32px);
  position: relative;
  transition: transform 320ms var(--ease), border-color 320ms var(--ease), box-shadow 320ms var(--ease);
}
.built-for__card-btn {
  color: var(--fg);
  font-size: 13px;
  font-weight: 500;
  margin-top: auto;
  text-decoration: none;
  white-space: nowrap;
}
.built-for__card-btn .cs-arrow {
  color: var(--accent);
  display: inline-block;
  transition: transform 200ms var(--ease);
}
.built-for__card-btn:hover .cs-arrow {
  transform: translateX(4px);
}
.built-for__card:hover {
  border-color: var(--card-line-hover);
  box-shadow: var(--card-lift);
  transform: translateY(-2px);
}
.built-for__card:hover::before {
  border-color: var(--border-strong);
}
/* Opaque surface of the card, rendered as a pseudo-element so the gradient
   layer below it stays hidden in the centre and only shows where it's offset
   past the card edges (bottom + right). */
.built-for__card::before {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: inherit;
  content: "";
  inset: 0;
  position: absolute;
  transition: border-color 320ms var(--ease);
  z-index: -1;
}
/* "Gradient box-shadow": one layer behind the card, same shape and radius,
   offset down-right. The opaque ::before hides the centre, so only the bottom
   + right edges of this layer show, forming an L with a naturally rounded
   corner. The diagonal gradient (pink bottom-left → orange top-right) reads as
   one continuous run around the corner. A vertical mask fades the top of the
   right bar to transparent so the L tapers off near the top edge. */
.built-for__card::after {
  -webkit-mask: linear-gradient(180deg, transparent 0%, transparent 10%, #000 55%, #000 100%);
  background: linear-gradient(45deg, var(--accent-to) 0%, var(--accent-from) 100%);
  border-radius: inherit;
  content: "";
  inset: 0;
  mask: linear-gradient(180deg, transparent 0%, transparent 10%, #000 55%, #000 100%);
  pointer-events: none;
  position: absolute;
  transform: translate(11px, 11px);
  z-index: -2;
}
.built-for__card-icon {
  align-items: center;
  display: inline-flex;
  height: clamp(30px, 2.4vw, 40px);
  justify-content: center;
  width: clamp(30px, 2.4vw, 40px);
}
.built-for__card-icon svg, .built-for__card-icon img {
  height: 100%;
  object-fit: contain;
  width: 100%;
}
.built-for__card-heading {
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(18px, 1.6vw, 24px);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.15;
  margin: 0;
  max-width: 12ch;
  text-wrap: balance;
}
.built-for__card-heading::after {
  background: rgba(255, 255, 255, 0.18);
  content: "";
  display: block;
  height: 1px;
  margin-top: clamp(10px, 0.9vw, 16px);
  width: clamp(36px, 3.6vw, 56px);
}
.built-for__card-lede {
  color: var(--text-dim);
  flex: 1;
  font-size: clamp(13px, 0.9vw, 14px);
  line-height: 1.45;
  margin: 0;
}
.built-for__card-cta {
  align-self: stretch;
  font-size: clamp(12px, 0.85vw, 14px);
  font-weight: 600;
  gap: 0.6em;
  height: auto;
  justify-content: center;
  margin-top: clamp(4px, 0.5vw, 10px);
  padding: clamp(9px, 0.8vw, 13px) clamp(14px, 1.2vw, 20px);
}
.built-for__card-arrow {
  display: inline-block;
  transition: transform 240ms var(--ease);
}
.built-for__card-cta:hover .built-for__card-arrow {
  transform: translateX(3px);
}
@media (max-width: 980px) {
  .built-for__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 560px) {
  .built-for__grid {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* ========================================================= */
/*   CRAFTSMAN+ wheel  comparison story.                      */
/*   Intro block sits above the pinned wheel: big two-line    */
/*   title + static "Classic" timeline rebuilt as HTML from   */
/*   the white graphic inside `rive_way_infographic.riv`.     */
/*   Then a 200vh track + 100vh sticky pin parks the SVG      */
/*   wheel while the page scrolls past; JS only writes        */
/*   stroke-dashoffset and per-point `data-active`.           */
/* ========================================================= */
.craftsman-wheel {
  background: var(--bg);
  border-top: 1px solid var(--rule);
  position: relative;
}
/* Intro block follows the SAME layout tokens as `.rive-way` so the
   title + classic timeline render at the same scale as the original
   Rive section (which renders the title as HTML inside `.rive-way__inner`
   and the timeline inside the .riv canvas). Tokens reused:
     - padding-block: var(--pad-section)
     - max-width: var(--max-w)
     - padding-inline: var(--pad-x)
     - head margin: clamp(36px, 4vw, 56px)
     - title font-size: clamp(40px, 5.4vw, 80px)
   Timeline sizing scaled up so the white graphic feels comparable in
   weight to the surrounding hero typography. */
.craftsman-wheel__intro {
  box-sizing: border-box;
  margin: 0 auto;
  max-width: var(--max-w);
  padding-block: var(--pad-section) clamp(48px, 6vw, 96px);
  padding-inline: var(--pad-x);
  position: relative;
}
.craftsman-wheel__intro-head {
  margin: 0 0 clamp(36px, 4vw, 56px);
  text-align: left;
}
.craftsman-wheel__intro-title {
  font-family: var(--font-display);
  font-size: clamp(40px, 5.4vw, 80px);
  letter-spacing: 0em;
  line-height: 1.02;
  margin: 0;
  padding-top: 0.04em;
  text-wrap: balance;
}
.craftsman-wheel__intro-title-line {
  color: var(--title-fill);
  display: block;
  font-family: var(--font-display);
  font-weight: 900;
  letter-spacing: 0em;
}
.craftsman-wheel__intro-title-line--accent {
  -webkit-background-clip: text;
  background: var(--accent-gradient);
  background-clip: text;
  color: transparent;
  font-weight: 900;
  width: fit-content;
}
/* 2-column intro header, active only when the optional lede/cards are filled
   (client feedback, Jul 10: the title's right half read "barren" — 2 columns
   + cards). New classes only: Services reuses the wheel classes with its own
   markup and keeps the single-column header. */
.craftsman-wheel__intro-head--split {
  align-items: end;
  display: grid;
  gap: 24px;
  grid-template-columns: repeat(12, 1fr);
}
.craftsman-wheel__intro-head--split .craftsman-wheel__intro-title {
  grid-column: 1 / 9;
}
.craftsman-wheel__intro-aside {
  display: grid;
  gap: 18px;
  grid-column: 9 / 13;
  justify-items: start;
}
.craftsman-wheel__intro-lede {
  color: var(--text-muted);
  font-size: 16px;
  line-height: 1.6;
  margin: 0;
  max-width: 42ch;
}
.craftsman-wheel__intro-cards {
  display: grid;
  gap: 12px;
  /* Side by side: keeps the aside no taller than the title, so the two
     columns of the header finish on the same line. */
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100%;
}
.craftsman-wheel__intro-card {
  background: var(--card-grad);
  border: 1px solid var(--card-line);
  border-radius: var(--card-r);
  box-shadow: var(--card-edge);
  display: grid;
  gap: 6px;
  padding: 14px 16px;
}
.craftsman-wheel__intro-card-label {
  color: var(--text-muted);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.craftsman-wheel__intro-card-row {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.craftsman-wheel__intro-card-value {
  font-family: var(--font-display);
  font-size: clamp(22px, 1.9vw, 28px);
  font-weight: 900;
  line-height: 1;
}
.craftsman-wheel__intro-card:last-child .craftsman-wheel__intro-card-value {
  -webkit-background-clip: text;
  background: var(--accent-gradient);
  background-clip: text;
  color: transparent;
}
.craftsman-wheel__intro-card-caption {
  color: var(--text-muted);
  font-size: 14px;
}
@media (max-width: 960px) {
  .craftsman-wheel__intro-head--split {
    display: block;
  }
  .craftsman-wheel__intro-aside {
    margin-top: 28px;
  }
  .craftsman-wheel__intro-cards {
    /* Back to row flow: the desktop column flow would force 2 implicit
       columns and overflow a 375px viewport. */
    grid-auto-flow: row;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  }
}
.craftsman-wheel__classic {
  margin: 0 auto;
  width: 100%;
}
.craftsman-wheel__classic-heading {
  color: var(--subtitle-fill);
  font-family: var(--font-display);
  font-size: clamp(30px, 3vw, 50px);
  /* 800 pairs it with the "CRAFTSMAN+ Way" title — the two headings of the
     same comparison sat at 700 vs 900 (client: inconsistent styles). */
  font-weight: 900;
  letter-spacing: -0.005em;
  margin: 0 auto clamp(32px, 4vw, 64px);
}
/* Eight-step horizontal timeline. Each step is a column with the label
   on top, a dot in the middle and the time at the bottom; a thin rule
   sits at the dot's vertical centre and runs from the first dot to the
   last. Grid keeps the steps evenly spaced regardless of label width. */
.craftsman-wheel__timeline {
  align-items: stretch;
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
}
.craftsman-wheel__timeline::after {
  content: "";
  position: absolute;
  /* Line sits at the dot row's vertical centre. With the bigger labels
     the dot row is roughly at 50 percent of the step's intrinsic
     height; explicit alignment via the dot's gap keeps it locked. */
  top: 50%;
  left: calc(100% / 16);
  /* `--line-progress` is 0..1, driven by JS. The line spans from dot
     #1 centre to dot #N centre, which is 7 of the 8 equal columns =
     87.5 percent of the timeline width. */
  width: calc(var(--line-progress, 1) * 87.5%);
  height: 1px;
  background: var(--text);
  transform: translateY(-0.5px);
  z-index: 0;
  /* transition: width 1100ms cubic-bezier(0.76, 0, 0.24, 1); */
  transition: width 450ms cubic-bezier(0.76, 0, 0.24, 1);
}
.craftsman-wheel__step {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(14px, 1.4vw, 22px);
  min-width: 0;
  /* Slower fade + upward drift = softer reveal than a hard opacity flip.
     The opacity curve is a stronger ease-in-out (quart) so each element
     creeps in quietly before resolving, instead of jumping mid-fade.
     transform stays on an ease-out-expo so the element settles into
     position a beat after the fade lands. */
  transition: opacity 1100ms cubic-bezier(0.76, 0, 0.24, 1),
  transform 450ms cubic-bezier(0.16, 1, 0.3, 1);
}
.craftsman-wheel__step[data-visible="0"] {
  opacity: 0;
  transform: translateY(10px);
}
.craftsman-wheel__step-label {
  color: var(--text);
  font-family: var(--font-display);
  /* Bigger, more readable (client feedback, Jul 10 — the section felt
     barren; applies to Platform and the Services approach twin). */
  font-size: clamp(16px, 1.45vw, 22px);
  font-weight: 500;
  text-align: center;
  white-space: nowrap;
}
/* Last step ("1 concept") gets an animated underline: a ::after pseudo
   replaces `text-decoration` so its width can transition from 0 to
   100 percent as the step's `data-visible` flips to "1". Width animates
   over 1 second with the same ease-in-out-quart used on the step fade,
   so the underline draws itself across the word in sync with the
   element's appearance. */
.craftsman-wheel__step:last-child .craftsman-wheel__step-label {
  color: var(--text);
  position: relative;
}
.craftsman-wheel__step:last-child .craftsman-wheel__step-label::after {
  background: currentColor;
  bottom: -0.2em;
  content: "";
  height: 1px;
  left: 0;
  position: absolute;
  transition: width 1000ms cubic-bezier(0.76, 0, 0.24, 1);
  width: 0;
}
.craftsman-wheel__step:last-child[data-visible="1"] .craftsman-wheel__step-label::after {
  width: 100%;
}
.craftsman-wheel__step-dot {
  background: var(--text);
  border-radius: 50%;
  flex-shrink: 0;
  height: 1.8rem;
  width: 1.8rem;
}
.craftsman-wheel__step-time {
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(14px, 1.15vw, 18px);
  font-weight: 500;
  text-align: center;
  white-space: nowrap;
}
.craftsman-wheel__classic-caption {
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(20px, 1.8vw, 30px);
  font-weight: 600;
  margin: clamp(40px, 4.5vw, 72px) 0 0;
  text-align: center;
}
@media (max-width: 720px) {
  .craftsman-wheel__timeline {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    row-gap: 28px;
  }
  .craftsman-wheel__timeline::after {
    /* Two rows of four; remove the single horizontal rule and let the
       dots stand on their own at this width. */
    display: none;
  }
}
.craftsman-wheel__track {
  /* Dividing line between the Classic block above and the Craftsman Way
     scrolly below (client feedback, Jul 10). */
  border-top: 1px solid var(--rule);
  margin-top: clamp(32px, 4vw, 56px);
  min-height: 200vh;
  position: relative;
}
.craftsman-wheel__pin {
  align-items: center;
  display: flex;
  height: 100vh;
  position: sticky;
  top: 0;
}
.craftsman-wheel__inner {
  box-sizing: border-box;
  display: grid;
  gap: clamp(20px, 3vw, 40px);
  grid-template-rows: auto 1fr;
  height: 100%;
  margin: 0 auto;
  max-width: var(--max-w);
  /* More air above the Craftsman Way title — it sat too close to the top
     (client feedback, Jul 10). */
  padding-block: clamp(56px, 9vh, 128px) clamp(40px, 6vh, 96px);
  padding-inline: var(--pad-x);
  width: 100%;
}
.craftsman-wheel__head {
  margin: 0 auto;
  text-align: start;
  width: 100%;
}
.craftsman-wheel__title {
  font-family: var(--font-display);
  font-size: clamp(36px, 5vw, 72px);
  /* Same weight as .craftsman-wheel__classic-heading: the two headings of
     the Classic vs Way comparison must match (client: inconsistent styles). */
  font-weight: 900;
  letter-spacing: 0em;
  line-height: 1.02;
  margin: 0;
  padding-top: 0.04em;
}
.vertical-carousel__stage {
  margin: -3rem -1.75rem;
  max-width: min(520px, 72vh);
}
/* Gradient lives on an inline-block span so its background traverses
   only the glyph run, not the full h2 width. Mirrors the convention
   used by `.platform-performance__title-line--accent` elsewhere on the
   site. Padding + negative margin add breathing room for descenders
   without changing the visual position. */
.craftsman-wheel__title-word--accent {
  -webkit-background-clip: text;
  background: var(--accent-gradient);
  background-clip: text;
  background-size: 100% 100%;
  color: transparent;
  display: inline-block;
  font-size: clamp(30px, 3vw, 50px);
  margin: -0.1em;
}
.craftsman-wheel__stage {
  display: grid;
  /* Top margin reserves headroom for the top step label, which hangs ~80px
     ABOVE the ring: without it the label sat level with the "The CRAFTSMAN+
     Way" title (client, Jul 30). Shifting the stage shifts the points
     overlay with it (it lives inside the stage). */
  margin-top: clamp(56px, 13vh, 96px);
  min-height: 0;
  place-items: center;
  position: relative;
}
.craftsman-wheel__ring {
  aspect-ratio: 1;
  display: block;
  /* 56vh/600px, was 60vh/620px: slightly smaller so the "15 mins."
     callout fits under the wheel inside the pinned viewport (copy
     finale, Jul 30). Mirrored in the two width breakpoints below. */
  width: min(560px, 45vh, 92%);
}
/* Callout under the wheel — the CRAFTSMAN+ Way twin of
   .craftsman-wheel__classic-caption, tighter margin (it lives inside
   the pinned viewport). Optional: rendered only when filled. */
.craftsman-wheel__caption {
  /* Back to white with a touch more air (client, Jul 30, after trying
     solid orange). */
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(15px, 1.3vw, 20px);
  font-weight: 600;
  margin: clamp(60px, 10vh, 104px) 0 0;
  padding-top: 10px;
  text-align: center;
}
.craftsman-wheel__fill {
  fill: none;
}
.craftsman-wheel__points {
  aspect-ratio: 1;
  inset: 0;
  margin: auto;
  pointer-events: none;
  position: absolute;
  /* Kept in lockstep with .craftsman-wheel__ring (56vh/600px, Jul 30). */
  width: min(560px, 45vh, 92%);
}
.craftsman-wheel__point {
  height: 0;
  left: var(--x);
  position: absolute;
  top: var(--y);
  width: 0;
}
.craftsman-wheel__dot {
  background: #27272A;
  border-radius: 999px;
  color: #71717A;
  display: grid;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 900;
  height: 56px;
  left: -28px;
  letter-spacing: 0.01em;
  place-items: center;
  position: absolute;
  top: -28px;
  transition: color 320ms var(--ease);
  width: 56px;
}
/* When the fill arc crosses a point's threshold, JS flips `data-active`
   from "0" to "1" and the keyframe pulse runs: the dot grows to a peak
   of 1.18 at mid-animation, then settles back to 1.10 (not all the way
   to its resting size). `forwards` retains the final keyframe so the
   dot holds its slightly-bigger state for as long as the step stays
   active. Scrolling back below the threshold drops `data-active` to
   "0", the rule no longer matches, and the dot snaps back to scale 1. */
.craftsman-wheel__point[data-active="1"] .craftsman-wheel__dot {
  animation: cw-pulse 1s ease-in-out forwards;
  color: #FA6C00;
}
@keyframes cw-pulse {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.18);
  }
  100% {
    transform: scale(1.10);
  }
}


.craftsman-wheel__label {
  color: #E0E0E0;
  font-size: clamp(17px, 1.3vw, 20px);
  font-weight: 500;
  margin: 0;
  max-width: min(38vw, 300px);
  position: absolute;
  text-align: center;
  transition: color 320ms var(--ease);
  width: max-content;
}
.craftsman-wheel__point[data-side="top"] .craftsman-wheel__label {
  bottom: 36px;
  left: 50%;
  transform: translateX(-50%);
}

.craftsman-wheel__point[data-side="bottom"] .craftsman-wheel__label {
  left: 50%;
  /* The two bottom dots sit ~0.59 x ring apart and the ring is vh-capped:
     a vh-based width cap keeps #3 and #4 from ever overlapping (client,
     Jul 30: they sat on top of each other on a 900px-tall Mac). */
  max-width: min(24vh, 240px);
  top: 36px;
  transform: translateX(-50%);
}
.craftsman-wheel__point[data-side="right"] .craftsman-wheel__label {
  left: 36px;
  text-align: left;
  top: 50%;
  transform: translateY(-50%);
}
.craftsman-wheel__point[data-side="left"] .craftsman-wheel__label {
  right: 36px;
  text-align: right;
  top: 50%;
  transform: translateY(-50%);
}
.craftsman-wheel__point[data-active="1"] .craftsman-wheel__label {
  color: #E0E0E0;
}
.craftsman-wheel__sr {
  border: 0;
  clip: rect(0, 0, 0, 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}
/* Tablet + portrait-tablet: the dots near the horizontal edges sit too
   close to the viewport sides for "left"/"right" labels to fit outside
   the donut, so collapse them onto a centred-above-dot layout and shrink
   the donut a touch to give the top/bottom labels room. */
@media (max-width: 1100px) {
  .craftsman-wheel__ring, .craftsman-wheel__points {
    width: min(500px, 47vh, 78vw);
  }
  .craftsman-wheel__label {
    max-width: min(28vw, 220px);
  }
  .craftsman-wheel__point[data-side="right"] .craftsman-wheel__label,
  .craftsman-wheel__point[data-side="left"] .craftsman-wheel__label {
    bottom: 36px;
    left: 50%;
    right: auto;
    text-align: center;
    top: auto;
    transform: translateX(-50%);
  }
}

/* Short laptop/tablet-landscape viewports: the enlarged 47vh ring pushes the
   top step label back level with the "Way" title (the ring centres in a 1fr
   row, so stage margin alone cannot buy the headroom). Trim the ring back to
   its pre-enlargement 44vh only there. */
@media (max-width: 1100px) and (max-height: 800px) {
  .craftsman-wheel__ring,
  .craftsman-wheel__points {
    width: min(500px, 44vh, 78vw);
  }
}
@media (max-width: 720px) {
  .craftsman-wheel__ring, .craftsman-wheel__points {
    width: min(66vw, 44vh);
  }
  .craftsman-wheel__dot {
    font-size: 15px;
    height: 44px;
    left: -22px;
    top: -22px;
    width: 44px;
  }
  .craftsman-wheel__label {
    font-size: 12px;
    max-width: min(32vw, 160px);
  }
  .craftsman-wheel__point[data-side="top"] .craftsman-wheel__label,
  .craftsman-wheel__point[data-side="right"] .craftsman-wheel__label,
  .craftsman-wheel__point[data-side="left"] .craftsman-wheel__label {
    bottom: 28px;
  }
  .craftsman-wheel__point[data-side="bottom"] .craftsman-wheel__label {
    /* Restore the mobile cap: the base bottom rule's 24vh would out-rank the
       generic label cap above and let #3/#4 collide on narrow screens. */
    max-width: min(32vw, 160px);
    top: 28px;
  }
}

/* Desktop/Mac only: buy visible air between the hanging #3/#4 labels and the
   "15 mins." callout. The grid pins the callout to the bottom of the pinned
   viewport, so margins/padding on the callout itself cannot move it — instead
   trade part of the pin's bottom padding for gap (32px always stays, so the
   callout can never leave the viewport) and lift the ring a touch. */
@media (min-width: 1101px) {
  .craftsman-wheel__inner {
    padding-block-end: clamp(32px, 3.5vh, 56px);
  }
}
@media (min-width: 1101px) and (min-height: 801px) {
  .craftsman-wheel__stage {
    /* The raised top cap re-balances what the bottom lift takes from the
       title/label-1 clearance on tall screens. */
    margin-bottom: 28px;
    margin-top: clamp(56px, 13vh, 120px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .craftsman-wheel__track {
    min-height: 0;
  }
  .craftsman-wheel__pin {
    height: auto;
    position: static;
  }
  .craftsman-wheel__inner {
    padding-block: var(--pad-section);
  }
  .craftsman-wheel__fill {
    stroke-dashoffset: 0;
  }
  .craftsman-wheel__point {
    --active: 1;
  }
  .craftsman-wheel__point .craftsman-wheel__dot {
    animation: none;
    color: #FA6C00;
    transform: scale(1.10);
    transition: none;
  }
  .craftsman-wheel__point .craftsman-wheel__label {
    color: var(--text);
  }
}
/* ========================================================= */
/*   VERTICAL CAROUSEL — pinned scrollytelling section.       */
/*   500vh outer track + 100vh sticky pin. Left column holds  */
/*   the active slide copy + segmented progress bar. Right    */
/*   column is a stage clipping a vertical reel of frames.    */
/*   The reel is laid out in REVERSED slide order with        */
/*   sentinels at both ends so the top peek shows the next    */
/*   slide and the bottom peek shows the previous (cyclic).   */
/* ========================================================= */
.vertical-carousel {
  background: var(--bg);
  border-top: 1px solid var(--rule);
  position: relative;
}
.vertical-carousel__track {
  min-height: 500vh;
  position: relative;
}
.vertical-carousel__pin {
  align-items: center;
  display: flex;
  height: 100vh;
  position: sticky;
  top: 0;
}
.vertical-carousel__inner {
  box-sizing: border-box;
  display: grid;
  grid-template-rows: auto 1fr;
  height: 100%;
  margin: 0 auto;
  max-width: var(--max-w);
  overflow: hidden;
  padding-block: clamp(32px, 5vh, 72px);
  padding-inline: var(--pad-x);
  width: 100%;
}
.vertical-carousel__head {
  /* Pushed down so the pinned section reads vertically centred — the header
     used to sit glued to the top of the viewport (client feedback, Jul 10). */
  margin-top: clamp(20px, 8vh, 96px);
  text-align: left;
}
.vertical-carousel__sub {
  /* Section subhead (copy finale, Jul 31): pinned in the header under the
     title — unlike the slide copy, it never rotates. */
  color: var(--text-muted);
  font-size: clamp(15px, 1.2vw, 17px);
  line-height: 1.6;
  margin: 14px 0 0;
  max-width: 56ch;
}
@media (min-width: 981px) {
  .vertical-carousel__sub {
    /* Two-column mode: the stage is pulled up level with the header, so a
       full-header-width line would run under the peek frames (seen at
       1024x768) — keep the subhead inside the copy column. */
    max-width: min(56ch, 44vw);
  }
}
.vertical-carousel__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 5.4vw, 80px);
  font-weight: 900;
  letter-spacing: 0em;
  line-height: 1.02;
  margin: 2.5rem 0;
  padding-top: 0.04em;
}
.vertical-carousel__title-line {
  color: var(--title-fill);
  display: block;
}
.vertical-carousel__title-line--accent {
  -webkit-background-clip: text;
  background: var(--accent-gradient);
  background-clip: text;
  background-size: 100% 100%;
  color: transparent;
  display: inline-block;
  margin: -0.05em 0 -0.05em -0.05em;
  padding: 0.05em 0.2em 0.05em 0;
}
.vertical-carousel__body {
  align-items: center;
  display: grid;
  gap: clamp(40px, 6vw, 96px);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  min-height: 0;
}
/* Left column: slides + progress bar pinned to the bottom. */
.vertical-carousel__copy {
  /* align-self, NOT height:100% (client, Aug 24: "carousel breaks after a
     certain screen size"): in the stacked mode the column sits in an
     auto-sized grid row, so the percentage height had no reference and the
     whole column collapsed to 0 — slide copy, progress bar and stage then
     piled up over the header on any 721-980px viewport. Stretch gives the
     same full-row height in the two-column mode without the cycle. */
  align-self: stretch;
  display: grid;
  gap: clamp(32px, 4vh, 64px);
  grid-template-rows: 1fr auto;
  min-height: 0;
}
/* The slides wrapper holds N absolutely-positioned articles. Only the
   one with data-active="1" shows. The wrapper reserves its tallest
   slide's height via the relative-grid trick. */
.vertical-carousel__slides {
  display: grid;
  min-height: 0;
  position: relative;
}
.vertical-carousel__slide {
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 2vh, 28px);
  /* All slides share one grid cell: the wrapper really does reserve the
     tallest slide's height (the old absolute+inset version bled over the
     header on small screens once the pinned subhead landed there). */
  grid-area: 1 / 1;
  justify-content: center;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 320ms var(--ease),
    transform 360ms var(--ease),
    visibility 0s linear 320ms;
  visibility: hidden;
}
.vertical-carousel__slide[data-active="1"] {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 320ms var(--ease),
    transform 360ms var(--ease),
    visibility 0s linear 0s;
  visibility: visible;
}
.vertical-carousel__eyebrow {
  align-items: baseline;
  color: var(--text);
  display: inline-flex;
  font-family: var(--font-display);
  font-size: clamp(16px, 1.3vw, 20px);
  font-weight: 500;
  gap: 0.8em;
  letter-spacing: 0.26em;
  margin: 0;
  text-transform: uppercase;
}
.vertical-carousel__num {
  color: var(--accent);
  font-size: 1.4em;
  font-weight: 900;
  letter-spacing: 0;
}
.vertical-carousel__heading {
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(28px, 3vw, 48px);
  font-weight: 900;
  letter-spacing: -0.01em;
  line-height: 1.05;
  margin: 0;
  text-wrap: balance;
}
.vertical-carousel__lede {
  color: var(--text);
  font-size: clamp(14px, 1vw, 17px);
  line-height: 1.6;
  margin: 0;
  max-width: 32em;
}
/* Segmented progress bar at the bottom of the left column. */
.vertical-carousel__progress {
  align-self: end;
  display: grid;
  gap: clamp(10px, 1.2vw, 18px);
  grid-auto-columns: 1fr;
  grid-auto-flow: column;
  margin-bottom: 3rem;
  margin-top: auto;
  transform: translateY(clamp(-80px, -7vh, -40px));
}
.vertical-carousel__progress-seg {
  background: #D9D9D9;
  border-radius: 2px;
  height: 3px;
  overflow: hidden;
}
.vertical-carousel__progress-fill {
  background: var(--accent-gradient);
  border-radius: inherit;
  height: 100%;
  transition: width 80ms linear;
  width: var(--fill, 0%);
}
/* Right column: layered stack of frames. Both the active and background
   slides sit near the centre of the stage with a SUBTLE offset: the
   active is the larger frame slightly down and to the right; the next
   slide sits a touch up and to the left, scaled to ~80 percent and
   dimmed. Stage is capped by both width AND viewport height so the
   section never pushes past 100vh on shorter screens. Frames share a
   base size and transition via transform for cheap GPU compositing. */
.vertical-carousel__stage {
  aspect-ratio: 1 / 1;
  margin: -3rem 0;
  /* Reversal (client, Jul 28: images read as chopped, "half the image is
     black" on laptops): the Jul-10 enlarge-and-fade treatment is gone — no
     more 124% bleed. The stage fits its column and the frames fit the
     stage, so the artwork shows in full at every width. The 68vh cap keeps
     the three-frame stack clear of the viewport bottom on short laptops
     (tuned at 1440x900 / 1366x768). */
  max-width: min(790px, 68vh);
  /* Soft right-edge fade kept per client (Jul 28 follow-up, settled after
     two passes): asked for "10% of the image max", 10% read as invisible,
     settled at the last 20% of the stage (client validated on Mac). */
  -webkit-mask-image: linear-gradient(90deg, #000 80%, transparent 100%);
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-size: 100% 200%;
  -webkit-mask-position: left center;
  mask-image: linear-gradient(90deg, #000 80%, transparent 100%);
  mask-repeat: no-repeat;
  mask-size: 100% 200%;
  mask-position: left center;
  position: relative;
  transform: translateY(-7rem);
  width: 100%;
}
.vertical-carousel__frame {
  /* Native ratio of the Aug 25 slide set (1440x1024) — was 16/10 for the
     old exports; the role transforms below are derived from this. */
  aspect-ratio: 1440 / 1024;
  border-radius: var(--radius-md);
  left: 0;
  overflow: hidden;
  position: absolute;
  top: 0;
  transform-origin: top left;
  transition: transform 520ms var(--ease),
    opacity 320ms var(--ease),
    filter 320ms var(--ease),
    box-shadow 320ms var(--ease);
  width: 100%;
  will-change: transform, opacity;
}
.vertical-carousel__frame img {
  display: block;
  height: 100%;
  object-fit: cover;
  width: 100%;
}
/* Three visible layers centred horizontally on the stage:
     - active in the middle (full base size, scale 1)
     - previous slide peeking at the top, scaled down + dimmed (cyclic so
       slide 1 shows slide N as its `previous`)
     - background (next slide) peeking at the bottom, scaled down + dimmed
   Stage is 1:1. Frame base is 100% wide, 71.11% tall (1440/1024, Aug 25
   set) — nothing overflows the stage. Active is vertically centred: free
   space (100 - 71.11)/2 = 14.44% of stage = 20.3% of frame height. Peeks
   scale 0.76 (visible width 76%): centring offset 12% of stage; bottom
   peek y = (100 - 54.05)% of stage = 64.6% of frame height. */
.vertical-carousel__frame[data-role="active"] {
  z-index: 3;
  opacity: 1;
  filter: none;
  transform: translate3d(0, 20.3%, 0) scale(1);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);
}
.vertical-carousel__frame[data-role="background"] {
  z-index: 1;
  opacity: 0.55;
  filter: brightness(0.65) saturate(0.85);
  transform: translate3d(12%, 64.6%, 0) scale(0.76);
  box-shadow: none;
}
.vertical-carousel__frame[data-role="previous"] {
  z-index: 1;
  opacity: 0.55;
  filter: brightness(0.65) saturate(0.85);
  transform: translate3d(12%, 0, 0) scale(0.76);
  box-shadow: none;
}
.vertical-carousel__frame[data-role="hidden"] {
  box-shadow: none;
  filter: brightness(0.65) saturate(0.85);
  opacity: 0;
  pointer-events: none;
  transform: translate3d(12%, 0, 0) scale(0.76);
  z-index: 0;
}
/* Stacked mode is PORTRAIT-only (client, Aug 24): it was tuned for tablets
   and needs the vertical room. A desktop window resized under 980px wide is
   landscape — there the two-column layout still fits (the 68vh stage cap
   handles short heights), whereas stacking pushed the active frame past the
   100vh pin. */
@media (max-width: 980px) and (orientation: portrait) {
  /* Header rhythm re-cut (client, Aug 28: too much air above the header and
     between title and subhead, not enough before the carousel). The tighter
     top padding also buys the stage the room it needs to fit the pin. */
  .vertical-carousel__inner {
    padding-block: 24px clamp(32px, 5vh, 72px);
  }
  .vertical-carousel__head {
    /* Clears the 60px sticky nav while the section is pinned, without the
       old 8vh + 2.5rem stack of air. */
    margin-top: clamp(48px, 7vh, 72px);
    margin-bottom: 24px;
  }
  .vertical-carousel__title {
    margin: 0 0 12px;
  }
  .vertical-carousel__body {
    gap: 32px;
    grid-template-columns: 1fr;
  }
  .vertical-carousel__stage {
    aspect-ratio: 1 / 1;
    /* Centred, no upward pull and no overflow (client, Aug 28: the card
       overflowed and got cut off — was margin -3rem -1.75rem with a -2rem
       upward pull). Phones go full-bleed in the 720 block below. */
    margin: 0 auto;
    max-width: min(520px, 72vh);
    position: relative;
    transform: none;
    width: 100%;
    /* No right-edge fade in the stacked mode: it read as the artwork being
       cut off. */
    -webkit-mask-image: none;
    mask-image: none;
  }
  .vertical-carousel__track {
    min-height: 400vh;
  }
  .vertical-carousel__pin {
    /* The whole stacked band uses the phone recipe (Aug 24): a 100vh pin
       cannot hold head + copy + stage once the copy column has its real
       height (the align-self fix above), and the shorter grid rows Chrome
       squeezes out of it are what shoved the frames over the copy. 120vh
       gives the stack its room; the overflow tail stays below the fold
       while pinned, as on phones. */
    height: 120vh;
  }
  .vertical-carousel__copy {
    /* Stacked mode is normal block flow, NOT a stretched grid (Aug 24, the
       "breaks after a certain screen size" bug): stretch/1fr inside the
       body's auto row is what collapsed the column to 0 here, and even
       content-sized nested grids under-contribute to the row's intrinsic
       size in Chrome, leaving the stage overlapping the copy. In two-column
       mode none of this applies — the stage's height defines the shared
       row. align-self:start beats the base align-items:center. */
    align-self: start;
    display: block;
  }
  .vertical-carousel__progress {
    /* Block flow killed the copy grid's gap, so the bar's own margin takes
       over: 2.5rem (was 6rem — trimmed Aug 28 so the full-width stage fits
       the 120vh pin without the frame clipping at the bottom) + the lost
       gap. The base -7vh translate still eats most of it. */
    margin-top: calc(2.5rem + clamp(32px, 4vh, 64px));
  }
}
/* Portrait-gated like the stacked mode above: the 120vh pin assumes the
   stacked composition — in phone-landscape the two-column layout takes over
   and a 120vh pin would push the progress bar below the fold. */
@media (max-width: 720px) and (orientation: portrait) {
  .vertical-carousel__track {
    min-height: 400vh;
  }
  /* Full-bleed on phones: the frame spans exactly the phone width
     (client, Aug 28: "can we have the card with the phone width").
     50% - 50vw reaches the real viewport edge through both the inner
     padding AND its 90% max-width. */
  .vertical-carousel__stage {
    margin: 0 calc(50% - 50vw);
    max-width: none;
    width: auto;
  }
  .vertical-carousel__heading {
    font-size: clamp(22px, 5vw, 32px);
  }
  .vertical-carousel__lede {
    font-size: 14px;
  }
  .vertical-carousel__progress {
    align-self: end;
    display: grid;
    gap: clamp(10px, 1.2vw, 18px);
    grid-auto-columns: 1fr;
    grid-auto-flow: column;
    /* Kept in lockstep with the 980-portrait rule above (block-flow copy has
       no grid gap, the margin carries it). Was 6rem — trimmed Aug 28 so the
       full-width stage fits the 120vh pin without clipping. */
    margin-top: calc(2.5rem + clamp(32px, 4vh, 64px));
    transform: translateY(clamp(-80px, -7vh, -40px));
  }
  .vertical-carousel__pin {
    align-items: center;
    display: flex;
    height: 105vh;
    position: sticky;
    top: 0rem;
  }
}
@media (prefers-reduced-motion: reduce) {
  .vertical-carousel__track {
    min-height: 0;
  }
  .vertical-carousel__pin {
    height: auto;
    position: static;
  }
  .vertical-carousel__inner {
    padding-block: var(--pad-section);
  }
  .vertical-carousel__slides {
    display: grid;
    gap: 48px;
  }
  .vertical-carousel__slide {
    grid-area: auto;
    opacity: 1;
    position: static;
    transform: none;
    transition: none;
    visibility: visible;
  }
  .vertical-carousel__progress-fill {
    transition: none;
    width: 100%;
  }
  .vertical-carousel__stage {
    aspect-ratio: 1 / 1;
    /* Kept in sync with the 980-portrait block (no upward pull, Aug 24; no
       horizontal bleed, Aug 28 — the -1.75rem margins cut the frame off on
       phones): here the frames flow statically. */
    margin: 0 auto;
    max-width: min(520px, 72vh);
    position: relative;
    width: 100%;
  }
  .vertical-carousel__frame {
    filter: none;
    opacity: 1;
    position: relative;
    transform: none;
    width: 100%;
  }
}
/* ========================================================= */
/*   OUR CLIENTS — alternating quote + screenshot cards row   */
/* ========================================================= */
.our-clients {
  background: #09090b;
  border-top: 1px solid var(--rule);
  padding-block: var(--pad-section);
  position: relative;
}
.our-clients__inner {
  margin: 0 auto;
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
}
.our-clients__head {
  align-items: end;
  display: grid;
  gap: 24px;
  grid-template-columns: 1fr auto;
  margin-bottom: clamp(36px, 4vw, 56px);
}
/* Optional "View Case Studies" CTA (copy doc, built Jul 31): the lede lives
   in column 1 under the title, the CTA takes the next row of that column. */
.our-clients__cta {
  grid-column: 1;
  justify-self: start;
}
/* One slide of the our-clients carousel: a horizontal pair (article left, media right)
   shaped like [[][]]. The pair sizes to its content so each child .client-card
   keeps its natural width and aspect-ratio. */
.our-clients__pair {
  align-items: stretch;
  background: var(--surface-2);
  border: var(--border-strong) 1px solid;
  border-radius: 18px;
  display: flex;
  flex: 0 0 auto;
  flex-direction: row;
  gap: clamp(8px, 0.8vw, 14px);
  padding: 4px;
  scroll-snap-align: start;
}
.our-clients__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 5.4vw, 80px);
  grid-column: 1;
  letter-spacing: 0em;
  line-height: 1.02;
  margin: 0;
  padding-top: 0.04em;
}
.our-clients__title-word {
  color: var(--title-fill);
  font-family: var(--font-display);
  font-weight: 900;
  letter-spacing: 0em;
}
.our-clients__title-word--accent {
  -webkit-background-clip: text;
  background: var(--accent-gradient);
  background-clip: text;
  color: transparent;
  font-weight: 900;
}
.our-clients__lede {
  color: var(--text-muted);
  font-size: 16px;
  grid-column: 1;
  line-height: 1.6;
  margin: clamp(8px, 1vw, 16px) 0 0;
  max-width: 38ch;
}
/* See-All pill is the .btn .btn--ghost shape with grid placement, uppercase
   tracking, and a coloured ">" suffix in the trailing span. */
.our-clients__seeall {
  align-self: start;
  gap: 4px;
  grid-column: 2;
  grid-row: 1 / span 2;
  letter-spacing: 0.08em;
  padding: 0 17px;
  text-transform: uppercase;
}
.our-clients__carousel {
  position: relative;
}
.our-clients__track {
  display: flex;
  gap: clamp(12px, 1.4vw, 22px);
  overflow-x: auto;
  /* `proximity` instead of `mandatory`: with only ~207px of scroll headroom
     and the next pair-start snap point well past max, mandatory was forcing
     scrollLeft back to 0 every time the user advanced. Proximity snaps only
     when the scroll position is close to a snap point. */
  scroll-snap-type: x proximity;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding-block: 6px;
  /* Edge fade so the track doesn't slam against the section padding. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
}
.our-clients__track::-webkit-scrollbar {
  display: none;
}
.our-clients__nav {
  -webkit-backdrop-filter: blur(8px);
  align-items: center;
  backdrop-filter: blur(8px);
  background: rgba(9, 9, 11, 0.82);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  color: #fff;
  cursor: pointer;
  display: inline-flex;
  height: 44px;
  justify-content: center;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  transition: background 160ms var(--ease), border-color 160ms var(--ease), opacity 160ms var(--ease), transform 160ms var(--ease);
  width: 44px;
  z-index: 2;
}
.our-clients__nav:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.28);
}
.our-clients__nav:active {
  transform: translateY(-50%) scale(0.96);
}
.our-clients__nav[disabled] {
  cursor: default;
  opacity: 0.35;
  pointer-events: none;
}
.our-clients__nav--prev {
  left: -18px;
}
.our-clients__nav--next {
  right: -18px;
}
.client-card {
  aspect-ratio: 9 / 16;
  background: #141414;
  border: 1px solid #2a2a2a;
  border-radius: var(--radius-lg);
  flex: 0 0 clamp(180px, 16vw, 240px);
  overflow: hidden;
  position: relative;
  scroll-snap-align: start;
  transition: transform 220ms var(--ease), border-color 220ms var(--ease), box-shadow 220ms var(--ease);
}
.client-card:hover {
  border-color: rgba(250, 108, 0, 0.32);
  box-shadow: 0 24px 48px -24px rgba(0, 0, 0, 0.7);
  transform: translateY(-3px);
}
/* Standardized case-study card (client feedback, Jul 10 + video 35:30-36:49):
   landscape, hero image on top, unbolded bigger quote below, button linking
   to the case study. Replaces the old 9:16 quote+media pair; fed by the
   cs_home_feature toggle. Surfaces mirror the .cs-vcard recipe. */
.client-card--cs {
  aspect-ratio: auto;
  background: var(--card-grad);
  border-color: var(--card-line);
  border-radius: var(--card-r);
  display: flex;
  flex-basis: clamp(420px, 36vw, 560px);
  flex-direction: column;
}
.client-card__visual {
  aspect-ratio: 16 / 10;
  display: block;
  overflow: hidden;
  position: relative;
}
.client-card__visual > img:first-child {
  display: block;
  height: 100%;
  object-fit: cover;
  width: 100%;
}
/* Brand logo chip: contained, never cropped (hard rule). */
.client-card__brand {
  bottom: 12px;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.55));
  left: 16px;
  max-height: 38px;
  max-width: 40%;
  object-fit: contain;
  position: absolute;
}
.client-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: clamp(14px, 1.2vw, 20px);
  padding: clamp(18px, 1.6vw, 26px);
}
/* Scoped under --cs so it beats the shared .client-card__quote rules that
   are declared later in this sheet (unbolded + bigger, client feedback). */
.client-card--cs .client-card__quote--cs {
  color: var(--text);
  flex: 1;
  font-family: var(--font-sans);
  font-size: clamp(18px, 1.4vw, 21px);
  font-weight: 500;
  line-height: 1.35;
  margin: 0 0 0;
}
.client-card--cs .client-card__cta {
  align-self: flex-start;
}
.client-card--quote {
  display: flex;
  flex-direction: column;
  font-weight: 700;
  padding: clamp(16px, 1.4vw, 22px);
}
.client-card__logo {
  display: block;
  height: 26px;
  margin-bottom: clamp(14px, 1.4vw, 20px);
  max-width: 100%;
  object-fit: contain;
  object-position: left center;
  opacity: 0.92;
  width: auto;
}
.client-card__quote {
  color: var(--text);
  flex: 1;
  font-family: var(--font-sans);
  font-size: large;
  font-weight: 700;
  line-height: 1;
  margin: 0 0 clamp(14px, 1.4vw, 20px);
}
.client-card__mark {
  color: var(--text-muted);
  font-family: var(--font-display);
  font-size: 1em;
  font-style: italic;
}
.client-card__cta {
  align-items: center;
  align-self: stretch;
  border-radius: var(--radius-sm);
  display: inline-flex;
  font-size: 13px;
  font-weight: 500;
  height: 36px;
  justify-content: center;
  text-decoration: none;
}
.client-card--media {
  background: #0e0e11;
  border-color: transparent;
  display: block;
  text-decoration: none;
}
.client-card--media img {
  display: block;
  height: 100%;
  object-fit: cover;
  object-position: center;
  width: 100%;
}
@media (max-width: 720px) {
  .our-clients__head {
    grid-template-columns: 1fr;
  }
  .our-clients__seeall {
    grid-column: 1;
    grid-row: auto;
    justify-self: start;
  }
  .our-clients__nav {
    display: none;
  }
  /* Under tablet: each pair becomes a single full-width slide with the
     testimony stacked above the visual. !important defeats the inline
     `scroll-snap-type: none` set by the carousel JS, which is needed for
     the desktop button-driven nearest-snap but kills native touch snap. */
  .our-clients__track {
    -webkit-mask-image: none;
    gap: 14px;
    mask-image: none;
    scroll-padding-inline: 0;
    scroll-snap-type: x mandatory !important;
  }
  .our-clients__pair {
    align-items: stretch;
    box-sizing: border-box;
    flex: 0 0 100%;
    flex-direction: column;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    width: 100%;
  }
  .our-clients__pair>.client-card {
    aspect-ratio: auto;
    flex: 0 0 auto;
    width: 100%;
  }
  .our-clients__pair>.client-card--quote {
    min-height: clamp(220px, 32vh, 280px);
  }
  .our-clients__pair>.client-card--media {
    aspect-ratio: 16 / 10;
    height: auto;
    width: 100%;
  }
  .our-clients__pair>.client-card--media img {
    aspect-ratio: 16 / 10;
    height: auto;
  }
  /* The standardized case-study cards sit directly in the track (no
     .our-clients__pair wrapper), so the pair rules above never reached
     them: their 420px desktop minimum overflowed phone viewports and the
     right edge was cut off (client, Sep 1). One full-width slide per
     view, same behaviour as the pairs. */
  .our-clients__track > .client-card--cs {
    flex-basis: 100%;
  }
}
/* ========================================================= */
/*   ENGAGEMENT FULLY VISUALIZED — copy left, stacked images  */
/* ========================================================= */
.engagement {
  background: var(--bg);
  border-top: 1px solid var(--rule);
  overflow: hidden;
  padding-block: var(--pad-section);
  position: relative;
}
.engagement__inner {
  align-items: center;
  display: grid;
  gap: clamp(24px, 4vw, 48px);
  grid-template-columns: repeat(12, 1fr);
  margin: 0 auto;
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
}
.engagement__copy {
  display: flex;
  flex-direction: column;
  grid-column: 1 / 6;
}
.engagement__copy .eyebrow {
  margin: 0 0 16px;
}
.engagement__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 5.4vw, 80px);
  letter-spacing: 0em;
  line-height: 1.02;
  margin: 0 0 22px;
  padding-top: 0.04em;
  text-wrap: balance;
}
.engagement__title-word {
  color: var(--title-fill);
  display: block;
  font-family: var(--font-display);
  font-weight: 900;
  letter-spacing: 0em;
  line-height: 1.1;
}
.engagement__title-word--accent {
  -webkit-background-clip: text;
  background: var(--accent-gradient);
  background-clip: text;
  color: transparent;
  font-weight: 900;
}
.engagement__lede {
  color: var(--text-muted);
  font-size: 16px;
  line-height: 1.6;
  margin: 0;
  max-width: 42ch;
}
.engagement__media {
  display: block;
  grid-column: 6 / 13;
  perspective: 1400px;
  position: relative;
}
.engagement__card {
  position: relative;
  /* Card chrome removed (client, Aug 28: "remove the thick grey border
     around the analytics images") — the artwork sits bare, keeping only
     the parallax/tilt and the drop shadow. */
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 30px 60px -20px rgba(0, 0, 0, 0.55);
  transform-style: preserve-3d;
  /* --rotz is the resting Z-axis tilt (set per card below). Pointer tilt
     (rotateX/Y) layers on top. */
  transform: translate3d(0, var(--parallax-y, 0px), 0) perspective(1100px) rotateZ(var(--rotz, 0deg)) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  transition: transform 200ms var(--ease);
  will-change: transform;
}
.engagement__card--scene {
  --rotz: -2.4deg;
}
.engagement__card--chart {
  --rotz: 3.2deg;
}
/* Cursor-tracked highlight, same recipe as the phone cards + hero panel. */
.engagement__card::before {
  background: radial-gradient(55% 55% at var(--mx, 50%) var(--my, 50%),
      rgba(255, 255, 255, 0.12),
      rgba(255, 255, 255, 0) 65%);
  border-radius: inherit;
  content: "";
  inset: 0;
  mix-blend-mode: screen;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  transition: opacity 280ms var(--ease);
  z-index: 5;
}
.engagement__card[data-tilted="true"]::before {
  opacity: 1;
}
/* Chart card overlaps the bottom-left of the scene, like the Figma. */
.engagement__card--chart {
  border-radius: 18px;
  margin: -28% auto 0 8%;
  width: 72%;
  z-index: 1;
}
.engagement__media-scene, .engagement__media-chart {
  background: transparent;
  /* Follows the card radius now that the padded frame is gone. */
  border-radius: 18px;
  display: block;
  height: auto;
  width: 100%;
}
@media (max-width: 960px) {
  .engagement__copy, .engagement__media {
    grid-column: 1 / -1;
  }
  .engagement__card--chart {
    margin: -12% auto 0;
    width: 92%;
  }
}
/* ========================================================= */
/*   MEET PLAY — full-bleed wordmark                          */
/* ========================================================= */
.meet-play {
  border-top: 1px solid var(--rule);
  overflow: hidden;
  padding-block: var(--pad-section);
  position: relative;
}
.meet-play__header {
  align-items: end;
  display: grid;
  gap: 24px;
  grid-template-columns: repeat(12, 1fr);
  margin: 0 auto clamp(48px, 6vw, 88px);
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
  position: relative;
  z-index: 1;
}
.meet-play__header .eyebrow {
  grid-column: 1 / 4;
  margin: 0 0 16px;
}
/* Ported 2026-08-24 from the colleague's Aug 3 staging hot-fixes: weight
   900 -> 800 on the words, tighter 1.1 leading, balanced wrapping, one word
   per line. The dropped color/font-weight/grid-column declarations were
   redundant (color and placement come from later rules). Shared by the
   Platform hero and the home "Your Creative Infrastructure" header. */
.meet-play__title {
  font-family: var(--font-display);
  font-size: clamp(40px, 5.4vw, 80px);
  letter-spacing: 0em;
  line-height: 1.02;
  margin: 0;
  padding-top: 0.04em;
  text-wrap: balance;
}
.meet-play__title-word {
  display: block;
  font-family: var(--font-display);
  font-weight: 900;
  letter-spacing: 0em;
  line-height: 1.1;
}
.meet-play__title-word--accent {
  -webkit-background-clip: text;
  background: var(--accent-gradient);
  background-clip: text;
  color: transparent;
  font-weight: 900;
  margin-left: 0;
}
.meet-play__lede {
  color: var(--text-muted);
  font-size: 16px;
  grid-column: 1 / 8;
  line-height: 1.6;
  margin: clamp(16px, 1.6vw, 24px) 0 0;
  max-width: 42ch;
}
/* "Your Interactive Workspace" variant: title stacks on two lines, lede
   floats to the right column instead of sitting below. */
.meet-play--workspace .meet-play__header {
  align-items: start;
  row-gap: 8px;
}
.meet-play--workspace .meet-play__header .eyebrow {
  grid-column: 1 / 13;
  margin: 0;
}
.meet-play--workspace .meet-play__title {
  grid-column: 1 / 9;
  grid-row: 2;
}
.meet-play--workspace .meet-play__title-word {
  display: block;
  line-height: 1.1;
}
.meet-play--workspace .meet-play__lede {
  align-self: end;
  /* Wider so it wraps on two lines max, never four (client feedback, Jul 10). */
  grid-column: 8 / 13;
  grid-row: 2;
  margin: clamp(8px, 1.2vw, 22px) 0 0;
  max-width: 52ch;
}
@media (max-width: 960px) {
  .meet-play--workspace .meet-play__title, .meet-play--workspace .meet-play__lede {
    grid-column: 1 / -1;
    grid-row: auto;
  }
  .meet-play--workspace .meet-play__lede {
    margin-top: 20px;
  }
}
/* Platform hero — Figma layout: left-aligned title stack, ghost CTA on its
   own row below, then the perspective-tilted screenshot that lifts up to
   bleed behind the title block. Tilt is baked into the PNG. */
.platform-hero {
  padding-block: clamp(80px, 11vw, 160px) 0;
}
/* Super header aligned on the homepage hero: ghost first line + bigger
   gradient second line, same sizes and weight (client feedback, Jul 10). */
.platform-hero__title {
  font-weight: 900;
  line-height: 0.92;
}
.platform-hero__title-line--ghost {
  color: var(--title-ghost);
  display: block;
  font-size: clamp(44px, 5vw, 72px);
  letter-spacing: -0.051em;
}
.platform-hero__title-line--gradient {
  display: inline-block;
  /* Ported 2026-08-24 from the colleague's Aug 3 staging hot-fixes: the
     oversized gradient line (was clamp(64px, 7.3vw, 105px) at -0.051em)
     drops to the sitewide title scale, wraps balanced. */
  font-size: clamp(40px, 5.4vw, 80px);
  letter-spacing: 0em;
  text-wrap: balance;
}
/* Subheader sentence under the headline ("like Linear does"). */
.platform-hero__lede {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: clamp(16px, 1.25vw, 19px);
  grid-column: 1 / 8;
  /* Row pinned since the hero CTA landed (copy finale, Jul 30): the doc
     order is headline -> subhead -> CTA, and the CTA slot predates the
     lede (it was row 2 back when the header had no subheader). */
  grid-row: 2;
  line-height: 1.55;
  /* 0 0 22px, was 14px 0 0 — ported 2026-08-24 from the colleague's Aug 3
     staging hot-fixes (air moved below the subhead, before the CTA). */
  margin: 0 0 22px;
  max-width: 48ch;
}
.platform-hero__header {
  align-items: start;
  margin-bottom: 0;
  row-gap: clamp(24px, 3vw, 40px);
  z-index: 3;
}
.platform-hero .meet-play__title {
  font-size: clamp(44px, 6.4vw, 96px);
  grid-column: 1 / 10;
  grid-row: 1;
  letter-spacing: -0.01em;
  line-height: 1.02;
}
.platform-hero .platform-hero__cta {
  align-items: center;
  display: inline-flex;
  grid-column: 1 / 4;
  /* Row 3: under the subheader (see .platform-hero__lede note). */
  grid-row: 3;
  height: 38px;
  justify-self: start;
  letter-spacing: 0.04em;
  padding: 0 18px;
}
/* Ported 2026-08-24 from the colleague's Aug 3 staging hot-fixes: dormant
   variant — no template emits --no-lede yet, but with it the CTA climbs
   into the empty subhead row instead of leaving a hole. */
.platform-hero .platform-hero__header--no-lede .platform-hero__cta {
  grid-row: 2;
}
.platform-hero .demo-panel--plain {
  /* Native ratio of the Aug 25 flat export (2880x2046); the old 1309/712
     was the retired baked-tilt composite's footprint. */
  aspect-ratio: 2880 / 2046;
  /* Reduced pull (was -clamp(40px,5vw,80px)): with the left-forward swing
     the artwork slid under the hero CTA on Mac widths (Charles, Aug 27). */
  margin-top: calc(-1 * clamp(8px, 1vw, 16px));
  z-index: 0;
}
/* The Linear-style lean (client, Aug 25): the screenshot now ships FLAT
   and the perspective lives here — tune the angle by changing rotateX
   alone. Origin at the bottom edge keeps the base pinned while the top
   recedes; the panel's bottom melt (below) does the fade-out. */
.platform-hero .demo-platform__media {
  /* Rounded like the retired baked-tilt asset (client, Aug 27) — the
     radius rides the tilted plane, so it stays natural in perspective. */
  border-radius: 14px;
  /* Linear-style compound tilt, soft version (client, Aug 27 test): the
     straight backward lean gains a light horizontal swing (rotateY) and a
     hint of cant (rotateZ). scale absorbs the corner wedges the rotation
     opens against the panel's clip box. */
  /* Mirrored per Charles (Aug 27): left edge forward, right edge receding.
     The small translate keeps the swung top-left corner inside the panel's
     clip box and clear of the CTA (was cut and kissing the button on Mac). */
  transform: perspective(1600px) translate3d(2.5%, 2%, 0) rotateX(9deg) rotateY(6deg) rotateZ(-2deg) scale(1.02);
  transform-origin: 45% 90%;
}
/* Linear-style melt (client, Aug 24: "we don't want to see the entire
   product image, it should fade out"): the stock 32vh fade only ate the
   very bottom of this tall panel, so the whole editor UI stayed readable.
   Sized in % of the PANEL, not vh, so the melt tracks the artwork at every
   viewport: visible to ~40% down, melting 40-82%, gone below. The tilt
   angle itself is baked into the PNG — softening it happens in the asset
   (or in CSS if the next export comes flat). */
.platform-hero .demo-panel--plain:after {
  background: linear-gradient(to bottom, rgba(9, 9, 11, 0) 0%, var(--bg) 70%);
  height: 60%;
}
@media (max-width: 960px) {
  .platform-hero .meet-play__title, .platform-hero .platform-hero__cta {
    grid-column: 1 / -1;
  }
  .platform-hero .demo-panel--plain {
    margin-top: clamp(24px, 4vw, 48px);
  }
}
.demo-panel--plain {
  aspect-ratio: 16 / 10;
  grid-column: unset;
  margin: 0 auto;
  max-width: var(--max-w);
  position: relative;
  width: calc(100% - calc(var(--pad-x) * 2));
  z-index: 1;
}
.demo-panel--plain:after, .demo-panel--plain .demo-panel__media::after {
  background: linear-gradient(to bottom, rgba(9, 9, 11, 0) 0%, var(--bg) 75%);
  content: "";
  height: 32vh;
  inset: auto 0 0 0;
  pointer-events: none;
  position: absolute;
  z-index: 1000;
}
.demo-panel--plain .demo-panel__bar {
  display: none;
}
/* Home feature panel only (Platform hero shares the classes but has its own
   art). The old workspace.png gutter-crop hack is gone with the asset
   (client, Aug 25: creative-infrastructure.png ships clean, full-bleed, at
   its native 1440x1024) — the panel radius still owns the corners. */
.meet-play--workspace:not(.platform-hero) .demo-panel--plain {
  aspect-ratio: 1440 / 1024;
  border-radius: var(--card-r);
  overflow: hidden;
}
/* Home only (client, Aug 24: caption + CTA sat too far under the artwork):
   a shorter melt reveals the screenshot further down the panel, and the
   caption is pulled up into the overlay's SOLID band. The gradient is
   opaque background from 75% of its height down (here 25% of 22vh =
   5.5vh), so a 5vh lift can never put the text over visible artwork, at
   any viewport — both lengths scale with the same unit. The platform hero
   shares these classes and keeps the stock 32vh fade. */
.meet-play--workspace:not(.platform-hero) .demo-panel--plain:after,
.meet-play--workspace:not(.platform-hero) .demo-panel--plain .demo-panel__media::after {
  height: 22vh;
}
.meet-play--workspace:not(.platform-hero) .demo-panel__caption {
  margin-top: calc(24px - 5vh);
  /* Above the panel (z-index:1): its solid-bg overlay would otherwise
     paint over the lifted text. */
  position: relative;
  z-index: 2;
}
.demo-panel__media {
  display: block;
  height: 100%;
  inset: 0;
  object-fit: cover;
  position: absolute;
  width: 100%;
  z-index: 1;
}
.demo-platform__media {
  display: block;
  height: 100%;
  inset: 0;
  position: absolute;
  width: 100%;
  z-index: 1;
}
@media (max-width: 960px) {
  .meet-play__header .eyebrow, .meet-play__title, .meet-play__lede {
    grid-column: 1 / -1;
  }
  .meet-play__lede {
    margin-top: 20px;
  }
}
/* ========================================================= */
/*   RESULTS — pinned scrollytelling, ghost numbers            */
/*   Tall track + sticky pin keeps the inner stage on-screen   */
/*   while the page scrolls through N slots, one per stat.     */
/* ========================================================= */
.results {
  background: var(--bg);
  border-top: 1px solid var(--rule);
  position: relative;
}
.results__track {
  min-height: 400vh;
  position: relative;
}
.results__pin {
  align-items: center;
  display: flex;
  height: 100vh;
  overflow: hidden;
  position: sticky;
  top: 0;
}
.results__inner {
  align-items: stretch;
  box-sizing: border-box;
  display: grid;
  gap: clamp(40px, 5vw, 96px);
  grid-template-columns: minmax(320px, 1.15fr) 1.85fr;
  height: 100%;
  margin: 0 auto;
  max-width: var(--max-w);
  padding-bottom: clamp(32px, 5vh, 72px);
  padding-inline: var(--pad-x);
  padding-top: calc(72px + clamp(24px, 4vh, 56px));
  position: relative;
  width: 100%;
}
.results__inner::before {
  background: var(--border-strong);
  bottom: 0;
  content: "";
  left: clamp(400px, 40%, 520px);
  pointer-events: none;
  position: absolute;
  top: 0;
  width: 1px;
}
.results__copy {
  align-self: center;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.results__copy .eyebrow {
  margin: 0;
}
.results__title {
  color: var(--title-fill);
  display: flex;
  flex-direction: column;
  font-family: var(--font-display);
  font-size: clamp(36px, 4vw, 62px);
  font-weight: 900;
  letter-spacing: -0.01em;
  line-height: 1.02;
  margin: 6px 0 0;
  padding-top: 0.04em;
}
.results__title-line {
  white-space: nowrap;
}
/* .results__title-line--gradient handled by shared rule */
.results__lede {
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.55;
  margin: 0;
  max-width: 32ch;
}
.results__cta {
  align-self: flex-start;
  margin-top: 8px;
}
/* ---------- Right panel: tabs + stage ---------- */
.results__panel {
  --panel-inset: clamp(36px, 4.2vw, 84px);
  display: grid;
  gap: clamp(28px, 3vh, 48px);
  grid-template-rows: auto 1fr;
  height: 100%;
  min-height: 0;
  min-width: 0;
  padding-left: var(--panel-inset);
}
.results__tabs {
  display: grid;
  gap: clamp(16px, 2vw, 32px);
  grid-template-columns: repeat(4, 1fr);
  padding-right: clamp(40px, 5vw, 96px);
}
.results__tab {
  align-items: center;
  background: transparent;
  border: 0;
  cursor: pointer;
  display: flex;
  padding: 16px 0 0;
  text-align: left;
}
.results__tab-bar {
  background: var(--border-strong);
  display: block;
  height: 1px;
  position: relative;
  transition: background 200ms var(--ease);
  width: 100%;
}
.results__tab-fill {
  display: none;
}
.results__tab.is-active .results__tab-bar {
  background: var(--accent-gradient);
  height: 2px;
}
.results__tab:hover .results__tab-bar {
  background: rgba(255, 255, 255, 0.22);
}
.results__tab.is-active:hover .results__tab-bar {
  background: var(--accent-gradient);
}
/* ---------- Stage: stacked slides, only one visible ---------- */
.results__stage {
  min-height: 0;
  position: relative;
}
.results__slide {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.4vw, 36px);
  inset: 0;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  visibility: hidden;
}
.results__slide.is-active {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}
.results__rank {
  align-items: baseline;
  color: var(--text);
  display: flex;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  gap: 14px;
  letter-spacing: 0.22em;
  margin: 0;
  text-transform: uppercase;
}
.results__rank-num {
  -webkit-background-clip: text;
  background: var(--accent-gradient);
  background-clip: text;
  color: transparent;
  font-family: var(--font-display);
  font-size: clamp(22px, 1.8vw, 28px);
  font-weight: 900;
  letter-spacing: 0;
}
.results__big {
  align-items: center;
  align-self: flex-start;
  color: var(--title-fill);
  display: flex;
  font-family: var(--font-display);
  font-size: clamp(140px, 32vh, 340px);
  font-variant-numeric: tabular-nums;
  font-weight: 900;
  justify-content: flex-start;
  letter-spacing: -0.04em;
  line-height: 0.9;
  margin: 0;
  margin-left: calc(-1 * var(--panel-inset) + clamp(16px, 2vw, 40px));
  margin-top: auto;
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 700ms var(--ease-out-quart), transform 700ms var(--ease-out-quart);
  user-select: none;
  will-change: transform, opacity;
}
.results__slide.is-active .results__big {
  opacity: 1;
  transform: translateY(0);
}
.results__big-prefix, .results__big-num, .results__big-suffix {
  display: inline-block;
}
.results__case-link {
  align-self: flex-start;
  border-bottom: 1px solid var(--accent-from);
  color: var(--accent-from);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.22em;
  margin-bottom: auto;
  margin-top: clamp(12px, 2.5vh, 24px);
  padding-bottom: 6px;
  text-transform: uppercase;
  transition: color 160ms var(--ease), border-color 160ms var(--ease);
}
.results__case-link:hover {
  border-bottom-color: var(--accent-to);
  color: var(--accent-to);
}
@media (max-width: 960px) {
  .results__track {
    min-height: 360vh;
  }
  .results__inner {
    gap: 32px;
    grid-template-columns: 1fr;
    padding-bottom: clamp(24px, 4vh, 56px);
    padding-top: calc(72px + clamp(16px, 3vh, 40px));
  }
  .results__inner::before {
    display: none;
  }
  .results__big {
    font-size: clamp(80px, 18vh, 180px);
  }
}
@media (max-width: 520px) {
  .results__tabs {
    gap: 10px;
  }
  .results__big {
    font-size: clamp(72px, 14vh, 140px);
  }
}
/* ========================================================= */
/*   RESULTS — row variant: simple stat strip                 */
/*   Overrides are scoped under .results--row so the          */
/*   scrollytelling version above stays untouched.            */
/* ========================================================= */
.results--row {
  padding-block: clamp(36px, 3.5vw, 56px);
}
.results--row .results__inner {
  align-items: center;
  gap: clamp(32px, 4vw, 64px);
  grid-template-columns: minmax(260px, 1fr) 2.6fr;
  height: auto;
  padding-bottom: 0;
  padding-top: 0;
}
.results--row .results__inner::before {
  display: none;
}
.results--row .results__copy {
  align-self: auto;
  gap: 10px;
}
.results--row .results__title {
  color: var(--text);
  font-size: clamp(28px, 3vw, 44px);
  letter-spacing: 0em;
  line-height: 1.04;
  margin: 0;
}
.results--row .results__lede {
  color: var(--text-muted);
  font-size: 15px;
  font-weight: 400;
  max-width: 38ch;
}
.stat-strip {
  display: grid;
  gap: clamp(20px, 2.5vw, 40px);
  grid-template-columns: repeat(4, 1fr);
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
}
.stat-strip__item {
  border-left: 1px solid var(--rule);
  display: flex;
  flex-direction: column;
  gap: clamp(8px, 0.8vw, 14px);
  padding-left: clamp(20px, 2vw, 36px);
}
.stat-strip__item:first-child {
  border-left: 0;
  padding-left: 0;
}
.stat-strip__value {
  align-items: baseline;
  color: var(--text);
  display: inline-flex;
  font-family: var(--font-display);
  font-size: clamp(30px, 3vw, 44px);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: 0em;
  line-height: 1;
}
.stat-strip__num, .stat-strip__prefix, .stat-strip__suffix {
  display: inline-block;
}
.stat-strip__label {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.45;
}
@media (max-width: 960px) {
  .results--row .results__inner {
    gap: 32px;
    grid-template-columns: 1fr;
  }
  .stat-strip {
    gap: 24px;
    grid-template-columns: repeat(2, 1fr);
  }
  .stat-strip__item:nth-child(3) {
    border-left: 0;
    border-top: 1px solid var(--rule);
    padding-left: 0;
    padding-top: 24px;
  }
}
@media (max-width: 520px) {
  .stat-strip {
    grid-template-columns: 1fr;
  }
  .stat-strip__item {
    border-left: 0;
    border-top: 1px solid var(--rule);
    padding-left: 0;
    padding-top: 20px;
  }
  .stat-strip__item:first-child {
    border-top: 0;
    padding-top: 0;
  }
}
/* ========================================================= */
/*   SERVICES — numbered editorial list                       */
/* ========================================================= */
.services {
  position: relative;
  background: var(--bg);
  padding-block: 0;
  border-top: 1px solid var(--rule);
  /* No overflow constraint here — sticky inside breaks if the section
     establishes any scrolling context. The diagram clips its own overflow. */
}
/* Section header sits inside the sticky pin, on the centre-left of the
   balance. The spread of the spokes (±38° outer, ±13° inner) leaves enough
   vertical room for the full eyebrow + title + lede + CTA. The inner
   active line still crosses through the title; z-index keeps text on top. */
.services__header--inline {
  display: flex;
  flex-direction: column;
  gap: clamp(14px, 1.2vw, 22px);
  left: var(--pad-x) !important;
  left: 5% !important;
  pointer-events: auto;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: clamp(280px, 32vw, 520px);
  z-index: 3;
}
.services__header--inline .eyebrow {
  margin: 0;
}
.services__title {
  color: var(--text);
  display: flex;
  flex-direction: column;
  font-family: var(--font-display);
  font-size: clamp(38px, 5vw, 75px);
  font-weight: 900;
  letter-spacing: -2px;
  line-height: 1;
  margin: 0;
}
.services__lede {
  color: var(--text-muted);
  font-size: clamp(14px, 1vw, 16px);
  line-height: 1.55;
  margin: 0;
  max-width: 38ch;
}
.services__cta {
  align-self: start;
  margin-top: 4px;
}
/* ---------- Services balance (off-screen-left hub, scroll-rotated spokes) ---------- */
/* Tall scroll track. Its height is the total scroll distance over which the
   balance rotates from first to last service. Bigger = slower, more deliberate.
   (Client, Jul 28: section far too tall on laptops — 320vh -> 240vh.) */
.services__track {
  height: 240vh;
  margin-top: clamp(56px, 8vw, 120px);
  position: relative;
}
.services__sticky {
  align-items: center;
  display: flex;
  height: 100vh;
  justify-content: center;
  position: sticky;
  top: 0;
}
.services-diagram {
  /* Client, Jul 28: the composition sat 160px high in the pinned frame
     (huge void at the bottom on laptops). The old -10rem top/bottom shift
     is gone; header, reel and items are all centred on this box, so the
     box must fill the sticky frame exactly. */
  bottom: 0;
  height: 100%;
  isolation: isolate;
  /* Safe zone: every internal position (header 5%, reel right offset, hub,
     spoke lines, cables viewBox) is relative to this box, and its vw-based
     values are px-clamped, so capping here bounds the whole composition.
     Below 1600px viewport the 90% side of the token applies: the section
     picks up the same side margins as the rest of the site. */
  margin-inline: auto;
  max-width: var(--max-w);
  overflow: hidden;
  position: relative;
  top: 0;
  width: 100%;
}
.services-balance {
  /* Reveal phase: --reveal goes 0 → 1 once we cross into phase B. JS sets it. */
  --reveal: 0;
  /* Anchor for the active-spoke line + dot. Bumped 80px right of the original
     clamp so the line clears the inline header on wide screens. Tweak these
     two values to nudge the X start and Y position of the orange bar+dot. */
  --accent-anchor-x: clamp(520px, calc(34vw + 100px), 660px);
  --accent-anchor-y: -45px;
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  /* The balance rises into place: shifts up from +120px to 0 as --reveal
     goes 0 → 1, then settles. */
  transform: translate3d(0, calc((1 - var(--reveal)) * 120px), 0);
  opacity: var(--reveal);
  will-change: transform, opacity;
}
.services-balance__spoke {
  /* JS sets --slide per frame: 0 = centred/active, ±1 = one slot away,
     beyond ±1 = off-stage. The display element reads --slide for its
     translate + rotate transform; the line/dot only react to .is-active. */
  --slide: 0;
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 0;
  transform-style: preserve-3d;
}
/* Stable horizontal track. Starts past the inline header on the left and
   runs to the right edge of the diagram. All spokes overlap, so visually
   you only ever see one line — the active spoke's. */
.services-balance__line {
  background: rgba(255, 255, 255, 0.10);
  height: 1px;
  left: var(--accent-anchor-x);
  opacity: 0;
  position: absolute;
  right: 0;
  top: var(--accent-anchor-y);
  transition: background 360ms var(--ease), opacity 320ms var(--ease), filter 360ms var(--ease);
}
.services-balance__spoke.is-active .services-balance__line {
  background: rgba(250, 108, 0, 0.55);
  filter: drop-shadow(0 0 8px rgba(250, 108, 0, 0.45));
  opacity: 1;
}
.services-balance__dot {
  background: rgba(255, 255, 255, 0.22);
  border-radius: 50%;
  height: 14px;
  left: var(--accent-anchor-x);
  margin-left: -7px;
  margin-top: -7px;
  opacity: 0;
  position: absolute;
  top: var(--accent-anchor-y);
  transition: background 320ms var(--ease), box-shadow 320ms var(--ease), transform 320ms var(--ease), opacity 320ms var(--ease);
  width: 14px;
}
.services-balance__spoke.is-active .services-balance__dot {
  background: var(--accent-from);
  box-shadow: 0 0 0 6px rgba(250, 108, 0, 0.18), 0 0 22px rgba(250, 108, 0, 0.78);
  opacity: 1;
  transform: scale(1.25);
}
/* Hidden on desktop; shown on mobile as a stacked list. */
.services-diagram__list {
  display: none;
}
/* Showreel card: dominant right-side panel during phase A. As the section
   scrolls it scales down and fades out so the balance can take over.
   --reel-show (0..1): JS sets this. 1 = fully visible, 0 = hidden.
   --tilt-x / --tilt-y / --mx / --my: pointer-driven 3D + glare. */
.services__reel {
  position: absolute;
  top: 50%;
  right: clamp(24px, 4vw, 80px);
  /* Container-relative, not vw: the diagram is capped to the safe zone, and
     the old 52vw/820px reel overlapped the header inside the 1440 box,
     leaving the cables no run. 50%/640px keeps ~50-130px of cable between
     the title and the card at every width. */
  width: clamp(440px, 50%, 640px);
  aspect-ratio: 16 / 10;
  --reel-show: 1;
  --reel-rotz: -2deg;
  border-radius: 18px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: linear-gradient(180deg, rgba(250, 108, 0, 0.05), rgba(235, 81, 115, 0.02) 50%, transparent), #0e0e11;
  box-shadow: 0 60px 120px -36px rgba(0, 0, 0, 0.75);
  transform-style: preserve-3d;
  /* Master transform: opacity + smooth scale + drift driven by --reel-show.
     Pointer tilt + resting Z-axis tilt layer on top for the card feel. */
  transform: translate(calc((1 - var(--reel-show)) * 80px), calc(-50% + (1 - var(--reel-show)) * 110px)) perspective(1100px) rotateZ(calc(var(--reel-rotz) + (1 - var(--reel-show)) * -3deg)) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) scale(calc(0.78 + var(--reel-show) * 0.22));
  opacity: var(--reel-show);
  /* No CSS transition — the JS-driven CSS variable updates already sit on
     the smoothstep curve, so motion stays in sync with the scroll. */
  will-change: transform, opacity;
  z-index: 3;
  pointer-events: auto;
}
.services__reel::before {
  background: radial-gradient(55% 55% at var(--mx, 50%) var(--my, 50%),
      rgba(255, 255, 255, 0.18),
      rgba(255, 255, 255, 0) 65%);
  border-radius: inherit;
  content: "";
  inset: 0;
  mix-blend-mode: screen;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  transition: opacity 280ms var(--ease);
  z-index: 2;
}
.services__reel[data-tilted="true"]::before {
  opacity: 1;
}
.services__reel-video {
  background: transparent;
  display: block;
  height: 100%;
  object-fit: cover;
  width: 100%;
}
.services__reel-tag {
  -webkit-backdrop-filter: blur(6px);
  align-items: center;
  backdrop-filter: blur(6px);
  background: rgba(0, 0, 0, 0.55);
  border-radius: 999px;
  color: var(--accent-from);
  display: inline-flex;
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 600;
  height: 22px;
  left: 12px;
  letter-spacing: 0.18em;
  padding: 0 10px;
  position: absolute;
  text-transform: uppercase;
  top: 12px;
  z-index: 1;
}
@property --swing {
  inherits: false;
  initial-value: 0deg;
  syntax: "<angle>";
}
.services-balance__display {
  position: absolute;
  /* Anchored to the same vars as the line + dot so the whole item moves as
     one block. Left edge flush with the dot (dot = anchor-x - 7px), 20px
     below the line (client, Jul 28: "01 ..." must align with the orange dot). */
  top: calc(var(--accent-anchor-y) - 20px);
  left: calc(var(--accent-anchor-x) - 7px);
  right: clamp(24px, 4vw, 80px);
  padding-top: clamp(25px, 2.1vw, 45px);
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: auto auto;
  column-gap: clamp(20px, 1.8vw, 32px);
  row-gap: clamp(16px, 1.4vw, 24px);
  align-items: baseline;
  /* Vertical travel distance at |slide| = 1 (one full transition into / out of view). */
  --slide-dist: clamp(220px, 26vh, 340px);
  --swing: 0deg;
  transform-origin: 50% 0%;
  transform-style: preserve-3d;
  backface-visibility: hidden;
  transform: translateY(calc(var(--slide, 0) * var(--slide-dist) * -1)) perspective(1100px) rotateX(var(--swing));
  opacity: var(--display-opacity, 0);
  will-change: transform, opacity;
}
@keyframes spokeSwing {
  0% {
    --swing: -19deg;
  }
  24% {
    --swing: 10deg;
  }
  48% {
    --swing: -4deg;
  }
  70% {
    --swing: 1.8deg;
  }
  88% {
    --swing: -0.6deg;
  }
  100% {
    --swing: 0deg;
  }
}
.services-balance__spoke.is-active .services-balance__display {
  animation: spokeSwing 1100ms cubic-bezier(0.42, 0, 0.58, 1) both;
}
.services-balance__display-num {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: clamp(18px, 1.4vw, 24px);
  font-weight: 500;
  grid-column: 1;
  grid-row: 1;
  letter-spacing: 0.06em;
  white-space: nowrap;
}
.services-balance__display-desc {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: clamp(16px, 1.3vw, 22px);
  grid-column: 2;
  grid-row: 2;
  line-height: 1.5;
  margin: 0;
  max-width: 40ch;
}
/* Narrow-screen fallback: hide the balance + editorial display, render a
   stacked list. 860px, not 720: between 720 and ~860 (portrait tablets) the
   balance's anchored item column gets too narrow and the 40px titles clip
   (client audit, Jul 28). Keep in sync with the driver guard in script.js. */
@media (max-width: 860px) {
  /* Kill the sticky pin: with no rotating balance to scrub through, the
     tall scroll range only created dead air between this section and the
     next. */
  .services__track {
    height: auto;
    margin-top: 0;
  }
  .services__sticky {
    display: block;
    height: auto;
    position: static;
    top: auto;
  }
  .services-diagram {
    bottom: 0;
    height: auto;
    max-width: 100%;
    overflow: visible;
    padding-block: clamp(40px, 8vw, 64px);
    padding-inline: var(--pad-x);
    top: 0;
  }
  .services-balance, .services__reel {
    display: none;
  }
  /* Show the editorial header (title, lede, CTA) inline above the list.
     Override absolute positioning + transform from the desktop layout. */
  .services__header--inline {
    display: flex;
    left: auto !important;
    margin-bottom: clamp(28px, 6vw, 40px);
    padding-inline: 0;
    position: static;
    top: auto;
    transform: none;
    width: auto;
  }
  .services__title {
    font-size: clamp(36px, 9vw, 56px);
  }
  .services__cta {
    align-self: flex-start;
  }
  .services-diagram__list {
    display: flex;
    flex-direction: column;
    gap: 28px;
    list-style: none;
    margin: 0;
    padding: 0;
  }
  .services-diagram__list li {
    align-items: baseline;
    border-top: 1px solid var(--rule);
    column-gap: 14px;
    display: grid;
    grid-template-columns: auto 1fr;
    padding-top: 24px;
    row-gap: 6px;
  }
  .services-diagram__list-num {
    align-self: start;
    color: var(--text-muted);
    font-family: var(--font-sans);
    font-size: 13px;
    grid-row: 1 / span 2;
    letter-spacing: 0.04em;
    padding-top: 0.4em;
  }
  .services-diagram__list h3 {
    color: var(--text-muted);
    font-family: var(--font-display);
    font-size: clamp(28px, 7vw, 40px);
    font-weight: 900;
    letter-spacing: 0em;
    line-height: 1.1;
    margin: 0;
  }
  .services-diagram__list p {
    color: var(--text-muted);
    font-family: var(--font-sans);
    font-size: 1rem;
    line-height: 1.5;
    margin: 0;
  }
}
/* ========================================================= */
/*   CTA BANNER — bottom-anchored editorial spread            */
/* ========================================================= */
/* The whole banner is one big <a> click target. A "BOOK A DEMO" marquee
   scrolls in outline behind the headline; on hover the default cursor
   hides and a circular stamp follows the pointer. No buttons. */
.cta-banner {
  background: var(--bg);
  border-top: 1px solid var(--rule);
  color: inherit;
  cursor: none;
  display: block;
  isolation: isolate;
  overflow: hidden;
  padding-block: var(--pad-section);
  position: relative;
  text-decoration: none;
}
.cta-banner::before {
  background: radial-gradient(60% 50% at 50% 100%, rgba(250, 108, 0, 0.2) 0%, rgba(250, 108, 0, 0) 65%),
    radial-gradient(50% 40% at 20% 80%, rgba(235, 81, 115, 0.14) 0%, rgba(235, 81, 115, 0) 65%);
  content: "";
  filter: blur(40px);
  inset: 0;
  pointer-events: none;
  position: absolute;
  z-index: -1;
}
.cta-banner__inner {
  align-items: end;
  display: grid;
  gap: clamp(24px, 3vw, 48px);
  grid-template-columns: repeat(12, 1fr);
  margin: 0 auto;
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
  position: relative;
  z-index: 1;
}
.cta-banner__inner .eyebrow {
  grid-column: 1 / 5;
  margin: 0 0 16px;
}
.cta-banner__title {
  /* JS sets these from the cursor position in viewport pixels so the
     spotlight crosses both lines as a single coherent light. */
  --mouse-x: 50vw;
  --mouse-y: 50vh;
  --spotlight: clamp(240px, 28vw, 500px);
  grid-column: 1 / 10;
  margin: 0;
  font-family: "Poppins", var(--font-display);
  font-weight: 900;
  /* Was clamp(64px, 11vw, 180px) (client, Aug 24: banner read 4-5 lines,
     wanted ~3): 7vw/112px keeps the longest copy ("This Is / What
     Intelligent Ads Look Like.") on 3 visual lines from 961px up — checked
     at 961/975/1100/1512, the tightest being the 961-1200 window where the
     title still spans only columns 1-10. The 64px floor is untouched. */
  font-size: clamp(64px, 7vw, 112px);
  line-height: 1;
  display: flex;
  flex-direction: column;
  gap: 0;
  text-wrap: balance;
  padding-top: 0.05em;
}
/* Hollow outlined letters by default; a radial gradient anchored at the
   cursor paints the fill inside the spotlight via background-clip:text.
   The cursor position arrives in viewport pixels (--mouse-x/--mouse-y on
   the title) and each line subtracts its own viewport offset
   (--line-x/--line-y, kept fresh by js/script.js on pointer move and
   scroll), yielding box-local coordinates. NOT background-attachment:fixed:
   combined with background-clip:text, WebKit mispaints it and clips glyph
   ink outside the line box (with line-height:1 that eats descenders, e.g.
   the y of "your"). Local coords on every line keep the pool one coherent
   beam across lines in all engines. */
.cta-banner__title-line {
  color: transparent;
  -webkit-text-stroke: 1px rgba(255, 255, 255, 0.2);
  /* background-clip:text only paints ink INSIDE the border box, and with
     line-height:1 the box is shorter than Poppins' ink: descenders (the y
     of "your", the g of "intelligent") stayed hollow on single-line spans.
     Vertical padding grows the paint box to cover the ink; the negative
     margins give the space back so the layout does not move. */
  padding: 0.25em 0 0.35em;
  margin: -0.25em 0 -0.35em;
  /* Solid orange disc anchored at the cursor — letters fill in flat orange
     inside the spotlight, hollow outside. No gradient inside the disc, no
     soft edge around it. */
  background-image: radial-gradient(circle var(--spotlight) at calc(var(--mouse-x) - var(--line-x, 0px)) calc(var(--mouse-y) - var(--line-y, 0px)),
      var(--accent-from) 0%,
      var(--accent-from) 70%,
      transparent 72%);
  -webkit-background-clip: text;
  background-clip: text;
  background-repeat: no-repeat;
  transition: -webkit-text-stroke-color 280ms var(--ease);
}
.cta-banner:hover .cta-banner__title-line {
  -webkit-text-stroke-color: rgba(255, 255, 255, 0.35);
}
.cta-banner__lede {
  color: var(--text-muted);
  font-size: 15px;
  grid-column: 10 / 13;
  line-height: 1.5;
  margin: 0 0 8px;
  max-width: 28ch;
}
@media (max-width: 960px) {
  .cta-banner__inner .eyebrow, .cta-banner__title, .cta-banner__lede {
    grid-column: 1 / -1;
  }
  .cta-banner__lede {
    margin-top: 20px;
  }
}
/* ---------- Custom cursor stamp ----------
   White text ring rotating around a solid orange hub. The white arrow
   sits on top of the hub and stays still while the ring spins. */
.cta-stamp {
  filter: drop-shadow(0 12px 32px rgba(0, 0, 0, 0.55));
  height: 140px;
  left: 0;
  opacity: 0;
  pointer-events: none;
  position: fixed;
  top: 0;
  transform: translate(-50%, -50%) scale(0.7) rotate(-10deg);
  transition: opacity 240ms var(--ease),
    transform 380ms cubic-bezier(0.25, 1, 0.5, 1);
  width: 140px;
  will-change: transform, opacity, left, top;
  z-index: 100;
}
.cta-banner.is-hot .cta-stamp {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1) rotate(0);
}
.cta-stamp__svg {
  display: block;
  height: 100%;
  overflow: visible;
  width: 100%;
}
.cta-stamp__rotor {
  animation: cta-stamp-spin 9s linear infinite;
  transform-origin: 70px 70px;
}
@keyframes cta-stamp-spin {
  from {
    transform: rotate(0);
  }
  to {
    transform: rotate(360deg);
  }
}
.cta-stamp__text {
  fill: var(--text);
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.cta-stamp__hub {
  fill: var(--accent-from);
}
.cta-stamp__arrow line, .cta-stamp__arrow polyline {
  fill: none;
  stroke: var(--text);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2.4;
}
/* Touch / no-hover devices: show the stamp pinned bottom-right of the
   section instead of as a cursor follower, so the affordance still reads. */
@media (hover: none),
(pointer: coarse) {
  .cta-banner {
    cursor: pointer;
  }
  .cta-stamp {
    bottom: clamp(32px, 5vw, 64px);
    left: auto;
    opacity: 1;
    position: absolute;
    right: clamp(24px, 5vw, 80px);
    top: auto;
    transform: rotate(-8deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .cta-stamp__rotor {
    animation: none;
  }
}
/* ========================================================= */
/*   WORDMARK + FOOTER                                        */
/* ========================================================= */
/* Crane shot reveal: wordmark starts zoomed-in + offset-down + invisible
   when entering viewport, then scales back, slides up and fades in as the
   user scrolls — like a camera craning back from a close-up. JS drives
   --crane-scale, --crane-y and --crane-opacity each frame. */
.wordmark {
  background: var(--bg);
  border-top: 1px solid var(--rule);
  overflow: hidden;
  padding-bottom: 0;
  padding-top: 40px;
  position: relative;
  z-index: 2;
}
/* Mask is on the IMG (not the section) so the SVG still fades at the
   bottom for the editorial bleed effect, while the section itself stays
   fully opaque. */
.wordmark__img {
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 55%, transparent 100%);
  display: block;
  height: auto;
  margin: 0 auto;
  margin-bottom: .1rem;
  mask-image: linear-gradient(180deg, #000 0%, #000 55%, transparent 100%);
  max-width: var(--max-w);
  transform-origin: 50% 50%;
  width: 100%;
}
/* The crane reveal (scale-in + fade) is progressive enhancement: only hide and
   transform the wordmark when motion is allowed and the JS drives --crane-*.
   Base state above stays visible, so a JS failure never leaves the footer
   wordmark blank for a normal-motion visitor. */
@media (prefers-reduced-motion: no-preference) {
  .wordmark__img {
    opacity: var(--crane-opacity, 0);
    transform: scale(var(--crane-scale, 2.6)) translate3d(0, var(--crane-y, 90px), 0);
    will-change: transform, opacity;
  }
}
/* Contact block sitting above the big wordmark: two columns of plain white
   text (address / press + sales). */
.wordmark__contact {
  width: var(--max-w);
  max-width: var(--max-w);
  margin: 0 auto;
  padding-bottom: clamp(32px, 5vw, 64px);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  text-align: center;
  gap: clamp(40px, 8vw, 120px);
}
/* TEMPORARY (Charles, Jul 21): hide the footer Address / Press / Sales block
   until the real contact details are ready. Markup is untouched — delete this
   single rule to bring it back. */
.wordmark__contact {
  display: none;
}
.wordmark__contact-label {
  margin: 0 0 8px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted);
}
.wordmark__contact-col .wordmark__contact-label:not(:first-child) {
  margin-top: 24px;
}
.wordmark__contact-text {
  margin: 0;
  font-size: 15px;
  line-height: 1.7;
  color: #fff;
}
.wordmark__contact-text a {
  color: #fff;
  text-decoration: none;
}
.wordmark__contact-text a:hover {
  text-decoration: underline;
}
@media (max-width: 560px) {
  .wordmark__contact {
    flex-direction: column;
    gap: 28px;
  }
}
/* LangChain-inspired footer (client, Jul 31): page-link columns + Follow
   block up top, the crane-reveal wordmark in the middle, compliance badges
   + optional CRM menu at the bottom. */
.footer {
  background: var(--bg);
  border-top: 1px solid var(--rule);
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.4vw, 32px);
  padding: clamp(28px, 3.5vw, 48px) var(--pad-x) clamp(16px, 2vw, 24px);
  position: relative;
  z-index: 2;
}
.footer__grid {
  display: grid;
  gap: clamp(24px, 4vw, 72px);
  grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
  margin: 0 auto;
  max-width: var(--max-w);
  width: 100%;
}
.footer__col {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.footer__follow {
  justify-self: end;
  /* Left like the mobile block below (client, Aug 24): right-aligned, the
     label hugged the block's right edge once the icon row grew to five. */
  text-align: left;
}
.footer__follow-label {
  color: var(--text-dim);
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  margin: 0 0 12px;
  text-transform: uppercase;
}
/* The wordmark section brings its own huge paddings when it lives between
   page sections; inside the footer the grid gap does the rhythm. */
.footer .wordmark {
  border-top: 0;
  padding-block: clamp(8px, 1.6vw, 24px) clamp(20px, 2.6vw, 44px);
}
.footer .wordmark__img {
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 72%, rgba(0, 0, 0, 0.18) 100%);
  mask-image: linear-gradient(180deg, #000 0%, #000 72%, rgba(0, 0, 0, 0.18) 100%);
}
.footer__badges {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(14px, 2vw, 28px);
}
.footer__badge {
  display: inline-flex;
}
.footer__badge img {
  display: block;
  height: 44px;
  width: auto;
}
.footer__crm {
  flex: none;
}
.footer__crm-list {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(16px, 2vw, 32px);
  list-style: none;
  margin: 0;
  padding: 0;
}
.footer__crm-list a {
  color: var(--text-dim);
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-decoration: none;
  text-transform: uppercase;
  transition: color 160ms var(--ease);
}
.footer__crm-list a:hover {
  color: var(--text);
}
.footer__col-link {
  color: var(--text-dim);
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-decoration: none;
  text-transform: uppercase;
  transition: color 160ms var(--ease);
}
.footer__col-link:hover {
  color: var(--text);
}
.footer__bottom {
  align-items: center;
  display: flex;
  gap: 24px;
  justify-content: space-between;
  margin: 0 auto;
  max-width: var(--max-w);
  width: 100%;
}
.footer__copy {
  color: var(--text-dim);
  font-size: 12px;
  letter-spacing: 0.04em;
  margin: 0 auto;
  max-width: var(--max-w);
  width: 100%;
}
.footer__social {
  display: flex;
  flex: none;
  gap: 10px;
  list-style: none;
  /* -10px = (36px tap box - 16px glyph) / 2, so the first VISIBLE icon sits
     flush with the "Follow Us" label above (client, Aug 24). */
  margin: 0 0 0 -10px;
  padding: 0;
}
.footer__social-link {
  align-items: center;
  color: var(--text-muted);
  display: flex;
  /* No ring, no orange (client, Jul 31) — the 36px box stays as an
     invisible tap target; hover keeps only the lift + brighten. */
  height: 36px;
  justify-content: center;
  transition: color 160ms var(--ease),
    transform 200ms cubic-bezier(0.25, 1, 0.5, 1);
  width: 36px;
}
.footer__social-link:hover {
  color: var(--text);
  transform: translateY(-2px);
}
@media (max-width: 880px) {
  .footer__grid {
    grid-template-columns: repeat(3, 1fr);
  }
  .footer__follow {
    grid-column: 1 / -1;
    justify-self: start;
    text-align: left;
  }
  .footer__bottom {
    align-items: flex-start;
    flex-direction: column;
  }
}
@media (max-width: 540px) {
  .footer__grid {
    grid-template-columns: 1fr 1fr;
  }
  .footer__badge img {
    height: 36px;
  }
}
/* ========================================================= */
/*   MENU PANEL — editorial full-bleed                        */
/* ========================================================= */
.menu-panel {
  align-items: center;
  display: flex;
  inset: 0;
  justify-content: center;
  padding: 24px;
  position: fixed;
  z-index: 100;
}
.menu-panel[hidden] {
  display: none;
}
.menu-panel__scrim {
  -webkit-backdrop-filter: blur(8px);
  animation: menu-scrim-in 240ms ease forwards;
  backdrop-filter: blur(8px);
  background: rgba(0, 0, 0, 0.78);
  inset: 0;
  opacity: 0;
  position: absolute;
}
.menu-panel__card {
  animation: menu-card-in 360ms cubic-bezier(0.2, 0.9, 0.3, 1) forwards;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 18px;
  box-shadow: 0 60px 140px -24px rgba(0, 0, 0, 0.8);
  height: auto;
  opacity: 0;
  padding: clamp(48px, 6vw, 88px);
  position: relative;
  transform: translateY(12px) scale(0.985);
  width: min(1200px, 100%);
}
@keyframes menu-scrim-in {
  to {
    opacity: 1;
  }
}
@keyframes menu-card-in {
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}
.menu-panel__close {
  align-items: center;
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  height: 44px;
  justify-content: center;
  padding: 0;
  position: absolute;
  right: 24px;
  top: 24px;
  transition: background-color 160ms var(--ease);
  width: 44px;
}
.menu-panel__close:hover {
  background: rgba(255, 255, 255, 0.06);
}
.menu-panel__close:focus-visible {
  outline: 2px solid rgba(250, 108, 0, 0.6);
  outline-offset: 2px;
}
/* The "+" glyph (same as in the CRAFTSMAN+ wordmark) is rotated 45° at rest so
   it reads as an X close affordance; it springs back to "+" on hover. Two
   overlapping paths cross-fade between the muted-gray rest fill and the
   orange gradient hover fill. */
.menu-panel__close {
  color: var(--text-muted);
}
.menu-panel__close-plus {
  transform: rotate(45deg);
  transform-box: fill-box;
  transform-origin: 50% 50%;
  transition: transform 240ms var(--ease), opacity 200ms var(--ease);
}
.menu-panel__close-plus--hover {
  opacity: 0;
}
.menu-panel__close:hover .menu-panel__close-plus, .menu-panel__close:focus-visible .menu-panel__close-plus {
  transform: rotate(0deg);
}
.menu-panel__close:hover .menu-panel__close-plus--rest, .menu-panel__close:focus-visible .menu-panel__close-plus--rest {
  opacity: 0;
}
.menu-panel__close:hover .menu-panel__close-plus--hover, .menu-panel__close:focus-visible .menu-panel__close-plus--hover {
  opacity: 1;
}
.menu-panel__body {
  align-items: start;
  display: grid;
  gap: 48px;
  grid-template-columns: 1.4fr 1fr;
  height: 100%;
  position: relative;
}
.menu-panel__primary {
  counter-reset: menu;
  display: flex;
  flex-direction: column;
  gap: 6px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.menu-panel__primary-item {
  counter-increment: menu;
  position: relative;
}
.menu-panel__primary-link {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(40px, 6vw, 80px);
  line-height: 1;
  /* letter-spacing: -0.035em; */
  color: rgba(224, 224, 224, 0.18);
  transition: color 200ms var(--ease);
}
.menu-panel__primary-item::before {
  color: var(--text-dim);
  content: "0" counter(menu);
  font-family: var(--font-sans);
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  left: -44px;
  letter-spacing: 0.18em;
  position: absolute;
  top: 12px;
}
.menu-panel__primary-item:hover .menu-panel__primary-link, .menu-panel__primary-item.is-active .menu-panel__primary-link, .menu-panel__primary-link:focus-visible {
  color: var(--text);
}
.menu-panel__sub {
  /* Anchor the sub-menu's bottom edge to the same y as the active title's
     underline. Both share --underline-y, set on .menu-panel__body by the JS. */
  position: absolute;
  right: 0;
  width: 100%;
  top: var(--underline-y, 0);
  /* translateY(-100%) puts the sub's bottom at "top"; +1px aligns with the
     underline's bottom (it's 1px tall). +6px is the resting offset for the
     slide-up entrance — overridden below by .is-active. */
  transform: translateY(calc(-100% + 1px + 6px));
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-align: right;
  opacity: 0;
  visibility: hidden;
  transition: opacity 220ms var(--ease), transform 220ms var(--ease), visibility 220ms;
  pointer-events: none;
}
.menu-panel__sub.is-active {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(calc(-100% + 1px));
  visibility: visible;
}
.menu-panel__sub a {
  color: var(--text-muted);
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 500;
  line-height: 1.3;
  transition: color 160ms var(--ease);
}
.menu-panel__sub a:hover {
  color: var(--text);
}
.menu-panel__underline {
  background: var(--accent-gradient);
  border-radius: 0;
  height: 1px;
  left: 0;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  right: 0;
  top: 0;
  transform: translateY(var(--underline-y, 0));
  transition: opacity 200ms var(--ease), transform 280ms var(--ease);
}
.menu-panel__body.has-active .menu-panel__underline {
  opacity: 1;
}
@media (max-width: 720px) {
  .menu-panel__card {
    height: auto;
    padding: 56px 32px;
  }
  .menu-panel__body {
    gap: 32px;
    grid-template-columns: 1fr;
  }
  .menu-panel__primary-item::before {
    display: none;
  }
  .menu-panel__sub {
    position: static;
    text-align: left;
    top: auto;
    transform: none;
  }
  .menu-panel__sub.is-active {
    transform: none;
  }
  .menu-panel__sub a {
    font-size: 16px;
  }
}
/* ========================================================= */
/*   Responsive catchall                                      */
/* ========================================================= */
@media (max-width: 720px) {
  :root {
    --pad-x: 16px;
  }
  .hero {
    height: 55vh;
    isolation: isolate;
    overflow: visible;
    padding: clamp(28px, 4vw, 56px) 0 0;
    position: relative;
  }
  .meet-play--workspace .meet-play__title-word {
    display: block;
    line-height: 1.1;
  }
  .nav {
    height: 60px;
  }
  .nav__logo img {
    height: 20px;
  }
  .nav__right {
    gap: 8px;
  }
  /* Icon-only menu button: kill the word "MENU", keep the "+" */
  .nav__menu {
    font-size: 0;
    gap: 0;
    justify-content: center;
    padding: 0;
    width: 40px;
  }
  /* Mobile MENU+ icon size, applied to the .orangespan suffix. */
  .nav__menu .orangespan {
    font-size: 22px;
    font-weight: 500;
    line-height: 1;
  }
  /* Swap "Request a Demo" → "Demo" at mobile without touching the DOM copy */
  .nav .btn--primary {
    font-size: 0;
    /* No flex gap: the ::before label and the collapsed 0-width text node are
       two flex items, so the inherited 10px gap shoved "Demo" off-centre
       (client, Aug 28: center-align the demo button text). */
    gap: 0;
    height: 36px;
    letter-spacing: 0;
    padding: 0 14px;
  }
  .nav .btn--primary::before {
    content: "Demo";
    font-size: 13px;
    font-weight: 500;
    letter-spacing: 0.02em;
  }
  /* Platform hero headline: sized so the stack wraps to 3-4 lines on
     phones, not 5 (client, Aug 28). The spans carry their own sizes, so
     the parent clamp alone could not rein them in. */
  .platform-hero__title-line--ghost {
    font-size: clamp(30px, 8.6vw, 40px);
  }
  .platform-hero__title-line--gradient {
    font-size: clamp(28px, 8vw, 38px);
  }
}
.meet-play__title, .results__title, .services__title, .cta-banner__title {
  color: var(--title-fill);
}
/* Try them yourself sits over a darker area and needs more contrast: keep
   it in light grey rather than the dark fill used elsewhere. */
.gallery-v2__title {
  color: #E0E0E0;
}
/* ========================================================= */
/*   BOOK A DEMO — dedicated page                            */
/* ========================================================= */
.book {
  isolation: isolate;
  padding: clamp(32px, 4vw, 56px) 0 clamp(48px, 5vw, 80px);
  position: relative;
}
.book::before {
  background: radial-gradient(60% 50% at 12% 18%, rgba(250, 108, 0, 0.16) 0%, rgba(250, 108, 0, 0) 60%),
    radial-gradient(50% 40% at 88% 70%, rgba(235, 81, 115, 0.12) 0%, rgba(235, 81, 115, 0) 65%);
  content: "";
  filter: blur(40px);
  inset: 0;
  pointer-events: none;
  position: absolute;
  z-index: -1;
}
.book__inner {
  align-items: start;
  display: grid;
  gap: clamp(40px, 5vw, 96px);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  margin: 0 auto;
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
}
.book__copy {
  display: flex;
  flex-direction: column;
  position: sticky;
  top: clamp(96px, 9vw, 120px);
}
.book__copy .eyebrow {
  margin: 0 0 16px;
}
.book__title {
  align-items: flex-start;
  color: var(--text);
  display: flex;
  flex-direction: column;
  font-family: var(--font-display);
  font-size: clamp(48px, 5.6vw, 88px);
  font-weight: 900;
  letter-spacing: -0.04em;
  line-height: 0.98;
  margin: 0 0 clamp(16px, 1.6vw, 24px);
  padding-top: 0.04em;
}
.book__title-line {
  display: inline-block;
}
.book__title-line--accent {
  font-weight: 900;
  margin-left: 0;
}
.book__lede {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: clamp(15px, 1.1vw, 18px);
  line-height: 1.55;
  margin: 0 0 clamp(28px, 3vw, 44px);
  max-width: 44ch;
}
/* "What You Get" heading over the steps (copy finale, Jul 30). Bold
   off-white, small-caps rhythm to sit between the lede and the list. */
.book__points-title {
  color: var(--fg, #fafafa);
  font-family: var(--font-display);
  font-size: clamp(18px, 1.5vw, 22px);
  font-weight: 900;
  letter-spacing: 0.02em;
  margin: 0 0 clamp(14px, 1.4vw, 20px);
}
.book__points {
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 1.6vw, 24px);
  list-style: none;
  margin: 0;
  padding: 0;
}
.book__points li {
  border-top: 1px solid var(--rule);
  display: grid;
  gap: 16px;
  grid-template-columns: 36px 1fr;
  padding-top: 14px;
}
.book__points-num {
  color: var(--accent-from);
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  padding-top: 2px;
}
.book__points h3 {
  color: var(--text);
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0 0 4px;
}
.book__points p {
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1.5;
  margin: 0;
}
/* ----- Form panel ----- */
.book-form {
  position: relative;
  width: 100%;
}
.demo-panel--book {
  aspect-ratio: auto;
  grid-column: auto;
  width: 100%;
}
.demo-panel--book .demo-panel__bar {
  height: 40px;
}
.demo-panel__title {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  margin-left: 8px;
  text-transform: uppercase;
}
.book-form__body {
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: .4rem;
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans) !important;
  gap: 10px;
  padding: clamp(18px, 1.8vw, 26px) clamp(20px, 2vw, 28px);
  position: relative;
  z-index: 1;
}
/* Fieldset wrapper for radio/checkbox groups (Company size, Creative types).
   Reset native fieldset chrome; the legend acts as the field label. */
.book-form__group {
  border: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
}
.book-form__group .book-form__label {
  color: var(--text-muted);
  display: block;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  padding: 0;
}
.book-form__field em, .book-form__group .book-form__label em {
  color: var(--accent-from);
  font-style: normal;
  margin-left: 2px;
}
.book-form__choices {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.book-form__choice {
  align-items: center;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 8px;
  color: var(--text);
  cursor: pointer;
  display: inline-flex;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  gap: 8px;
  padding: 8px 14px;
  position: relative;
  transition: border-color 160ms var(--ease), background 160ms var(--ease), color 160ms var(--ease);
}
.book-form__choice:hover {
  border-color: rgba(250, 108, 0, 0.32);
}
.book-form__choice input {
  cursor: pointer;
  height: 100%;
  inset: 0;
  margin: 0;
  opacity: 0;
  position: absolute;
  width: 100%;
}
.book-form__choice:has(input:focus-visible) {
  outline: 2px solid rgba(250, 108, 0, 0.55);
  outline-offset: 2px;
}
.book-form__choice:has(input:checked) {
  background: rgba(250, 108, 0, 0.10);
  border-color: rgba(250, 108, 0, 0.62);
  color: var(--text);
}
.book-form__consent {
  align-items: flex-start;
  color: var(--text-muted);
  display: flex;
  font-family: var(--font-sans);
  font-size: 12px;
  gap: 10px;
  line-height: 1.55;
  margin-top: 4px;
}
.book-form__consent input[type="checkbox"] {
  accent-color: var(--accent-from);
  cursor: pointer;
  flex: 0 0 auto;
  height: 16px;
  /* Zero inline margins: the UA sheet gives checkboxes a 4px left margin,
     which broke the flush-left line with the inputs (client, Aug 28). */
  margin: 3px 0 0;
  width: 16px;
}
.book-form__consent a {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.book-form__consent a:hover {
  color: var(--accent-from);
}
.book-form__status {
  border-radius: 8px;
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.5;
  margin: 6px 0 0;
  padding: 10px 12px;
}
.book-form__status[hidden] {
  display: none;
}
.book-form__status[data-type="error"] {
  background: rgba(235, 81, 115, 0.10);
  border: 1px solid rgba(235, 81, 115, 0.4);
  color: rgba(255, 210, 215, 0.95);
}
.book-form__honeypot {
  height: 1px;
  left: -10000px;
  overflow: hidden;
  position: absolute;
  top: auto;
  width: 1px;
}
.book-form__row {
  display: grid;
  gap: 12px;
  grid-template-columns: 1fr 1fr;
}
.book-form__field {
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans);
  gap: 4px;
}
.book-form__field>span {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-weight: 500;
}
.book-form__field input, .book-form__field select, .book-form__field textarea {
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: .4rem;
  color: var(--text);
  font: inherit;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.4;
  padding: 9px 12px;
  transition: border-color 160ms var(--ease), background 160ms var(--ease), box-shadow 160ms var(--ease);
  width: 100%;
}
.book-form__field textarea {
  font-family: var(--font-sans);
  min-height: 56px;
  resize: vertical;
}
.book-form__field select {
  -webkit-appearance: none;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
    linear-gradient(-45deg, transparent 50%, var(--text-muted) 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-repeat: no-repeat;
  background-size: 5px 5px;
  padding-right: 36px;
}
.book-form__field input::placeholder, .book-form__field textarea::placeholder {
  color: rgba(255, 255, 255, 0.28);
}
.book-form__field input:hover, .book-form__field select:hover, .book-form__field textarea:hover {
  border-color: rgba(255, 255, 255, 0.16);
}
.book-form__field input:focus, .book-form__field select:focus, .book-form__field textarea:focus {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--accent-from);
  border-width: 1px;
  box-shadow: 0 0 0 3px rgba(250, 108, 0, 0.18);
  outline: none;
}
.book-form__field input:user-invalid {
  border-color: rgba(235, 81, 115, 0.5);
}
.book-form__submit {
  align-self: flex-end;
  height: 42px;
  margin-top: 4px;
  padding: 0 22px;
}
.book-form__legal {
  color: var(--text-dim);
  font-size: 11px;
  line-height: 1.45;
  margin: 2px 0 0;
}
/* ----- Success state ----- */
.book-form__success {
  align-items: flex-start;
  background: linear-gradient(180deg, rgba(250, 108, 0, 0.06), rgba(235, 81, 115, 0.03) 50%, transparent),
    #0e0e11;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: clamp(40px, 5vw, 72px);
  text-align: left;
}
.book-form__success[hidden] {
  display: none;
}
.book-form__success .eyebrow {
  margin: 0;
}
.book-form__success-title {
  font-family: var(--font-display);
  font-size: clamp(36px, 4vw, 56px);
  font-weight: 900;
  letter-spacing: 0em;
  line-height: 1;
  margin: 4px 0;
}
.book-form__success-lede {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  margin: 0 0 12px;
  max-width: 42ch;
}
@media (max-width: 960px) {
  .book__inner {
    gap: clamp(32px, 5vw, 48px);
    grid-template-columns: 1fr;
  }
  .book__copy {
    position: static;
    top: auto;
  }
  .book-form__row {
    grid-template-columns: 1fr;
  }
}
/* Caption + CTA centred BELOW the feature panel (client, Jul 28 — it used
   to be absolutely overlaid bottom-centre inside the panel, above the
   gradient fade, with pointer-events off). Plain flow block now: the panel's
   own fade provides the transition, the caption reads on the page bg and
   the CTA is a real clickable target. */
.demo-panel__caption {
  margin: clamp(18px, 2.5vw, 32px) auto 0;
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
  font-family: var(--font-display);
  font-size: clamp(14px, 1.2vw, 17px);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--text-muted);
  text-align: center;
  white-space: nowrap;
}
.abscenter {
  bottom: clamp(20px, 3vw, 40px);
  left: 10%;
  letter-spacing: 0.08em;
  padding: 0 17px;
  position: absolute;
  top: 110%;
  transform: translateX(-50%);
  z-index: 1001;
}
.centered-text, .centered {
  margin: 0 auto;
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
  text-align: center;
}
@media (max-width: 720px) {
  .demo-panel__caption {
    font-size: 13px;
    max-width: calc(100% - 24px);
    white-space: normal;
  }
  .book-form__submit {
      align-self: flex-start;
      height: 42px;
      margin-top: 4px;
      padding: 0 22px;
  }
}
/* Floating "back to top" button. Fixed bottom-right of the viewport,
   fades in once the user scrolls past one viewport. visibility is
   delayed on hide so the opacity transition can play out. */
.go-up {
  bottom: clamp(16px, 2vw, 28px);
  display: inline-flex;
  height: 52px;
  opacity: 0;
  pointer-events: none;
  position: fixed;
  right: clamp(16px, 2vw, 28px);
  transform: translateY(8px);
  transition: opacity 220ms var(--ease), transform 220ms var(--ease), visibility 0s linear 220ms;
  visibility: hidden;
  width: 52px;
  z-index: 200;
}
.go-up.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  transition: opacity 220ms var(--ease), transform 220ms var(--ease), visibility 0s linear 0s;
  visibility: visible;
}
.go-up:hover {
  transform: translateY(-2px);
}
.go-up:active {
  transform: translateY(0);
}
.go-up svg {
  display: block;
  height: 100%;
  width: 100%;
}
@media (max-width: 720px) {
  .go-up {
    height: 44px;
    width: 44px;
  }
}
/* ========================================================= */
/*   CASE STUDIES — proof grid                                */
/* ========================================================= */
.case-studies {
  background: #09090b;
  padding-block: clamp(64px, 7vw, 120px) var(--pad-section);
  position: relative;
}
.case-studies__inner {
  margin: 0 auto;
  max-width: var(--max-w);
  padding-inline: var(--pad-x);
}
.case-studies__head {
  margin-bottom: clamp(40px, 4.5vw, 64px);
}
.case-studies__title {
  font-family: var(--font-display);
  font-size: clamp(34px, 4.4vw, 64px);
  font-weight: 900;
  letter-spacing: -0.02em;
  line-height: 1.06;
  margin: 0;
  max-width: 22ch;
  padding-bottom: 0.12em;
}
.case-studies__title-line {
  display: block;
}
.case-studies__title-line--accent {
  -webkit-background-clip: text;
  background: linear-gradient(90deg, #fa6c00 0%, #eb5173 51.4%, #8912a0 100%);
  background-clip: text;
  color: transparent;
  margin: -0.08em -0.04em -0.18em 0;
  padding: 0.08em 0.04em 0.18em 0;
}
.case-studies__grid {
  display: grid;
  gap: clamp(14px, 1.4vw, 22px);
  grid-template-columns: repeat(2, 1fr);
}
.case-study {
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: 18px;
  display: grid;
  gap: clamp(4px, 0.5vw, 8px);
  grid-template-columns: 1fr 1fr;
  padding: 4px;
  transition: border-color 220ms var(--ease), transform 220ms var(--ease), box-shadow 220ms var(--ease);
}
.case-study:hover {
  border-color: rgba(250, 108, 0, 0.32);
  box-shadow: 0 24px 48px -28px rgba(0, 0, 0, 0.7);
  transform: translateY(-2px);
}
.case-study--description {
  grid-template-columns: 1fr;
}
.case-study__text {
  background: var(--surface);
  border-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  min-height: clamp(220px, 22vw, 280px);
  padding: clamp(18px, 1.6vw, 26px);
}
.case-study__text--full {
  min-height: clamp(240px, 24vw, 300px);
}
.case-study__logo {
  height: 26px;
  margin-bottom: clamp(14px, 1.3vw, 18px);
  max-width: 92px;
  object-fit: contain;
  object-position: left center;
  width: auto;
}
.case-study__title {
  color: var(--text);
  font-family: var(--font-sans);
  font-size: clamp(15px, 1.05vw, 17px);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  margin: 0 0 clamp(16px, 1.4vw, 20px);
  text-wrap: balance;
}
.case-study__desc {
  color: var(--text-muted);
  font-size: 13px;
  line-height: 1.55;
  margin: 0 0 clamp(16px, 1.5vw, 22px);
  max-width: 52ch;
}
.case-study__stats {
  display: flex;
  gap: clamp(24px, 2.4vw, 40px);
  list-style: none;
  margin: 0 0 clamp(18px, 1.8vw, 24px);
  padding: 0;
}
.case-study__stats li {
  padding: 0;
}
.case-study__stat-value {
  color: var(--text);
  display: block;
  font-family: var(--font-display);
  font-size: clamp(22px, 1.9vw, 30px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
}
.case-study__stat-label {
  color: var(--text-muted);
  display: block;
  font-size: 11px;
  line-height: 1.3;
  margin-top: 6px;
}
.case-study__readmore {
  align-items: center;
  align-self: flex-start;
  color: var(--text);
  display: inline-flex;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  gap: 6px;
  margin-top: auto;
  transition: color 160ms var(--ease), gap 160ms var(--ease);
}
.case-study__readmore:hover {
  color: var(--accent-from);
  gap: 10px;
}
.case-study__media {
  background: #1a1a1d;
  border-radius: var(--radius-lg);
  display: block;
  min-height: clamp(220px, 22vw, 280px);
  overflow: hidden;
  position: relative;
}
.case-study__media>img:not(.case-study__brand) {
  height: 100%;
  object-fit: cover;
  object-position: center;
  width: 100%;
}
.case-study__media--clorox {
  align-items: center;
  background: radial-gradient(120% 90% at 50% 50%, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 60%),
    linear-gradient(135deg, #0b3fa3 0%, #1665c1 45%, #1f86e0 100%);
  display: flex;
  justify-content: center;
}
.case-study__media--clorox .case-study__brand {
  filter: drop-shadow(0 14px 32px rgba(0, 0, 0, 0.4));
  height: auto;
  max-width: 280px;
  width: 62%;
}
.case-study__media--placeholder {
  background: radial-gradient(100% 80% at 60% 40%, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0) 60%),
    #1a1a1d;
}
@media (max-width: 960px) {
  .case-studies__grid {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 640px) {
  .case-study, .case-study--description {
    grid-template-columns: 1fr;
  }
  .case-study__text, .case-study__media {
    min-height: 0;
  }
  .case-study__media {
    aspect-ratio: 16 / 10;
  }
}
.legal-page, .legal-page {
  background: #09090b;
  display: inline-block;
  padding: 1rem;
  padding-block: clamp(64px, 7vw, 120px) var(--pad-section);
  padding-left: 3rem;
  width: 90vw;
}
.control {
  font-family: "Inter", system-ui, -apple-system, Segoe UI, Helvetica, Arial, sans-serif;
  display: block;
  position: relative;
  padding-left: 30px;
  margin-bottom: 5px;
  padding-top: 3px;
  cursor: pointer;
  font-size: 16px;
  @keyframes s-ripple {
    0% {
      transform: scale(0);
    }
    20% {
      transform: scale(1);
    }
    100% {
      opacity: 0;
      transform: scale(1);
    }
  }
  @keyframes s-ripple-dup {
    0% {
      transform: scale(0);
    }
    30%, 60% {
      transform: scale(1);
    }
    100% {
      opacity: 0;
      transform: scale(1);
    }
  }
  .control-checkbox input+.control_indicator::before {
    animation: s-ripple 250ms ease-out;
  }
  .control-checkbox input:checked+.control_indicator::before {
    animation-name: s-ripple-dup;
  }
}
