/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  padding: 12px 22px;
  border-radius: var(--radius-m);
  font-weight: 600;
  font-size: 15px;
  white-space: nowrap;
  transition:
    background-color 150ms var(--ease-standard),
    border-color 150ms var(--ease-standard),
    transform 150ms var(--ease-standard),
    box-shadow 150ms var(--ease-standard);
}

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

.btn--primary {
  background: var(--brand-blue);
  color: var(--ink-inverse);
  border: 1px solid var(--brand-blue);
  box-shadow: 0 4px 14px rgb(25 118 210 / 32%);
}

.btn--primary:hover {
  background: var(--brand-blue-hover);
  border-color: var(--brand-blue-hover);
  box-shadow: 0 6px 20px rgb(25 118 210 / 42%);
}

.btn--ghost {
  background: transparent;
  border: 1px solid var(--brand-blue);
  color: var(--brand-blue);
}

.btn--ghost::after {
  content: "\2192";
  font-weight: 400;
}

.btn--ghost:hover {
  background: var(--brand-selection);
}

/* Solid, with dark text. Translucent white was the wrong answer twice over: at 6% it vanished
   against a dark band, and once it was opaque enough to see, white-on-white stopped being legible.
   A filled button with the band's own dark ink reads at any opacity and still sits clearly below
   the blue CTA. */
.section--dark .btn--ghost {
  border-color: var(--ink-inverse);
  color: var(--footer-bg);
  background: var(--ink-inverse);
}

.section--dark .btn--ghost:hover {
  background: rgb(255 255 255 / 88%);
  border-color: var(--ink-inverse);
}

.btn--lg {
  padding: 16px 28px;
  font-size: 16px;
}

.btn--block {
  width: 100%;
}

.btn.is-disabled,
.btn.is-disabled:hover {
  opacity: 0.5;
  cursor: not-allowed;
  background: var(--border-default);
  border-color: var(--border-default);
  color: var(--ink-subtle);
  box-shadow: none;
}

/* ---------- Header / Nav ----------
   Solid white ground with the dark wordmark, even over the dark hero -- the header is its own
   surface, not part of whatever section sits behind it. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--surface-card);
  border-bottom: 1px solid var(--border-default);
  transition:
    box-shadow 250ms var(--ease-standard),
    padding 250ms var(--ease-standard);
}

.site-header.is-scrolled {
  box-shadow: var(--shadow-card);
}

.site-header.is-scrolled .nav {
  padding-block: var(--space-2xs);
}

.nav {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  padding-block: var(--space-s);
  transition: padding 250ms var(--ease-standard);
}

.nav__logo {
  display: flex;
  align-items: center;
  height: 26px;
}

.nav__logo img {
  height: 100%;
  width: auto;
}

.nav__logo--wide {
  display: none;
}

.nav__logo--compact {
  display: block;
}

@media (min-width: 700px) {
  .nav__logo--wide {
    display: block;
  }
  .nav__logo--compact {
    display: none;
  }
}

.nav__links {
  display: none;
  align-items: center;
  gap: var(--space-l);
  font-size: 15px;
  font-weight: 500;
  color: var(--ink-secondary);
}

@media (min-width: 900px) {
  .nav__links {
    display: flex;
    /* Dead-centre of the header bar, independent of how wide the logo or actions are --
       not just "the gap between them", which shifts off-centre whenever those differ in width. */
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }
}

.nav__links a:hover,
.nav__links a.is-active {
  color: var(--brand-blue);
}

.nav__actions {
  display: flex;
  align-items: center;
  gap: var(--space-s);
}

/* Hidden below 900px (the same breakpoint .nav__links collapses at) -- alongside the logo,
   "Book a demo" and the menu toggle there isn't room for a third action too. It moves into
   #nav-mobile's own link list instead. */
.nav__login {
  display: none;
}

@media (min-width: 900px) {
  .nav__login {
    display: inline-flex;
  }
}

.nav-toggle {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 36px;
  height: 36px;
  padding: 8px;
}

@media (min-width: 900px) {
  .nav-toggle {
    display: none;
  }
}

.nav-toggle span {
  display: block;
  height: 2px;
  background: var(--ink);
  border-radius: 2px;
  transition: transform 200ms var(--ease-standard), opacity 200ms var(--ease-standard);
}

.nav-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}

.nav-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

.nav-mobile {
  display: none;
  flex-direction: column;
  gap: var(--space-xs);
  padding-block: var(--space-s);
  border-top: 1px solid var(--border-default);
  background: var(--surface-card);
  color: var(--ink);
  font-size: 16px;
  font-weight: 500;
}

.nav-mobile.is-open {
  display: flex;
}

@media (min-width: 900px) {
  .nav-mobile {
    display: none !important;
  }
}

/* ---------- Hero ----------
   Copy left, network visual right. */
.hero {
  position: relative;
  overflow: hidden;
  padding-top: calc(var(--space-section) * 0.9);
  padding-bottom: var(--space-section);
  background:
    radial-gradient(60rem 34rem at 78% 40%, rgb(25 118 210 / 32%), transparent 68%),
    linear-gradient(160deg, #0d2440 0%, var(--footer-bg) 62%);
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(rgb(255 255 255 / 6%) 1px, transparent 1px);
  background-size: 36px 36px;
  -webkit-mask-image: radial-gradient(52rem 30rem at 30% 20%, #000, transparent 75%);
  mask-image: radial-gradient(52rem 30rem at 30% 20%, #000, transparent 75%);
}

.hero__grid {
  position: relative;
  z-index: 1;
  display: grid;
  /* Explicit even without the 2-column override below: a bare `display: grid` with no template
     leaves a single implicit column sized to its content's max-content rather than stretched to
     the container, which is why the hero visual used to render at a fixed ~300px on every
     narrow viewport regardless of how much room was actually available. */
  grid-template-columns: 1fr;
  gap: var(--space-xl);
  align-items: center;
}

@media (min-width: 960px) {
  .hero__grid {
    grid-template-columns: 1fr 1.05fr;
    gap: var(--space-l);
  }
}

.hero__lede {
  max-width: 34rem;
}

.hero h1 {
  font-size: clamp(36px, 4.4vw, 56px);
  line-height: 1.12;
  letter-spacing: -0.025em;
  color: var(--ink-inverse);
}

.hero__sub {
  margin-top: var(--space-m);
  font-size: 17px;
  line-height: 1.6;
  color: rgb(255 255 255 / 72%);
  max-width: 32rem;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  margin-top: var(--space-l);
}

.hero__visual {
  display: grid;
  place-items: center;
}

/* max-width only, no margin-inline: auto. A grid item with an auto margin stops stretching and
   sizes to fit-content instead -- and the lines SVG's fit-content contribution collapsed to the
   CSS default replaced-element size (300x150, scaled by its aspect ratio) rather than its actual
   720x560, which is why the whole hero network used to render at a fixed ~300px on any viewport
   under 960px no matter how much room was actually available. */
@media (max-width: 959px) {
  .hero__visual {
    max-width: 34rem;
    /* Pulled up to tuck roughly its top half behind the headline/buttons above, instead of
       sitting in its own full-width band below them -- the negative margin scales with the
       viewport since the visual's own height does too. */
    margin-top: clamp(-170px, -40vw, -120px);
    position: relative;
    z-index: 0;
  }

  .hero__lede {
    position: relative;
    z-index: 1;
  }
}

/* The note on what the network's logos mean, at the foot of the hero: small and quiet, but
   readable -- it is a legal clarification, not decoration. In the flow below the two-column
   breakpoint, pinned to the hero's bottom edge above it. */
.hero__logo-note {
  position: relative;
  z-index: 1;
  max-width: 40rem;
  margin: var(--space-l) 0 0 auto;
  padding-inline: var(--space-s);
  text-align: right;
  font-size: 12px;
  line-height: 1.5;
  color: rgb(255 255 255 / 55%);
}

@media (min-width: 640px) {
  .hero__logo-note {
    padding-inline: var(--space-l);
  }
}

/* Right-aligned to the container's content edge, under the logo network. */
@media (min-width: 960px) {
  .hero__logo-note {
    position: absolute;
    inset-inline: auto max(0px, calc((100% - var(--container-max)) / 2));
    bottom: var(--space-m);
    margin: 0;
  }
}

/* ---------- Hero network ----------
   The connecting lines are one static SVG; the nodes are the real retailer/manufacturer logos
   from assets/logos/ (already used elsewhere on the site), positioned by percentage so they stay
   locked to the lines at any width. Each logo is tinted into the same blue duotone -- real color
   logos side by side would fight the headline and read as a rainbow, not a network. */
.hero-network {
  position: relative;
  width: 100%;
}

.hero-network__lines {
  display: block;
  width: 100%;
  height: auto;
  position: relative;
  z-index: 2;
  /* The halo logos sit well outside the 720x560 viewBox (the furthest is at -90% left), and an
     outer <svg> clips to its viewBox by default -- without this, the wires out to them stopped
     dead at the figure's edge. The element's own box is unchanged, so every logo's percentage
     position still lines up; the drawing just spills, exactly like the logos already do. */
  overflow: visible;
}

.hero-network__logo {
  position: absolute;
  z-index: 3;
  transform: translate(-50%, -50%);
  box-sizing: border-box;
  background: #eef4fc;
  border-radius: 50%;
  object-fit: contain;
  box-shadow: 0 10px 22px rgb(3 12 22 / 45%), 0 0 0 1px rgb(255 255 255 / 10%);
  /* Grayscale first so each logo's original hue is gone, then sepia + a 180deg hue-rotate remaps
     that single warm duotone to a cool blue one -- the standard CSS-only trick for tinting an
     arbitrary image a single color without a canvas/build step. Kept in a variable because the
     pulse-arrival keyframe in motion.css has to restate the whole filter to brighten it. */
  --duotone: grayscale(1) brightness(1) sepia(1) hue-rotate(180deg) saturate(1.7) contrast(1.15);
  filter: var(--duotone);
  /* main.js writes this per chip as the window changes; the transition is what turns that
     into the network breathing in and out rather than marks blinking on. */
  transition: opacity 320ms var(--ease-standard);
}

/* ---------- Hero network depth layers ----------
   Three extra planes give the constellation volume instead of one flat ring: a dim, blurred
   "back" halo sitting behind the connecting lines (further global manufacturers the network
   extends toward but doesn't draw a wire to), and a "pop" foreground of a few bigger marks
   sitting above everything (closer to the viewer). Every plane keeps the same blue duotone as
   the rest of the network -- real logo color only on the front plane read as a handful of
   brands randomly breaking the palette, not as depth. */
.hero-network__logo--back {
  /* Same plane as the front chips (DOM order still puts those on top), and fully opaque: the
     wires run underneath, so anything see-through here shows the line straight through the mark.
     Distance is carried by brightness instead -- the further back a chip sits the darker it is
     printed, which is what a mark receding into an unlit background actually does. */
  z-index: 3;
  opacity: 1;
  box-shadow: none;
  /* Depth tier. --halo scales the chip; the tier classes below pair each size with its own blur
     and opacity, because "further away" has to mean smaller AND softer AND fainter together --
     one flat halo at a single size reads as a sticker sheet, not as distance. */
  --halo: 1;
  --duotone: grayscale(1) brightness(0.92) sepia(1) hue-rotate(180deg) saturate(1.4) contrast(0.9) blur(1.6px);
  filter: var(--duotone);
}

.hero-network__logo--halo-s {
  --halo: 0.8;
  --duotone: grayscale(1) brightness(0.88) sepia(1) hue-rotate(180deg) saturate(1.4) contrast(0.8) blur(2.3px);
}

.hero-network__logo--halo-l {
  --halo: 1.28;
  --duotone: grayscale(1) brightness(0.96) sepia(1) hue-rotate(180deg) saturate(1.4) contrast(0.98) blur(1px);
}

/* Size and sharpness climb with the tier, but opacity is held back: at full strength the
   biggest halo chips read as foreground and out-compete the real front ring behind them. */
.hero-network__logo--halo-xl {
  --halo: 1.55;
  --duotone: grayscale(1) brightness(1) sepia(1) hue-rotate(180deg) saturate(1.4) contrast(1.05) blur(0.5px);
}

/* The outermost drift carries no look of its own: flattening every chip in it to one opacity
   cancelled out the depth steps above, which is the whole mechanism that makes distance read.
   Nor does it drop out at a breakpoint any more -- how far the network reaches is measured
   against the frame it actually has (see the hero network block in main.js), so the outer rings
   fade in and out with the window instead of snapping at one width. */
.hero-network__logo--far {
  opacity: 0.3;
}

/* Two further steps out again, for the drift trailing off behind the headline column. They have
   to come after --far: that rule sets one opacity for the whole outer plane, and the point of
   these is that each step is fainter and softer than the one before it, so they need to win.
   Nothing on the constellation proper uses them -- they exist so "further away" keeps meaning
   smaller, dimmer and more out of focus past where the network itself ends. */
.hero-network__logo--halo-xs {
  --halo: 0.66;
  --duotone: grayscale(1) brightness(0.84) sepia(1) hue-rotate(180deg) saturate(1.4) contrast(0.72) blur(2.8px);
}

.hero-network__logo--halo-2xs {
  --halo: 0.52;
  --duotone: grayscale(1) brightness(0.8) sepia(1) hue-rotate(180deg) saturate(1.4) contrast(0.64) blur(3.4px);
}

.hero-network__logo--xl {
  z-index: 4;
  box-shadow: 0 18px 38px rgb(3 12 22 / 50%), 0 0 0 1px rgb(255 255 255 / 16%);
}

/* Sized in three steps rather than scaling with the viewport: the figure itself shrinks a lot
   between a narrow phone and a desktop column, and chips large enough to read comfortably on
   desktop start overlapping their neighbours once that figure gets small enough. */
/* Padding is deliberately tight (~11% of the circle's own diameter, down from ~19%): the circle
   itself is what "chip size" earlier asks were about, but the logos inside were still reading
   small because most of that circle was empty margin. Same width/height as before, just less
   padding, so it's the logo -- not the white circle -- that grows. */
.hero-network__logo--lg {
  width: 46px;
  height: 46px;
  padding: 5px;
}

.hero-network__logo--sm {
  width: 34px;
  height: 34px;
  padding: 4px;
}

.hero-network__logo--back {
  width: calc(23px * var(--halo));
  height: calc(23px * var(--halo));
  padding: calc(4px * var(--halo));
}

.hero-network__logo--xl {
  width: 40px;
  height: 40px;
  padding: 5px;
}

@media (min-width: 520px) {
  .hero-network__logo--lg {
    width: 66px;
    height: 66px;
    padding: 7px;
  }

  .hero-network__logo--sm {
    width: 49px;
    height: 49px;
    padding: 5px;
  }

  .hero-network__logo--back {
    width: calc(32px * var(--halo));
    height: calc(32px * var(--halo));
    padding: calc(5px * var(--halo));
  }

  .hero-network__logo--xl {
    width: 58px;
    height: 58px;
    padding: 7px;
  }
}

@media (min-width: 900px) {
  .hero-network__logo--lg {
    width: 80px;
    height: 80px;
    padding: 9px;
  }

  .hero-network__logo--sm {
    width: 59px;
    height: 59px;
    padding: 6px;
  }

  .hero-network__logo--back {
    width: calc(38px * var(--halo));
    height: calc(38px * var(--halo));
    padding: calc(6px * var(--halo));
  }

  .hero-network__logo--xl {
    width: 88px;
    height: 88px;
    padding: 11px;
  }
}

/* ---------- Section titles ---------- */
/* text-wrap: wrap overrides the h1-h4 default of `balance` (base.css) -- balance actively
   equalises line lengths, which was breaking these two-line headlines much earlier than the
   available width allowed (e.g. splitting close to 50/50 instead of filling line 1). Normal wrap
   just fills every line greedily, breaking as far right as the text and max-width allow. */
.section-title {
  max-width: 34ch;
  margin-bottom: var(--space-l);
  font-size: clamp(30px, 3.6vw, 46px);
  line-height: 1.18;
  letter-spacing: -0.02em;
  font-weight: 700;
  text-wrap: wrap;
}

/* ---------- Feature cards (use cases + why A62i) ---------- */
.feature-grid {
  display: grid;
  gap: var(--space-m);
}

@media (min-width: 700px) {
  .feature-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.feature-card {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: var(--space-l) var(--space-m) var(--space-m);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-l);
  background: var(--surface-card);
  /* --shadow-card (0 1px 2px, 6% alpha) is too faint to read as a shadow on a flat white
     section -- bumped to the same --shadow-card-lg the founder/voice cards already use. */
  box-shadow: var(--shadow-card-lg);
  transition: border-color 300ms var(--ease-standard);
}

/* A soft glow that just fades in on hover, at a fixed spot -- nothing tracks the cursor any
   more (that was the "moving interior" the pointer-tracked version produced: the glow sliding
   around as the mouse moved inside the card read as content shifting, even though no element
   was actually repositioned). z-index: -1 keeps it under the flex children (icon, text) without
   having to touch each one's stacking. */
/* Follows the pointer -- JS (main.js) tracks the cursor into --spot-x/--spot-y on mousemove.
   Only this pseudo moves; the card box and everything inside it (icon, text) stay completely
   still, so nothing about the actual layout shifts, just this glow underneath it. */
.feature-card::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: var(--spot-x, 50%);
  top: var(--spot-y, 50%);
  width: 320px;
  height: 320px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgb(25 118 210 / 14%), transparent 68%);
  opacity: 0;
  transition: opacity 280ms var(--ease-standard);
  pointer-events: none;
}

/* No transform here -- the box itself (and everything inside it) must stay put on hover, only
   the border tints and the pointer glow fades in. */
.feature-card:hover {
  border-color: rgb(25 118 210 / 24%);
}

.feature-card:hover::before {
  opacity: 1;
}

/* Icon and label share a row instead of stacking: the label is what says which job the card is
   about, and reading it beside its own icon is faster than reading it under one. It is free to
   wrap to a second line next to the icon -- the row is centred, so a two-line label still lines
   up with it. */
.feature-card__head {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  margin-bottom: var(--space-m);
}

.feature-card__head .feature-card__icon {
  flex: none;
  width: 56px;
  height: 56px;
  margin-bottom: 0;
}

.feature-card__head .feature-card__icon svg {
  width: 28px;
  height: 28px;
}

.feature-card__head .feature-card__eyebrow {
  font-size: 17px;
  letter-spacing: 0.05em;
  line-height: 1.35;
  margin-bottom: 0;
}

.feature-card__icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: var(--space-m);
  border-radius: var(--radius-m);
  background: var(--brand-selection);
  color: var(--brand-blue);
}

.feature-card__icon svg {
  width: 22px;
  height: 22px;
}

.feature-card__eyebrow {
  font-size: 17px;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--brand-blue);
  margin-bottom: var(--space-xs);
}

.feature-card__question {
  font-size: 22px;
  line-height: 1.35;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.feature-card__body {
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink-secondary);
}

/* ---------- Flow track (how it works) ----------
   Five stages on one horizontal rail. The rail is a single line behind the badges so the eye
   reads one continuous route; on narrow screens it flips to a vertical rail on the left. */
.flow-section {
  background: var(--surface-flow);
}

.flow-track {
  position: relative;
  display: grid;
  /* Stacked, this gap is what is left of the rail between one badge's ring and the next one's:
     with 64px badges anything tighter reads as dashes rather than as a route. */
  gap: var(--space-l);
  /* The rail needs visibly more air under the heading than the heading's own bottom margin
     gives it: set that close and the five stages read as part of the title block. */
  margin: var(--space-xl) 0 0;
  list-style: none;
}

.flow-track::before {
  content: "";
  position: absolute;
  background: linear-gradient(180deg, var(--brand-blue), rgb(25 118 210 / 30%));
  /* Half a badge in from each end, and half a badge across: the rail has to come out of the
     centre of the first and last badges, so these track the badge size below. */
  top: 32px;
  bottom: 32px;
  left: 31px;
  width: 2px;
}

.flow-node {
  position: relative;
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: var(--space-s);
  align-items: center;
}

.flow-node__badge {
  z-index: 1;
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--brand-blue);
  color: var(--ink-inverse);
  box-shadow: 0 6px 16px rgb(25 118 210 / 32%), 0 0 0 6px var(--surface-flow);
  transition: transform 300ms var(--ease-standard);
}

.flow-node:hover .flow-node__badge {
  transform: scale(1.08);
}

.flow-node__badge svg {
  width: 30px;
  height: 30px;
}

.flow-node__label {
  font-size: 14px;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--brand-blue);
  line-height: 1.45;
}

/* Two lines only where the track runs horizontally and the column is narrow. Stacked, the label
   sits beside its badge with room to spare, so it reads as one line -- the space before the <br>
   in the markup is what keeps the two halves apart once the break itself is gone. */
.flow-node__label br {
  display: none;
}

@media (min-width: 860px) {
  .flow-track {
    grid-template-columns: repeat(5, 1fr);
    gap: var(--space-s);
    /* Top-aligned, not centred: "Understand the players" wraps to two lines, and centring would
       lift its badge off the rail the other four sit on. */
    align-items: start;
  }

  .flow-track::before {
    top: 32px;
    bottom: auto;
    left: 10%;
    right: 10%;
    width: auto;
    height: 2px;
    background: linear-gradient(90deg, var(--brand-blue), rgb(25 118 210 / 35%));
  }

  .flow-node {
    grid-template-columns: 1fr;
    justify-items: center;
    text-align: center;
    gap: var(--space-s);
  }

  .flow-node__label br {
    display: inline;
  }
}

/* ---------- Prism block ---------- */
/* The wheel is square, so left alone its width is what sets this section's height --
   which is how the band ended up ~860px tall with most of it empty air. Instead of
   shrinking the graphic to shrink the band, the wheel is now deliberately taller than
   the band and gets cropped by it (see .prism-wheel's negative block margin). The
   clip has to live here: without it the glow and the outer dots spill onto the light
   sections above and below. */
.prism-section {
  padding-block: calc(var(--space-section) * 0.5);
  overflow: hidden;
  background:
    radial-gradient(46rem 26rem at 76% 46%, rgb(25 118 210 / 16%), transparent 70%),
    var(--footer-bg);
}

.prism-block {
  display: grid;
  gap: var(--space-xl);
  align-items: center;
}

@media (min-width: 900px) {
  .prism-block {
    grid-template-columns: 0.85fr 1.15fr;
  }
}

.prism-block h2 {
  font-size: clamp(30px, 3.8vw, 48px);
  line-height: 1.15;
  letter-spacing: -0.025em;
  font-weight: 700;
  color: var(--ink-inverse);
  max-width: 20ch;
  text-wrap: wrap;
}

/* Brighter than the usual dark-section body copy, and carrying its own shadow: this paragraph
   is the one that sits over the wheel's glow, so 72% white left it competing with the artwork
   rather than reading over it. */
.prism-block__text {
  margin-top: var(--space-m);
  font-size: 16px;
  line-height: 1.65;
  color: rgb(255 255 255 / 94%);
  max-width: 34rem;
  text-shadow: 0 1px 12px rgb(10 28 43 / 75%);
}

.prism-block h2 {
  text-shadow: 0 1px 14px rgb(10 28 43 / 70%);
}

/* The wheel overlaps the copy instead of sitting beside it. On its own it read as a small
   graphic marooned in dark space -- worst on a phone, where the block stacks and the wheel
   lands in a band of its own. Tucking it behind the words gives it room to be larger and ties
   it to the text it illustrates; .prism-section already clips, so the edges crop cleanly. */
.prism-wheel {
  position: relative;
  display: grid;
  place-items: center;
  padding: var(--space-m);
  margin-top: -14%;
}

.prism-block__copy {
  position: relative;
  z-index: 1;
}

/* Desktop only. The negative block margin is what lets the graphic render bigger than
   the grid row it occupies, so the band gets shorter and the wheel gets larger at the
   same time -- the row stops paying for the wheel's full height. Narrow viewports are
   left alone: there the wheel is width-constrained and already short, so pulling it in
   would just drag it up into the copy above. */
@media (min-width: 900px) {
  .prism-wheel {
    margin-block: -10rem;
    /* Slides left so the wheel passes behind the copy column rather than starting where it ends. */
    margin-inline-start: -30%;
  }
}

/* Two halos rather than one: a tight core that reads as the wheel emitting light, and a wide,
   soft wash that lifts it off the band. Both sit behind the artwork, so they add to the screened
   glow instead of washing the linework out. Toned down on purpose: at full strength the band read
   as a light source rather than a dark section with a lit graphic in it. */
.prism-wheel::before {
  content: "";
  position: absolute;
  inset: 2%;
  border-radius: 50%;
  background: radial-gradient(circle, rgb(25 118 210 / 30%), transparent 72%);
  filter: blur(44px);
}

.prism-wheel::after {
  content: "";
  position: absolute;
  inset: -20%;
  border-radius: 50%;
  background: radial-gradient(circle, rgb(96 165 250 / 12%), transparent 68%);
  filter: blur(80px);
  z-index: -1;
}

/* The artwork is a glow drawn on black. Screen blending turns that black into nothing and
   leaves only the light, so the wheel sits in the section's own gradient instead of inside a
   visible black rectangle. */
.prism-wheel img {
  position: relative;
  width: 100%;
  max-width: 68rem;
  height: auto;
  mix-blend-mode: screen;
  /* A62I-642: below the render's own levels now. Even at 1.08 / 1.15 the polygon read as
     fluorescent cyan next to the rest of the page; less brightness and saturation pull it toward
     the brand blue the other sections use while the linework and dots stay legible. */
  filter: brightness(0.88) saturate(0.72);
}

/* Phones: the wheel is the whole point of this band, and inside the container's gutters it was
   a small circle in a tall dark box. It now breaks past those gutters, runs far wider than the
   viewport and climbs up under the copy; the section clips what hangs over the edges.
   Placed after the base rule above on purpose -- same specificity, so the later one has to be
   this one or the 100%/max-width above silently wins and nothing changes on a phone. */
@media (max-width: 899px) {
  /* The grid gap is what was holding the wheel off the copy, and a negative margin-top could not
     close it: on a grid item that shrinks the row rather than lifting the item over the one
     above. `top` moves the painted box without touching layout, so the wheel genuinely climbs
     under the text, and the matching negative margin-bottom gives back the space it vacates so
     the band does not grow taller. .prism-wheel is already position: relative. */
  .prism-block {
    gap: 0;
  }

  .prism-wheel {
    top: -3rem;
    margin-bottom: -2.5rem;
    margin-inline: calc(var(--space-xl) * -1);
  }

  .prism-wheel img {
    width: 190%;
    max-width: none;
  }
}

/* ---------- Voices carousel ----------
   One card on stage at a time, with the intro held in a column beside it. The roles change but
   the card frame never moves, so the reader's eye stays on the quote as it cycles. */
.voices-section {
  background:
    radial-gradient(50rem 28rem at 12% 10%, rgb(25 118 210 / 20%), transparent 68%),
    var(--footer-bg);
}

.voices {
  display: grid;
  gap: var(--space-l);
}

@media (min-width: 1000px) {
  .voices {
    grid-template-columns: 0.72fr 1.28fr;
    gap: var(--space-xl);
    /* Safe to centre now that .voice-card has a fixed height (see below) -- it used to track
       content and vary per quote, which made a centred heading drift as the carousel turned. */
    align-items: center;
  }
}

.voices__intro h2 {
  font-size: clamp(30px, 3.8vw, 48px);
  line-height: 1.15;
  letter-spacing: -0.025em;
  font-weight: 700;
  color: var(--ink-inverse);
  max-width: 20ch;
  text-wrap: wrap;
}

.voices__sub {
  margin-top: var(--space-m);
  font-size: 16px;
  line-height: 1.6;
  color: rgb(255 255 255 / 68%);
  max-width: 28rem;
}

.voices__cards {
  position: relative;
}

.voice-card {
  display: grid;
  overflow: hidden;
  border-radius: var(--radius-l);
  background: var(--surface-card);
  box-shadow: 0 24px 48px rgb(3 12 22 / 42%);
}

.voice-card[hidden] {
  display: none;
}

/* Fixed footprint on desktop so all six slides occupy exactly the same box regardless of how
   long a given quote/answer pair runs -- letting height track content was why the carousel used
   to resize itself as it rotated. */
@media (min-width: 620px) {
  .voice-card {
    grid-template-columns: 0.8fr 1fr;
    height: 380px;
  }
}

/* Wrapper is the actual grid cell; the img inside it bleeds 2px past every edge (see
   .voice-card__photo-img) rather than landing exactly on it. Chromium's rounded-corner clip on
   `.voice-card` below anti-aliases along the whole boundary, not just the curved corners, and an
   image edge that exactly coincides with that boundary let the clip's own feathering show the
   white card background through as a hairline band across the full width. */
.voice-card__photo {
  position: relative;
  overflow: hidden;
  background: var(--brand-selection);
}

/* That "white line" was never a rendering bug -- it's each portrait's own white shirt collar,
   which sits right at the bottom edge of every one of these square source photos. cover's own
   scale math already matches this box's height with zero slack (so there was never any spare
   image content to bleed in past the edge), meaning the only real fix is to crop the visible
   window differently. Scaling up from the TOP edge keeps the face framed exactly as before
   (there's headroom above the hair in every source photo to absorb it) while pushing the
   bottom ~15% of the image -- the collar -- down past the frame, where overflow:hidden clips
   it away for good. */
.voice-card__photo-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  transform: scale(1.15);
  transform-origin: 50% 0%;
}

@media (max-width: 619px) {
  .voice-card__photo {
    aspect-ratio: 3 / 2;
  }
}

.voice-card__body {
  display: flex;
  flex-direction: column;
  padding: var(--space-l);
  overflow: hidden;
}

.voice-card__eyebrow {
  font-size: 16px;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--brand-blue);
}

.voice-card__quote {
  margin: var(--space-s) 0 var(--space-s);
  font-size: clamp(18px, 1.8vw, 23px);
  line-height: 1.32;
  font-weight: 400;
  letter-spacing: -0.015em;
  color: var(--ink);
}

/* This is the line that actually explains what the product does for that role, so it reads at
   the same size as body copy elsewhere on the page rather than as a caption under the quote. */
.voice-card__answer {
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink-secondary);
}

.voice-card__answer strong {
  color: var(--brand-blue);
  font-weight: 700;
}

.voice-card__role {
  margin-top: auto;
  padding-top: var(--space-m);
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--brand-blue);
}

/* Wraps rather than overflowing: with eight dots there is not room for the row plus both arrows
   on a phone, and space-between pushed the arrows off the edge of the screen. */
.voices__controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-s) var(--space-m);
  margin-top: var(--space-m);
}

@media (min-width: 620px) {
  .voices__controls {
    flex-wrap: nowrap;
    justify-content: space-between;
  }
}

.voices__dots {
  flex-wrap: wrap;
  justify-content: center;
}

.voices__dots {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
}

/* A62I-642: brighter at rest -- at 35% brand blue on the dark band the row was easy to miss, and
   with it the hint that there is more than one card. */
.voices__dot {
  position: relative;
  width: 10px;
  height: 10px;
  padding: 0;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: rgb(144 192 255 / 45%);
  transition: background 220ms var(--ease-standard), width 220ms var(--ease-standard);
}

.voices__dot:hover {
  background: rgb(144 192 255 / 80%);
}

/* The active dot stretches into a bar that fills up over the card's time on stage; its fill
   running out is what advances the carousel (see main.js). */
.voices__dot.is-active {
  width: 36px;
  background: rgb(144 192 255 / 30%);
}

.voices__dot-fill {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--brand-blue);
  transform: scaleX(0);
  transform-origin: left center;
}

.voices__dot.is-active .voices__dot-fill {
  animation: voices-countdown 9s linear forwards;
}

.voices__stage:is(.is-paused, .is-offscreen) .voices__dot.is-active .voices__dot-fill {
  animation-play-state: paused;
}

@keyframes voices-countdown {
  to {
    transform: scaleX(1);
  }
}

/* No countdown, so no auto-advance: the active bar just shows as full. */
@media (prefers-reduced-motion: reduce) {
  .voices__dot.is-active .voices__dot-fill {
    animation: none;
    transform: none;
  }
}

.voices__arrows {
  display: flex;
  gap: var(--space-2xs);
}

.voices__arrow {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid rgb(255 255 255 / 24%);
  color: var(--ink-inverse);
  transition: background 200ms var(--ease-standard), border-color 200ms var(--ease-standard);
}

.voices__arrow:hover {
  background: rgb(255 255 255 / 12%);
  border-color: rgb(255 255 255 / 50%);
}

.voices__arrow svg {
  width: 18px;
  height: 18px;
}

/* ---------- Founder ---------- */
.founder-section {
  background: var(--surface-card);
}

.founder-card {
  display: grid;
  gap: 0;
  overflow: hidden;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-l);
  background: var(--surface-card);
  box-shadow: var(--shadow-card-lg);
}

@media (min-width: 800px) {
  .founder-card {
    /* Was 0.64fr with no cap: at full container width that rendered the portrait around 420px
       tall, so the section read as a poster of Juan with a caption rather than a quote with a
       face attached to it. Narrower column + a capped card shrinks him without shrinking the
       quote. `stretch` (not `center`) matters here -- centred, the now-smaller square portrait
       floated with white gaps above and below it inside the card. */
    /* The card is what shrinks (1160px wide down to 832px), not the portrait's share of it:
       keeping the cell close to square is what stops `cover` from cropping into Juan's face,
       which is how he ended up looking oversized in the first place. */
    /* A62I-642: the portrait no longer fills its column edge to edge. Stretched to the quote's
       height it was drawn about 480px tall from a 640px source -- twice that on a retina screen,
       which is what read as pixelated -- and it still dominated the card. It is now a framed
       square, small enough to stay sharp at 2x, centred against the quote. */
    grid-template-columns: 18rem 1fr;
    align-items: center;
    max-width: 52rem;
    margin-inline: auto;
  }
}

/* Same bleed as .voice-card__photo below: an img edge landing exactly on the card's rounded-clip
   boundary let Chromium's anti-aliasing there show the white card background through as a
   hairline band. Also now gives the figure its own aspect-ratio: pulling the img out of flow
   (for the absolute-positioned bleed) left it with nothing to size itself against, and the
   1:1 ratio here is what the img's own intrinsic 640x640 size used to establish implicitly. */
.founder-card__portrait {
  position: relative;
  width: 12rem;
  margin: var(--space-l) var(--space-l) 0;
  overflow: hidden;
  border-radius: var(--radius-m);
  background: var(--brand-selection);
  aspect-ratio: 1;
}

/* Beside the quote: 256px square, so 512 device pixels on a retina screen -- inside the 640px
   source instead of stretched past it. */
@media (min-width: 800px) {
  .founder-card__portrait {
    width: auto;
    margin: var(--space-l) 0 var(--space-l) var(--space-l);
  }
}

/* Same fix as .voice-card__photo-img: the "white line" is the shirt collar in the source photo
   itself, not a rendering artifact -- scaling up from the top edge crops it out of frame. */
.founder-card__portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  /* Just enough to push the collar past the bottom edge; it used to be 1.15, which on top of the
     cell's own crop made Juan noticeably larger than he was shot. */
  transform: scale(1.06);
  transform-origin: 50% 0%;
}


.founder-card__body {
  padding: var(--space-l);
}

@media (min-width: 800px) {
  .founder-card__body {
    padding: var(--space-xl);
  }
}

.founder-card .eyebrow {
  color: var(--brand-blue);
  font-size: 17px;
  letter-spacing: 0.05em;
  font-weight: 900;
}

.founder-card__body h2 {
  font-size: clamp(26px, 3vw, 38px);
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: 700;
  margin-bottom: var(--space-m);
  text-wrap: wrap;
}

.founder-card__quote {
  font-size: 16px;
  line-height: 1.7;
  color: var(--ink-secondary);
  margin-bottom: var(--space-l);
}

.founder-card__name {
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
}

.founder-card__role {
  font-size: 14px;
  color: var(--ink-secondary);
}

/* ---------- Demo band ---------- */
.demo-band {
  position: relative;
  overflow: hidden;
  background: linear-gradient(120deg, var(--brand-selection), var(--surface-flow) 70%);
  border-top: 1px solid var(--border-default);
}

/* A drifting orbit graphic behind the button -- transparent PNG, so it sits directly on the
   band's own gradient rather than needing its own backdrop. */
.demo-band::before {
  content: "";
  position: absolute;
  top: 50%;
  right: -2%;
  width: min(64rem, 88%);
  aspect-ratio: 3500 / 1550;
  transform: translateY(-50%);
  pointer-events: none;
  background: url("/assets/img/demo-band-orbits.webp") center / contain no-repeat;
  /* Full opacity plus a contrast/saturation boost -- the graphic's own lines are a light blue
     that nearly disappears against this section's similarly pale gradient at lower opacity. */
  opacity: 1;
  filter: saturate(1.6) contrast(1.25);
}

.demo-band__inner {
  position: relative;
  z-index: 1;
  display: grid;
  gap: var(--space-l);
  align-items: center;
}

@media (min-width: 900px) {
  .demo-band__inner {
    grid-template-columns: 1.4fr auto;
    gap: var(--space-xl);
  }
}

.demo-band .eyebrow {
  color: var(--brand-blue);
  letter-spacing: 0.09em;
  font-weight: 700;
}

.demo-band h2 {
  font-size: clamp(28px, 3.4vw, 42px);
  line-height: 1.18;
  letter-spacing: -0.02em;
  font-weight: 700;
  margin-bottom: var(--space-s);
  text-wrap: wrap;
}

.demo-band__text {
  font-size: 16px;
  line-height: 1.65;
  color: var(--ink-secondary);
  max-width: 44rem;
}

/* ---------- Text link ---------- */
.text-link {
  color: var(--brand-blue);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.text-link:hover {
  color: var(--brand-blue-hover);
}

/* ---------- Footer ---------- */
.footer {
  padding-block: var(--space-l);
  border-top: 1px solid rgb(255 255 255 / 10%);
}

.footer--spec {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
}

.footer__logo-link {
  display: inline-flex;
}

.footer__logo {
  height: 22px;
  width: auto;
}

.footer__inline-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-m);
  font-size: 14px;
  color: rgb(255 255 255 / 72%);
}

.footer__inline-nav a:hover {
  color: var(--ink-inverse);
}

.footer__copy {
  font-size: 13px;
  color: rgb(255 255 255 / 55%);
}

/* ---------- Legal stub pages ---------- */
.legal-page {
  padding-block: var(--space-xl);
  max-width: 46rem;
}

.legal-page__logo {
  display: inline-flex;
  margin-bottom: var(--space-l);
}

.legal-page__logo img {
  height: 26px;
  width: auto;
}

.legal-page .text-secondary {
  margin-bottom: var(--space-l);
}

/* ---------- Legal documents ----------
   privacy / terms / cookies: long, numbered, list-heavy prose. Narrow measure and generous
   leading, because these are read in full by someone checking a specific clause, not skimmed. */
.legal-doc__head {
  margin-bottom: var(--space-xl);
  padding-bottom: var(--space-m);
  border-bottom: 1px solid var(--border-default);
}

.legal-doc__meta {
  margin-top: var(--space-xs);
  color: var(--ink-subtle);
  font-size: 14px;
}

.legal-doc__lang {
  margin-top: var(--space-s);
  font-size: 14px;
}

.legal-doc {
  color: var(--ink-secondary);
  font-size: 15.5px;
  line-height: 1.7;
}

.legal-doc > * + * {
  margin-top: var(--space-s);
}

.legal-doc__h2 {
  margin-top: var(--space-xl);
  font-size: 19px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink-primary);
}

.legal-doc__h3 {
  margin-top: var(--space-l);
  font-size: 16px;
  font-weight: 600;
  color: var(--ink-primary);
}

/* First heading sits right under the rule, so it doesn't need the section gap. */
.legal-doc > .legal-doc__h2:first-child {
  margin-top: 0;
}

.legal-doc__list {
  padding-left: 1.25rem;
  list-style: disc;
}

.legal-doc__list li + li {
  margin-top: 4px;
}

.legal-doc__address {
  color: var(--ink-primary);
}

/* The cookie table is the one thing here wider than the measure; it scrolls in its own box
   rather than pushing the page sideways. */
.legal-doc__table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-m);
}

.legal-doc__table {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  font-size: 14px;
  text-align: left;
}

.legal-doc__table th,
.legal-doc__table td {
  padding: 10px 14px;
  vertical-align: top;
  border-bottom: 1px solid var(--border-default);
}

.legal-doc__table th {
  color: var(--ink-primary);
  font-weight: 600;
  white-space: nowrap;
  background: var(--surface-subtle);
}

.legal-doc__table tbody tr:last-child td {
  border-bottom: 0;
}

.legal-doc__back {
  margin-top: var(--space-xl);
}

/* ---------- 404 ---------- */
.notfound {
  display: grid;
  place-items: center;
  gap: var(--space-s);
  min-height: 70vh;
  text-align: center;
}

.notfound .code {
  font-size: var(--fs-stat);
  font-weight: 700;
  color: var(--brand-blue);
}

/* ---------- Consent banner ---------- */
.consent-banner {
  position: fixed;
  bottom: var(--space-s);
  left: var(--space-s);
  right: var(--space-s);
  z-index: 200;
  display: none;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  max-width: 40rem;
  margin-inline: auto;
  padding: var(--space-s) var(--space-m);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-m);
  background: var(--surface-card);
  box-shadow: var(--shadow-card-lg);
  font-size: 14px;
}

.consent-banner.is-visible {
  display: flex;
}

.consent-banner p {
  flex: 1 1 16rem;
  color: var(--ink-secondary);
}

.consent-banner__link {
  color: var(--brand-blue);
  text-decoration: underline;
}

.consent-banner__actions {
  display: flex;
  gap: var(--space-2xs);
}

.consent-banner__actions button {
  padding: 8px 16px;
  border-radius: var(--radius-s);
  font-size: 14px;
  font-weight: 600;
}

.consent-banner__accept {
  background: var(--brand-blue);
  color: var(--ink-inverse);
}

.consent-banner__accept:hover {
  background: var(--brand-blue-hover);
}

.consent-banner__decline {
  border: 1px solid var(--border-default);
  color: var(--ink-secondary);
}

.consent-banner__decline:hover {
  background: var(--surface-subtle);
}

/* ---------- Phone scale ----------
   The clamp() floors (hero 36px, section titles 30px) never drop on a ~390px screen,
   and a few graphics (prism wheel at 190% width, hero network pulled up by ~150px)
   were sized for a tablet. This block only kicks in below 640px. */
@media (max-width: 639px) {
  .hero {
    padding-top: 20px;
    padding-bottom: 28px;
  }

  .hero h1 {
    font-size: 26px;
    line-height: 1.18;
  }

  .hero__sub {
    font-size: 15px;
  }

  .hero__actions {
    margin-top: var(--space-m);
  }

  .hero__visual {
    margin-top: -56px;
  }

  .btn--lg {
    padding: 12px 18px;
    font-size: 15px;
  }

  .section-title,
  .prism-block h2,
  .voices__intro h2,
  .founder-card__body h2,
  .demo-band h2 {
    font-size: 22px;
  }

  .feature-card {
    padding: var(--space-m) var(--space-s) var(--space-s);
  }

  .feature-card__head .feature-card__icon {
    width: 40px;
    height: 40px;
  }

  .feature-card__head .feature-card__icon svg {
    width: 22px;
    height: 22px;
  }

  .feature-card__head .feature-card__eyebrow {
    font-size: 14px;
  }

  .feature-card__question {
    font-size: 18px;
  }

  .feature-card__body,
  .voice-card__answer {
    font-size: 15px;
  }

  .flow-node {
    grid-template-columns: 48px 1fr;
  }

  .flow-node__badge {
    width: 48px;
    height: 48px;
    box-shadow: 0 4px 12px rgb(25 118 210 / 32%), 0 0 0 4px var(--surface-flow);
  }

  .flow-node__badge svg {
    width: 22px;
    height: 22px;
  }

  .flow-track::before {
    top: 24px;
    bottom: 24px;
    left: 23px;
  }

  .prism-block {
    gap: 0.75rem;
  }

  /* The tablet rule and the base -14% margin pull the wheel up over the paragraph.
     Zeroing that margin and the wheel's own padding leaves a short gap, not a band. */
  .prism-wheel {
    top: 0;
    margin-top: 0;
    margin-bottom: 0;
    margin-inline: calc(var(--space-m) * -1);
    padding: 0;
  }

  .prism-wheel::after {
    inset: -6%;
    filter: blur(36px);
  }

  .prism-wheel img {
    width: 118%;
  }

  /* The 3:2 frame plus the 1.15 top-anchored zoom cropped these square portraits
     at the neck. A square frame shows the head and shoulders the photo was drawn with. */
  .voice-card__photo {
    aspect-ratio: 1 / 1;
  }

  .voice-card__photo-img {
    object-position: center 35%;
    transform: scale(1.05);
    transform-origin: center 35%;
  }

  .voice-card__body {
    padding: var(--space-m);
  }

  .voice-card__quote {
    font-size: 16px;
  }

  .founder-card__portrait {
    width: 8.5rem;
    margin: var(--space-m) var(--space-m) 0;
  }

  .founder-card__body {
    padding: var(--space-m);
  }

  .founder-card .eyebrow {
    font-size: 14px;
  }
}

/* ---------- Book a demo dialog ---------- */
.btn--outline {
  background: var(--surface-card);
  border: 1px solid var(--brand-blue);
  color: var(--brand-blue);
}

.btn--outline:hover {
  background: var(--brand-selection);
}

.btn[disabled] {
  opacity: 0.6;
  cursor: progress;
}

.demo-dialog {
  /* The base reset zeroes margins; a modal <dialog> centres itself with margin: auto. */
  margin: auto;
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  padding: 0;
  border: 0;
  border-radius: var(--radius-l);
  background: var(--surface-card);
  color: var(--ink);
  box-shadow: 0 24px 64px rgb(10 28 43 / 28%);
  overflow: auto;
}

.demo-dialog.has-calendar {
  width: min(960px, calc(100vw - 32px));
}

.demo-dialog::backdrop {
  background: rgb(10 28 43 / 55%);
}

.demo-dialog__panel {
  position: relative;
  padding: var(--space-l);
}

.demo-dialog__close {
  position: absolute;
  top: var(--space-xs);
  right: var(--space-xs);
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink-secondary);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
}

.demo-dialog__close:hover {
  background: var(--surface-subtle);
  color: var(--ink);
}

.demo-dialog__step {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}

.demo-dialog__step[hidden] {
  display: none;
}

.demo-dialog__step .eyebrow {
  color: var(--brand-blue);
}

.demo-dialog__title {
  margin: 0;
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
}

.demo-dialog__text {
  margin: 0;
  color: var(--ink-secondary);
}

.demo-dialog__calendar iframe {
  display: block;
  width: 100%;
  min-height: 560px;
  border: 0;
  border-radius: var(--radius-m);
  background: var(--surface-subtle);
}

.demo-form__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-s);
}

.demo-form__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  font-size: 14px;
  font-weight: 600;
}

.demo-form__field--wide {
  grid-column: 1 / -1;
}

.demo-form__field em {
  color: var(--ink-subtle);
  font-style: normal;
  font-weight: 400;
}

.demo-form__field input,
.demo-form__field textarea {
  width: 100%;
  padding: 11px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-s);
  background: var(--surface-card);
  color: var(--ink);
  font: inherit;
  font-weight: 400;
  font-size: 15px;
}

.demo-form__field textarea {
  resize: vertical;
}

.demo-form__field input:focus,
.demo-form__field textarea:focus {
  outline: 2px solid var(--brand-blue);
  outline-offset: 1px;
  border-color: var(--brand-blue);
}

.demo-form__field input[aria-invalid="true"] {
  border-color: var(--error);
}

/* Honeypot: off-screen for people, still in the form for bots. */
.demo-form__trap {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.demo-form__error {
  margin: 0;
  color: var(--error);
  font-size: 14px;
}

.demo-callback {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-top: var(--space-2xs);
  padding: var(--space-m);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-m);
  background: var(--surface-subtle);
}

.demo-callback__title {
  margin: 0;
  font-weight: 700;
  font-size: 17px;
}

.demo-callback__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}

.demo-dialog__done {
  align-items: flex-start;
  padding: var(--space-s) 0;
}

@media (max-width: 600px) {
  .demo-dialog__panel {
    padding: var(--space-m) var(--space-s);
  }

  .demo-form__grid {
    grid-template-columns: 1fr;
  }

  .demo-callback {
    padding: var(--space-s);
  }
}
